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 }