github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/GUI/onglets/dons.go (about) 1 package onglets 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/benoitkugler/goACVE/server/core/apiserver" 8 dm "github.com/benoitkugler/goACVE/server/core/datamodel" 9 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 10 "github.com/benoitkugler/goACVE/server/core/rawdata/matching" 11 12 "github.com/benoitkugler/goACVE/client/GUI/basic" 13 "github.com/benoitkugler/goACVE/client/GUI/common" 14 "github.com/benoitkugler/goACVE/client/GUI/fields" 15 "github.com/benoitkugler/goACVE/client/GUI/lists" 16 "github.com/benoitkugler/goACVE/client/controllers" 17 "github.com/therecipe/qt/core" 18 "github.com/therecipe/qt/widgets" 19 ) 20 21 func (d *Dons) Render() { 22 d.liste.Model().SetData(d.controller.Liste) 23 if d.controller.Etat.DonCurrent != nil { 24 d.liste.SelectRow(d.controller.Etat.DonCurrent) 25 } 26 d.stats.SetData(d.controller.GetStats()) 27 d.liste.SetSearch(d.controller.Etat.Recherche) 28 d.annee.SetYears(d.controller.PossibleYears()) 29 d.annee.SetData(d.controller.Etat.CritereAnnee) 30 d.emis.SetData(d.controller.Etat.CritereEmis) 31 d.showOrga.SetData(d.controller.Etat.CritereOrganismes) 32 } 33 34 func (d *Dons) ConfirmeForceRecuFiscal(don dm.AccesDon) bool { 35 dona := don.GetDonateur() 36 if _, isOrga := dona.(dm.AccesOrganisme); isOrga { 37 return true 38 } 39 label := "annonyme" 40 if dona, ok := dona.(dm.AccesPersonne); ok { 41 label = "de " + dona.RawData().NomPrenom().String() 42 } 43 msg := fmt.Sprintf("Le don <i>%s</i>, d'un montant de %s et reçu le %s, <br/>a déjà été émis dans un reçu fiscal. <br/>"+ 44 "Etes-vous sur de vouloir de <b>ré-éditer</b> un reçu le contenant ?", 45 label, don.RawData().Valeur, don.RawData().DateReception) 46 return basic.Confirmation(msg, "Editer le reçu fiscal", basic.ONAction) 47 } 48 49 type Dons struct { 50 *widgets.QFrame 51 controller *controllers.Dons 52 parent *widgets.QTabWidget 53 toolbar *widgets.QToolBar 54 55 liste lists.Table 56 stats common.Statistics 57 58 annee fields.Year 59 showOrga fields.OptionnalBool 60 emis fields.OptionnalBool 61 } 62 63 func newDons(ct *controllers.Dons) *Dons { 64 p := Dons{QFrame: basic.Frame(), controller: ct} 65 66 p.liste = lists.Table{ 67 Liste: lists.Liste{ 68 Headers: ct.Header, 69 Title: "Dons", 70 OnDoubleClick: func(acces rd.Item, _ int) { 71 p.editeDon(acces.Id.Int64()) 72 }, 73 OnClick: func(acces rd.Item, _ int) { 74 ct.Etat.DonCurrent = acces.Id 75 p.UpdateToolbar() 76 }, 77 OnSearch: func(pattern string) { 78 ct.Etat.Recherche = pattern 79 ct.Reset() 80 }, 81 OnRightClick: func(acces rd.Item, pos *core.QPoint) { 82 don := ct.Base.NewDon(acces.Id.Int64()) 83 menu := widgets.NewQMenu(p) 84 if donateur := don.GetDonateur(); donateur != nil { 85 menu.AddAction("Aller au donateur").ConnectTriggered(func(bool) { 86 ct.Main().Controllers.Personnes.SelectPersonne(donateur) 87 }) 88 } 89 if don.RawData().ModePaiement == rd.MPHelloasso { 90 menu.AddAction("Modifier l'ID HelloAsso").ConnectTriggered(func(bool) { 91 p.setIDHelloasso(don.RawData()) 92 }) 93 } 94 if len(menu.Actions()) > 0 { 95 p.liste.ShowContextMenu(menu, pos) 96 } 97 }, 98 OnSort: func(field rd.Field, reverse bool) { 99 lists.DefaultSort(p.liste, field, reverse) 100 }, 101 }, 102 } 103 104 p.emis = fields.NewOptionnalBool(true, "Edité", "Non édité") 105 p.emis.ConnectCurrentIndexChanged(func(_ int) { 106 ct.Etat.CritereEmis = p.emis.GetData() 107 ct.Reset() 108 }) 109 p.annee = fields.NewYear() 110 p.annee.ConnectActivated(func(int) { 111 ct.Etat.CritereAnnee = p.annee.GetData() 112 ct.Reset() 113 }) 114 p.showOrga = fields.NewOptionnalBool(true, "Organismes", "Personnes") 115 p.showOrga.ConnectCurrentIndexChanged(func(_ int) { 116 ct.Etat.CritereOrganismes = p.showOrga.GetData() 117 ct.Reset() 118 }) 119 120 rightHeader := widgets.NewQHBoxLayout() 121 rightHeader.AddWidget(basic.Label("Donateur :"), 1, 0) 122 rightHeader.AddWidget(p.showOrga, 1, core.Qt__AlignLeft) 123 rightHeader.AddWidget(basic.Label("Année :"), 1, 0) 124 rightHeader.AddWidget(p.annee, 1, core.Qt__AlignLeft) 125 rightHeader.AddWidget(basic.Label("Reçu fiscal :"), 1, 0) 126 rightHeader.AddWidget(p.emis, 1, core.Qt__AlignRight) 127 128 p.liste.RightHeader = rightHeader 129 p.liste.Init() 130 p.liste.View.(*widgets.QTableView).HorizontalHeader().SetMinimumSectionSize(200) 131 132 p.stats = common.NewStatistics(true) 133 134 lay := widgets.NewQVBoxLayout2(p) 135 lay.AddWidget(p.stats, 0, 0) 136 lay.AddWidget(p.liste, 1, 0) 137 return &p 138 } 139 140 func (d *Dons) creer() { 141 f := common.NewFicheDon(d.controller.Base, true) 142 det := common.NewDetails(&f, true) 143 newDon := rd.Don{DateReception: rd.Date(time.Now())} 144 f.SetData(newDon, rd.DonDonateur{}) // date par défaut à aujourdhui 145 if det.Exec() == 0 { 146 return 147 } 148 don, donateur := f.GetData() 149 params := apiserver.CreateDonIn{Don: don, Donateur: donateur} 150 151 // notification optionnelle 152 if donateur.IId() != nil { 153 dial := basic.Dialog2("Notifier par mail") 154 155 b1 := basic.Button("Enregistrer et notifier") 156 b1.SetObjectName(basic.ONAdd) 157 b1.ConnectClicked(func(bool) { 158 params.Notifie = true 159 dial.Accept() 160 }) 161 162 b2 := basic.Button("Ne pas notifier") 163 b2.SetObjectName(basic.ONAction) 164 b2.ConnectClicked(func(bool) { 165 params.Notifie = false 166 dial.Accept() 167 }) 168 169 h := widgets.NewQHBoxLayout() 170 h.AddWidget(b2, 1, 0) 171 h.AddStretch(3) 172 h.AddWidget(b1, 1, 0) 173 174 lay := widgets.NewQVBoxLayout2(dial) 175 lay.AddWidget(basic.Label("Souhaitez-vous notifier le donateur par mail ?"), 2, 0) 176 lay.AddLayout(h, 1) 177 178 if dial.Exec() == 0 { 179 return // annulation 180 } 181 } 182 183 d.controller.CreeDon(params) 184 } 185 186 func (d *Dons) editeDon(id int64) { 187 f := common.NewFicheDon(d.controller.Base, d.controller.EditRight) 188 f.SetData(d.controller.Base.Dons[id], d.controller.Base.DonDonateurs[id]) 189 det := common.NewDetails(&f, d.controller.EditRight) 190 if det.Exec() == 0 { 191 return 192 } 193 don, donateur := f.GetData() 194 d.controller.UpdateDon(apiserver.CreateDonIn{Don: don, Donateur: donateur}) 195 } 196 197 func (d *Dons) supprimer() { 198 keep := basic.Confirmation("Etes-vous sur de vouloir <b>supprimer</b> ce don ?", 199 "Supprimer", basic.ONDelete) 200 if keep { 201 d.controller.SupprimeDon() 202 } 203 } 204 205 func (d *Dons) setIDHelloasso(don rd.Don) { 206 dial := basic.Dialog("ID HelloAsso") 207 208 e := fields.NewString(true) 209 e.SetData(rd.String(don.Infos.IdPaiementHelloAsso)) // valeur initiale 210 valid := basic.Button("Modifier l'ID") 211 valid.SetObjectName(basic.ONAction) 212 valid.ConnectClicked(func(bool) { 213 don.Infos.IdPaiementHelloAsso = e.GetData().String() 214 d.controller.UpdateDon(apiserver.CreateDonIn{ 215 Don: don, 216 Donateur: d.controller.Base.DonDonateurs[don.Id], 217 }) 218 dial.Accept() 219 }) 220 221 dial.Layout().AddWidget(basic.Label(`Définir un ID HelloAsso permet de rattacher 222 ce don à un don HelloAsso. <br/> Le don HelloAsso n'apparaitra alors plus lors de la procédure d'import.`)) 223 dial.Layout().AddWidget(e) 224 dial.Layout().AddWidget(valid) 225 dial.Exec() 226 } 227 228 func (d *Dons) exporterListe() { 229 dial := basic.Dialog2("Export des dons courants") 230 231 avecCoord := fields.NewBool(true) 232 valid := basic.Button("Exporter") 233 valid.SetObjectName(basic.ONAction) 234 235 valid.ConnectClicked(func(_ bool) { 236 dial.Close() 237 path := d.controller.ExporteListe(bool(avecCoord.GetData())) 238 if path != "" { 239 basic.ShowFile(path) 240 } 241 }) 242 243 lay := widgets.NewQVBoxLayout2(dial) 244 lay.AddWidget(basic.Label("Veuillez choisir les options d'export :"), 1, 0) 245 form := widgets.NewQFormLayout(nil) 246 form.AddRow3("Afficher les coordonnées des donateurs", avecCoord) 247 lay.AddLayout(form, 2) 248 lay.AddWidget(valid, 1, core.Qt__AlignCenter) 249 dial.Exec() 250 } 251 252 func (d *Dons) editerRecu() { 253 dial := basic.Dialog2("Edition de reçus fiscaux") 254 num := fields.NewInt(true) 255 reEdit := fields.NewBool(true) 256 preview := basic.Label("") 257 preview.SetMaximumWidth(500) 258 preview.SetWordWrap(true) 259 valid := basic.Button("Editer les reçus") 260 valid.SetObjectName(basic.ONAction) 261 var params dm.OptionsRecuFiscal 262 263 valid.ConnectClicked(func(_ bool) { 264 valid.SetText("Edition en cours...") 265 valid.SetEnabled(false) 266 basic.Delay(func() { 267 path := d.controller.EditeRecusFiscaux(params) 268 dial.Accept() 269 if path != "" { 270 basic.ShowFile(path) 271 } 272 }) 273 }) 274 275 onChanged := func() { 276 params = dm.OptionsRecuFiscal{ 277 Annee: int(num.GetData()), 278 ReEmet: bool(reEdit.GetData()), 279 } 280 rfs := params.Select(d.controller.Base.Dons, d.controller.Base.DonDonateurs) 281 text := fmt.Sprintf(`<br/> 282 Reçus fiscaux à émettre : <b>%d</b><br/> 283 Dons concernés : <i>%d</i><br/> 284 Dons annonymes ignorés : <i>%d</i><br/>`, 285 len(rfs.Recus), len(rfs.IdsDons), rfs.NbAnnonyme) 286 preview.SetText(text) 287 valid.SetEnabled(len(rfs.Recus) != 0) 288 } 289 290 num.ConnectValueChanged(func(_ int) { onChanged() }) 291 reEdit.ConnectClicked(func(_ bool) { onChanged() }) 292 293 now := time.Now() 294 year := now.Year() 295 if now.Month() < time.April { 296 year = now.Year() - 1 297 } 298 num.SetData(rd.Int(year)) // valeur par défaut 299 300 lay := widgets.NewQFormLayout(dial) 301 lay.AddRow3("Année", num) 302 lay.AddRow3("Ré-emettre les dons déjà traités", reEdit) 303 lay.AddRow5(preview) 304 lay.AddRow5(valid) 305 dial.Exec() 306 } 307 308 func (d *Dons) importHelloasso() { 309 dial := basic.Dialog2("Import des dons HelloAsso") 310 311 var ( 312 liste lists.Table 313 dons controllers.DonsImported 314 curentIndex = -1 // pour pouvoir enlever le don après identification 315 comp *common.Similaires 316 ) 317 comp = common.NewSimilaires(d.controller.Base, func(target matching.IdentifieTarget) { 318 d.controller.IdentifieDonHelloasso(dons[curentIndex], target) 319 // comme la mise à jour est asynchrone, on espère ne pas 320 // avoir d'erreur et on enlève directement le don de la liste 321 // Au pire, l'utilisateur relancera l'import 322 dons = append(dons[:curentIndex], dons[curentIndex+1:]...) 323 liste.Model().SetData(dons.Table()) 324 comp.SetData(rd.BasePersonne{}, false) // désactive 325 }) 326 327 liste = lists.Table{ 328 Liste: lists.Liste{ 329 Placeholder: "Chargement des dons HelloAsso...", 330 Headers: controllers.HeaderDonImported, 331 OnClick: func(acces rd.Item, _ int) { 332 index := int(acces.Id.Int64()) 333 curentIndex = index 334 if index >= len(dons) { // ne devrait jamais arriver 335 return 336 } 337 comp.SetData(dons[index].Donateur, true) 338 }, 339 OnRightClick: func(acces rd.Item, pos *core.QPoint) {}, 340 OnSort: func(field rd.Field, reverse bool) { 341 lists.DefaultSort(liste, field, reverse) 342 }, 343 Title: "Dons présents sur le serveur HelloAsso", 344 }, 345 } 346 liste.Init() 347 liste.View.(*widgets.QTableView).HorizontalHeader().SetMinimumSectionSize(200) 348 lay := widgets.NewQVBoxLayout2(dial) 349 lay.SetSpacing(1) 350 lay.AddWidget(liste, 4, 0) 351 lay.AddWidget(basic.Label("<b>Identifier</b> le donateur :"), 1, 0) 352 lay.AddWidget(comp, 3, 0) 353 dial.SetMinimumHeight(500) 354 dial.Show() 355 356 basic.Delay(func() { 357 dons = d.controller.ChargeDonsHelloasso() 358 if len(dons) == 0 { 359 dial.Reject() 360 return 361 } 362 liste.Model().SetData(dons.Table()) 363 }) 364 365 }