github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/controllers/cont_paiements.go (about)

     1  package controllers
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/http"
     7  	"sort"
     8  
     9  	"github.com/benoitkugler/goACVE/logs"
    10  
    11  	"github.com/benoitkugler/goACVE/server/core/utils/table"
    12  
    13  	"github.com/benoitkugler/goACVE/server/core/apiserver"
    14  	dm "github.com/benoitkugler/goACVE/server/core/datamodel"
    15  	rd "github.com/benoitkugler/goACVE/server/core/rawdata"
    16  )
    17  
    18  var (
    19  	headersBordereauSimple = []rd.Header{
    20  		{Field: dm.PaiementLabelPayeur, Label: "Payé par"},
    21  		{Field: dm.PaiementValeur, Label: "Montant"},
    22  		{Field: dm.PaiementNumero, Label: "Numéro"},
    23  		{Field: dm.PaiementNomBanque, Label: "Banque"},
    24  		{Field: dm.PaiementDateReglement, Label: "Ajouté le"},
    25  		{Field: dm.PaiementDetails, Label: "Détails"},
    26  	}
    27  )
    28  
    29  type ButtonsPaiements struct {
    30  	Cree, Supprimer, ExportBordereau EtatSideButton
    31  }
    32  
    33  type OngletPaiements interface {
    34  	baseOnglet
    35  
    36  	ConfirmeSupprimePaiement(dm.AccesPaiement) bool
    37  	ConfirmeExportPaiements(alreadyExported []rd.Paiement, nb int) (keep bool, simpleHeader bool, marque bool)
    38  }
    39  
    40  type EtatPaiements struct {
    41  	PaiementCurrent          rd.IId
    42  	CriterePaiementValide    rd.OptionnalBool
    43  	CriterePaiementBordereau rd.Time
    44  	CriterePaiementFacture   rd.OptionnalId
    45  	CritereFactureCamp       rd.IId
    46  	CritereFactureAcquittee  rd.OptionnalBool
    47  }
    48  
    49  type Paiements struct {
    50  	Onglet          OngletPaiements
    51  	main            *MainController
    52  	ListePaiements  rd.Table
    53  	ListeFactures   rd.Table
    54  	HeaderPaiements []rd.Header
    55  	HeaderFactures  []rd.Header
    56  	Base            *dm.BaseLocale
    57  	Etat            EtatPaiements
    58  
    59  	// Droit d'édition/ajout/suppression de paiements
    60  	// Droit d'invalidation d'un paiement
    61  	EditRight, InvalideRight bool
    62  }
    63  
    64  func NewPaiements(main *MainController, permission int) *Paiements {
    65  	p := Paiements{main: main, Base: main.Base, EditRight: permission >= 2,
    66  		InvalideRight: permission >= 3,
    67  		HeaderPaiements: []rd.Header{
    68  			{Field: dm.PaiementLabelPayeur, Label: "Payé par"},
    69  			{Field: dm.PaiementValeur, Label: "Montant"},
    70  			{Field: dm.PaiementModePaiement, Label: "Mode de paiement"},
    71  			{Field: dm.PaiementNumero, Label: "Numéro"},
    72  			{Field: dm.PaiementNomBanque, Label: "Banque"},
    73  			{Field: dm.PaiementDateReglement, Label: "Ajouté le"},
    74  			{Field: dm.PaiementDetails, Label: "Détails"},
    75  			{Field: dm.PaiementInBordereau, Label: "Bordereau au"},
    76  			{Field: dm.PaiementIsAcompte, Label: "Acompte ?"},
    77  			{Field: dm.PaiementIsRemboursement, Label: "Remboursement ?"},
    78  		},
    79  		HeaderFactures: []rd.Header{
    80  			{Field: dm.PersonneNomPrenom, Label: "Responsable"},
    81  			{Field: dm.FactureCamps, Label: "Camps"},
    82  		},
    83  	}
    84  	p.resetData()
    85  	return &p
    86  }
    87  
    88  func (c *Paiements) resetData() {
    89  	c.ListePaiements = nil
    90  	cValide := c.Etat.CriterePaiementValide
    91  	cBordereau := c.Etat.CriterePaiementBordereau
    92  	cFacture := c.Etat.CriterePaiementFacture
    93  	for id, paiement := range c.Base.Paiements {
    94  		match := cValide == 0 || cValide.Bool() != bool(paiement.IsInvalide)
    95  		match = match && (cFacture.IsNil() || cFacture.Int64 == paiement.IdFacture)
    96  		match = match && (cBordereau.Time().IsZero() || paiement.InBordereau == cBordereau)
    97  		if match {
    98  			c.ListePaiements = append(c.ListePaiements, c.Base.NewPaiement(id).AsItem())
    99  		}
   100  	}
   101  	sort.Slice(c.ListePaiements, func(i, j int) bool { // déterminisme
   102  		return c.ListePaiements[i].Id.Int64() < c.ListePaiements[j].Id.Int64()
   103  	})
   104  	dm.SortStableBy(c.ListePaiements, dm.PaiementDateReglement, true)
   105  
   106  	// besoin de déselectionner si absent des camps affichés
   107  	if c.Etat.PaiementCurrent != nil && !HasId(c.ListePaiements, c.Etat.PaiementCurrent) {
   108  		c.Etat.PaiementCurrent = nil
   109  	}
   110  
   111  	c.ListeFactures = nil
   112  	cCamp := c.Etat.CritereFactureCamp
   113  	cAcquittee := c.Etat.CritereFactureAcquittee
   114  	cache := c.Base.NewCacheEtatFinancier() // mise en cache
   115  	cache2 := c.Base.ResoudMessages()
   116  	for id := range c.Base.Factures {
   117  		acFac := c.Base.NewFacture(id)
   118  		match := true
   119  		if cCamp != nil {
   120  			match = false
   121  			for _, part := range acFac.GetDossiers(cache.FParticipants) {
   122  				if part.GetCamp().Id == cCamp.Int64() {
   123  					match = true
   124  					break
   125  				}
   126  			}
   127  		}
   128  		if cAcquittee != 0 {
   129  			bilan := acFac.EtatFinancier(cache, false)
   130  			match = match && cAcquittee.Bool() == bilan.IsAcquitte()
   131  		}
   132  		if match {
   133  			c.ListeFactures = append(c.ListeFactures, acFac.AsItem(cache.FParticipants, cache2, cache.FPaiements))
   134  		}
   135  	}
   136  	sort.Slice(c.ListeFactures, func(i, j int) bool { // déterminisme
   137  		return c.ListeFactures[i].Id.Int64() < c.ListeFactures[j].Id.Int64()
   138  	})
   139  	dm.SortStableBy(c.ListeFactures, dm.PersonneNomPrenom, false)
   140  }
   141  
   142  func (c *Paiements) SideButtons() ButtonsPaiements {
   143  	bs := ButtonsPaiements{}
   144  	if c.EditRight {
   145  		bs.Cree = ButtonActivated
   146  		bs.Supprimer = ButtonPresent
   147  		if c.Etat.PaiementCurrent != nil {
   148  			bs.Supprimer = ButtonActivated
   149  		}
   150  	}
   151  	bs.ExportBordereau = ButtonActivated
   152  	return bs
   153  }
   154  
   155  // CreePaiement crée un nouveau paiement sur le serveur,
   156  // de manière SYNCHRONE.
   157  func (c *Paiements) CreePaiement(paiement rd.Paiement) {
   158  	c.main.ShowStandard("Ajout du paiement...", true)
   159  	out := new(rd.Paiement)
   160  	err := requete(apiserver.UrlPaiements, http.MethodPut, paiement, out)
   161  	if err != nil {
   162  		c.main.ShowError(err)
   163  		return
   164  	}
   165  	c.Base.Paiements[out.Id] = *out
   166  	c.main.ShowStandard("Paiement ajouté avec succès.", false)
   167  	c.main.ResetAllControllers()
   168  }
   169  
   170  // UpdatePaiement met à jour les paiement transmis sur le serveur
   171  func (c *Paiements) UpdatePaiement(paiement rd.Paiement) {
   172  	job := func() (interface{}, error) {
   173  		var out rd.Paiement
   174  		err := requete(apiserver.UrlPaiements, http.MethodPost, paiement, &out)
   175  		return out, err
   176  	}
   177  	onSuccess := func(_out interface{}) {
   178  		out := _out.(rd.Paiement)
   179  		c.Base.Paiements[out.Id] = out
   180  		c.main.ShowStandard("Paiements mis à jour avec succès.", false)
   181  		c.main.ResetAllControllers()
   182  
   183  	}
   184  	c.main.ShowStandard("Mise à jour des paiements...", true)
   185  	c.main.Background.Run(job, onSuccess)
   186  }
   187  
   188  // SupprimePaiement supprime le paiement courant, après confirmation
   189  func (c *Paiements) SupprimePaiement() {
   190  	if c.Etat.PaiementCurrent == nil {
   191  		return
   192  	}
   193  	idPaiement := c.Etat.PaiementCurrent.Int64()
   194  	if !c.Onglet.ConfirmeSupprimePaiement(c.Base.NewPaiement(idPaiement)) {
   195  		c.main.ShowStandard("Suppression annulée.", false)
   196  		return
   197  	}
   198  
   199  	job := func() (interface{}, error) {
   200  		var out rd.Paiement
   201  		err := requete(apiserver.UrlPaiements, http.MethodDelete, IdAsParams(idPaiement), &out)
   202  		return out, err
   203  	}
   204  	onSuccess := func(_out interface{}) {
   205  		out := _out.(rd.Paiement)
   206  		c.main.ShowStandard(fmt.Sprintf("Paiement (%d) supprimé avec succès.", out.Id), false)
   207  		c.Etat.PaiementCurrent = nil
   208  		delete(c.Base.Paiements, out.Id)
   209  		c.main.ResetAllControllers()
   210  	}
   211  	c.main.ShowStandard("Suppression du paiement...", true)
   212  	c.main.Background.Run(job, onSuccess)
   213  }
   214  
   215  // marque les paiements comme émis (aysnchrone)
   216  func (c *Paiements) marqueAsBordereau(ids rd.Ids) {
   217  	job := func() (interface{}, error) {
   218  		var out rd.Paiements
   219  		err := requete(apiserver.UrlPaiementsBordereau, http.MethodPost, ids, &out)
   220  		return out, err
   221  	}
   222  	onSuccess := func(_out interface{}) {
   223  		out := _out.(rd.Paiements)
   224  		for _, paiement := range out {
   225  			c.Base.Paiements[paiement.Id] = paiement
   226  		}
   227  		c.main.ShowStandard("Paiements bien marqués en bordereau.", false)
   228  		c.main.ResetAllControllers()
   229  
   230  	}
   231  	c.main.ShowStandard("Mise à jour des paiements...", true)
   232  	c.main.Background.Run(job, onSuccess)
   233  }
   234  
   235  // ExportPaiements demande confirmation, puis génère une liste des paiements
   236  // transmis. Lance éventuellement en arrière plan le marquage en bordereau.
   237  // Renvoie le chemin du fichier créé.
   238  func (c *Paiements) ExportPaiements(ids []int64) string {
   239  	if len(ids) == 0 {
   240  		c.main.ShowError(errors.New("Veuillez sélectionner des paiements !"))
   241  		return ""
   242  	}
   243  	var (
   244  		alreadyExported []rd.Paiement
   245  		items           rd.Table
   246  		total           rd.Euros
   247  	)
   248  	for _, id := range ids {
   249  		acPaie := c.Base.NewPaiement(id)
   250  		rawPaie := acPaie.RawData()
   251  		total += rawPaie.Valeur
   252  		if !rawPaie.InBordereau.Time().IsZero() {
   253  			alreadyExported = append(alreadyExported, rawPaie)
   254  		}
   255  		items = append(items, acPaie.AsItem())
   256  	}
   257  	keep, simpleHeader, marque := c.Onglet.ConfirmeExportPaiements(alreadyExported, len(ids))
   258  	if !keep {
   259  		c.main.ShowStandard("Export annulé.", false)
   260  		return ""
   261  	}
   262  	headers := c.HeaderPaiements
   263  	if simpleHeader {
   264  		headers = headersBordereauSimple
   265  	}
   266  	path, err := table.EnregistreListePaiements(headers, items, total, LocalFolder)
   267  	if err != nil {
   268  		c.main.ShowError(fmt.Errorf("L'export des paiements a échoué : %s", err))
   269  		return ""
   270  	}
   271  	if marque {
   272  		c.marqueAsBordereau(ids)
   273  	}
   274  	c.main.ShowStandard("Paiements exportés dans "+path, false)
   275  	return path
   276  }
   277  
   278  // SelectPaiement sélectionne le paiement donné, en remettant à zéro les filtres si nécessaire.
   279  func (c *Paiements) SelectPaiement(id int64) {
   280  	if _, in := c.Base.Paiements[id]; !in {
   281  		c.main.ShowError(fmt.Errorf("Le paiement demandé (id %d) n'est pas dans la base !", id))
   282  		return
   283  	}
   284  	c.Etat.PaiementCurrent = rd.Id(id)
   285  
   286  	if !HasId(c.ListePaiements, rd.Id(id)) { // affiche toutes les personnes
   287  		c.Etat.CriterePaiementBordereau = rd.Time{}
   288  		c.Etat.CriterePaiementFacture = rd.OptionnalId{}
   289  		c.Etat.CriterePaiementValide = 0
   290  		c.resetData()
   291  	}
   292  	c.Onglet.GrabFocus()
   293  	c.ResetRender()
   294  }
   295  
   296  // IdentifieVirement décode le label donné et renvoie le dossier correspondant.
   297  func (c *Paiements) IdentifieVirement(label int) (dm.AccesFacture, bool) {
   298  	id, err := logs.LabelVirement.Parse(label)
   299  	if err != nil {
   300  		c.main.ShowError(err)
   301  		return dm.AccesFacture{}, false
   302  	}
   303  	// le label peut passer le test du décryptage mais être quand même invalide
   304  	if _, ok := c.Base.Factures[id]; !ok {
   305  		c.main.ShowError(fmt.Errorf("Le dossier (id %d) n'existe pas.", id))
   306  		return dm.AccesFacture{}, false
   307  	}
   308  	return c.Base.NewFacture(id), true
   309  }