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

     1  package acvegestion
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/benoitkugler/goACVE/server/core/apiserver"
    10  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    11  	"github.com/benoitkugler/goACVE/server/core/rawdata/composites"
    12  	"github.com/benoitkugler/goACVE/server/core/utils/mails"
    13  	"github.com/benoitkugler/goACVE/server/shared"
    14  	"github.com/labstack/echo"
    15  )
    16  
    17  const notifDonReplyTo = "contact@acve.asso.fr"
    18  
    19  // ajoute les actions spécifiques au CRUD
    20  
    21  func (ct Controller) deleteAide(idAide int64) (rd.Ids, error) {
    22  	tx, err := ct.DB.Begin()
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	// liens
    28  	rows, err := tx.Query("DELETE FROM document_aides WHERE id_aide = $1 RETURNING id_document", idAide)
    29  	if err != nil {
    30  		return nil, shared.Rollback(tx, err)
    31  	}
    32  	deletedDocuments, err := rd.ScanIds(rows)
    33  	if err != nil {
    34  		return nil, shared.Rollback(tx, err)
    35  	}
    36  
    37  	//documents
    38  	_, err = rd.DeleteDocumentsByIds(tx, deletedDocuments...)
    39  	if err != nil {
    40  		return nil, shared.Rollback(tx, err)
    41  	}
    42  
    43  	_, err = rd.DeleteAideById(tx, idAide)
    44  	if err != nil {
    45  		return nil, shared.Rollback(tx, err)
    46  	}
    47  	err = tx.Commit()
    48  	return deletedDocuments, err
    49  }
    50  
    51  func (ct Controller) createCamp(params apiserver.CreateCampIn) (rd.Camp, error) {
    52  	tx, err := ct.DB.Begin()
    53  	if err != nil {
    54  		return rd.Camp{}, err
    55  	}
    56  	camp, err := params.Camp.Insert(tx)
    57  	if err != nil {
    58  		return camp, shared.Rollback(tx, err)
    59  	}
    60  
    61  	// on importe le html de la lettre directeur
    62  	if idCamp := params.CopyLettreFrom; idCamp.IsNotNil() {
    63  		lettre, found, err := rd.SelectLettredirecteurByIdCamp(tx, idCamp.Int64)
    64  		if err != nil {
    65  			return camp, shared.Rollback(tx, err)
    66  		}
    67  		if found {
    68  			lettre.IdCamp = camp.Id
    69  			err = rd.InsertManyLettredirecteurs(tx, lettre)
    70  			if err != nil {
    71  				return camp, shared.Rollback(tx, err)
    72  			}
    73  		} // sinon, le camp source n'a pas de lettre, on ne fait rien
    74  
    75  		// copie des potentielles images associées
    76  		// le lien d'accès n'est pas changé ce qui évite de devoir modifier
    77  		// le contenu de la lettre
    78  		imgs, err := rd.SelectImageuploadedsByIdCamps(tx, idCamp.Int64)
    79  		if err != nil {
    80  			return camp, shared.Rollback(tx, err)
    81  		}
    82  		for i := range imgs {
    83  			imgs[i].IdCamp = camp.Id // on redirige vers le nouveau camp
    84  		}
    85  		err = rd.InsertManyImageuploadeds(tx, imgs...)
    86  		if err != nil {
    87  			return camp, shared.Rollback(tx, err)
    88  		}
    89  
    90  		// les tables lettre et images sont ignorées par le client :
    91  		// on en renvoit rien
    92  	}
    93  	err = tx.Commit()
    94  	return camp, err
    95  }
    96  
    97  // CreateCamp ajoute un camp
    98  func (ct Controller) CreateCamp(c echo.Context) error {
    99  	if err := authentifie(c); err != nil {
   100  		return err
   101  	}
   102  	var in apiserver.CreateCampIn
   103  	if err := c.Bind(&in); err != nil {
   104  		return fmt.Errorf("Camp invalide : %s", err)
   105  	}
   106  	out, err := ct.createCamp(in)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	return c.JSON(200, out)
   111  }
   112  
   113  // UpdateCamp met à jour et renvoie le camp modifié; le statut simple
   114  // ne peut pas être modifié.
   115  func (ct Controller) UpdateCamp(c echo.Context) error {
   116  	if err := authentifie(c); err != nil {
   117  		return err
   118  	}
   119  	in := new(rd.Camp)
   120  	if err := c.Bind(in); err != nil {
   121  		return fmt.Errorf("Camp invalide : %s", err)
   122  	}
   123  	trueCamp, err := rd.SelectCamp(ct.DB, in.Id)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	if trueCamp.InscriptionSimple != in.InscriptionSimple {
   128  		return errors.New("Le mode d'inscription ne peut pas être modifié.")
   129  	}
   130  	updated, err := in.Update(ct.DB)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	return c.JSON(200, updated)
   135  }
   136  
   137  // renvoie une erreur si un équipier ou un participant est encore inscrit
   138  // en revanche, cascade la suppression des groupes et documents du séjour
   139  func (ct Controller) deleteCamp(idCamp int64) (apiserver.DeleteCampOut, error) {
   140  	var out apiserver.DeleteCampOut
   141  	tx, err := ct.DB.Begin()
   142  	if err != nil {
   143  		return out, err
   144  	}
   145  
   146  	// liens documents
   147  	rows, err := tx.Query("DELETE FROM document_camps WHERE id_camp = $1 RETURNING id_document", idCamp)
   148  	if err != nil {
   149  		return out, shared.Rollback(tx, err)
   150  	}
   151  	out.IdsDocuments, err = rd.ScanIds(rows)
   152  	if err != nil {
   153  		return out, shared.Rollback(tx, err)
   154  	}
   155  
   156  	//documents
   157  	_, err = rd.DeleteDocumentsByIds(tx, out.IdsDocuments...)
   158  	if err != nil {
   159  		return out, shared.Rollback(tx, err)
   160  	}
   161  
   162  	// contraintes demandées
   163  	_, err = rd.DeleteCampContraintesByIdCamps(tx, idCamp)
   164  	if err != nil {
   165  		return out, shared.Rollback(tx, err)
   166  	}
   167  
   168  	// liens groupes
   169  	_, err = tx.Exec(`DELETE FROM groupe_contraintes 
   170  		USING groupes WHERE groupes.id_camp = $1 AND 
   171  		groupe_contraintes.id_groupe = groupes.id`, idCamp)
   172  	if err != nil {
   173  		return out, shared.Rollback(tx, err)
   174  	}
   175  
   176  	// groupes
   177  	out.IdsGroupes, err = rd.DeleteGroupesByIdCamps(tx, idCamp)
   178  	if err != nil {
   179  		return out, shared.Rollback(tx, err)
   180  	}
   181  
   182  	_, err = rd.DeleteCampById(tx, idCamp)
   183  	if err != nil {
   184  		return out, shared.Rollback(tx, err)
   185  	}
   186  	err = tx.Commit()
   187  	return out, err
   188  }
   189  
   190  // DeleteCamp supprime
   191  func (ct Controller) DeleteCamp(c echo.Context) error {
   192  	if err := authentifie(c); err != nil {
   193  		return err
   194  	}
   195  	id, err := shared.ParseId(c, "id")
   196  	if err != nil {
   197  		return fmt.Errorf("Id (Camp) invalide : %s", err)
   198  	}
   199  	out, err := ct.deleteCamp(id)
   200  	if err != nil {
   201  		return err
   202  	}
   203  	return c.JSON(200, out)
   204  }
   205  
   206  // charger les données et trouve un groupe pour le nouveau participant
   207  // ne commit ni ne rollback
   208  func (ct Controller) findGroupe(tx *sql.Tx, participant rd.Participant) (rd.GroupeParticipant, bool, error) {
   209  	personne, err := rd.SelectPersonne(tx, participant.IdPersonne)
   210  	if err != nil {
   211  		return rd.GroupeParticipant{}, false, err
   212  	}
   213  	// charge le camp et les groupes
   214  	camp, err := rd.SelectCamp(tx, participant.IdCamp)
   215  	if err != nil {
   216  		return rd.GroupeParticipant{}, false, err
   217  	}
   218  
   219  	groupes, err := rd.SelectGroupesByIdCamps(tx, camp.Id)
   220  	if err != nil {
   221  		return rd.GroupeParticipant{}, false, err
   222  	}
   223  	var gp rd.GroupeParticipant
   224  	groupe, foundGroupe := composites.NewCampGroupes(camp, groupes).TrouveGroupe(personne.DateNaissance)
   225  	if foundGroupe { // on ajoute
   226  		gp = rd.GroupeParticipant{IdCamp: camp.Id, IdGroupe: groupe.Id, IdParticipant: participant.Id}
   227  		err = rd.InsertManyGroupeParticipants(tx, gp)
   228  		if err != nil {
   229  			return rd.GroupeParticipant{}, false, err
   230  		}
   231  	}
   232  	return gp, foundGroupe, nil
   233  }
   234  
   235  // CreateParticipant ajoute et renvoie, avec un éventuel groupe
   236  func (ct Controller) CreateParticipant(c echo.Context) error {
   237  	if err := authentifie(c); err != nil {
   238  		return err
   239  	}
   240  	in := new(rd.Participant)
   241  	if err := c.Bind(in); err != nil {
   242  		return fmt.Errorf("Participant invalide : %s", err)
   243  	}
   244  	tx, err := ct.DB.Begin()
   245  	if err != nil {
   246  		return err
   247  	}
   248  	var out apiserver.CreateParticipantOut
   249  	out.Participant, err = in.Insert(tx)
   250  	if err != nil {
   251  		return shared.Rollback(tx, err)
   252  	}
   253  
   254  	out.GroupeParticipant, out.FoundGroupe, err = ct.findGroupe(tx, out.Participant)
   255  	if err != nil {
   256  		return shared.Rollback(tx, err)
   257  	}
   258  	if err = tx.Commit(); err != nil {
   259  		return err
   260  	}
   261  	return c.JSON(200, out)
   262  }
   263  
   264  // UpdateParticipant met à jour le groupe si le camp change
   265  func (ct Controller) UpdateParticipant(c echo.Context) error {
   266  	if err := authentifie(c); err != nil {
   267  		return err
   268  	}
   269  	in := new(rd.Participant)
   270  	if err := c.Bind(in); err != nil {
   271  		return fmt.Errorf("Participant invalide : %s", err)
   272  	}
   273  	tx, err := ct.DB.Begin()
   274  	if err != nil {
   275  		return err
   276  	}
   277  	currentParticipant, err := rd.SelectParticipant(tx, in.Id)
   278  	if err != nil {
   279  		return shared.Rollback(tx, err)
   280  	}
   281  	isChangingCamp := in.IdCamp != currentParticipant.IdCamp
   282  	if isChangingCamp {
   283  		// on doit transférer un éventuel groupe
   284  		_, err := rd.DeleteGroupeParticipantsByIdParticipants(tx, in.Id)
   285  		if err != nil {
   286  			return shared.Rollback(tx, err)
   287  		}
   288  	}
   289  	var out apiserver.CreateParticipantOut
   290  	out.Participant, err = in.Update(tx)
   291  	if err != nil {
   292  		return shared.Rollback(tx, err)
   293  	}
   294  	if isChangingCamp {
   295  		// on affecte à un nouveau groupe
   296  		out.GroupeParticipant, out.FoundGroupe, err = ct.findGroupe(tx, out.Participant)
   297  		if err != nil {
   298  			return shared.Rollback(tx, err)
   299  		}
   300  	}
   301  	if err = tx.Commit(); err != nil {
   302  		return err
   303  	}
   304  	return c.JSON(200, out)
   305  }
   306  
   307  // CreateFacture génère une clé unique, ratache les participants donnés à la facture
   308  // et renvois la facture et les participants mis à jour.
   309  // Note: Si un participant était déjà dans un dossier, il est simplement redirigé.
   310  func (ct Controller) CreateFacture(c echo.Context) error {
   311  	if err := authentifie(c); err != nil {
   312  		return err
   313  	}
   314  	in := new(apiserver.CreateFactureIn)
   315  	if err := c.Bind(in); err != nil {
   316  		return fmt.Errorf("Facture invalide : %s", err)
   317  	}
   318  	tx, err := ct.DB.Begin()
   319  	if err != nil {
   320  		return err
   321  	}
   322  	key, err := shared.GetNewKey(tx)
   323  	if err != nil {
   324  		err = fmt.Errorf("Création de la clé du dossier impossible : %s", err)
   325  		return shared.Rollback(tx, err)
   326  	}
   327  	// l'inscription est créé par le centre, donc elle est valide
   328  	// le champ IsConfirmed n'a pas vraiment de sens : il faut mieux
   329  	// le cocher pour éviter des avertissements
   330  	// On permet le partage des coordonnées: permissif par défaut
   331  	fac := rd.Facture{IdPersonne: in.IdResponsable, Key: rd.String(key),
   332  		IsValidated: true, IsConfirmed: true, PartageAdressesOK: true}
   333  	fac, err = fac.Insert(tx)
   334  	if err != nil {
   335  		return shared.Rollback(tx, err)
   336  	}
   337  	out := apiserver.CreateFactureOut{Facture: fac}
   338  
   339  	rows, err := tx.Query("UPDATE participants SET id_facture = $1 WHERE id = ANY($2) RETURNING *",
   340  		fac.Id, in.IdParticipants.AsSQL())
   341  	if err != nil {
   342  		return shared.Rollback(tx, err)
   343  	}
   344  	out.Participants, err = rd.ScanParticipants(rows)
   345  	if err != nil {
   346  		return shared.Rollback(tx, err)
   347  	}
   348  
   349  	// enregistre le moment "d'inscription"
   350  	out.Message = rd.Message{Kind: rd.MInscription, Created: rd.Time(time.Now()), IdFacture: fac.Id}
   351  	out.Message, err = out.Message.Insert(tx)
   352  	if err != nil {
   353  		return shared.Rollback(tx, err)
   354  	}
   355  
   356  	if err = tx.Commit(); err != nil {
   357  		return err
   358  	}
   359  	return c.JSON(200, out)
   360  }
   361  
   362  // supprime la facture et les paiements associés,
   363  // et met à jour les participants si besoin
   364  func (ct Controller) deleteFacture(id int64) (apiserver.DeleteFactureOut, error) {
   365  	var out apiserver.DeleteFactureOut
   366  	tx, err := ct.DB.Begin()
   367  	if err != nil {
   368  		return out, err
   369  	}
   370  
   371  	out.DeletedPaiements, err = rd.DeletePaiementsByIdFactures(tx, id)
   372  	if err != nil {
   373  		return out, shared.Rollback(tx, err)
   374  	}
   375  
   376  	rows, err := tx.Query("UPDATE participants SET id_facture = NULL WHERE id_facture = $1 RETURNING *", id)
   377  	if err != nil {
   378  		return out, shared.Rollback(tx, err)
   379  	}
   380  	out.Participants, err = rd.ScanParticipants(rows)
   381  	if err != nil {
   382  		return out, shared.Rollback(tx, err)
   383  	}
   384  
   385  	_, err = rd.DeleteFactureById(tx, id)
   386  	if err != nil {
   387  		return out, shared.Rollback(tx, err)
   388  	}
   389  	err = tx.Commit()
   390  	return out, err
   391  }
   392  
   393  // supprime aussi les aides (et leurs justificatifs)
   394  // si le participant est temporaire, supprime aussi la personne associée
   395  func (ct Controller) deleteParticipant(id int64) (apiserver.DeleteParticipantOut, error) {
   396  	tx, err := ct.DB.Begin()
   397  	if err != nil {
   398  		return apiserver.DeleteParticipantOut{}, err
   399  	}
   400  	out, err := deleteOneParticipant(tx, id)
   401  	if err != nil {
   402  		return out, shared.Rollback(tx, err)
   403  	}
   404  	err = tx.Commit()
   405  	return out, err
   406  }
   407  
   408  // supprime aussi les aides associées (et leurs documents)
   409  func (ct Controller) deleteStructureaide(id int64) (apiserver.DeleteStructureaideOut, error) {
   410  	var out apiserver.DeleteStructureaideOut
   411  	tx, err := ct.DB.Begin()
   412  	if err != nil {
   413  		return out, err
   414  	}
   415  
   416  	// liens documents
   417  	rows, err := tx.Query(`DELETE FROM document_aides 
   418  		USING aides WHERE aides.id = document_aides.id_aide AND aides.id_structureaide = $1
   419  		RETURNING document_aides.id_document`, id)
   420  	if err != nil {
   421  		return out, shared.Rollback(tx, err)
   422  	}
   423  	out.IdsDocuments, err = rd.ScanIds(rows)
   424  	if err != nil {
   425  		return out, shared.Rollback(tx, err)
   426  	}
   427  
   428  	_, err = rd.DeleteDocumentsByIds(tx, out.IdsDocuments...)
   429  	if err != nil {
   430  		return out, shared.Rollback(tx, err)
   431  	}
   432  
   433  	out.IdsAides, err = rd.DeleteAidesByIdStructureaides(tx, id)
   434  	if err != nil {
   435  		return out, shared.Rollback(tx, err)
   436  	}
   437  
   438  	_, err = rd.DeleteStructureaideById(tx, id)
   439  	if err != nil {
   440  		return out, shared.Rollback(tx, err)
   441  	}
   442  	err = tx.Commit()
   443  	return out, err
   444  }
   445  
   446  // si l'équipier est temporaire, supprime aussi la personne associée (et ses documents)
   447  func (ct Controller) deleteEquipier(id int64) (apiserver.DeleteEquipierOut, error) {
   448  	var out apiserver.DeleteEquipierOut
   449  
   450  	tx, err := ct.DB.Begin()
   451  	if err != nil {
   452  		return out, err
   453  	}
   454  
   455  	equipier, err := rd.DeleteEquipierById(tx, id)
   456  	if err != nil {
   457  		return out, shared.Rollback(tx, err)
   458  	}
   459  
   460  	isTmp, docs, err := shared.DeletePersonne(tx, equipier.IdPersonne)
   461  	if err != nil {
   462  		return out, shared.Rollback(tx, err)
   463  	}
   464  	out.IdsDocuments = docs
   465  
   466  	// si la personne était temporaire
   467  	out.IdPersonne = -1
   468  	if isTmp {
   469  		out.IdPersonne = equipier.IdPersonne
   470  	}
   471  
   472  	err = tx.Commit()
   473  	return out, err
   474  }
   475  
   476  func (ct Controller) deleteDocument(id int64) (rd.Contraintes, error) {
   477  	tx, err := ct.DB.Begin()
   478  	if err != nil {
   479  		return nil, err
   480  	}
   481  
   482  	// les liens et le contenu cascadent
   483  
   484  	// contrainte
   485  	rows, err := tx.Query("UPDATE contraintes SET id_document = NULL WHERE id_document = $1 RETURNING *", id)
   486  	if err != nil {
   487  		return nil, shared.Rollback(tx, err)
   488  	}
   489  	contraintes, err := rd.ScanContraintes(rows)
   490  	if err != nil {
   491  		return nil, shared.Rollback(tx, err)
   492  	}
   493  	_, err = rd.DeleteDocumentById(tx, id)
   494  	if err != nil {
   495  		return nil, shared.Rollback(tx, err)
   496  	}
   497  	err = tx.Commit()
   498  	return contraintes, err
   499  }
   500  
   501  // supprime les liens et marque le message
   502  func (ct Controller) deleteMessages(ids rd.Ids) (rd.Messages, error) {
   503  	// on ne supprime pas vraiment, on change 'kind', donc on doit "manuellement"
   504  	// supprimer les liens
   505  	tx, err := ct.DB.Begin()
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	for _, table := range rd.ComplementMessagesTables {
   510  		_, err := tx.Exec(fmt.Sprintf("DELETE FROM %s WHERE id_message = ANY($1)", table), ids.AsSQL())
   511  		if err != nil {
   512  			return nil, shared.Rollback(tx, err)
   513  		}
   514  	}
   515  	rows, err := tx.Query("UPDATE messages SET (kind, modified) = ($1 , $2) WHERE id = ANY($3) RETURNING *",
   516  		rd.MSupprime, rd.Time(time.Now()), ids.AsSQL())
   517  	if err != nil {
   518  		return nil, shared.Rollback(tx, err)
   519  	}
   520  	out, err := rd.ScanMessages(rows)
   521  	if err != nil {
   522  		return nil, shared.Rollback(tx, err)
   523  	}
   524  	err = tx.Commit()
   525  	return out, err
   526  }
   527  
   528  // DeleteMessages supprime
   529  func (ct Controller) DeleteMessages(c echo.Context) error {
   530  	if err := authentifie(c); err != nil {
   531  		return err
   532  	}
   533  	var ids rd.Ids
   534  	if err := c.Bind(&ids); err != nil {
   535  		return fmt.Errorf("Ids (Messages) invalides : %s", err)
   536  	}
   537  	out, err := ct.deleteMessages(ids)
   538  	if err != nil {
   539  		return err
   540  	}
   541  	return c.JSON(200, out)
   542  }
   543  
   544  // Ajoute l'équipier transmit et ajoute des contraintes par défaut
   545  func (ct Controller) CreateEquipier(c echo.Context) error {
   546  	if err := authentifie(c); err != nil {
   547  		return err
   548  	}
   549  	in := new(rd.Equipier)
   550  	if err := c.Bind(in); err != nil {
   551  		return fmt.Errorf("Equipier invalide : %s", err)
   552  	}
   553  	tx, err := ct.DB.Begin()
   554  	if err != nil {
   555  		return err
   556  	}
   557  	var out apiserver.CreateEquipierOut
   558  	out.Equipier, err = in.Insert(tx)
   559  	if err != nil {
   560  		return shared.Rollback(tx, err)
   561  	}
   562  	out.Contraintes, err = shared.AddEquipierDefautContraintes(tx, ct.ContraintesEquipiers, out.Equipier)
   563  	if err != nil {
   564  		return shared.Rollback(tx, err)
   565  	}
   566  	if err = tx.Commit(); err != nil {
   567  		return err
   568  	}
   569  	return c.JSON(200, out)
   570  }
   571  
   572  func (ct Controller) CreateDon(c echo.Context) error {
   573  	if err := authentifie(c); err != nil {
   574  		return err
   575  	}
   576  	var in apiserver.CreateDonIn
   577  	if err := c.Bind(&in); err != nil {
   578  		return fmt.Errorf("Détails du don invalides : %s", err)
   579  	}
   580  	out, err := ct.createDon(in)
   581  	if err != nil {
   582  		return err
   583  	}
   584  	return c.JSON(200, out)
   585  }
   586  
   587  // inscrit le don et envoie un mail de remerciement, sauf pour les dons Helloasso
   588  func (ct Controller) createDon(params apiserver.CreateDonIn) (apiserver.CreateDonIn, error) {
   589  	tx, err := ct.DB.Begin()
   590  	if err != nil {
   591  		return params, err
   592  	}
   593  	params.Don, err = params.Don.Insert(tx)
   594  	if err != nil {
   595  		return params, shared.Rollback(tx, err)
   596  	}
   597  	// ajout de l'id nouvellement créé
   598  	params.Donateur.IdDon = params.Don.Id
   599  
   600  	// on n'ajoute pas de lien pour les dons anonymes
   601  	if params.Donateur.IId() != nil {
   602  		err = rd.InsertManyDonDonateurs(tx, params.Donateur)
   603  		if err != nil {
   604  			return params, shared.Rollback(tx, err)
   605  		}
   606  	}
   607  
   608  	// mail de remerciement
   609  	if params.Notifie && params.Don.ModePaiement != rd.MPHelloasso {
   610  
   611  		// on résoud le donateur
   612  		contact, mail := mails.Contact{}, ""
   613  		if p := params.Donateur.IdPersonne; p.IsNotNil() {
   614  			donateur, err := rd.SelectPersonne(tx, p.Int64)
   615  			if err != nil {
   616  				return params, shared.Rollback(tx, err)
   617  			}
   618  			contact = mails.Contact{Prenom: donateur.FPrenom(), Sexe: donateur.Sexe}
   619  			mail = donateur.Mail.String()
   620  		} else if p := params.Donateur.IdOrganisme; p.IsNotNil() {
   621  			orga, err := rd.SelectOrganisme(tx, p.Int64)
   622  			if err != nil {
   623  				return params, shared.Rollback(tx, err)
   624  			}
   625  			if p := orga.IdContactDon; p.IsNotNil() {
   626  				donateur, err := rd.SelectPersonne(tx, p.Int64)
   627  				if err != nil {
   628  					return params, shared.Rollback(tx, err)
   629  				}
   630  				contact = mails.Contact{Prenom: donateur.FPrenom(), Sexe: donateur.Sexe}
   631  				mail = donateur.Mail.String()
   632  			} else if p := orga.IdContact; p.IsNotNil() {
   633  				donateur, err := rd.SelectPersonne(tx, p.Int64)
   634  				if err != nil {
   635  					return params, shared.Rollback(tx, err)
   636  				}
   637  				contact = mails.Contact{Prenom: donateur.FPrenom(), Sexe: donateur.Sexe}
   638  				mail = donateur.Mail.String()
   639  			} else { // adresse propre
   640  				contact = mails.Contact{}
   641  				mail = orga.Contact.Mail.String()
   642  			}
   643  		}
   644  
   645  		if mail == "" {
   646  			return params, shared.Rollback(tx, errors.New("Aucune adresse mail."))
   647  		}
   648  
   649  		body, err := mails.NewNotificationDon(contact, params.Don.Valeur)
   650  		if err != nil {
   651  			return params, shared.Rollback(tx, err)
   652  		}
   653  		err = mails.NewMailer(ct.SMTP).SendMail(mail, "[ACVE] - Remerciement", body, nil,
   654  			mails.CustomReplyTo(notifDonReplyTo))
   655  		if err != nil {
   656  			return params, shared.Rollback(tx, err)
   657  		}
   658  	}
   659  
   660  	err = tx.Commit()
   661  	return params, err
   662  }
   663  
   664  func (ct Controller) UpdateDon(c echo.Context) error {
   665  	if err := authentifie(c); err != nil {
   666  		return err
   667  	}
   668  	var in apiserver.CreateDonIn
   669  	if err := c.Bind(&in); err != nil {
   670  		return fmt.Errorf("Détails du don invalides : %s", err)
   671  	}
   672  	out, err := ct.updateDon(in)
   673  	if err != nil {
   674  		return err
   675  	}
   676  	return c.JSON(200, out)
   677  }
   678  
   679  func (ct Controller) updateDon(params apiserver.CreateDonIn) (apiserver.CreateDonIn, error) {
   680  	tx, err := ct.DB.Begin()
   681  	if err != nil {
   682  		return params, err
   683  	}
   684  	params.Don, err = params.Don.Update(tx)
   685  	if err != nil {
   686  		return params, shared.Rollback(tx, err)
   687  	}
   688  
   689  	// on assure la cohérence, sait-on jamais ?
   690  	params.Donateur.IdDon = params.Don.Id
   691  
   692  	// suppression de l'ancien lien ...
   693  	_, err = rd.DeleteDonDonateursByIdDons(tx, params.Don.Id)
   694  	if err != nil {
   695  		return params, shared.Rollback(tx, err)
   696  	}
   697  	// ... et ajout du nouveau
   698  	// on n'ajoute pas de lien pour les dons anonymes
   699  	if params.Donateur.IId() != nil {
   700  		err = rd.InsertManyDonDonateurs(tx, params.Donateur)
   701  		if err != nil {
   702  			return params, shared.Rollback(tx, err)
   703  		}
   704  	}
   705  	err = tx.Commit()
   706  	return params, err
   707  }
   708  
   709  func (ct Controller) deleteOrganisme(id int64) (apiserver.DeleteOrganismeOut, error) {
   710  	out := apiserver.DeleteOrganismeOut{Id: id}
   711  	tx, err := ct.DB.Begin()
   712  	if err != nil {
   713  		return out, err
   714  	}
   715  	liens, err := rd.DeleteDonDonateursByIdOrganismes(tx, id)
   716  	if err != nil {
   717  		return out, shared.Rollback(tx, err)
   718  	}
   719  	var ids rd.Ids
   720  	for idDon := range liens.ByIdDon() {
   721  		ids = append(ids, idDon)
   722  	}
   723  	out.Dons, err = rd.DeleteDonsByIds(tx, ids...)
   724  	if err != nil {
   725  		return out, shared.Rollback(tx, err)
   726  	}
   727  	_, err = rd.DeleteOrganismeById(tx, id)
   728  	if err != nil {
   729  		return out, shared.Rollback(tx, err)
   730  	}
   731  	err = tx.Commit()
   732  	return out, err
   733  }