github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/server/directeurs/groupes.go (about)

     1  package directeurs
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    10  	"github.com/benoitkugler/goACVE/server/core/rawdata/composites"
    11  	"github.com/benoitkugler/goACVE/server/shared"
    12  )
    13  
    14  var defaultGroupeFrom = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
    15  
    16  const defaultPlageLength = 2 * 365 * 24 * time.Hour // 2ans
    17  
    18  // charge et renvoie les groupes du séjour, ainsi que les assocation de contraintes
    19  func loadGroupes(db rd.DB, idCamp int64) (rd.Groupes, rd.CampContraintes, rd.GroupeContraintes, error) {
    20  	groupes, err := rd.SelectGroupesByIdCamps(db, idCamp)
    21  	if err != nil {
    22  		return nil, nil, nil, err
    23  	}
    24  	groupeContraintes, err := rd.SelectGroupeContraintesByIdGroupes(db, groupes.Ids()...)
    25  	if err != nil {
    26  		return nil, nil, nil, err
    27  	}
    28  	campContraintes, err := rd.SelectCampContraintesByIdCamps(db, idCamp)
    29  	if err != nil {
    30  		return nil, nil, nil, err
    31  	}
    32  	return groupes, campContraintes, groupeContraintes, nil
    33  }
    34  
    35  func (rc DriverCampComplet) createGroupe(newGroupe rd.Groupe) (rd.Groupe, error) {
    36  	newGroupe.Nom = newGroupe.Nom.TrimSpace()
    37  	if newGroupe.Nom == "" {
    38  		return rd.Groupe{}, errors.New("Merci de nommer le groupe à créer.")
    39  	}
    40  
    41  	groupes, _, _, err := loadGroupes(rc.DB, rc.camp.Id)
    42  	if err != nil {
    43  		return rd.Groupe{}, err
    44  	}
    45  
    46  	// on crée vraiment un nouveau groupe, en allongant la plage de date
    47  	var lastDate time.Time
    48  	for _, g := range groupes {
    49  		if g.Plage.To.Time().After(lastDate) {
    50  			lastDate = g.Plage.To.Time()
    51  		}
    52  	}
    53  	if lastDate.IsZero() {
    54  		lastDate = defaultGroupeFrom
    55  	}
    56  	lastDate = lastDate.Add(24 * time.Hour)
    57  	newGroupe.IdCamp = rc.camp.Id
    58  	newGroupe.Plage = rd.Plage{From: rd.Date(lastDate), To: rd.Date(lastDate.Add(defaultPlageLength))} // 2 ans
    59  
    60  	return newGroupe.Insert(rc.DB)
    61  }
    62  
    63  func (rc DriverCampComplet) updateGroupe(groupe rd.Groupe) (rd.Groupe, error) {
    64  	if groupe.IdCamp != rc.camp.Id {
    65  		return rd.Groupe{}, fmt.Errorf("Le camp associé à ce groupe n'est pas valide (%d)", groupe.IdCamp)
    66  	}
    67  
    68  	// un groupe modifié par l'utilisateur a forcément un nom
    69  	groupe.Nom = groupe.Nom.TrimSpace()
    70  	if groupe.Nom == "" {
    71  		return rd.Groupe{}, errors.New("Merci de nommer ce groupe.")
    72  	}
    73  
    74  	return groupe.Update(rc.DB)
    75  }
    76  
    77  func (rc DriverCampComplet) checkGroupe(idGroupe int64) error {
    78  	groupes, _, _, err := loadGroupes(rc.DB, rc.camp.Id)
    79  	if err != nil {
    80  		return shared.FormatErr("Impossible de vérifier l'origine du groupe.", err)
    81  	}
    82  	if _, inSejour := groupes[idGroupe]; !inSejour {
    83  		return fmt.Errorf("Ce groupe (id %d) n'est pas lié au séjour !", idGroupe)
    84  	}
    85  	return nil
    86  }
    87  
    88  func (rc DriverCampComplet) deleteGroupe(idGroupe int64) error {
    89  	if err := rc.checkGroupe(idGroupe); err != nil {
    90  		return err
    91  	}
    92  
    93  	tx, err := rc.DB.Begin()
    94  	if err != nil {
    95  		return err
    96  	}
    97  	// on supprime les liens
    98  	_, err = rd.DeleteGroupeContraintesByIdGroupes(tx, idGroupe)
    99  	if err != nil {
   100  		return shared.Rollback(tx, err)
   101  	}
   102  	_, err = rd.DeleteGroupeParticipantsByIdGroupes(tx, idGroupe)
   103  	if err != nil {
   104  		return shared.Rollback(tx, err)
   105  	}
   106  
   107  	_, err = rd.DeleteGroupeById(tx, idGroupe)
   108  	if err != nil {
   109  		return shared.Rollback(tx, err)
   110  	}
   111  	err = tx.Commit()
   112  	return err
   113  }
   114  
   115  // met à jour les appartenances de chaque groupe , en une seule requête
   116  // ne commit pas , ne rollback pas;
   117  // si la liste est vide, ne fait rien
   118  func updateIdGroupes(tx *sql.Tx, participants []groupeUpdate, idCamp int64) error {
   119  	if len(participants) == 0 {
   120  		return nil
   121  	}
   122  
   123  	var (
   124  		idsParticipants rd.Ids
   125  		gps             rd.GroupeParticipants
   126  	)
   127  	for _, part := range participants {
   128  		idsParticipants = append(idsParticipants, part.idParticipant)
   129  		if part.idGroupe.Valid {
   130  			gps = append(gps, rd.GroupeParticipant{
   131  				IdCamp:        idCamp,
   132  				Manuel:        false,
   133  				IdParticipant: part.idParticipant,
   134  				IdGroupe:      part.idGroupe.Int64,
   135  			})
   136  		}
   137  	}
   138  
   139  	// on commencer par supprimer les affectations courantes ...
   140  	_, err := rd.DeleteGroupeParticipantsByIdParticipants(tx, idsParticipants...)
   141  	if err != nil {
   142  		return err
   143  	}
   144  
   145  	// ... puis on ajoute les nouvelles
   146  	err = rd.InsertManyGroupeParticipants(tx, gps...)
   147  	return err
   148  }
   149  
   150  type groupeUpdate struct {
   151  	idParticipant int64
   152  	idGroupe      rd.OptionnalId
   153  }
   154  
   155  // updatePlages modifie les plages des groupes donnés et met à jour les participants
   156  // Si `erase` vaut `false`, les participants en mode manuel ne sont pas modifiés
   157  // Renvoie le nombre de participants modifiés.
   158  func (rc DriverCampComplet) updatePlages(groupes rd.Groupes, erase bool) (int, error) {
   159  	tx, err := rc.DB.Begin()
   160  	if err != nil {
   161  		return 0, err
   162  	}
   163  	// on commence par modifier les plages
   164  	for _, groupe := range groupes {
   165  		_, err := tx.Exec("UPDATE groupes SET plage = $1 WHERE id = $2", groupe.Plage, groupe.Id)
   166  		if err != nil {
   167  			return 0, shared.Rollback(tx, err)
   168  		}
   169  	}
   170  
   171  	// puis on s'assure d'avoir tous les groupes (mis à jour)
   172  	groupes, _, _, err = loadGroupes(tx, rc.camp.Id)
   173  	if err != nil {
   174  		return 0, shared.Rollback(tx, err)
   175  	}
   176  	// rc.camp.Base.Groupes = groupes
   177  	camp := composites.NewCampGroupes(rc.camp.RawData(), groupes)
   178  
   179  	// on calcule les nouvelles affectations
   180  
   181  	// on commence par charger les participants propres du camp ...
   182  	participants, err := rd.SelectParticipantsByIdCamps(tx, rc.camp.Id)
   183  	if err != nil {
   184  		return 0, shared.Rollback(tx, err)
   185  	}
   186  	// ... les affectations courantes ...
   187  	tmp, err := rd.SelectGroupeParticipantsByIdGroupes(tx, groupes.Ids()...)
   188  	if err != nil {
   189  		return 0, shared.Rollback(tx, err)
   190  	}
   191  
   192  	participantGroupe := tmp.ByIdParticipant()
   193  	// ... et les personnes pour les dates de naissance
   194  	rows, err := tx.Query(`SELECT personnes.* FROM personnes 
   195  		JOIN participants ON participants.id_personne = personnes.id
   196  		WHERE participants.id = ANY($1)`, participants.Ids().AsSQL())
   197  	if err != nil {
   198  		return 0, shared.Rollback(tx, err)
   199  	}
   200  	personnes, err := rd.ScanPersonnes(rows)
   201  	if err != nil {
   202  		return 0, shared.Rollback(tx, err)
   203  	}
   204  
   205  	var modifications []groupeUpdate
   206  	for _, part := range participants {
   207  		currentGroupe, has := participantGroupe[part.Id]
   208  		if !erase && has && currentGroupe.Manuel {
   209  			// le participant a déjà un groupe attribué manuellement :
   210  			// on n'y touche pas
   211  			continue
   212  		}
   213  		var currentIdGroupe, targetIdGroupe rd.OptionnalId
   214  		if has {
   215  			currentIdGroupe = rd.NewOptionnalId(currentGroupe.IdGroupe)
   216  		}
   217  
   218  		groupe, found := camp.TrouveGroupe(personnes[part.IdPersonne].DateNaissance)
   219  		if found {
   220  			targetIdGroupe = rd.NewOptionnalId(groupe.Id)
   221  		}
   222  		if currentIdGroupe == targetIdGroupe {
   223  			// le participant est déjà dans le bon groupe (au sens large):
   224  			// on ne fait rien. C'est plus logique pour l'utilisateur
   225  			continue
   226  		}
   227  		modifications = append(modifications, groupeUpdate{idParticipant: part.Id, idGroupe: targetIdGroupe})
   228  	}
   229  
   230  	err = updateIdGroupes(tx, modifications, camp.Id)
   231  	if err != nil {
   232  		return 0, shared.Rollback(tx, err)
   233  	}
   234  	err = tx.Commit()
   235  	return len(modifications), err
   236  }