diff --git a/gestion/config/impression.py b/gestion/config/impression.py index 6ab36126..e4766fb1 100644 --- a/gestion/config/impression.py +++ b/gestion/config/impression.py @@ -47,3 +47,5 @@ From_imprimante = 'impression@crans.org' #: Informations supplémentaires sur l'état de l'imprimante, affichée sur l'intranet state_msg = [] +#: Temps sur lequel le script de recrédit des impressions peut regarder, en jours +delta_recredit_jours = 15 diff --git a/gestion/mail/template/remboursement_impressions/From/fr b/gestion/mail/template/remboursement_impressions/From/fr new file mode 100644 index 00000000..0a82390d --- /dev/null +++ b/gestion/mail/template/remboursement_impressions/From/fr @@ -0,0 +1 @@ + diff --git a/gestion/mail/template/remboursement_impressions/README b/gestion/mail/template/remboursement_impressions/README new file mode 100644 index 00000000..62350a76 --- /dev/null +++ b/gestion/mail/template/remboursement_impressions/README @@ -0,0 +1 @@ +Signale les remboursements d'impressions qui ne se sont pas déroulées correctement aux imprimeurs. diff --git a/gestion/mail/template/remboursement_impressions/Subject/fr b/gestion/mail/template/remboursement_impressions/Subject/fr new file mode 100644 index 00000000..13b67e12 --- /dev/null +++ b/gestion/mail/template/remboursement_impressions/Subject/fr @@ -0,0 +1 @@ +Remboursement d'impressions diff --git a/gestion/mail/template/remboursement_impressions/To/fr b/gestion/mail/template/remboursement_impressions/To/fr new file mode 100644 index 00000000..4d4ffe75 --- /dev/null +++ b/gestion/mail/template/remboursement_impressions/To/fr @@ -0,0 +1,3 @@ +{{To}} + + diff --git a/gestion/mail/template/remboursement_impressions/X-Mailer/fr b/gestion/mail/template/remboursement_impressions/X-Mailer/fr new file mode 100644 index 00000000..e74c9183 --- /dev/null +++ b/gestion/mail/template/remboursement_impressions/X-Mailer/fr @@ -0,0 +1,3 @@ +{{ mailer }} + + diff --git a/gestion/mail/template/remboursement_impressions/body/fr b/gestion/mail/template/remboursement_impressions/body/fr new file mode 100644 index 00000000..65686686 --- /dev/null +++ b/gestion/mail/template/remboursement_impressions/body/fr @@ -0,0 +1,8 @@ +L'imprimeur {{imprimeur}} a procédé à un ou plusieurs remboursements d'impressions pour l'adhérent {{adhname}}. + +Le montant total des remboursements s'élève à : {{montant}} Euros. + +Voici le détail de ces impressions : +{{taches}} + +Le script de remboursement. diff --git a/impression/recredit.py b/impression/recredit.py new file mode 100755 index 00000000..a4f7b2bd --- /dev/null +++ b/impression/recredit.py @@ -0,0 +1,163 @@ +#!/bin/bash /usr/scripts/python.sh +# -*- coding: utf-8 -*- +""" +Effectue la recréditation des impressions qui n'ont pas été effectuées +par l'imprimante, soit ne sont pas sorties correctement, et contiennent +des logs d'erreur. + +Le script est fait pour éviter de recréditer plusieurs fois les mêmes +impressions, et croise dans l'historique de l'adhérent les impressions +(débits), et les créditations que ce script a lui même déjà effectuées. + +Un mail est envoyé pour signaler les activités de crédits. +L'exécution se fait pas appel du script suivi de l'aid. + +Auteurs : Gabriel Détraz +Pierre-Elliott Bécue +avec contributions importantes de Daniel Stan, Valentin Samir et Lucas Serrano +""" + +# Import des modules génériques +import argparse +import sys +import datetime +import re +from utils.sendmail import actually_sendmail + +# Imports des scripts développés au Crans +import lc_ldap.shortcuts +import lc_ldap.filter2 as lfilter +import gestion.config as config +from pythondialog.dialog import Dialog +from gestion import mail + +# On ouvre une connexion LDAP à la base de test une fois pour toute. +conn = lc_ldap.shortcuts.lc_ldap_admin() + +# Des fonctions... +IMP_DONE_REGEX = re.compile(r".*respbats : debit (?P[^ ]*) Euros \[impression\((?P.*)\):.*fichiers/(?P.*)/(?P[^ ]*) .*\]") +IMP_DEJ_REMB_REGEX = re.compile(r".*credit (?P[^ ]*) Euros \[Impression ratee, jid=(?P.*), tache=(?P.*)\]") + +def do_remb(args): + """Fonction appelant l'interface curses et listant les remboursements + possibles. + + """ + + rendu = [] + + adh = conn.search(lfilter.human_to_ldap(args.filtre.decode(config.encoding.in_encoding)), mode="w") + # conn.search peut retourner une liste vide. + if not adh: + return [] + + with adh[0] as adh: + liste_rembs = find_rembs(adh) + while True: + (ret, val) = dialog_remb(liste_rembs) + if ret: + adh.save() + break + else: + selected = liste_rembs[int(val)] + if confirm(selected): + adh.update_solde(float(selected["montant"]), u"Impression ratee, jid=%(jid)s, tache=%(file)s" % selected) + _ = liste_rembs.pop(int(val)) + rendu.append(selected) + else: + continue + send_mail(rendu, adh) + return + +def find_rembs(adh): + """Fonction qui calcule les entrées remboursables d'un adhérent + + """ + + now = datetime.datetime.now() + begin = now - datetime.timedelta(days=config.impression.delta_recredit_jours) + liste_taches = list() + liste_rembs = list() + + for ent in adh["historique"]: + date = ent.get_datetime() + if date > begin: + match_entry = IMP_DONE_REGEX.match(unicode(ent)) + if match_entry is not None: + liste_taches.append(match_entry.groupdict()) + continue + match_entry = IMP_DEJ_REMB_REGEX.match(unicode(ent)) + if match_entry is not None: + liste_rembs.append(match_entry.groupdict()) + continue + + # On génère la liste avant le return, pour ne pas la regénérer autant de fois qu'il + # y a de tâches dans liste_taches + jid_rembs = [remb["jid"] for remb in liste_rembs] + return [tache for tache in liste_taches if tache['jid'] not in jid_rembs] + +def dialog_remb(liste_rembs): + """Crée une boîte de dialogue à partir de la liste des remboursements, pour + en faire un. + + """ + if not liste_rembs: + return (1, "") + + dialog = Dialog() + choices = [("%s" % liste_rembs.index(dico), "Remb impression du fichier %(file)s (montant: %(montant)s, jid: %(jid)s)." % dico) for dico in liste_rembs] + + return dialog.menu("Quelle impression souhaitez-vous rembourser ?", width=0, height=0, menu_height=0, title="Remboursement", choices=choices, cancel_label="Quitter", backtitle=u"Remboursement d'impressions ratées.") + +def confirm(dico): + """Demande avec dialog si on doit confirmer le remboursement. + + """ + dialog = Dialog() + return dialog.yesno("Confirmer le remboursement ?") == dialog.DIALOG_OK + +def send_mail(liste_rendus, adh): + """Si la liste des remboursements est non-vide, envoie un mail pour prévenir + les imprimeurs. + + """ + if not liste_rendus: + return + + montant = sum([float(dico["montant"]) for dico in liste_rendus]) + affs = [dico['file'] for dico in liste_rendus] + login = unicode(adh["uid"][0]) + + To = 'impression@crans.org' + From = 'impression@crans.org' + mailtxt = mail.generate('remboursement_impressions', { + 'To': To, + 'From': From, + 'adhname': login, + 'taches': u', '.join(affs), + 'montant': montant, + 'imprimeur': lc_ldap.shortcuts.current_user, + }) + +# print mailtxt.as_string() + actually_sendmail(From, (To,), mailtxt) + +#Un bloc de test +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Script pour recréditer les adhérents.", add_help=False) + parser.add_argument('-h', '--help', help="Affiche ce message et quitte.", action="store_true") + parser.add_argument('filtre', type=str, nargs="?", help="Le filtre LDAP à utiliser") + + # Et on parse + args = parser.parse_args() + + if args.help: + parser.print_help() + sys.exit(0) + else: + if not args.filtre: + parser.print_help() + sys.exit(1) + else: + do_remb(args) + diff --git a/respbats/recredit b/respbats/recredit new file mode 100755 index 00000000..dfd47a68 --- /dev/null +++ b/respbats/recredit @@ -0,0 +1,3 @@ +#!/bin/bash + +sudo -u respbats /usr/scripts/impression/recredit.py "$@"