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

     1  package directeurs
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"path"
     8  	"sort"
     9  	"time"
    10  
    11  	dm "github.com/benoitkugler/goACVE/server/core/datamodel"
    12  	cd "github.com/benoitkugler/goACVE/server/core/documents"
    13  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    14  	"github.com/benoitkugler/goACVE/server/core/utils/pdf"
    15  	"github.com/benoitkugler/goACVE/server/core/utils/table"
    16  	"github.com/benoitkugler/goACVE/server/documents"
    17  	"github.com/benoitkugler/goACVE/server/shared"
    18  	"github.com/labstack/echo"
    19  )
    20  
    21  // ResumeMessage simplifie l'affichage des messages
    22  // pour le directeur.
    23  type ResumeMessage struct {
    24  	Label   string         `json:"label"`
    25  	Created time.Time      `json:"created"`
    26  	Contenu string         `json:"contenu"`
    27  	Kind    rd.MessageKind `json:"kind"`
    28  }
    29  
    30  type Responsable struct {
    31  	Valide            bool            `json:"valide"`
    32  	Id                int64           `json:"id"`
    33  	Nom               string          `json:"nom"`
    34  	Prenom            string          `json:"prenom"`
    35  	Mail              string          `json:"mail"`
    36  	MailsCopies       []string        `json:"mails_copies"`
    37  	Tels              string          `json:"tels"`
    38  	Adresse           string          `json:"adresse"`
    39  	CodePostal        string          `json:"code_postal"`
    40  	Ville             string          `json:"ville"`
    41  	PaiementComplet   rd.Completion   `json:"paiement_complet"`
    42  	Messages          []ResumeMessage `json:"messages"`
    43  	InscriptionValide bool            `json:"inscription_valide"`
    44  }
    45  
    46  type InscritWritable struct {
    47  	IdGroupe rd.OptionnalId        `json:"id_groupe"`
    48  	Options  rd.OptionsParticipant `json:"options"`
    49  	Mail     string                `json:"mail"`
    50  }
    51  
    52  func (i InscritWritable) ToPersonneParticipant(personne *rd.BasePersonne, participant *rd.Participant) {
    53  	participant.Options = i.Options
    54  
    55  	personne.Mail = rd.String(i.Mail)
    56  }
    57  
    58  type Inscrit struct {
    59  	InscritWritable
    60  
    61  	Id int64 `json:"id"`
    62  
    63  	IsAttente       bool `json:"is_attente"`
    64  	HasAnniversaire bool `json:"has_anniversaire"`
    65  	AgeDebutCamp    int  `json:"age_debut_camp"`
    66  
    67  	Responsable Responsable `json:"responsable"`
    68  
    69  	Nom           string  `json:"nom"`
    70  	Prenom        string  `json:"prenom"`
    71  	Sexe          rd.Sexe `json:"sexe"`
    72  	DateNaissance rd.Date `json:"date_naissance"`
    73  
    74  	// Fiche sanitaire et vaccins
    75  	FicheSanitaire           rd.FicheSanitaire          `json:"fiche_sanitaire"`
    76  	Vaccins                  []documents.PublicDocument `json:"vaccins"`
    77  	IsFicheSanitaireUpToDate rd.OptionnalBool           `json:"is_fiche_sanitaire_up_to_date"`
    78  	LienFicheSanitaire       string                     `json:"lien_fiche_sanitaire"`
    79  
    80  	// Pour les participants simples
    81  	Info rd.String `json:"info"`
    82  }
    83  
    84  type optionsExportExcel struct {
    85  	bus         rd.Bus
    86  	triGroupe   bool
    87  	withAttente bool
    88  	showColors  bool
    89  	simple      bool
    90  }
    91  
    92  func newOptionsExportExcel(c echo.Context) optionsExportExcel {
    93  	return optionsExportExcel{
    94  		bus:         rd.Bus(c.QueryParam("bus")),
    95  		triGroupe:   c.QueryParam("with_groupe") != "",
    96  		withAttente: c.QueryParam("with_attente") != "",
    97  		showColors:  c.QueryParam("show_colors") != "",
    98  		simple:      c.QueryParam("simple") != "",
    99  	}
   100  }
   101  
   102  // charge les infos de base et met à jour `base`
   103  // (fonctionnalité partagée avec joomeo)
   104  func (rc DriverCampComplet) loadInscritsResponsables() error {
   105  	groupes, campContraintes, groupesContraintes, err := loadGroupes(rc.DB, rc.camp.Id)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	rows, err := rc.DB.Query(`SELECT factures.* FROM factures
   111  		JOIN participants ON participants.id_facture = factures.id 	
   112  		WHERE participants.id_camp = $1`, rc.camp.Id)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	factures, err := rd.ScanFactures(rows)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	// on charge les participants : concernés directement ou via un dossier
   122  	rows, err = rc.DB.Query("SELECT * FROM participants WHERE id_camp = $1 OR id_facture = ANY($2)", rc.camp.Id, factures.Ids().AsSQL())
   123  	if err != nil {
   124  		return err
   125  	}
   126  	participants, err := rd.ScanParticipants(rows)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	// les personnes sont les responsables et les participants (même indirects)
   132  	rows, err = rc.DB.Query(`SELECT * FROM personnes WHERE 
   133  		id = ANY(SELECT id_personne FROM factures WHERE id = ANY($1)) OR
   134  		id = ANY(SELECT id_personne FROM participants WHERE id = ANY($2))`,
   135  		factures.Ids().AsSQL(), participants.Ids().AsSQL())
   136  	if err != nil {
   137  		return err
   138  	}
   139  	personnes, err := rd.ScanPersonnes(rows)
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	rc.camp.Base.Groupes = groupes
   145  	rc.camp.Base.GroupeContraintes = groupesContraintes.ByIdGroupe()
   146  	rc.camp.Base.CampContraintes = campContraintes.ByIdCamp()
   147  	rc.camp.Base.Personnes = personnes
   148  	rc.camp.Base.Participants = participants
   149  	rc.camp.Base.Factures = factures
   150  	return nil
   151  }
   152  
   153  // renvoie les données nécessaires aux inscrits (dont informations financières et documents)
   154  func (d DriverCampComplet) loadDataInscrits() error {
   155  	err := d.loadInscritsResponsables()
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	aides, err := rd.SelectAidesByIdParticipants(d.DB, d.camp.Base.Participants.Ids()...)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	structures, err := rd.SelectAllStructureaides(d.DB) // on peut se permettre de tout renvoyer, la table est petite.
   166  	if err != nil {
   167  		return err
   168  	}
   169  
   170  	paiements, err := rd.SelectPaiementsByIdFactures(d.DB, d.camp.Base.Factures.Ids()...)
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	docs, err := d.LoadDocuments(d.camp.Base.Personnes.Ids())
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	if err = d.loadMessages(); err != nil {
   181  		return err
   182  	}
   183  
   184  	// répartitions des groupes
   185  	groupeParticipants, err := rd.SelectGroupeParticipantsByIdCamps(d.DB, d.camp.Id)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	// ajout des animateurs de référence
   191  	participantEquipiers, err := rd.SelectParticipantEquipiersByIdParticipants(d.DB, d.camp.Base.Participants.Ids()...)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	d.camp.Base.Contraintes = docs.Contraintes
   197  	d.camp.Base.Paiements = paiements
   198  	d.camp.Base.Aides = aides
   199  	d.camp.Base.Structureaides = structures
   200  	d.camp.Base.Documents = docs.Documents
   201  	d.camp.Base.DocumentPersonnes = docs.Liens
   202  	d.camp.Base.ParticipantGroupe = groupeParticipants.ByIdParticipant()
   203  	d.camp.Base.ParticipantEquipier = participantEquipiers.ByIdParticipant()
   204  	return nil
   205  }
   206  
   207  func (d DriverCampComplet) loadMessages() error {
   208  	messages, err := rd.SelectMessagesByIdFactures(d.DB, d.camp.Base.Factures.Ids()...)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	mAttestations, err := rd.SelectMessageAttestationsByIdMessages(d.DB, messages.Ids()...)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	mDocuments, err := rd.SelectMessageDocumentsByIdMessages(d.DB, messages.Ids()...)
   218  	if err != nil {
   219  		return err
   220  	}
   221  	mMessages, err := rd.SelectMessageMessagesByIdMessages(d.DB, messages.Ids()...)
   222  	if err != nil {
   223  		return err
   224  	}
   225  	mSondages, err := rd.SelectMessageSondagesByIdMessages(d.DB, messages.Ids()...)
   226  	if err != nil {
   227  		return err
   228  	}
   229  	mPlaceliberes, err := rd.SelectMessagePlaceliberesByIdMessages(d.DB, messages.Ids()...)
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	d.camp.Base.Messages = messages
   235  	d.camp.Base.MessageAttestations = mAttestations.ByIdMessage()
   236  	d.camp.Base.MessageDocuments = mDocuments.ByIdMessage()
   237  	d.camp.Base.MessageMessages = mMessages.ByIdMessage()
   238  	d.camp.Base.MessageSondages = mSondages.ByIdMessage()
   239  	d.camp.Base.MessagePlaceliberes = mPlaceliberes.ByIdMessage()
   240  	return nil
   241  }
   242  
   243  // lien vers l'espace perso, avec un tag pour éviter de marquer la connexion
   244  // a maintenir synchronisé avec le frontend espace_perso
   245  func getLienFicheSanitaire(host string, fac rd.Facture, nomPrenom string) string {
   246  	pathFs := path.Join(fac.UrlEspacePerso("espace_perso"), "fiches_sanitaires")
   247  	return shared.BuildUrl(host, pathFs, map[string]string{
   248  		"directeur":  "ok",
   249  		"nom_prenom": nomPrenom,
   250  	})
   251  }
   252  
   253  func (d DriverCampComplet) getInscrits(host string) ([]Inscrit, error) {
   254  	insc := append(d.camp.GetInscrits(nil), d.camp.GetAttente(nil)...)
   255  	out := make([]Inscrit, len(insc))
   256  	cache := d.camp.Base.ResoudDocumentsPersonnes()
   257  	cache2, cache3 := d.camp.Base.ResoudMessages(), d.camp.Base.ResoudPaiements()
   258  	_, cache4 := d.camp.Base.ResoudParticipants()
   259  
   260  	for index, part := range insc {
   261  		var (
   262  			respo              Responsable
   263  			lienFicheSanitaire string
   264  		)
   265  		rawPart, pers := part.RawData(), part.GetPersonne()
   266  
   267  		if fac, has := part.GetFacture(); has {
   268  			persResp := fac.GetPersonne().AsItem(0)
   269  			respo.Valide = true
   270  			respo.Id = fac.Id
   271  			respo.Nom = persResp.Fields.Data(dm.PersonneNom).String()
   272  			respo.Prenom = persResp.Fields.Data(dm.PersonnePrenom).String()
   273  			respo.Mail = persResp.Fields.Data(dm.PersonneMail).String()
   274  			respo.MailsCopies = fac.RawData().CopiesMails
   275  			respo.Tels = persResp.Fields.Data(dm.PersonneTels).String()
   276  			respo.Adresse = persResp.Fields.Data(dm.PersonneAdresse).String()
   277  			respo.CodePostal = persResp.Fields.Data(dm.PersonneCodePostal).String()
   278  			respo.Ville = persResp.Fields.Data(dm.PersonneVille).String()
   279  			respo.PaiementComplet = fac.EtatFinancier(dm.CacheEtatFinancier{}, false).StatutPaiement()
   280  			respo.InscriptionValide = fac.RawData().IsValidated
   281  			lienFicheSanitaire = getLienFicheSanitaire(host, fac.RawData(), pers.RawData().NomPrenom().String())
   282  
   283  			for _, message := range fac.GetEtat(cache2, cache3).PseudoMessages(dm.POVCentre) {
   284  				str, _ := message.Contenu.(dm.ContenuPerso) // simple
   285  				respo.Messages = append(respo.Messages, ResumeMessage{
   286  					Kind:    message.Kind,
   287  					Label:   message.Label,
   288  					Contenu: string(str), // souvent zero
   289  					Created: message.Created.Time(),
   290  				})
   291  			}
   292  		}
   293  
   294  		var vaccins []documents.PublicDocument
   295  		for _, doc := range pers.GetDocuments(cache) {
   296  			lien := doc.GetContrainte()
   297  			if lien.Builtin == rd.CVaccin {
   298  				publicDoc, err := documents.PublieDocument(d.Signing, host, doc.RawData())
   299  				if err != nil {
   300  					return nil, err
   301  				}
   302  				vaccins = append(vaccins, publicDoc)
   303  			}
   304  		}
   305  		partFields := part.AsItem(cache4).Fields
   306  		groupe, hasGroupe := part.GetGroupe()
   307  		var idGroupe rd.OptionnalId
   308  		if hasGroupe {
   309  			idGroupe = rd.NewOptionnalId(groupe.Id)
   310  		}
   311  		out[index] = Inscrit{
   312  			Id: part.Id,
   313  			InscritWritable: InscritWritable{
   314  				Options:  rawPart.Options,
   315  				Mail:     pers.RawData().Mail.String(),
   316  				IdGroupe: idGroupe,
   317  			},
   318  			IsAttente:                !rawPart.ListeAttente.IsInscrit(),
   319  			AgeDebutCamp:             part.AgeDebutCamp().Age(),
   320  			HasAnniversaire:          part.HasAnniversaire(),
   321  			Nom:                      partFields.Data(dm.PersonneNom).String(),
   322  			Prenom:                   partFields.Data(dm.PersonnePrenom).String(),
   323  			Sexe:                     pers.RawData().Sexe,
   324  			DateNaissance:            pers.RawData().DateNaissance,
   325  			FicheSanitaire:           pers.RawData().FicheSanitaire,
   326  			Vaccins:                  vaccins,
   327  			Responsable:              respo,
   328  			IsFicheSanitaireUpToDate: part.IsFicheSanitaireUpToDate(),
   329  			LienFicheSanitaire:       lienFicheSanitaire,
   330  		}
   331  	}
   332  	sort.Slice(out, func(i, j int) bool {
   333  		return out[i].Nom+out[i].Prenom < out[j].Nom+out[j].Prenom
   334  	})
   335  	return out, nil
   336  }
   337  
   338  // -------------------- Fiches sanitaires  ----------------------------------
   339  
   340  // renvoie les données nécessaires
   341  func fetchFicheSanitaire(part dm.AccesParticipant) (personne, responsable rd.Personne, nom string, err error) {
   342  	personne = part.GetPersonne().RawData()
   343  	fac, has := part.GetFacture()
   344  	if !has {
   345  		err = errors.New(`Le participant n'est pas suivi par un dossier. 
   346  		La fiche sanitaire ne peut pas être modifiée dans ce cas, cette situation ne devrait pas donc se produire.
   347  		Pouvez-vous le signaler (au centre d'inscriptions ou par mail) ?`)
   348  		return
   349  	}
   350  	responsable = fac.GetPersonne().RawData()
   351  	nom = reduitNom(personne.NomPrenom().String())
   352  	return
   353  }
   354  
   355  func (d DriverCampComplet) downloadFicheSanitaire(idParticipant int64) (*bytes.Buffer, string, error) {
   356  	pers, resp, s, err := fetchFicheSanitaire(d.camp.Base.NewParticipant(idParticipant))
   357  	s = fmt.Sprintf("fiche_sanitaire_%s.pdf", s)
   358  	out := new(bytes.Buffer)
   359  	if err == nil {
   360  		err = pdf.FicheSanitaire(pers, resp, out)
   361  	}
   362  	return out, s, err
   363  }
   364  
   365  func filterPartsFicheSanitaire(camp dm.AccesCamp, onlyMineurs, triGroupe bool) []dm.AccesParticipant {
   366  	insc, att := camp.GetInscrits(nil), camp.GetAttente(nil)
   367  	dm.TriParticipants(insc, triGroupe)
   368  	dm.TriParticipants(att, triGroupe)
   369  	var final []dm.AccesParticipant
   370  	for _, part := range append(insc, att...) {
   371  		if onlyMineurs && part.AgeDebutCamp().Age() >= 18 {
   372  			continue
   373  		}
   374  		if part.GetPersonne().RawData().FicheSanitaire.IsNone() {
   375  			continue
   376  		}
   377  		final = append(final, part)
   378  	}
   379  	return final
   380  }
   381  
   382  // downloadFichesSanitaires regroupe les fiches sanitaires et les vaccins
   383  // (contrairement à `downloadFicheSanitaire`)
   384  func (d DriverCampComplet) downloadFichesSanitaires(onlyMineurs bool) (*bytes.Buffer, error) {
   385  	insc := filterPartsFicheSanitaire(d.camp, onlyMineurs, false)
   386  
   387  	archive := cd.NewArchiveZip()
   388  
   389  	for _, part := range insc {
   390  		pers, resp, proprio, err := fetchFicheSanitaire(part)
   391  		if err != nil {
   392  			return nil, err
   393  		}
   394  		content := new(bytes.Buffer)
   395  		err = pdf.FicheSanitaire(pers, resp, content)
   396  		if err != nil {
   397  			return nil, err
   398  		}
   399  		filename := fmt.Sprintf("%s_fiche_sanitaire.pdf", proprio)
   400  		archive.AddFile(filename, content)
   401  		var idsVaccins []int64
   402  		acPers := part.GetPersonne()
   403  		for _, doc := range acPers.GetDocuments(nil) {
   404  			if doc.GetContrainte().Builtin == rd.CVaccin {
   405  				idsVaccins = append(idsVaccins, doc.Id)
   406  			}
   407  		}
   408  		archive.Renamer = func(s string) string {
   409  			return fmt.Sprintf("%s_vaccin_%s", proprio, s)
   410  		}
   411  		if err = documents.LoadDocsAndAdd(d.DB, idsVaccins, archive, nil); err != nil {
   412  			return nil, err
   413  		}
   414  	}
   415  	return archive.Close()
   416  }
   417  
   418  func (d DriverCampComplet) downloadFichesSanitairesOneDocument(onlyMineurs, triGroupe bool) (*bytes.Buffer, error) {
   419  	insc := filterPartsFicheSanitaire(d.camp, onlyMineurs, triGroupe)
   420  	var personnes, responsables []rd.Personne
   421  	for _, part := range insc {
   422  		pers, resp, _, err := fetchFicheSanitaire(part)
   423  		if err != nil {
   424  			return nil, err
   425  		}
   426  		personnes, responsables = append(personnes, pers), append(responsables, resp)
   427  	}
   428  	buf := new(bytes.Buffer)
   429  	err := pdf.FicheSanitaires(personnes, responsables, buf)
   430  	return buf, err
   431  }
   432  
   433  func (d DriverCampComplet) modifieInscrit(modif InscritWritable, id int64) error {
   434  	if modif.IdGroupe.Valid && d.camp.Base.Groupes[modif.IdGroupe.Int64].IdCamp != d.camp.Id {
   435  		return fmt.Errorf("Le groupe (%d) n'est pas lié au séjour", modif.IdGroupe.Int64)
   436  	}
   437  
   438  	tx, err := d.DB.Begin()
   439  	if err != nil {
   440  		return err
   441  	}
   442  	participant, err := rd.SelectParticipant(tx, id)
   443  	if err != nil {
   444  		return shared.Rollback(tx, err)
   445  	}
   446  	personne, err := rd.SelectPersonne(tx, participant.IdPersonne)
   447  	if err != nil {
   448  		return shared.Rollback(tx, err)
   449  	}
   450  	modif.ToPersonneParticipant(&personne.BasePersonne, &participant)
   451  	participant, err = participant.Update(tx)
   452  	if err != nil {
   453  		return shared.Rollback(tx, err)
   454  	}
   455  	personne, err = personne.Update(tx)
   456  	if err != nil {
   457  		return shared.Rollback(tx, err)
   458  	}
   459  	// mise à jour locale
   460  	d.camp.Base.Participants[participant.Id] = participant
   461  	d.camp.Base.Personnes[personne.Id] = personne
   462  
   463  	// modification du groupe
   464  
   465  	gp, hasGroupe, err := rd.SelectGroupeParticipantByIdParticipant(tx, participant.Id)
   466  	if err != nil {
   467  		return shared.Rollback(tx, err)
   468  	}
   469  	// dans le cas ou on ne change pas de groupe, on ne supprime pas l'animateur de référence éventuel
   470  	noChange := hasGroupe && modif.IdGroupe.Valid && (gp.IdGroupe == modif.IdGroupe.Int64)
   471  
   472  	if !noChange { // on supprime le groupe et l'animateur de référence
   473  		_, err = rd.DeleteParticipantEquipiersByIdParticipants(tx, participant.Id)
   474  		if err != nil {
   475  			return shared.Rollback(tx, err)
   476  		}
   477  		_, err = rd.DeleteGroupeParticipantsByIdParticipants(tx, participant.Id)
   478  		if err != nil {
   479  			return shared.Rollback(tx, err)
   480  		}
   481  		delete(d.camp.Base.ParticipantGroupe, participant.Id)
   482  	}
   483  
   484  	if idGroupe := modif.IdGroupe; !noChange && idGroupe.Valid { // on ajoute un lien
   485  		// le mode manuel est activé si la modification est différente du groupe actuel
   486  		// ou que le mode est déjà manuel
   487  		manuel := gp.Manuel || gp.IdGroupe != idGroupe.Int64
   488  		gp = rd.GroupeParticipant{IdCamp: participant.IdCamp, IdParticipant: participant.Id, IdGroupe: idGroupe.Int64, Manuel: manuel}
   489  		err = rd.InsertManyGroupeParticipants(tx, gp)
   490  		if err != nil {
   491  			return shared.Rollback(tx, err)
   492  		}
   493  		d.camp.Base.ParticipantGroupe[participant.Id] = gp // mise à jour locale
   494  	}
   495  
   496  	err = tx.Commit()
   497  	return err
   498  }
   499  
   500  func (d DriverCampComplet) exportListeInscrits(options optionsExportExcel) (*bytes.Buffer, error) {
   501  	// les animateurs nécéssitent les équipiers
   502  	personnes, equipiers, err := d.scanDataEquipiers()
   503  	if err != nil {
   504  		return nil, err
   505  	}
   506  	// fusion avec existant
   507  	for _, personne := range personnes {
   508  		d.camp.Base.Personnes[personne.Id] = personne
   509  	}
   510  	d.camp.Base.Equipiers = equipiers
   511  
   512  	inscrits, attente := d.camp.GetListes(options.triGroupe, options.bus, true, false)
   513  	if !options.withAttente {
   514  		attente = nil
   515  	}
   516  	hideColumns := func(header []rd.Header, ignoreFields ...rd.Field) []rd.Header {
   517  		var out []rd.Header
   518  		ignore := map[rd.Field]bool{}
   519  		for _, field := range ignoreFields {
   520  			ignore[field] = true
   521  		}
   522  		for _, h := range header {
   523  			if ignore[h.Field] {
   524  				continue
   525  			}
   526  			out = append(out, h)
   527  		}
   528  		return out
   529  	}
   530  
   531  	// choisit un mode simplifié
   532  	headerInscrit := HeaderExportInscrits
   533  	if options.simple {
   534  		headerInscrit = HeaderExportInscritsSimple
   535  	}
   536  	headerResponsable := HeaderExportResponsables
   537  	if options.simple {
   538  		headerResponsable = HeaderExportResponsablesSimple
   539  	}
   540  
   541  	// enlève les champs inutile
   542  	var ignoreFields []rd.Field
   543  	if showBus := d.camp.RawData().Options.Bus.Actif; !showBus {
   544  		ignoreFields = append(ignoreFields, dm.ParticipantBus)
   545  	}
   546  	if showSki := d.camp.RawData().Options.MaterielSki.Actif; !showSki {
   547  		ignoreFields = append(ignoreFields, dm.ParticipantMaterielSki, dm.ParticipantMaterielSkiType)
   548  	}
   549  	if showGroupes := len(d.camp.GetGroupes()) > 0; !showGroupes {
   550  		ignoreFields = append(ignoreFields, dm.ParticipantGroupe, dm.ParticipantAnimateur)
   551  	}
   552  	if hasOption := d.camp.RawData().OptionPrix.Active != ""; !hasOption {
   553  		ignoreFields = append(ignoreFields, dm.ParticipantOptionPrix)
   554  	}
   555  	headerInscrit = hideColumns(headerInscrit, ignoreFields...)
   556  
   557  	return table.GenereListeParticipants(headerInscrit, headerResponsable,
   558  		inscrits, attente, options.showColors)
   559  }
   560  
   561  func (d DriverCampComplet) exportListeFinances() (*bytes.Buffer, error) {
   562  	parts, totalDemande, totalAides := d.camp.Base.SuiviFinancier(d.camp)
   563  	return table.GenereSuiviFinancierCamp(cd.HeadersSuiviParticipants, parts, totalDemande, totalAides)
   564  }