Commit 49cef4c0 authored by Pierre-Elliott Bécue's avatar Pierre-Elliott Bécue

Passage aux adhésions glissantes (partie I, sans lc_ldap)

parent 169b7000
......@@ -28,7 +28,7 @@ Options :
-h : Afficher cette aide
-v : Afficher les totaux par personne
-vv : Afficher tous les mouvements par personne
Les dates doivent etre de la forme jj/mm/aaaa."""
import sys
......@@ -40,7 +40,6 @@ import time
db = crans_ldap()
date_debut_ann_scol = time.mktime((ann_scol, 8, 1, 0, 0, 0, 0, 0, 0))
paiement_ann_scol = "paiement+%d" % ann_scol
def datestrtoint(strdate):
u""" Convertit une date en entier. """
......@@ -139,6 +138,6 @@ def calcul_soldes():
cprint('Credit total : ' + str(totalcredit) + ' euros', 'vert')
if verbose >= 1:
cprint('=' * 80, 'bleu')
if __name__ == "__main__":
calcul_soldes()
......@@ -18,7 +18,7 @@ def _need_conn(f):
if __name__.endswith('annuaires_pg_test'):
host='localhost'
else:
host='pgsql.adm.crans.org'
host='pgsql.v4.adm.crans.org'
# "connecting …"
try:
if not conn:
......
......@@ -13,6 +13,7 @@ import datetime
import time
import re
import lc_ldap.shortcuts
from lc_ldap.crans_utils import fromGeneralizedTimeFormat, toGeneralizedTimeFormat
conn = lc_ldap.shortcuts.lc_ldap_admin()
import mail as mail_module
......@@ -38,12 +39,11 @@ year = config.ann_scol
delai = config.demenagement_delai
# On récupère ceux qui n'ont pas payé cette année
now = time.time()
if config.periode_transitoire:
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(paiement=%d)(!(paiement=%d)))' % (year-1,year))
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(|(&(paiement=%d)(!(paiement=%d)))(finAdhesion<=%s)(finConnexion<=%s)))' % (year-1, year, toGeneralizedTimeFormat(now), toGeneralizedTimeFormat(now)))
else:
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(paiement=%d))' % year)
now = time.time()
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(|(paiement=%d)(finAdhesion>=%s)(finConnexion>=%s)))' % (year, toGeneralizedTimeFormat(now), toGeneralizedTimeFormat(now)))
to_print = []
to_error = []
......@@ -56,7 +56,7 @@ for clandestin in bad_boys_e_s:
if x <> None:
kickout_date = x.group(1)
exchambre = x.group(2)
machine_liste = clandestin.machines()
# On lui accorde un délai
kickout_date = time.mktime(time.strptime(kickout_date, "%d/%m/%Y %H:%M"))
......@@ -77,7 +77,7 @@ for clandestin in bad_boys_e_s:
mailer = os.popen("/usr/sbin/sendmail -t", "w")
mailer.write(mail + "\n.")
mailer.close()
else:
for m in machine_liste:
try:
......
......@@ -30,7 +30,8 @@ else:
ann_scol = dat[0]
periode_transitoire = False
duree_periode_transitoire = 45*86400
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
## Bloquage si carte d'étudiants manquante pour l'année en cours
# /!\ Par sécurité, ces valeurs sont considérées comme False si
......
......@@ -21,19 +21,25 @@ Retournent None si pas d'objet trouvé.
# Destinataires, si vide n'envoi rien
To = ['root@crans.org']
import string, os, sys
import string
import os
import sys
import dialog
from whos import aff
import signal, getopt
from time import strftime, strptime, localtime, mktime
import signal
import getopt
from time import strftime, strptime, localtime, mktime, time
import re
import affich_tools, config
import affich_tools
import config
import config.cotisation as cotisation
from affich_tools import cprint, to_encoding, to_unicode
from lock import make_lock, remove_lock
from ldap_crans import crans_ldap, blacklist_items, ann_scol, droits_possibles, droits_critiques, smtpserv, script_utilisateur
from ldap_crans import Adherent, AssociationCrans, Club, Facture
from ldap_crans import Machine, MachineFixe, MachineWifi, MachineCrans, BorneWifi
from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat
import user_tests
isadm = user_tests.isadm()
......@@ -61,7 +67,7 @@ else:
def dialog(arg):
return affich_tools.dialog(u'Gestion des adhérents et machines du Crans', arg, dialog_theme)
in_facture = None
#########################################################################
## Fonctions de remplissage ou modification des paramètres d'un adhérent
......@@ -717,7 +723,6 @@ def del_adher(adher):
arg += u'--msgbox "Machines détruites\n\n\n" 0 0'
dialog(arg)
return
del(machines)
while 1:
arg = u'--title "Destruction adhérent %s " --colors ' % adher.Nom()
......@@ -1125,9 +1130,12 @@ def set_vente(proprio):
def confirm(clas):
u""" Demande confirmation avant enregistrement"""
global in_facture
# On va faire en texte, les couleurs ne passent pas en curses
os.system('clear')
aff(clas)
if in_facture is not None:
cprint("Une facture d'un montant total de %s € sera confirmée." % (in_facture.total()), "rouge")
while 1:
r = affich_tools.prompt(u"Valider et enregister ? [O/N]")
if r == 'O' or r == 'o':
......@@ -1136,11 +1144,15 @@ def confirm(clas):
return 1
try:
res = clas.save()
except (EnvironmentError, RuntimeError) as c:
if in_facture is not None:
in_facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
in_facture.save()
except Exception as c:
arg = u'--title "Enregistrement" '
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
dialog(arg)
return 1
in_facture = None
cprint(res)
affich_tools.prompt(u"Appuyez sur ENTREE pour continuer")
......@@ -1288,11 +1300,7 @@ def set_admin(proprio):
has_card = proprio.idn != 'cid'
# Initialisation des différentes checkbox
carte = on_off(ann_scol in proprio.carteEtudiant())
prev_carte = on_off(ann_scol - 1 in proprio.carteEtudiant())
paiement = on_off(ann_scol in proprio.paiement())
prev_paiement = on_off(ann_scol - 1 in proprio.paiement())
paiement_ok = on_off('p' in proprio.controle())
carte = on_off(proprio.carteEtudiant())
carte_ok = on_off('c' in proprio.controle())
if has_card: charte_MA = on_off(proprio.charteMA())
......@@ -1302,35 +1310,17 @@ def set_admin(proprio):
if has_card:
if carte_ok == 'off' or iscontroleur:
checklist.append(u'"1" "Carte d\'étudiant %d/%d fournie" "%s"' %
(ann_scol, ann_scol+1, carte))
checklist.append(u'"1" "Carte d\'étudiant fournie" "%s"' %
(carte,))
else:
texte.append(u'Carte vérifiée')
if isinstance(proprio, Club) or proprio.adherentPayant():
if paiement_ok == 'off' or iscontroleur:
checklist.append(u'"2" "Cotisation %d/%d réglée et charte signée" "%s"' %
(ann_scol, ann_scol+1, paiement))
else:
texte.append(u'Cotisation/charte vérifiées')
else:
texte.append(u'Adhérent non payant')
if iscontroleur:
if has_card:
checklist.append(u'"4" "Carte d\'étudiant vérifiée" "%s"' % carte_ok)
checklist.append(u'"5" "Cotisation/charte vérifiées" "%s"' % paiement_ok)
checklist.append(u'"2" "Carte d\'étudiant vérifiée" "%s"' % carte_ok)
# Carte et paiement de l'année précédente
if has_card:
checklist.append(u'"6" "Carte d\'étudiant %d/%d fournie" "%s"' %
(ann_scol - 1, ann_scol, prev_carte))
if isinstance(proprio, Club) or proprio.adherentPayant():
checklist.append(u'"7" "Cotisation %d/%d réglée et charte signée" "%s"' %
(ann_scol - 1, ann_scol, prev_paiement))
if (isbureau or isadm) and has_card:
checklist.append(u'"8" "Charte des MA signee" "%s"' % charte_MA)
checklist.append(u'"3" "Charte des MA signee" "%s"' % charte_MA)
if not checklist:
# Il n'y a rien de modifiable
......@@ -1351,71 +1341,189 @@ def set_admin(proprio):
# Traitement
if has_card:
if '1\n' in result:
proprio.carteEtudiant(ann_scol)
proprio.carteEtudiant(True)
elif iscontroleur or carte_ok == 'off':
proprio.carteEtudiant(-ann_scol)
if iscontroleur:
if '6\n' in result:
proprio.carteEtudiant(ann_scol - 1)
else:
proprio.carteEtudiant(-(ann_scol - 1))
if '4\n' in result:
proprio.carteEtudiant(False)
if '2\n' in result:
proprio.controle('+c')
else:
proprio.controle('-c')
if '2\n' in result and ann_scol not in proprio.paiement():
# On est en train de renouveller l'adhésion
# Combien a-t-il de machines ?
if proprio.idn != 'cid' and len(proprio.machines_fixes()) > 1 and not proprio.droits():
# Il a plus d'une machine fixe et n'est pas membre actif
arg = u'--title "Trop de machines fixes" '
arg += u'--menu " Cet adhérent a trop de machines fixes.\n\n'
arg += u'Seuls les membres actifs peuvent posséder plusieurs\n'
arg += u'machines fixes. L\'adhérent actuel n\'est pas membre\n'
arg += u'actif, il n\'est donc pas possible de lui laisser autant\n'
arg += u'de machines fixes..." 0 0 0 '
arg += u'"OK" "OK, je vais lui virer des machines" '
annul, result = dialog(arg)
if not annul and result[0] == "Si":
proprio.paiement(ann_scol)
else:
set_admin(proprio)
return
else:
proprio.paiement(ann_scol)
elif '2\n' not in result and (paiement_ok == 'off' or iscontroleur):
proprio.paiement(-ann_scol)
if has_card:
if '3\n' in result:
proprio.charteMA(True)
elif isadm or isbureau:
proprio.charteMA(False)
if iscontroleur:
if '7\n' in result:
proprio.paiement(ann_scol - 1)
def set_adhesion(proprio):
"""Maj de la période d'accès de l'adhérent"""
global in_facture
end = proprio.adhesion()
annul = 0
args = u'--title "Adhésion de %s" ' % proprio.Nom()
if end and end - cotisation.delai_readh > time():
t_end = strftime('%d/%m/%Y %H:%M:%S', localtime(end))
args += u'--msgbox "Actuellement adhérent jusqu\'au %s.\nMerci de revenir lorsqu\'il restera moins de %s jours avant la fin." 0 0 ' % (t_end, cotisation.delai_readh_jour,)
dialog(args)
return 1
elif end and end - cotisation.delai_readh <= time():
t_end = strftime('%d/%m/%Y %H:%M:%S', localtime(end))
args += u'--yesno "Adhésion jusqu\'au %s. Réadhérer ?" 0 0 ' %(t_end,)
annul, res = dialog(args)
else:
args += u'--yesno "Adhésion pour un an, continuer ?" 0 0 '
annul, res = dialog(args)
if annul:
return 1
if in_facture is not None:
f = in_facture
else:
f = None
facture = proprio.adhesion(True, f)
if float(facture.total()) == 0.0:
facture.modePaiement('liquide')
while True and not facture.modePaiement():
arg = u'--title "Mode de paiement pour l\'adhésion de %s" ' % (proprio.Nom(),)
arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(), )
arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
if proprio.solde() - facture.total() > 0:
arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
annul, res = dialog(arg)
if annul:
facture._set('finAdhesion', [])
facture._set('debutAdhesion', [])
facture.supprime(pop=True)
return 1
res = res[0]
if res == "Liquide" or res == "Cheque":
arg = u'--title "Avertissement" '
arg += u'--msgbox "Une facture sera créée, après validation par le trésorier, l\'adhérent\npourra y accéder via l\'intranet ou la demander." 0 0'
dialog(arg)
facture.modePaiement(res.lower())
break
else:
proprio.paiement(-(ann_scol - 1))
facture.modePaiement(res.lower())
break
in_facture = facture
def set_connexion(proprio):
"""Maj de la période d'accès de l'adhérent"""
global in_facture
# Si l'adhérent ne l'est plus, on commence par le faire adhérer, sauf s'il a une facture adhésion.
adhEnd = proprio.adhesion()
if in_facture is not None:
adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))
if adhEnd < time():
stat = set_adhesion(proprio)
if stat == 1:
return 1
if '3\n' in result:
proprio.paiement(ann_scol+1)
elif paiement_ok == 'off' or iscontroleur:
proprio.paiement(-ann_scol-1)
end = proprio.connexion()
args = u'--title "Connexion de %s" ' % proprio.Nom()
if end > adhEnd:
args += u'--msgbox "La fin de l\'adhésion arrivera avant le début de cette connexion.\nIl faudra d\'abord réadhérer." 0 0 ' % (t_end, cotisation.delai_readh_jour,)
dialog(args)
return 1
if '5\n' in result:
proprio.controle('+p')
elif iscontroleur:
proprio.controle('-p')
while True:
while True:
args = u'--title "Connexion de %s" ' % proprio.Nom()
if proprio.connexion() > time():
args += u'--menu "Connexion jusqu\'au %s, choisir une durée de prolongation. : " 0 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S"),)
else:
args += u'--menu "Connexion actuellement inactive, choisir une durée. : " 0 0 0 '
args += u'"An" "Prolonger d\'un an." '
args += u'"Select" "Prolonger de plusieurs mois." '
args += u'"Mois" "Prolonger d\'un mois." '
annul, res = dialog(args)
if annul:
in_facture.supprime(pop=True)
return 1
res = res[0]
if res == "An":
nb_mois = 12
break
elif res == "Select":
back = False
while True:
arg = u'--title "Nombre de mois de connexion ? (entre 2 et 12)" '
arg += u'--inputbox "" 0 0 "1" '
annul, res = dialog(arg)
if annul:
back = True
break
else:
try:
res = int(res[0])
except:
arg = u'--title "Opération impossible" '
arg += u'--msgbox "On a dit un entier…" 0 0'
dialog(arg)
continue
if res < 2 or res > 12:
arg = u'--title "Opération impossible" '
arg += u'--msgbox "On a dit un entier entre 2 et 12." 0 0'
dialog(arg)
continue
else:
nb_mois = res
break
if back:
continue
break
else:
nb_mois = 1
break
if has_card:
if '8\n' in result:
proprio.charteMA(True)
elif isadm or isbureau:
proprio.charteMA(False)
if in_facture is not None:
f = in_facture
else:
f = None
facture = proprio.connexion(nb_mois, f)
if 'C\n' in result:
proprio.controle('+k')
if not iscontroleur:
proprio.controle('-p')
elif iscontroleur:
proprio.controle('-k')
newEnd = fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0])
if newEnd > adhEnd and newEnd - adhEnd >= (cotisation.duree_conn_max - cotisation.duree_conn_plafond)*30*86400:
arg = u'--title "Avertissement" '
arg += u'--yesno "La fin de la connexion de l\'adhérent (%s) tombera après la fin de son adhésion (%s).\nS\'il veut en profiter, il lui faudra réadhérer. Continuer ?" 0 0' %(strftime('%d/%m/%Y %H:%M:%S', localtime(newEnd)), strftime('%d/%m/%Y %H:%M:%S', localtime(adhEnd)), )
no, res = dialog(arg)
if no:
facture._set('finConnexion', [])
facture._set('debutConnexion', [])
facture.supprime(pop=True)
continue
if not f.modePaiement():
arg = u'--title "Mode de paiement pour la connexion de %s" ' % (proprio.Nom(),)
arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(), )
arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
if proprio.solde() - facture.total() > 0:
arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
annul, res = dialog(arg)
if annul:
facture._set('finConnexion', [])
facture._set('debutConnexion', [])
facture.supprime(pop=True)
continue
res = res[0]
if res == "Liquide" or res == "Cheque":
arg = u'--title "Avertissement" '
arg += u'--msgbox "Une facture sera créée, après validation par le trésorier, l\'adhérent\npourra y accéder via l\'intranet ou la demander." 0 0'
dialog(arg)
facture.modePaiement(res.lower())
break
else:
facture.modePaiement(res.lower())
break
in_facture = facture
###############################################################
## Fonctions de remplissage ou modification des paramètres club
......@@ -1441,7 +1549,7 @@ def new_club(club):
if step == 4:
# Administratif
if set_admin(club): step -= 1
if set_adhesion(club): step -= 1
else: step += 1
if step == 5:
......@@ -1459,12 +1567,15 @@ def modif_club(club):
Modification du club fourni (instance de club)
Retourne 1 si annulation.
"""
global in_facture
arg = u'--title "Modification du club %s" ' % club.Nom()
arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
arg += u'"NomClub" "Modifier le nom du club" '
arg += u'"Responsable" "Changer le responsable du club %s" ' % club.responsable().Nom()
arg += u'"Imprimeurs" "Changer la liste des imprimeurs" '
arg += u'"Administratif" "Précâblage" '
arg += u'"Administratif" "Données administratives" '
arg += u'"Adhesion" "Pour les réadhésions" '
arg += u'"Local" "Modifier le local du club" '
arg += u'"Compte" "Créer un compte crans." '
if club.compte():
......@@ -1485,8 +1596,8 @@ def modif_club(club):
set_responsable(club)
elif res[0] == 'Imprimeurs':
set_imprimeurs(club)
elif res[0] == 'Administratif':
set_admin(club)
elif res[0] == 'Adhesion':
set_adhesion(club)
elif res[0] == 'Compte':
set_club_compte(club)
elif res[0] == 'Remarque':
......@@ -1811,7 +1922,8 @@ def new_adher(adher):
if set_bases(adher): return 1
steps = [set_etudes,
#set_type_de_connexion, # Plus de connexion gratuite. Les adhérents sont payant par défaut
set_adhesion,
set_connexion,
set_admin,
set_mail,
set_rque]
......@@ -1828,6 +1940,8 @@ def modif_adher(adher):
Modification de l'adhérent fourni (instance de adhérent)
Retourne 1 si annulation.
"""
global in_facture
# Préliminaire : si la chambre est inconnue on force la question
if adher.chbre() == '????':
res= ['Chambre']
......@@ -1845,8 +1959,8 @@ def modif_adher(adher):
arg = u'--title "Modification de %s" ' % adher.Nom()
arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
arg += u'"Administratif" "Précâblage, carte d\'étudiant, études" '
if not payant:
arg += u'"Connexion" "Changer de type de connexion(gratuit->payant)" '
arg += u'"Adhesion" "Pour toute réadhésion" '
arg += u'"Connexion" "Mise à jour de l\'accès Internet" '
arg += u'"Etat-civil" "Nom, prénom" '
if adher.chbre() == 'EXT':
arg += u'"Adresse" "Déménagement" '
......@@ -1881,8 +1995,7 @@ def modif_adher(adher):
elif res[0] == 'Etat-civil':
set_etat_civil(adher)
elif res[0] == 'Administratif':
if not set_admin(adher):
set_etudes(adher)
set_admin(adher)
elif res[0] == 'Mail':
set_mail(adher)
elif res[0] == 'Remarque':
......@@ -1891,18 +2004,14 @@ def modif_adher(adher):
set_droits(adher)
elif res[0] == 'Blackliste':
set_blackliste(adher)
elif res[0] == 'Charte des MA' :
set_charte_MA(adher)
elif res[0] == 'Adhesion':
set_adhesion(adher)
elif res[0] == 'Connexion':
if dlg.yesno(u"Passer à un compte payant ?") == 0:
# On attribue une ip à toute les machines
for m in adher.machines():
m.ip("<automatique>")
adher.adherentPayant(True)
set_connexion(adher)
elif res[0] == 'Adresse' or res[0] == 'Chambre':
arg = u'--title "Déménagement de %s" ' % adher.Nom()
arg += u'--menu "Question :" 0 0 0 '
arg += u'"1" "Déménagement sur le campus ? " '
arg += u'"1" "Déménagement sur le campus ?" '
arg += u'"2" "Déménagement à l\'extérieur en conservant les machines ?" '
arg += u'"3" "Départ du campus en conservant son compte ?" '
arg += u'"4" "Départ du campus en supprimant son compte ?" '
......@@ -2003,7 +2112,7 @@ def modif_adher(adher):
elif res[0] == 'Vente':
set_vente(adher)
if adher.modifs:
if adher.modifs or in_facture is not None:
return confirm(adher)
def modif_machine(machine):
......@@ -2344,6 +2453,7 @@ def menu_principal():
if not proprio: continue
if del_adher(proprio): continue
del(proprio) ; proprio= None
del(becane) ; becane= None
elif choix == 'dM':
# Destruction machine
......@@ -2533,7 +2643,7 @@ if __name__ == '__main__':
sys.stderr = sys.__stderr__
traceback = s.getvalue()
try:
if To:
if not debug and To:
# Paramètres pour le mail
From = script_utilisateur + '@crans.org'
......
......@@ -17,6 +17,7 @@ import os
import random
import string
import time
import datetime
import sys
import pwd
import errno
......@@ -25,15 +26,17 @@ import ldap.modlist
import ldap_passwd
import netaddr
import time
import annuaires_pg as annuaires
import config
import config.impression
import config.cotisation as cotisation
import iptools
import ip6tools
import cPickle
import config_mail
from chgpass import change_password
from calendar import monthrange
from affich_tools import coul, prompt, cprint
from email_tools import send_email
from syslog import openlog, closelog, syslog
......@@ -151,6 +154,27 @@ def decode(s):
else:
return s.decode('utf-8', 'ignore') # On ignore les erreurs
def tz(thetz):
abstz = 100*abs(thetz)
if thetz == 0:
return "Z"
else:
return "%s%04d" % ("+"*(thetz < 0) + "-"*(thetz > 0), abstz)
def generalizedTimeFormat(stamp):
"""Converts a timestamp (local) in a generalized time format
for LDAP
"""
return "%s%s" % (time.strftime("%Y%m%d%H%M%S", time.localtime(stamp)), tz(time.altzone/3600))
def fromGeneralizedTimeFormat(gtf):
"""Converts a GTF stamp to unix timestamp
"""
return time.mktime(time.strptime(gtf.split("-", 1)[0].split("+", 1)[0].split('Z', 1)[0], "%Y%m%d%H%M%S"))
def strip_accents(a, sois_un_porc_avec_les_espaces = True):
""" Supression des accents de la chaîne fournie """
res = normalize('NFKD', decode(a)).encode('ASCII', 'ignore')
......@@ -392,7 +416,8 @@ class CransLdap:
'historique', 'blacklist', 'droits', 'uidNumber', 'info',
'solde', 'controle', 'contourneGreylist', 'rewriteMailHeaders',
'ablacklist', 'homepageAlias', 'charteMA',
'adherentPayant', 'gpgFingerprint'], \
'adherentPayant', 'gpgFingerprint', 'debutConnexion', 'finConnexion',
'debutAdhesion', 'finAdhesion'], \
'club': \