github.com/benoitkugler/goacve@v0.0.0-20201217100549-151ce6e55dc8/client/GUI/onglets/suivi_dossiers_fil.go (about) 1 package onglets 2 3 import ( 4 "errors" 5 "fmt" 6 "path/filepath" 7 8 "github.com/benoitkugler/goACVE/server/core/utils/importmail" 9 10 "github.com/benoitkugler/goACVE/client/GUI/basic" 11 "github.com/benoitkugler/goACVE/client/GUI/fields" 12 "github.com/benoitkugler/goACVE/client/GUI/messages" 13 "github.com/benoitkugler/goACVE/client/controllers" 14 dm "github.com/benoitkugler/goACVE/server/core/datamodel" 15 "github.com/benoitkugler/goACVE/server/core/documents" 16 rd "github.com/benoitkugler/goACVE/server/core/rawdata" 17 "github.com/therecipe/qt/core" 18 "github.com/therecipe/qt/widgets" 19 ) 20 21 type fil struct { 22 *widgets.QFrame 23 24 msgList *messages.List 25 fold *widgets.QAction 26 showDeleted *widgets.QAction 27 28 btnActions *widgets.QPushButton 29 btnDocuments *widgets.QPushButton 30 31 ct *controllers.SuiviDossiers 32 } 33 34 func newFil(ct *controllers.SuiviDossiers) fil { 35 f := fil{QFrame: basic.Frame(), ct: ct} 36 f.SetObjectName("cadre") 37 38 f.msgList = messages.NewList(ct) 39 40 options := widgets.NewQMenu(f) 41 f.fold = options.AddAction("Déplier/Replier") 42 f.fold.SetCheckable(true) 43 f.fold.SetChecked(false) 44 f.fold.ConnectTriggered(func(checked bool) { 45 f.msgList.ToogleContent(!checked) 46 }) 47 f.showDeleted = options.AddAction("Afficher/masquer les messages supprimés") 48 f.showDeleted.SetCheckable(true) 49 f.showDeleted.SetChecked(true) 50 f.showDeleted.ConnectTriggered(func(checked bool) { 51 f.msgList.ShowDeleted(checked) 52 }) 53 54 f.SetContextMenuPolicy(core.Qt__CustomContextMenu) 55 f.ConnectCustomContextMenuRequested(func(pos *core.QPoint) { 56 options.Exec2(f.QWidget_PTR().MapToGlobal(pos), nil) 57 }) 58 59 f.btnActions = basic.Button("Actions...") 60 f.btnActions.SetObjectName(basic.ONAction) 61 actions := widgets.NewQMenu(nil) 62 f.setupActions(actions) 63 f.btnActions.SetMenu(actions) 64 65 f.btnDocuments = basic.Button("Documents...") 66 actionsDocs := widgets.NewQMenu(nil) 67 f.setupActionsDocs(actionsDocs) 68 f.btnDocuments.SetMenu(actionsDocs) 69 70 importMail := basic.Button("Importer un message...") 71 importMail.SetIcon(basic.Icons.Mails) 72 importMail.SetToolTip("Ajoute un message du responsable dans le fil de discussion.") 73 importMail.ConnectClicked(func(_ bool) { f.importMail() }) 74 75 layoutMsg := widgets.NewQGridLayout(f) 76 layoutMsg.AddWidget2(basic.Label("Fil de discussion"), 0, 0, 0) 77 layoutMsg.AddWidget2(importMail, 0, 1, 0) 78 layoutMsg.AddWidget2(f.btnActions, 0, 2, 0) 79 layoutMsg.AddWidget2(f.btnDocuments, 0, 3, 0) 80 layoutMsg.AddWidget3(f.msgList, 1, 0, 1, 4, 0) 81 layoutMsg.SetColumnStretch(0, 1) 82 layoutMsg.SetRowStretch(1, 1) 83 return f 84 } 85 86 func (f fil) setupActions(menu *widgets.QMenu) { 87 menu.AddAction("Nouveau message...").ConnectTriggered(func(_ bool) { f.nouveauMessage() }) 88 menu.AddSeparator() 89 menu.AddAction("Envoyer un accusé de réception").ConnectTriggered(func(_ bool) { f.envoiAccuse() }) 90 menu.AddAction("Déclarer un accompte...").ConnectTriggered(func(_ bool) { f.declareAccompte() }) 91 menu.AddAction("Envoyer la facture").ConnectTriggered(func(_ bool) { f.envoiFacture() }) 92 menu.AddAction("Déclarer un paiement...").ConnectTriggered(func(_ bool) { f.declarePaiement() }) 93 menu.AddSeparator() 94 menu.AddAction("Envoyer les documents...").ConnectTriggered(func(_ bool) { f.envoiDocuments() }) 95 menu.AddSeparator() 96 menu.AddAction("Envoyer une facture acquittée").ConnectTriggered(func(_ bool) { f.envoiFactureAc() }) 97 menu.AddAction("Envoyer une attestation de présence").ConnectTriggered(func(_ bool) { f.envoiAttestationPres() }) 98 } 99 100 func (f fil) setupActionsDocs(menu *widgets.QMenu) { 101 menu.AddAction("RIB").ConnectTriggered(func(_ bool) { f.exportRIB() }) 102 menu.AddSeparator() 103 menu.AddAction("Facture").ConnectTriggered(func(_ bool) { f.exportFacture() }) 104 menu.AddAction("Attestation de présence").ConnectTriggered(func(_ bool) { f.exportAttestionPresence() }) 105 } 106 107 func (f fil) setMessages(messages []dm.PseudoMessage) { 108 cache := f.ct.NewCacheGroupes() 109 f.msgList.SetData(cache.PromoteMessages(messages)) 110 f.msgList.ToogleContent(!f.fold.IsChecked()) 111 f.msgList.ShowDeleted(f.showDeleted.IsChecked()) 112 } 113 114 func (f fil) enableActions(actif bool) { 115 f.btnActions.SetEnabled(actif) 116 f.btnDocuments.SetEnabled(actif) 117 } 118 119 func (f fil) facture() (dm.AccesFacture, bool) { 120 id := f.ct.Etat.FactureCurrent 121 if id == nil { 122 return dm.AccesFacture{}, false 123 } 124 return f.ct.Base.NewFacture(id.Int64()), true 125 } 126 127 func (f fil) nouveauMessage() { 128 fac, ok := f.facture() 129 if !ok { 130 return 131 } 132 contenu, ok := messages.EditMessage("", true, 0) 133 if !ok { 134 return 135 } 136 f.ct.EnvoiMessageCentre(fac, contenu) 137 } 138 func (f fil) envoiAccuse() { 139 fac, ok := f.facture() 140 if !ok { 141 return 142 } 143 f.ct.EnvoiAccuseReception(fac) 144 145 } 146 func (f fil) declareAccompte() { 147 f.createPaiement(true) 148 } 149 func (f fil) envoiFacture() { 150 fac, ok := f.facture() 151 if !ok { 152 return 153 } 154 if fac.EtatFinancier(dm.CacheEtatFinancier{}, false).IsAcquitte() { 155 b := basic.Confirmation("Le paiement pour ce dossier est complet. Ne voulez-vous pas plutôt envoyer une <b>facture acquittée</b> ?", "Envoyer quand même", basic.ONAction) 156 if !b { 157 return 158 } 159 } 160 f.ct.EnvoiFacture(fac) 161 } 162 163 func (f fil) declarePaiement() { 164 f.createPaiement(false) 165 } 166 167 func (f fil) envoiDocuments() { 168 fac, ok := f.facture() 169 if !ok { 170 return 171 } 172 173 var camps []dm.AccesCamp 174 idsCamps, _ := fac.Camps(nil) 175 for idCamp := range idsCamps { 176 camps = append(camps, fac.Base.NewCamp(idCamp)) 177 } 178 179 d := basic.Dialog2("Choix du séjour") 180 choixCamp := fields.NewCamp(true, fac.Base, false) 181 choixCamp.SetChoices(camps) 182 183 valid := basic.Button("Envoyer") 184 valid.SetEnabled(false) 185 choixCamp.ConnectCurrentIndexChanged(func(_ int) { 186 valid.SetEnabled(choixCamp.GetData() != nil) 187 }) 188 189 if len(camps) > 0 { // choix par défaut 190 choixCamp.SetData(rd.NewOptionnalId(camps[0].Id)) 191 } 192 193 valid.SetObjectName(basic.ONAction) 194 valid.ConnectClicked(func(_ bool) { 195 d.Accept() 196 }) 197 198 layout := widgets.NewQFormLayout(d) 199 layout.AddRow3("Séjour", choixCamp) 200 layout.AddRow5(valid) 201 202 if d.Exec() == 0 { 203 f.ct.Main().ShowStandard("Envoi annulé.", false) 204 return 205 } 206 207 streamEnvoiDocuments(f.ct, choixCamp.GetData().Id, rd.Ids{fac.Id}) 208 } 209 210 func (f fil) envoiFactureAc() { 211 fac, ok := f.facture() 212 if !ok { 213 return 214 } 215 f.ct.EnvoiFactureAcquittee(fac) 216 } 217 func (f fil) envoiAttestationPres() { 218 fac, ok := f.facture() 219 if !ok { 220 basic.ShowError(errors.New("Aucun dossier n'est sélectionné.")) 221 return 222 } 223 f.ct.EnvoiAttestationPresence(fac) 224 } 225 226 // ------------- documents ------------------------------ 227 228 func (f fil) exportRIB() { 229 path, err := filepath.Abs(documents.PathRIB) 230 if err != nil { 231 basic.ShowError(err) 232 return 233 } 234 basic.ShowFile(path) 235 } 236 237 func choixDestinataire(fac dm.AccesFacture) (rd.Destinataire, bool) { 238 ds := fac.ChoixDestinataires() 239 if len(ds) == 1 { 240 return ds[0], true 241 } 242 // on propose le choix du destinataire 243 244 d := basic.Dialog2("Choix du destinataire") 245 246 choix := widgets.NewQComboBox(nil) 247 for i, dest := range ds { 248 label := fmt.Sprintf("%s (%s)", dest.NomPrenom, dest.Ville) 249 if i == 0 { 250 label += " - Responsable" 251 } 252 choix.AddItem(label, core.NewQVariant()) 253 } 254 choix.SetCurrentIndex(0) 255 256 valid := basic.Button("Valider") 257 valid.ConnectClicked(func(_ bool) { 258 d.Accept() 259 }) 260 261 lay := widgets.NewQFormLayout(d) 262 lay.SetVerticalSpacing(10) 263 lay.AddRow5(basic.Label("Vous pouvez choisir un <b>destinataire</b> différent du responsable pour l'entête du document :")) 264 lay.AddRow3("Destinataire", choix) 265 lay.AddRow5(valid) 266 267 ok := d.Exec() > 0 268 out := ds[choix.CurrentIndex()] 269 return out, ok 270 } 271 272 func (f fil) exportFacture() { 273 fac, ok := f.facture() 274 if !ok { 275 return 276 } 277 dest, ok := choixDestinataire(fac) 278 if !ok { 279 return 280 } 281 meta := documents.Facture{ 282 Destinataire: dest, 283 Bilan: fac.EtatFinancier(dm.CacheEtatFinancier{}, false), 284 } 285 path := f.ct.Main().GenereExportDocument(meta) 286 if path != "" { 287 basic.ShowFile(path) 288 } 289 } 290 291 func (f fil) exportAttestionPresence() { 292 fac, ok := f.facture() 293 if !ok { 294 return 295 } 296 dest, ok := choixDestinataire(fac) 297 if !ok { 298 return 299 } 300 meta := documents.Presence{ 301 Destinataire: dest, 302 // on ne sélectionne pas la liste d'attente 303 Participants: fac.EtatFinancier(dm.CacheEtatFinancier{}, false).Participants, 304 } 305 path := f.ct.Main().GenereExportDocument(meta) 306 if path != "" { 307 basic.ShowFile(path) 308 } 309 } 310 311 func (f fil) createPaiement(isAcompte bool) { 312 fac, ok := f.facture() 313 if !ok { 314 return 315 } 316 paie := CreePaiement(f.ct.Base, rd.NewOptionnalId(fac.Id), InitialPaiement{ 317 IsAcompte: isAcompte, 318 // par défaut, le responsable, mais peut changer 319 LabelPayeur: fac.GetPersonne().RawData().NomPrenom().String(), 320 }) 321 if paie == nil { 322 return 323 } 324 f.SetEnabled(false) 325 f.Repaint() 326 f.ct.AjoutePaiement(*paie) 327 f.SetEnabled(true) 328 } 329 330 func (f fil) importMail() { 331 dial := basic.Dialog2("Importer un message") 332 333 content := fields.NewMultiLineString(true, "Message...") 334 content.SetMinimumHeight(300) 335 idRespo := fields.NewBoutonIdFacture(true, f.ct.Base) 336 hint := basic.Label(`<i>Astuce :</i> Pour importer un email depuis <b>Thunderbird</b>, utiliser <br/> 337 la suite de raccourci <b>Ctrl + U</b>, <b>Ctrl + A</b>, <b>Ctrl + C</b>, <b>Ctrl + W</b>, 338 puis cliquer sur le bouton <i>Charger depuis le presse-papier</i> ci-dessous.`) 339 importMa := basic.Button("Charger depuis le presse-papier") 340 valid := basic.Button("Valider") 341 valid.SetEnabled(false) 342 343 // condition de validation 344 setValidEnabled := func() { 345 isValid := content.GetData().TrimSpace() != "" && idRespo.GetData() != nil 346 valid.SetEnabled(isValid) 347 } 348 content.ConnectTextChanged(setValidEnabled) 349 idRespo.DataChanged = setValidEnabled 350 351 var imported importmail.Mail 352 importMa.ConnectClicked(func(_ bool) { 353 rawMail := basic.Paste() 354 imp, id, err := f.ct.ImportMail(rawMail) 355 if err != nil { 356 f.ct.Main().ShowError(err) 357 return 358 } 359 content.SetData(rd.String(imp.Content)) 360 idRespo.SetData(id.AsIId()) 361 imported = imp 362 setValidEnabled() // modification "interne" 363 f.ct.Main().ShowStandard("Mail importé", false) 364 }) 365 366 valid.ConnectClicked(func(_ bool) { 367 // utilise les valeurs utilisateur 368 f.ct.CreateMessageResponsable(content.GetData(), idRespo.GetData().Int64(), imported) 369 dial.Accept() 370 }) 371 372 // initialise avec le dossier courant, s'il existe 373 if fc := f.ct.Etat.FactureCurrent; fc != nil { 374 idRespo.SetData(fc) 375 } 376 377 layout := widgets.NewQFormLayout(dial) 378 layout.AddRow3("Dossier", idRespo) 379 layout.AddRow3("Contenu", content) 380 layout.AddRow5(hint) 381 rowButtons := widgets.NewQHBoxLayout() 382 rowButtons.AddWidget(importMa, 1, 0) 383 rowButtons.AddStretch(2) 384 rowButtons.AddWidget(valid, 1, 0) 385 layout.AddRow6(rowButtons) 386 dial.Exec() 387 }