github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/GUI/onglets/suivi_dossiers.go (about) 1 package onglets 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 9 "github.com/benoitkugler/goACVE/client/GUI/onglets/notifications" 10 11 "github.com/benoitkugler/goACVE/client/GUI/messages" 12 "github.com/benoitkugler/goACVE/server/core/apiserver" 13 14 dm "github.com/benoitkugler/goACVE/server/core/datamodel" 15 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 16 17 "github.com/benoitkugler/goACVE/client/GUI/fields" 18 "github.com/benoitkugler/goACVE/client/GUI/lists" 19 20 "github.com/benoitkugler/goACVE/client/GUI/common" 21 22 "github.com/benoitkugler/goACVE/client/GUI/basic" 23 24 "github.com/therecipe/qt/core" 25 26 "github.com/therecipe/qt/widgets" 27 28 "github.com/benoitkugler/goACVE/client/controllers" 29 ) 30 31 func (t *SuiviDossiers) Render() { 32 t.dossiers.Model().SetData(t.controller.Liste) 33 t.dossiers.SetSearch(t.controller.Etat.Recherche) 34 t.updateSelection() 35 36 t.acquite.SetData(t.controller.Etat.CritereAcquite) 37 t.attente.SetData(string(t.controller.Etat.CritereAttente)) 38 t.camp.Update() 39 t.camp.SetData(t.controller.Etat.CritereCamp) 40 41 // on affiche une fois au démarrage les notifications 42 if t.IsActif() && !t.hasShownNotifications { 43 if t.controller.HasNotifications() { 44 t.hasShownNotifications = true 45 t.showNotifications() 46 } 47 } 48 } 49 50 func (t *SuiviDossiers) updateSelection() { 51 if current := t.controller.Etat.FactureCurrent; current != nil { 52 t.dossiers.SelectRow(current) 53 t.fil.setMessages(t.controller.Base.NewFacture(current.Int64()).GetEtat(nil, nil).PseudoMessages(dm.POVCentre)) 54 t.fil.enableActions(true) 55 } else { 56 t.fil.setMessages(nil) 57 t.fil.enableActions(false) 58 } 59 } 60 61 func (t *SuiviDossiers) ConfirmeSupprimeFacture(html string) bool { 62 return basic.Confirmation(html, "Supprimer", basic.ONDelete) 63 } 64 65 func (t *SuiviDossiers) ConfirmeMail(fac dm.AccesFacture, messageKind rd.MessageKind) bool { 66 adresses := []string{fac.GetPersonne().RawData().Mail.String()} 67 for _, adresse := range fac.RawData().CopiesMails { 68 adresses = append(adresses, adresse) 69 } 70 message := fmt.Sprintf("Confirmez-vous l'envoi d'un mail <i>%s</i> à <br/> <b>%s</b> ? ", messageKind, 71 strings.Join(adresses, ", ")) 72 return basic.Confirmation(message, "Envoyer", basic.ONAction) 73 } 74 75 func (t *SuiviDossiers) ConfirmeSupprimeMessages(nb int) bool { 76 message := "Confirmez-vous la suppression du message ?" 77 if nb > 1 { 78 message = fmt.Sprintf("Confirmez-vous la suppression de %d messages ?", nb) 79 } 80 message += "<br/> Attention, cette opération est <b>irréversible</b>." 81 return basic.Confirmation(message, "Supprimer", basic.ONDelete) 82 } 83 84 func (t *SuiviDossiers) ShowPreviewFacture(fac dm.AccesFacture) { 85 d := basic.Dialog("Bilan financier") 86 facHtml := basic.Label(fac.Description()) 87 facHtml.ConnectLinkActivated(func(link string) { 88 id, err := strconv.Atoi(link) 89 if err != nil { 90 return 91 } 92 d.Reject() 93 t.controller.Main().Controllers.Paiements.SelectPaiement(int64(id)) 94 }) 95 d.Layout().AddWidget(facHtml) 96 d.Exec() 97 } 98 99 type SuiviDossiers struct { 100 *widgets.QFrame 101 controller *controllers.SuiviDossiers 102 parent *widgets.QTabWidget 103 toolbar *widgets.QToolBar 104 105 dossiers lists.Table 106 acquite fields.Completion 107 attente fields.Enum 108 camp fields.Camp 109 fil fil 110 111 hasShownNotifications bool 112 } 113 114 func newSuiviDossiers(ct *controllers.SuiviDossiers) *SuiviDossiers { 115 p := SuiviDossiers{QFrame: basic.Frame(), controller: ct} 116 117 p.dossiers = lists.Table{ 118 Liste: lists.Liste{ 119 SingleSelection: true, 120 Headers: ct.Header, 121 Title: "Dossiers", 122 OnClick: func(acces rd.Item, _ int) { 123 ct.Etat.FactureCurrent = acces.Id 124 // on ne reset pas complètement pour éviter de perdre le click 125 // ce qui empèche le double clic 126 p.updateSelection() 127 p.UpdateToolbar() 128 }, 129 OnDoubleClick: func(acces rd.Item, _ int) { 130 // dossier courant 131 p.editDossier() 132 }, 133 OnSearch: func(pattern string) { 134 ct.Etat.Recherche = pattern 135 ct.Reset() 136 }, 137 OnSort: func(field rd.Field, reverse bool) { 138 ct.Etat.CriteresTri = ct.Etat.CriteresTri.AddCritere(field, reverse) 139 ct.Reset() 140 }, 141 OnRightClick: func(acces rd.Item, pos *core.QPoint) { 142 fac := ct.Base.NewFacture(acces.Id.Int64()) 143 menu := widgets.NewQMenu(p.dossiers) 144 menu.AddAction("Afficher les paiements...").ConnectTriggered(func(_ bool) { 145 p.ShowPreviewFacture(fac) 146 }) 147 menu.AddSeparator() 148 menu.AddAction("Copier le lien vers l'espace de suivi").ConnectTriggered(func(_ bool) { 149 url := fac.RawData().UrlEspacePerso("/espace_perso") 150 url = controllers.Server.EndpointURL(url) 151 basic.Copy(url) 152 ct.Main().ShowStandard("Lien : "+url, false) 153 }) 154 menu.AddAction("Copier le label de virement").ConnectTriggered(func(_ bool) { 155 label := fac.LabelVirement() 156 basic.Copy(label) 157 ct.Main().ShowStandard("Label du virement : "+label, false) 158 }) 159 160 menu.AddSeparator() 161 menu.AddAction("Aller au responsable").ConnectTriggered(func(_ bool) { 162 ct.Main().Controllers.Personnes.SelectPersonne(fac.GetPersonne()) 163 }) 164 menu.AddSeparator() 165 for _, doss := range fac.GetDossiers(nil) { 166 resp, doss := doss.GetPersonne(), doss 167 menu.AddAction("Aller au participant " + resp.RawData().NomPrenom().String()).ConnectTriggered(func(_ bool) { 168 ct.Main().Controllers.Camps.SelectParticipant(doss) 169 }) 170 } 171 menu.AddSeparator() 172 menu.AddAction("Fusionner vers ...").ConnectTriggered(func(bool) { 173 p.fusionneDossier(fac) 174 }) 175 p.dossiers.ShowContextMenu(menu, pos) 176 }, 177 }, 178 } 179 p.dossiers.Init() 180 p.dossiers.HorizontalHeader().SetMinimumSectionSize(300) 181 182 p.acquite = fields.NewCompletion(true) 183 p.acquite.ConnectCurrentIndexChanged(func(_ int) { 184 ct.Etat.CritereAcquite = p.acquite.GetData() 185 ct.Reset() 186 }) 187 p.attente = fields.NewEnum([]fields.Choice{ 188 {Field: string(controllers.Indifferent), Label: "Tous"}, 189 {Field: string(controllers.MasquerNoAttente), Label: "Avec participants en attente"}, 190 {Field: string(controllers.MasquerOnlyAttente), Label: "Avec participants inscrits"}, 191 }, true) 192 p.attente.ConnectCurrentIndexChanged(func(_ int) { 193 ct.Etat.CritereAttente = controllers.CritereAttente(p.attente.GetData()) 194 ct.Reset() 195 }) 196 p.camp = fields.NewCamp(true, ct.Base, true) 197 p.camp.ShowTerminated(false) 198 p.camp.ConnectActivated(func(_ int) { 199 var optId rd.OptionnalId 200 if p.camp.GetData() != nil { 201 optId = rd.NewOptionnalId(p.camp.GetData().Id) 202 } 203 ct.Etat.CritereCamp = optId 204 ct.Reset() 205 }) 206 showTerminated := widgets.NewQCheckBox2("Afficher les camps terminés", nil) 207 showTerminated.ConnectClicked(func(checked bool) { 208 p.camp.ShowTerminated(checked) 209 }) 210 211 // options 212 options := widgets.NewQFormLayout(nil) 213 options.AddRow3("Choix du camp :", p.camp) 214 options.AddRow5(showTerminated) 215 options.AddRow3("Liste d'attente :", p.attente) 216 options.AddRow3("Etat du règlement :", p.acquite) 217 218 // bandeau du haut 219 haut := widgets.NewQHBoxLayout() 220 haut.AddWidget(p.dossiers, 3, 0) 221 haut.AddLayout(options, 1) 222 223 // fil de discussion 224 p.fil = newFil(ct) 225 p.fil.msgList.OnMessageGroupe = p.onMessageGroupe 226 227 // final 228 lay := widgets.NewQVBoxLayout2(p) 229 lay.AddLayout(haut, 2) 230 lay.AddWidget(p.fil, 3, 0) 231 return &p 232 } 233 234 func (t *SuiviDossiers) creer() { 235 dial := basic.Dialog2("Créer un dossier") 236 237 choixRespo := fields.NewBoutonIdPersonne(true, t.controller.Base, false) 238 239 listeP := lists.Table{ 240 Liste: lists.Liste{ 241 Headers: lists.HeadersRechercheParticipants, 242 Title: "Participants à lier au dossier", 243 MinHeight: 200, 244 }, 245 HideHeaders: true, 246 } 247 listeP.OnAdd = func() { 248 dial := lists.NewRechercheParticipant(t.controller.Base) 249 dial.Exec() 250 if dial.Out != nil { 251 participant := t.controller.Base.NewParticipant(dial.Out.Int64()) 252 listeP.Model().SetData(append(listeP.Model().GetData(), participant.AsItem(nil))) 253 } 254 } 255 listeP.OnDelete = func(_ rd.Item, index int) { 256 listeP.Model().SetData(controllers.RemoveItem(listeP.Model().GetData(), index)) 257 } 258 listeP.Init() 259 260 valid := basic.Button("Créer le dossier") 261 valid.SetObjectName(basic.ONAdd) 262 valid.ConnectClicked(func(_ bool) { 263 if choixRespo.GetData() == nil { 264 basic.ShowError(errors.New("Veuillez choisir un <b>responsable</b> pour le dossier !")) 265 return 266 } 267 dial.Accept() 268 }) 269 lay := widgets.NewQFormLayout(dial) 270 271 lay.AddRow(basic.Label("<b>Choix du responsable :</b>"), choixRespo) 272 lay.AddRow5(listeP) 273 lay.AddRow5(valid) 274 275 if dial.Exec() == 0 { 276 return 277 } 278 t.SetEnabled(false) 279 t.controller.CreeFacture(choixRespo.GetData().Int64(), listeP.Model().GetData()) 280 t.SetEnabled(true) 281 } 282 283 func (t *SuiviDossiers) supprimer() { 284 t.controller.SupprimeFacture() 285 } 286 287 func (t *SuiviDossiers) onMessageGroupe(idMessages rd.Ids) { 288 idFactures := rd.NewSet() 289 for _, idMessage := range idMessages { 290 idFactures.Add(t.controller.Base.Messages[idMessage].IdFacture) 291 } 292 t.writeStreamMessages(idFactures.Keys()) 293 } 294 295 func (t *SuiviDossiers) writeStreamMessages(idFactures rd.Ids) { 296 contenu, keep := messages.EditMessage("", true, len(idFactures)) 297 if !keep { 298 t.controller.Main().ShowStandard("Envoi annulé", false) 299 return 300 } 301 302 dialog := messages.NewMonitor(len(idFactures)) 303 sender := common.NewMailsSender(nil) 304 // mise en place des callbacks 305 sender.Monitor.OnError = dialog.OnError 306 sender.Monitor.OnSuccess = func(index int, out apiserver.NotifieManyOut) { 307 t.controller.OnSuccesNotifieMessage(out) 308 dialog.OnSuccess(index) 309 } 310 sender.OnFinished = dialog.ShowBilan 311 312 job := func(monitor controllers.SendMessagesSignals) ([]controllers.DossierError, error) { 313 return t.controller.EnvoiManyMessages(contenu, idFactures, monitor) 314 } 315 sender.SetupLaunch(job) 316 } 317 318 func (t *SuiviDossiers) envoiManyMessages() { 319 ids := messages.SelectFactures(t.controller) 320 if len(ids) == 0 { 321 t.controller.Main().ShowStandard("Envoi annulé", false) 322 return 323 } 324 t.writeStreamMessages(ids) 325 } 326 327 func (t *SuiviDossiers) envoiDocumentsCamp() { 328 idCamp, resend, keep := messages.SelectCamp(t.controller.Base) 329 if !keep { 330 t.controller.Main().ShowStandard("Envoi annulé", false) 331 return 332 } 333 334 ids := t.controller.SelectionneFacturesDocuments(idCamp, resend) 335 if len(ids) == 0 { 336 basic.ShowError(errors.New("Il n'y a aucun message à envoyer.")) 337 return 338 } 339 streamEnvoiDocuments(t.controller, idCamp, ids) 340 } 341 342 func streamEnvoiDocuments(ct *controllers.SuiviDossiers, idCamp int64, idsFactures rd.Ids) { 343 // on vérifie que les envois ne sont pas verrouillés 344 if err := ct.Base.NewCamp(idCamp).RawData().CheckEnvoisLock(); err != nil { 345 basic.ShowError(err) 346 return 347 } 348 349 dialog := messages.NewMonitor(len(idsFactures)) 350 sender := common.NewDocumentsSender(nil) 351 // mise en place des callbacks 352 sender.Monitor.OnError = dialog.OnError 353 sender.Monitor.OnSuccess = func(index int, out apiserver.NotifieDocumentsOut) { 354 ct.OnSuccesNotifieDocuments(out) 355 dialog.OnSuccess(index) 356 } 357 sender.OnFinished = dialog.ShowBilan 358 359 job := func(monitor controllers.SendDocumentsSignals) ([]controllers.DossierError, error) { 360 return ct.EnvoiDocumentsCamp(idCamp, idsFactures, monitor) 361 } 362 sender.SetupLaunch(job) 363 } 364 365 func (t *SuiviDossiers) editDossier() { 366 det := NewDetailsDossier(t.controller, common.MainWindow.StatusBar()) 367 det.Exec() 368 } 369 370 func (t *SuiviDossiers) fusionneDossier(fac dm.AccesFacture) { 371 dial := basic.Dialog2("Fusionner deux dossiers") 372 373 idTarget := fields.NewBoutonIdFacture(true, t.controller.Base) 374 375 notifie := fields.NewBool(true) 376 notifie.SetData(true) // cochée par défaut 377 378 valid := basic.Button("Fusionner") 379 valid.SetObjectName(basic.ONAction) 380 valid.ConnectClicked(func(bool) { dial.Accept() }) 381 382 valid.SetEnabled(false) 383 idTarget.DataChanged = func() { 384 valid.SetEnabled(idTarget.GetData() != nil) 385 } 386 387 lay := widgets.NewQFormLayout(dial) 388 lay.AddRow3("Dossier vers lequel fusionner:", idTarget) 389 lay.AddRow3("Notifier par mail", notifie) 390 lay.AddRow5(valid) 391 392 dial.SetMinimumWidth(500) 393 if dial.Exec() == 0 { 394 t.controller.Main().ShowStandard("Fusion annulée", false) 395 return 396 } 397 params := apiserver.FusionneFacturesIn{ 398 From: fac.Id, 399 To: idTarget.GetData().Int64(), 400 Notifie: bool(notifie.GetData()), 401 } 402 t.controller.FusionneDossiers(params) 403 } 404 405 // ----------------------------- Alertes nouveaux messages / aides ----------------------------- 406 407 func (t *SuiviDossiers) showNotifications() { 408 aides, factures := t.controller.Notifications() 409 410 d := basic.Dialog("Notifications") 411 listAides := notifications.NewList() 412 listFactures := notifications.NewList() 413 for _, aide := range aides { 414 aide := aide // variable in closure 415 listAides.AddNotification(aide.Label(), func() { 416 t.controller.Main().Controllers.Aides.SelectAide(aide) 417 }) 418 } 419 420 for _, facture := range factures { 421 facture := facture // variable in closure 422 isInscription := !facture.RawData().IsValidated 423 label := facture.GetPersonne().RawData().NomPrenom().String() 424 onLink := func() { 425 t.controller.SelectFacture(facture) 426 } 427 if isInscription { 428 label = "<i>(Nouvelle inscription)</i> " + label 429 onLink = nil // on désactive le goTo 430 } 431 listFactures.AddNotification(label, onLink) 432 } 433 434 d.SetMinimumWidth(600) 435 436 d.Layout().AddWidget(basic.Label(fmt.Sprintf("<b>Nouveaux messages (%d) :</b>", len(factures)))) 437 if len(factures) == 0 { 438 d.Layout().AddWidget(basic.Label("<i>Aucun message en attente.</i>")) 439 } else { 440 d.Layout().AddWidget(listFactures) 441 } 442 443 d.Layout().AddWidget(basic.Label(fmt.Sprintf("<b>Aides déclarées et non vérifiées (%d) :</b>", len(aides)))) 444 if len(aides) == 0 { 445 d.Layout().AddWidget(basic.Label("<i>Aucune aide en attente.</i>")) 446 } else { 447 d.Layout().AddWidget(listAides) 448 } 449 450 d.Show() 451 } 452 453 // --------------------------------------------------------------------------------------------- 454 // --------------------- Fiche de détails : facture + participants + aides --------------------- 455 // --------------------------------------------------------------------------------------------- 456 457 type widgetParticipant struct { 458 *widgets.QFrame 459 460 deleteButton *widgets.QPushButton 461 label *widgets.QLabel 462 boutonOption *fields.OptionsFinanceParticipant 463 aides lists.Table 464 editable bool 465 466 onDeleteAide func(a rd.Item) bool 467 onAjouteAide func() 468 onChangeOptions func(rd.Remises, rd.OptionPrixParticipant) 469 onGoToParticipant func(id int64) 470 } 471 472 func newWidgetParticipant(editable bool) *widgetParticipant { 473 w := widgetParticipant{QFrame: basic.Frame(), editable: editable} 474 w.SetObjectName("participant") 475 lay := widgets.NewQGridLayout(w) 476 w.label = basic.Label("") 477 w.label.SetTextFormat(core.Qt__RichText) 478 w.label.ConnectLinkActivated(func(link string) { 479 if id, err := strconv.Atoi(link); err == nil { 480 w.onGoToParticipant(int64(id)) 481 } 482 }) 483 484 w.boutonOption = fields.NewOptionsFinanceParticipant(editable, "", nil, rd.Plage{}) 485 w.boutonOption.DataChanged = func() { 486 w.onChangeOptions(w.boutonOption.GetData()) 487 } 488 489 w.deleteButton = basic.Button("Supprimer") 490 w.deleteButton.SetIcon(basic.Icons.Delete) 491 w.deleteButton.SetObjectName(basic.ONDelete) 492 w.deleteButton.SetEnabled(editable) 493 494 w.aides = lists.Table{ 495 Liste: lists.Liste{ 496 Headers: []rd.Header{ 497 {Field: dm.AideStructure, Label: "Organisme"}, 498 {Field: dm.AideMontant, Label: "Montant"}, 499 {Field: dm.AideValide, Label: "Valide"}, 500 }, 501 MaxWidth: 600, 502 MinHeight: 110, 503 Placeholder: "Aucune aide n'est déclarée.", 504 }, 505 ResizeToContents: true, 506 } 507 if editable { 508 w.aides.OnAdd = func() { 509 w.onAjouteAide() // variable a setup 510 } 511 w.aides.OnDelete = func(acces rd.Item, index int) { 512 mustDelete := w.onDeleteAide(acces) 513 if mustDelete { 514 w.aides.Model().SetData(controllers.RemoveItem(w.aides.Model().GetData(), index)) 515 } 516 } 517 } 518 w.aides.Init() 519 520 w.label.SetWordWrap(true) 521 w.label.SetMaximumWidth(400) 522 lay.AddWidget2(w.label, 0, 0, 0) 523 lay.AddWidget2(w.boutonOption, 1, 0, 0) 524 lay.AddWidget2(w.deleteButton, 2, 0, 0) 525 lay.AddWidget3(w.aides, 0, 1, 3, 2, 0) 526 return &w 527 } 528 529 // met à jour les widgets 530 func (w *widgetParticipant) render(part dm.AccesParticipant) { 531 camp := part.GetCamp().RawData() 532 533 // label 534 w.label.SetText(fmt.Sprintf("<i><a href='%d'>%s au %s</a></i>", part.Id, part.GetPersonne().RawData().NomPrenom(), 535 camp.Label())) 536 style := "padding: 2px;" 537 if !part.RawData().ListeAttente.IsInscrit() { 538 style += "background-color : " + dm.CouleurListeAttente.AHex() 539 } 540 w.label.SetStyleSheet(style) 541 542 // options financières 543 w.boutonOption.OptionPrixParticipant = fields.NewOptionPrixParticipant(w.editable, camp.OptionPrix.Active, camp.OptionPrix.Statut, 544 rd.Plage{From: camp.DateDebut, To: camp.DateFin}) 545 w.boutonOption.SetData(part.RawData().Remises, part.RawData().OptionPrix) 546 547 // aides 548 partAides := part.GetAides(nil) 549 data := make([]rd.Item, len(partAides)) 550 for index, aide := range partAides { 551 data[index] = aide.AsItem() 552 } 553 w.aides.Model().SetData(data) 554 } 555 556 type DetailsDossier struct { 557 *widgets.QDialog 558 acces dm.AccesFacture 559 560 ct *controllers.SuiviDossiers 561 statusBar *widgets.QStatusBar 562 scroll *widgets.QScrollArea 563 ficheFacture *common.FicheFacture 564 } 565 566 func NewDetailsDossier(ct *controllers.SuiviDossiers, mainStatusBar *widgets.QStatusBar) DetailsDossier { 567 d := DetailsDossier{QDialog: basic.Dialog2("Détails du dossier"), ct: ct} 568 lay := widgets.NewQGridLayout(d) 569 idFac := ct.Etat.FactureCurrent 570 if idFac == nil { 571 lay.AddWidget2(basic.Label("Aucun dossier courant !"), 0, 0, 0) 572 return d 573 } 574 d.acces = ct.Base.NewFacture(idFac.Int64()) 575 576 fac := common.NewFicheFacture(ct.Base, ct.EditRight) 577 fac.SetLayout() 578 fac.SetData(d.acces.RawData(), rd.NewOptionnalId(d.acces.GetPersonne().Id)) 579 d.ficheFacture = &fac 580 581 valid := basic.ButtonEnregistrer() 582 valid.ConnectClicked(func(_ bool) { 583 if err := fac.Valid(); err != nil { 584 basic.ShowError(err) 585 return 586 } 587 d.UpdateFacture(fac.GetData()) 588 }) 589 d.scroll = widgets.NewQScrollArea(nil) 590 d.scroll.SetMinimumHeight(500) 591 592 ajoutPart := basic.Button("Ajouter un participant") 593 ajoutPart.SetObjectName(basic.ONAction) 594 ajoutPart.ConnectClicked(func(_ bool) { d.AjouteParticipant() }) 595 596 d.drawParticipants() 597 598 valid.SetEnabled(ct.EditRight) 599 ajoutPart.SetEnabled(ct.EditRight) 600 601 d.SetMinimumWidth(800) 602 lay.AddWidget3(fac, 0, 0, 2, 1, 0) 603 lay.AddWidget2(valid, 2, 0, 0) 604 lay.AddWidget2(basic.Label("Participants :"), 0, 1, 0) 605 lay.AddWidget2(ajoutPart, 0, 3, 0) 606 lay.AddWidget3(d.scroll, 1, 1, 2, 3, 0) 607 608 lay.SetRowStretch(0, 1) 609 lay.SetRowStretch(1, 4) 610 lay.SetRowStretch(2, 4) 611 lay.SetColumnStretch(0, 2) 612 lay.SetColumnStretch(1, 3) 613 614 d.statusBar = widgets.NewQStatusBar(nil) 615 mainStatusBar.ConnectMessageChanged(func(message string) { 616 d.statusBar.ShowMessage(message, 4000) 617 d.statusBar.Repaint() 618 }) 619 lay.AddWidget3(d.statusBar, 3, 0, 1, 4, 0) 620 return d 621 } 622 623 func (d *DetailsDossier) drawParticipants() { 624 parts := d.acces.GetDossiers(nil) 625 f := basic.Frame() 626 pLay := widgets.NewQFormLayout(f) 627 for _, part := range parts { 628 part := part // attention aux closures dans les boucles 629 wPart := newWidgetParticipant(d.ct.EditRight) 630 wPart.onDeleteAide = d.DeleteAide 631 wPart.onAjouteAide = func() { d.AjouteAide(part) } 632 wPart.onChangeOptions = func(remises rd.Remises, options rd.OptionPrixParticipant) { 633 d.UpdateOptionsParticipant(part.RawData(), remises, options) 634 } 635 wPart.onGoToParticipant = func(id int64) { 636 d.Close() 637 d.ct.Main().ShowStandard("Modifications sur le dossier annulées.", false) 638 d.ct.Main().Controllers.Camps.SelectParticipant(d.ct.Base.NewParticipant(id)) 639 } 640 641 wPart.render(part) 642 pLay.AddRow5(wPart) 643 wPart.deleteButton.ConnectClicked(func(_ bool) { d.DeleteParticipant(part) }) 644 } 645 d.scroll.SetWidget(f) 646 } 647 648 func (d *DetailsDossier) AjouteParticipant() { 649 dial := lists.NewRechercheParticipant(d.ct.Base) 650 dial.Exec() 651 if dial.Out == nil { 652 d.statusBar.ShowMessage("Ajout d'un participant annulé.", 3000) 653 return 654 } 655 d.SetEnabled(false) 656 defer d.SetEnabled(true) 657 d.ct.AjouteParticipant(d.ct.Base.NewParticipant(dial.Out.Int64())) 658 d.drawParticipants() 659 } 660 661 func (d *DetailsDossier) UpdateOptionsParticipant(part rd.Participant, remises rd.Remises, options rd.OptionPrixParticipant) { 662 d.SetEnabled(false) 663 defer d.SetEnabled(true) 664 part.Remises = remises 665 part.OptionPrix = options 666 d.ct.UpdateParticipant(part) 667 d.drawParticipants() 668 } 669 670 func (d *DetailsDossier) DeleteParticipant(part dm.AccesParticipant) { 671 conf := basic.Dialog2("Confirmation") 672 label := basic.Label(fmt.Sprintf(`Confirmez-vous la <b>suppression</b> de <b>%s</b> (%s) ? 673 <br><br> 674 <i>Vous pouvez supprimer la participation ou seulement enlever le participant du dossier.</i> 675 `, part.GetPersonne().RawData().NomPrenom(), part.GetCamp().RawData().Label())) 676 677 buttonEnleve := basic.Button("Retirer du dossier") 678 buttonEnleve.SetObjectName(basic.ONAction) 679 buttonSupprime := basic.Button("Supprimer") 680 buttonSupprime.SetObjectName(basic.ONDelete) 681 682 var fullSupp bool 683 buttonEnleve.ConnectClicked(func(_ bool) { 684 conf.Accept() 685 }) 686 buttonSupprime.ConnectClicked(func(_ bool) { 687 fullSupp = true 688 conf.Accept() 689 }) 690 691 lay := widgets.NewQGridLayout(conf) 692 lay.AddWidget3(label, 0, 0, 1, 3, 0) 693 lay.AddWidget2(buttonSupprime, 1, 0, 0) 694 lay.AddWidget2(buttonEnleve, 1, 2, 0) 695 696 if conf.Exec() == 0 { 697 d.statusBar.ShowMessage("Suppression annulée.", 3000) 698 return 699 } 700 d.SetEnabled(false) 701 defer d.SetEnabled(true) 702 d.ct.RetireParticipant(part, fullSupp) 703 d.drawParticipants() 704 } 705 706 func (d *DetailsDossier) DeleteAide(aide rd.Item) bool { 707 dial := basic.Dialog("Confirmation") 708 valid := basic.Button("Confirmer la suppression") 709 valid.SetObjectName(basic.ONDelete) 710 valid.ConnectClicked(func(_ bool) { 711 dial.Accept() 712 }) 713 dial.Layout().AddWidget(basic.Label("Etes-vous sur de vouloir supprimer l'aide ?")) 714 dial.Layout().AddWidget(valid) 715 if dial.Exec() == 0 { 716 return false 717 } 718 d.ct.SupprimeAide(aide.Id.Int64()) 719 return true 720 } 721 722 func (d *DetailsDossier) AjouteAide(part dm.AccesParticipant) { 723 aide := CreeAide(d.ct.Base, rd.NewOptionnalId(part.Id)) 724 if aide == nil { 725 d.statusBar.ShowMessage("Ajout d'une aide annulé.", 3000) 726 return 727 } 728 d.SetEnabled(false) 729 defer d.SetEnabled(true) 730 d.statusBar.ShowMessage("Ajout de l'aide...", 0) 731 created := d.ct.AjouteAide(*aide) 732 if created != nil { 733 d.statusBar.ShowMessage("Aide ajoutée avec succès.", 5000) 734 d.drawParticipants() 735 } 736 } 737 738 func (d *DetailsDossier) UpdateFacture(rf rd.Facture) { 739 d.SetEnabled(false) 740 defer d.SetEnabled(true) 741 d.Repaint() 742 out := d.ct.UpdateFacture(rf) 743 if out != nil { 744 d.ficheFacture.SetData(rf, rd.NewOptionnalId(rf.IdPersonne)) 745 } 746 }