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

     1  package acvegestion
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"mime"
     8  	"strconv"
     9  	"time"
    10  
    11  	"github.com/benoitkugler/goACVE/server/documents"
    12  	"github.com/benoitkugler/goACVE/server/recufiscal"
    13  	"github.com/benoitkugler/goACVE/server/shared"
    14  
    15  	dm "github.com/benoitkugler/goACVE/server/core/datamodel"
    16  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    17  
    18  	"github.com/benoitkugler/goACVE/server/core/apiserver"
    19  
    20  	"github.com/labstack/echo"
    21  )
    22  
    23  // GetUsers renvoie la liste des utilisateurs du client
    24  func (ct Controller) GetUsers(c echo.Context) error {
    25  	if err := authentifie(c); err != nil {
    26  		return err
    27  	}
    28  	users, err := rd.SelectAllUsers(ct.DB)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	var out []rd.ClientUser
    33  	for _, user := range users {
    34  		out = append(out, rd.ClientUser{Id: user.Id, Label: user.Label.String(), IsAdmin: bool(user.IsAdmin)})
    35  	}
    36  	return c.JSON(200, out)
    37  }
    38  
    39  // CheckPassword vérifie le mot de passe de l'utilisateur donné
    40  func (ct Controller) CheckPassword(c echo.Context) error {
    41  	if err := authentifie(c); err != nil {
    42  		return err
    43  	}
    44  	in := new(apiserver.ParamsLoggin)
    45  	if err := c.Bind(in); err != nil {
    46  		return fmt.Errorf("Information de loggin invalides : %s", err)
    47  	}
    48  	user, err := rd.SelectUser(ct.DB, in.Id)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	var out apiserver.OutputLoggin
    53  	if user.Mdp.String() == in.Password {
    54  		out.Valid = true
    55  		out.User = user
    56  	}
    57  	return c.JSON(200, out)
    58  }
    59  
    60  // SertDB charge toute la base de donnée et la renvoie
    61  // sous forme compressée
    62  func (ct Controller) SertDB(c echo.Context) error {
    63  	if err := authentifie(c); err != nil {
    64  		return err
    65  	}
    66  	b, err := dm.LoadRemoteBaseLocale(ct.DB)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	buf := new(bytes.Buffer)
    71  	if err = b.MarshalCompress(buf); err != nil {
    72  		return err
    73  	}
    74  	c.Response().Header().Add("Content-Length", strconv.Itoa(len(buf.Bytes())))
    75  	return c.Stream(200, mime.TypeByExtension(".gz"), buf)
    76  }
    77  
    78  // SertDBHash charge toute la base de donnée et calcule un hash
    79  // compact, renvoyée encodée (multipart).
    80  // Voir SertDBPartiel pour la seconde partie du procédé.
    81  func (ct Controller) SertDBHash(c echo.Context) error {
    82  	if err := authentifie(c); err != nil {
    83  		return err
    84  	}
    85  	b, err := dm.LoadRemoteBaseLocale(ct.DB)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	hash, err := b.Hash()
    90  	if err != nil {
    91  		return shared.FormatErr("Impossible de calculer le hash.", err)
    92  	}
    93  	header, bin, err := hash.Compress()
    94  	if err != nil {
    95  		return shared.FormatErr("Impossible de compresser le hash.", err)
    96  	}
    97  	w := c.Response()
    98  	err = shared.WriteMultipart(w.Header(), w, map[string][]byte{
    99  		apiserver.DBHashHeaderJSONKey: header,
   100  		apiserver.DBHashBinaryKey:     bin,
   101  	})
   102  
   103  	return err
   104  }
   105  
   106  func (ct Controller) SertDBPartiel(c echo.Context) error {
   107  	if err := authentifie(c); err != nil {
   108  		return err
   109  	}
   110  	var items map[string][]int64
   111  	if err := c.Bind(&items); err != nil {
   112  		return err
   113  	}
   114  	b, err := dm.LoadRemoteBaseLocalePartiel(ct.DB, items)
   115  	if err != nil {
   116  		return err
   117  	}
   118  	buf := new(bytes.Buffer)
   119  	if err = b.MarshalCompress(buf); err != nil {
   120  		return err
   121  	}
   122  	c.Response().Header().Add("Content-Length", strconv.Itoa(len(buf.Bytes())))
   123  	return c.Stream(200, mime.TypeByExtension(".gz"), buf)
   124  }
   125  
   126  //--------------------------------------------------------------------
   127  //------------------------ Dons --------------------------------------
   128  //--------------------------------------------------------------------
   129  
   130  // GenereRecuFiscal génère une archive contenant les reçus fiscaux,
   131  // et renvoie aussi les dons mis à jour.
   132  func (ct Controller) GenereRecuFiscal(c echo.Context) error {
   133  	if err := authentifie(c); err != nil {
   134  		return err
   135  	}
   136  	in := new(dm.OptionsRecuFiscal)
   137  	if err := c.Bind(in); err != nil {
   138  		return fmt.Errorf("Paramètres de reçus fiscaux invalides : %s", err)
   139  	}
   140  
   141  	dons, archive, err := recufiscal.GenereRecuFiscaux(ct.DB, *in)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	donsJson, err := json.Marshal(dons)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	w := c.Response()
   152  	err = shared.WriteMultipart(w.Header(), w, map[string][]byte{
   153  		apiserver.RecusFiscauxJSONKey: donsJson,
   154  		apiserver.RecusFiscauxZIPKey:  archive.Bytes(),
   155  	})
   156  	return err
   157  }
   158  
   159  // ImportDonsHelloasso charge et renvoie les dons HelloAsso qui
   160  // n'ont pas encore été importé.
   161  func (ct Controller) ImportDonsHelloasso(c echo.Context) error {
   162  	if err := authentifie(c); err != nil {
   163  		return err
   164  	}
   165  	out, err := ct.importDonsHelloasso()
   166  	if err != nil {
   167  		return shared.FormatErr("Impossible de charger les dons HelloAsso", err)
   168  	}
   169  	return c.JSON(200, out)
   170  }
   171  
   172  // IdentifieDonHelloasso crée un nouveau don à partir
   173  // des infos HelloAsso et d'un donateur identifié.
   174  func (ct Controller) IdentifieDonHelloasso(c echo.Context) error {
   175  	if err := authentifie(c); err != nil {
   176  		return err
   177  	}
   178  	var params apiserver.IdentifieDonIn
   179  	if err := c.Bind(&params); err != nil {
   180  		return shared.FormatErr("Paramètres invalides", err)
   181  	}
   182  	out, err := ct.identifieDon(params)
   183  	if err != nil {
   184  		return shared.FormatErr("Impossible d'identifier le don", err)
   185  	}
   186  	return c.JSON(200, out)
   187  }
   188  
   189  //--------------------------------------------------------------------
   190  //------------------------ Inscriptions ------------------------------
   191  //--------------------------------------------------------------------
   192  
   193  // Marque l'inscription comme validée, après s'être assuré
   194  // qu'aucune personne impliquée n'est temporaire.
   195  // De plus, un mail de dé-verrouillage de la fiche sanitaire
   196  // est envoyé si nécessaire.
   197  // Renvoie la facture mise à jour.
   198  func (ct Controller) ValideInscription(c echo.Context) error {
   199  	if err := authentifie(c); err != nil {
   200  		return err
   201  	}
   202  
   203  	id, err := shared.ParseId(c, "id")
   204  	if err != nil {
   205  		return err
   206  	}
   207  	rows, err := ct.DB.Query(`SELECT personnes.* FROM personnes 
   208  		JOIN factures ON factures.id_personne = personnes.id
   209  		WHERE factures.id = $1
   210  		UNION
   211  		SELECT personnes.* FROM personnes
   212  		JOIN participants ON participants.id_personne = personnes.id
   213  		WHERE participants.id_facture = $1`, id)
   214  	if err != nil {
   215  		return err
   216  	}
   217  	personnes, err := rd.ScanPersonnes(rows)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	// on s'assure qu'aucune personne n'est temporaire
   223  	for _, personne := range personnes {
   224  		if personne.IsTemporaire {
   225  			return fmt.Errorf("%s n'a pas encore été identifié(e) !", personne.NomPrenom())
   226  		}
   227  	}
   228  
   229  	// on propose le partage des fiches sanitaires
   230  	err = ct.checkNotifiePartageFicheSanitaire(c.Request().Host, id)
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	row := ct.DB.QueryRow("UPDATE factures SET is_validated = true WHERE id = $1 RETURNING *", id)
   236  	out, err := rd.ScanFacture(row)
   237  	if err != nil {
   238  		return err
   239  	}
   240  	return c.JSON(200, out)
   241  }
   242  
   243  // DeleteInscription supprime
   244  func (ct Controller) DeleteInscription(c echo.Context) error {
   245  	if err := authentifie(c); err != nil {
   246  		return err
   247  	}
   248  	id, err := shared.ParseId(c, "id")
   249  	if err != nil {
   250  		return fmt.Errorf("Id (Inscription) invalide : %s", err)
   251  	}
   252  	out, err := ct.deleteInscription(id)
   253  	if err != nil {
   254  		return err
   255  	}
   256  	return c.JSON(200, out)
   257  }
   258  
   259  //-----------------------------------------------------------------------
   260  //------------------------ Rapprochements -------------------------------
   261  //-----------------------------------------------------------------------
   262  
   263  // IdentifieEquipier identifie un équipier temporaire
   264  func (ct Controller) IdentifieEquipier(c echo.Context) error {
   265  	if err := authentifie(c); err != nil {
   266  		return err
   267  	}
   268  	in := new(apiserver.IdentifieEquipierIn)
   269  	if err := c.Bind(in); err != nil {
   270  		return fmt.Errorf("Paramètres d'identification invalides : %s", err)
   271  	}
   272  	out, err := ct.identifieEquipier(*in)
   273  	if err != nil {
   274  		return err
   275  	}
   276  	return c.JSON(200, out)
   277  }
   278  
   279  // IdentifieParticipant identifie un participant temporaire
   280  func (ct Controller) IdentifieParticipant(c echo.Context) error {
   281  	if err := authentifie(c); err != nil {
   282  		return err
   283  	}
   284  	in := new(apiserver.IdentifieParticipantIn)
   285  	if err := c.Bind(in); err != nil {
   286  		return fmt.Errorf("Paramètres d'identification invalides : %s", err)
   287  	}
   288  	out, err := ct.identifieParticipant(*in)
   289  	if err != nil {
   290  		return err
   291  	}
   292  	return c.JSON(200, out)
   293  }
   294  
   295  // IdentifieResponsable identifie un responsable temporaire
   296  func (ct Controller) IdentifieResponsable(c echo.Context) error {
   297  	if err := authentifie(c); err != nil {
   298  		return err
   299  	}
   300  	in := new(apiserver.IdentifieResponsableIn)
   301  	if err := c.Bind(in); err != nil {
   302  		return fmt.Errorf("Paramètres d'identification invalides : %s", err)
   303  	}
   304  	out, err := ct.identifieResponsable(*in)
   305  	if err != nil {
   306  		return err
   307  	}
   308  	return c.JSON(200, out)
   309  }
   310  
   311  // IdentifieParticipantsimple identifie un participant simple temporaire
   312  // et envoie un message de confirmation.
   313  func (ct Controller) IdentifieParticipantsimple(c echo.Context) error {
   314  	if err := authentifie(c); err != nil {
   315  		return err
   316  	}
   317  	in := new(apiserver.IdentifieParticipantsimpleIn)
   318  	if err := c.Bind(in); err != nil {
   319  		return fmt.Errorf("Paramètres d'identification invalides : %s", err)
   320  	}
   321  	out, err := ct.identifieParticipantsimple(*in)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	err = ct.notifieInscriptionSimpleValide(out.Participantsimple)
   326  	if err != nil {
   327  		return fmt.Errorf("Le participant a bien été identifié, mais l'envoi d'un mail de confirmation a échoué : %s",
   328  			err)
   329  	}
   330  	return c.JSON(200, out)
   331  }
   332  
   333  //--------------------------------------------------------------------
   334  //------------------------ Paiements ---------------------------------
   335  //--------------------------------------------------------------------
   336  
   337  // MarquePaiementsBordereau
   338  func (ct Controller) MarquePaiementsBordereau(c echo.Context) error {
   339  	if err := authentifie(c); err != nil {
   340  		return err
   341  	}
   342  	var in rd.Ids
   343  	if err := c.Bind(&in); err != nil {
   344  		return fmt.Errorf("Paramètres d'identification invalides : %s", err)
   345  	}
   346  	rows, err := ct.DB.Query("UPDATE paiements SET in_bordereau = $1 WHERE id = ANY($2) RETURNING *", time.Now(), in.AsSQL())
   347  	if err != nil {
   348  		return err
   349  	}
   350  	out, err := rd.ScanPaiements(rows)
   351  	if err != nil {
   352  		return err
   353  	}
   354  	return c.JSON(200, out)
   355  }
   356  
   357  //--------------------------------------------------------------------
   358  //------------------------ Documents ---------------------------------
   359  //--------------------------------------------------------------------
   360  
   361  // GetDocumentsMiniatures renvoies les miniatures des documents demandés
   362  func (ct Controller) GetDocumentsMiniatures(c echo.Context) error {
   363  	if err := authentifie(c); err != nil {
   364  		return err
   365  	}
   366  	var in rd.Ids
   367  	if err := c.Bind(&in); err != nil {
   368  		return fmt.Errorf("Ids (documents) invalides : %s", err)
   369  	}
   370  	rows, err := ct.DB.Query("SELECT id_document, null, miniature FROM contenu_documents WHERE id_document = ANY ($1)", in.AsSQL())
   371  	if err != nil {
   372  		return err
   373  	}
   374  	out, err := rd.ScanContenuDocuments(rows)
   375  	if err != nil {
   376  		return err
   377  	}
   378  	return c.JSON(200, out)
   379  }
   380  
   381  // DownloadDocument renvoie le document décompressé demandé
   382  func (ct Controller) DownloadDocument(c echo.Context) error {
   383  	if err := authentifie(c); err != nil {
   384  		return err
   385  	}
   386  	id, err := shared.ParseId(c, "id")
   387  	if err != nil {
   388  		return err
   389  	}
   390  	contenu, found, err := rd.SelectContenuDocumentByIdDocument(ct.DB, id)
   391  	if err != nil {
   392  		return err
   393  	}
   394  	if !found {
   395  		return fmt.Errorf("Le document (id %d) n'a pas de contenu. Veuillez le supprimer.", id)
   396  	}
   397  	b := contenu.Contenu
   398  	c.Response().Header().Add("Content-length", strconv.Itoa(len(b)))
   399  	return c.Stream(200, "", bytes.NewReader(b))
   400  }
   401  
   402  // CreateDocument créé les meta-données et associe le document à
   403  // une cible (personne ou aide)
   404  func (ct Controller) CreateDocument(c echo.Context) error {
   405  	if err := authentifie(c); err != nil {
   406  		return err
   407  	}
   408  	var in apiserver.CreateDocumentIn
   409  	fileName, fileContent, err := documents.ReceiveUploadWithMeta(c, apiserver.DOCUMENT_MAX_SIZE, &in)
   410  	if err != nil {
   411  		return fmt.Errorf("Paramètres invalides : %s", err)
   412  	}
   413  	var out apiserver.CreateDocumentIn
   414  	tx, err := ct.DB.Begin()
   415  	if err != nil {
   416  		return err
   417  	}
   418  	out.Document, err = in.Document.Insert(tx)
   419  	if err != nil {
   420  		return shared.Rollback(tx, err)
   421  	}
   422  	switch target := in.Target.(type) {
   423  	case rd.DocumentPersonne:
   424  		target.IdDocument = out.Document.Id
   425  		out.Target = target
   426  		err = rd.InsertManyDocumentPersonnes(tx, target)
   427  	case rd.DocumentAide:
   428  		target.IdDocument = out.Document.Id
   429  		out.Target = target
   430  		err = rd.InsertManyDocumentAides(tx, target)
   431  	}
   432  	if err != nil {
   433  		return shared.Rollback(tx, err)
   434  	}
   435  
   436  	out.Document, err = documents.SaveDocument(tx, out.Document.Id, fileContent, fileName, true)
   437  	if err != nil {
   438  		return shared.Rollback(tx, err)
   439  	}
   440  
   441  	err = tx.Commit()
   442  	if err != nil {
   443  		return err
   444  	}
   445  	return c.JSON(200, out)
   446  }
   447  
   448  // UploadDocument met à jour le contenu du document
   449  func (ct Controller) UploadDocument(c echo.Context) error {
   450  	if err := authentifie(c); err != nil {
   451  		return err
   452  	}
   453  	id, err := shared.ParseId(c, "id")
   454  	if err != nil {
   455  		return fmt.Errorf("Paramètres d'upload invalides : %s", err)
   456  	}
   457  	fileHeader, err := c.FormFile("file")
   458  	if err != nil {
   459  		return fmt.Errorf("Fichier manquant : %s", err)
   460  	}
   461  	filename, content, err := documents.ReceiveUpload(fileHeader, apiserver.DOCUMENT_MAX_SIZE)
   462  	if err != nil {
   463  		return err
   464  	}
   465  
   466  	tx, err := ct.DB.Begin()
   467  	if err != nil {
   468  		return err
   469  	}
   470  	doc, err := documents.SaveDocument(tx, id, content, filename, true)
   471  	if err != nil {
   472  		return shared.Rollback(tx, err)
   473  	}
   474  
   475  	if err = tx.Commit(); err != nil {
   476  		return err
   477  	}
   478  	return c.JSON(200, doc)
   479  }
   480  
   481  //--------------------------------------------------------------------
   482  //------------------------- Messages ---------------------------------
   483  //--------------------------------------------------------------------
   484  
   485  // NotifieMessage ajoute le message donné et envoie un mail de notification
   486  func (ct Controller) NotifieMessage(c echo.Context) error {
   487  	if err := authentifie(c); err != nil {
   488  		return err
   489  	}
   490  	var in apiserver.NotifieMessageIn
   491  	if err := c.Bind(&in); err != nil {
   492  		return err
   493  	}
   494  	out, err := ct.creeMessageCentre(c.Request().Host, in.IdFacture, in.Contenu)
   495  	if err != nil {
   496  		return err
   497  	}
   498  	return c.JSON(200, out)
   499  }
   500  
   501  // EditMessage modifie le contenu, sans envoi de mail
   502  func (ct Controller) EditMessage(c echo.Context) error {
   503  	if err := authentifie(c); err != nil {
   504  		return err
   505  	}
   506  	var in apiserver.EditMessageIn
   507  	if err := c.Bind(&in); err != nil {
   508  		return err
   509  	}
   510  	out, err := ct.editMessageCentre(in)
   511  	if err != nil {
   512  		return err
   513  	}
   514  	return c.JSON(200, out)
   515  }
   516  
   517  // CreateMessageMessage ajoute un message personnalisé, sans
   518  // notification.
   519  func (ct Controller) CreateMessageMessage(c echo.Context) error {
   520  	if err := authentifie(c); err != nil {
   521  		return err
   522  	}
   523  	var in apiserver.CreateMessageMessage
   524  	if err := c.Bind(&in); err != nil {
   525  		return err
   526  	}
   527  	out, err := ct.createMessageMessage(in)
   528  	if err != nil {
   529  		return err
   530  	}
   531  	return c.JSON(200, out)
   532  }
   533  
   534  // NotifieSimple envoie un message sans contenu additionnel
   535  func (ct Controller) NotifieSimple(c echo.Context) error {
   536  	if err := authentifie(c); err != nil {
   537  		return err
   538  	}
   539  	var in apiserver.NotifieSimple
   540  	if err := c.Bind(&in); err != nil {
   541  		return err
   542  	}
   543  	out, err := ct.notifieSimple(c.Request().Host, in)
   544  	if err != nil {
   545  		return err
   546  	}
   547  	return c.JSON(200, out)
   548  }
   549  
   550  // NotifiePlaceLiberee modifie le participant et
   551  // envoie un message
   552  func (ct Controller) NotifiePlaceLiberee(c echo.Context) error {
   553  	if err := authentifie(c); err != nil {
   554  		return err
   555  	}
   556  	var in apiserver.NotifiePlaceLibereeIn
   557  	if err := c.Bind(&in); err != nil {
   558  		return err
   559  	}
   560  	out, err := ct.notifiePlaceLiberee(c.Request().Host, in)
   561  	if err != nil {
   562  		return err
   563  	}
   564  	return c.JSON(200, out)
   565  }
   566  
   567  // NotifieAttestation envoie un message contenant une attestation
   568  func (ct Controller) NotifieAttestation(c echo.Context) error {
   569  	if err := authentifie(c); err != nil {
   570  		return err
   571  	}
   572  	var in apiserver.NotifieAttestationIn
   573  	if err := c.Bind(&in); err != nil {
   574  		return err
   575  	}
   576  	out, err := ct.notifieAttestation(c.Request().Host, in)
   577  	if err != nil {
   578  		return err
   579  	}
   580  	return c.JSON(200, out)
   581  }
   582  
   583  // NotifieDocuments stream l'envoi des documents
   584  func (ct Controller) NotifieDocuments(c echo.Context) error {
   585  	if err := authentifie(c); err != nil {
   586  		return err
   587  	}
   588  	var in apiserver.NotifieDocumentsIn
   589  	if err := c.Bind(&in); err != nil {
   590  		return err
   591  	}
   592  	if err := ct.checkVerrouDocuments(in.IdCamp); err != nil {
   593  		return err
   594  	}
   595  	s := streamDocument{idCamp: in.IdCamp}
   596  	if err := s.checkListeAttente(in.IdsFactures, ct.DB); err != nil {
   597  		return err
   598  	}
   599  	message := rd.Message{Kind: rd.MDocuments}
   600  	err := ct.notifieManyMessages(c.Request().Host, s, message, in.IdsFactures, c.Response())
   601  	return err
   602  }
   603  
   604  // NotifieSondages stream l'envoi des sondages
   605  func (ct Controller) NotifieSondages(c echo.Context) error {
   606  	if err := authentifie(c); err != nil {
   607  		return err
   608  	}
   609  	var in apiserver.NotifieDocumentsIn
   610  	if err := c.Bind(&in); err != nil {
   611  		return err
   612  	}
   613  
   614  	message := rd.Message{Kind: rd.MSondage}
   615  	err := ct.notifieManyMessages(c.Request().Host, streamSondage{idCamp: in.IdCamp}, message, in.IdsFactures, c.Response())
   616  	return err
   617  }
   618  
   619  // NotifieMany stream l'envoi d'un message à plusieurs dossiers
   620  func (ct Controller) NotifieMany(c echo.Context) error {
   621  	if err := authentifie(c); err != nil {
   622  		return err
   623  	}
   624  	var in apiserver.NotifieManyIn
   625  	if err := c.Bind(&in); err != nil {
   626  		return err
   627  	}
   628  
   629  	if in.Contenu.TrimSpace() == "" {
   630  		return fmt.Errorf("Message vide !")
   631  	}
   632  
   633  	message := rd.Message{Kind: rd.MCentre}
   634  	err := ct.notifieManyMessages(c.Request().Host, streamMessage{contenu: in.Contenu}, message, in.IdsFactures, c.Response())
   635  	return err
   636  }
   637  
   638  // LoadInscriptions renvoie les inscriptions brutes, telles
   639  // qu'effectuées sur le formulaire publique, sous forme d'un dictionnaire
   640  func (ct Controller) LoadInscriptions(c echo.Context) error {
   641  	if err := authentifie(c); err != nil {
   642  		return err
   643  	}
   644  	out, err := rd.SelectAllInscriptions(ct.DB)
   645  	if err != nil {
   646  		return err
   647  	}
   648  	return c.JSON(200, out)
   649  }
   650  
   651  // FusionneFactures redirige les participants, paiements, messages et
   652  // sondages d'un dossier vers un autre, avant de supprimer le dossier
   653  // maintenant vide. Un mail de notification peut être envoyé.
   654  func (ct Controller) FusionneFactures(c echo.Context) error {
   655  	if err := authentifie(c); err != nil {
   656  		return err
   657  	}
   658  	var params apiserver.FusionneFacturesIn
   659  	if err := c.Bind(&params); err != nil {
   660  		return shared.FormatErr("Paramètres invalides", err)
   661  	}
   662  	out, err := ct.fusionneFactures(c.Request().Host, params)
   663  	if err != nil {
   664  		return err
   665  	}
   666  	return c.JSON(200, out)
   667  }