583 lines
30 KiB
Python
583 lines
30 KiB
Python
#!/bin/bash /usr/scripts/python.sh
|
|
# -*- coding: utf-8 -*-
|
|
|
|
u"""
|
|
Copyright (C) Valentin Samir
|
|
Licence : GPLv3
|
|
|
|
"""
|
|
import os
|
|
import sys
|
|
import copy
|
|
import ldap
|
|
if '/usr/scripts' not in sys.path:
|
|
sys.path.append('/usr/scripts')
|
|
|
|
from gestion.chgpass import check_password
|
|
import gestion.config as config
|
|
import gestion.config.factures
|
|
|
|
import lc_ldap.objets as objets
|
|
import lc_ldap.attributs as attributs
|
|
import lc_ldap.crans_utils as lc_utils
|
|
|
|
import machine
|
|
import blacklist
|
|
from CPS import TailCall, tailcaller, Continue
|
|
|
|
class Dialog(machine.Dialog, blacklist.Dialog):
|
|
def modif_proprio_attributs(self, proprio, attr, cont):
|
|
"""Juste un raccourci vers edit_attributs spécifique aux proprios"""
|
|
return self.edit_attributs(obj=proprio, update_obj='proprio', attr=attr, title="Modification de %s %s" % (proprio.get('prenom', [''])[0], proprio['nom'][0]), cont=cont)
|
|
@tailcaller
|
|
def proprio_compte_create(self, proprio, cont, warning=True, guess_login=True, guess_pass=0, return_obj=False, update_obj='proprio'):
|
|
"""Permet de créer un compte crans à un proprio (club ou adhérent)"""
|
|
def box_warning(warning, proprio, cont):
|
|
# Affiche-t-on le warning sur la consutation de l'adresse crans
|
|
if warning:
|
|
if self.dialog.yesno(
|
|
text="\Zr\Z1AVERTISSEMENT :\Zn \nL'adhérent devra impérativement consulter l'adresse mail associée\n\n\n\ZnContinuer ?",
|
|
title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
defaultno=True,
|
|
width=70,
|
|
colors=True, timeout=self.timeout) != self.dialog.DIALOG_OK:
|
|
raise Continue(cont)
|
|
|
|
def get_login(guess_login, guess_pass, proprio, self_cont, cont):
|
|
# Essaye-t-on de deviner le login à utiliser
|
|
if not guess_login:
|
|
(code, login) = self.dialog.inputbox(
|
|
text="Le login doit faire au maximum %s caractères\nIl ne doit pas être un pseudo ou prénom mais doit être relié au nom de famille\nSeuls les caractères alphabétiques et le trait d'union sont autorisés" % config.maxlen_login,
|
|
title="Choix du login pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
init=str(proprio['nom'][0]).lower(),
|
|
width=60,
|
|
height=10, timeout=self.timeout)
|
|
if code != self.dialog.DIALOG_OK:
|
|
raise Continue(cont)
|
|
else:
|
|
# Si oui, de quelle manière
|
|
if guess_pass == 0:
|
|
login = str(proprio['nom'][0])
|
|
elif guess_pass == 1 and proprio.get('prenom', [''])[0]:
|
|
login = "%s%s" % (str(proprio['prenom'][0])[0], proprio['nom'][0])
|
|
# Si toutes les manières ont échoués, la prochaine fois, ça on n'essaye pas de deviner
|
|
else:
|
|
raise Continue(self_cont(warning=False, guess_login=False, guess_pass=2))
|
|
return login
|
|
|
|
def create_compte(proprio, login, guess_login, self_cont, cont):
|
|
try:
|
|
proprio.compte(login=unicode(login, 'utf-8'))
|
|
except ValueError:
|
|
# Il y a eu une erreur, si on essaye de deviner, on essaye la manière suivante
|
|
if guess_login:
|
|
raise Continue(self_cont(warning=False, guess_login=True, guess_pass=guess_pass+1))
|
|
# Sinon on propage l'erreur pour l'afficher à l'utilisateur
|
|
else:
|
|
raise
|
|
self.dialog.msgbox(
|
|
text="Le compte ne sera créé que lors de l'enregistrement des données\n\nL'adresse mail de l'adhérent est : %s\nL'adhérent possède également l'alias :\n%s\n" % (proprio['mail'][0], proprio['canonicalAlias'][0]),
|
|
title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
width=75,
|
|
height=12, timeout=self.timeout,
|
|
)
|
|
return proprio
|
|
|
|
@tailcaller
|
|
def set_password(proprio, update_obj, cont):
|
|
if self.dialog.yesno("Attribuer un mot de passe maintenant ?",
|
|
title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
timeout=self.timeout
|
|
) == self.dialog.DIALOG_OK:
|
|
#return self.proprio_compte_password(proprio=proprio, return_obj=return_obj, cont=cont(**{update_obj:proprio}))
|
|
proprio = self.proprio_compte_password(proprio=proprio, return_obj=True, cont=TailCall(set_password, proprio, update_obj, cont))
|
|
if return_obj:
|
|
return proprio
|
|
else:
|
|
raise Continue(cont(**{update_obj:proprio}))
|
|
elif return_obj:
|
|
return proprio
|
|
else:
|
|
raise Continue(cont(**{update_obj:proprio}))
|
|
|
|
def todo(proprio, warning, guess_login, guess_pass, return_obj, self_cont, cont):
|
|
box_warning(warning, proprio, cont)
|
|
login = get_login(guess_login, guess_pass, proprio, self_cont, cont)
|
|
if return_obj:
|
|
proprio = create_compte(proprio, login, guess_login, self_cont, cont)
|
|
return set_password(proprio, update_obj, cont)
|
|
else:
|
|
with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio:
|
|
proprio = create_compte(proprio, login, guess_login, self_cont, cont)
|
|
if not self.confirm_item(item=proprio, title="Création du compte crans pour l'adhérent ?"):
|
|
raise Continue(cont)
|
|
else:
|
|
proprio.validate_changes()
|
|
proprio.history_gen()
|
|
proprio.save()
|
|
self.dialog.msgbox(
|
|
text="Compte créé avec succès.",
|
|
title="Création du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
timeout=self.timeout
|
|
)
|
|
return set_password(proprio, update_obj, cont)
|
|
|
|
self_cont = TailCall(self.proprio_compte_create, proprio=proprio, cont=cont, warning=warning, guess_login=guess_login, guess_pass=guess_pass)
|
|
return self.handle_dialog_result(
|
|
code=self.dialog.DIALOG_OK,
|
|
output="",
|
|
cancel_cont=cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], todo, [proprio, warning, guess_login, guess_pass, return_obj, self_cont, cont])]
|
|
)
|
|
|
|
@tailcaller
|
|
def proprio_compte_password(self, proprio, cont, return_obj=False):
|
|
"""Permet de changer le mot de passe d'un compte crans"""
|
|
def test_password(password, self_cont):
|
|
(good, msg) = check_password(password, dialog=True)
|
|
if not good:
|
|
self.dialog.msgbox(
|
|
msg,
|
|
title="Erreur dans le mot de passe de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
colors=True,
|
|
width=70, timeout=self.timeout)
|
|
raise Continue(self_cont)
|
|
else:
|
|
return True
|
|
def todo(passwords, proprio, return_obj, self_cont, cont):
|
|
password = self.get_password(cont=cont,
|
|
title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
backtitle="Le mot de passe doit être assez difficile")
|
|
|
|
if test_password(password, self_cont):
|
|
if return_obj:
|
|
proprio['userPassword']=unicode(lc_utils.hash_password(password))
|
|
return proprio
|
|
else:
|
|
with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio:
|
|
proprio['userPassword']=unicode(lc_utils.hash_password(password))
|
|
proprio.validate_changes()
|
|
proprio.history_gen()
|
|
proprio.save()
|
|
self.dialog.msgbox(
|
|
"Mot de passe changé avec succès",
|
|
title="Choix du mot de passe pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
width=70, timeout=self.timeout
|
|
)
|
|
raise Continue(cont(proprio=proprio))
|
|
#(code, passwords) = self.handle_dialog(cont, box)
|
|
(code, passwords) = (self.dialog.DIALOG_OK, "")
|
|
self_cont = TailCall(self.proprio_compte_password, proprio=proprio, cont=cont)
|
|
return self.handle_dialog_result(
|
|
code=code,
|
|
output=passwords,
|
|
cancel_cont=cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], todo, [passwords, proprio, return_obj, self_cont, cont])]
|
|
)
|
|
|
|
@tailcaller
|
|
def proprio_compte_delete(self, proprio, cont, force=False):
|
|
"""Permet la suppression du compte crans d'un proprio"""
|
|
def todo(proprio, self_cont, cont):
|
|
if force or self.confirm_item(item=proprio, title="Voulez vous vraiement supprimer le compte de %s %s ?" % (proprio.get('prenom', [''])[0], proprio["nom"][0]), defaultno=True):
|
|
(code, mail) = self.dialog.inputbox(
|
|
text="Il faut choisir une nouvelle adresse de contact.\n(On regarde s'il y a une adresse optionnel)",
|
|
title="Choix d'une adresse de contact pour %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
init=str(proprio.get("mailExt", [""])[0]),
|
|
width=50, timeout=self.timeout)
|
|
if not code == self.dialog.DIALOG_OK:
|
|
raise Continue(cont)
|
|
elif not mail:
|
|
raise ValueError("Il faut entrer une adresse mail")
|
|
with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio:
|
|
proprio.delete_compte(unicode(mail, 'utf-8'))
|
|
proprio.validate_changes()
|
|
proprio.history_gen()
|
|
proprio.save()
|
|
self.dialog.msgbox("Le compte a bien été supprimée", timeout=self.timeout, title="Suppression du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]))
|
|
raise Continue(cont(proprio=proprio))
|
|
else:
|
|
raise Continue(cont)
|
|
|
|
self_cont = TailCall(self.proprio_compte_delete, proprio=proprio, cont=cont, force=force)
|
|
return self.handle_dialog_result(
|
|
code=self.dialog.DIALOG_OK,
|
|
output="",
|
|
cancel_cont=cont(proprio=proprio),
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], todo, [proprio, self_cont, cont])]
|
|
)
|
|
|
|
def proprio_compte_etat(self, proprio, disable, cont):
|
|
"""Permet de d'éastiver ou activer un compte crans avec l'attribut shadowExpire"""
|
|
with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio:
|
|
if disable:
|
|
proprio["shadowExpire"]=0
|
|
else:
|
|
proprio["shadowExpire"]=[]
|
|
proprio.validate_changes()
|
|
proprio.history_gen()
|
|
proprio.save()
|
|
raise Continue(cont(proprio=proprio))
|
|
|
|
def proprio_compte_shell(self, proprio, cont, choices_values=None):
|
|
"""Permet de modifier le shell d'un compte crans"""
|
|
a = attributs
|
|
# Seul les nounous peuvent changer les shells restrictifs
|
|
shells_droits = {
|
|
'default' : [a.soi, a.nounou, a.cableur],
|
|
'rbash' : [a.nounou],
|
|
'rssh' : [a.nounou],
|
|
'badPassSh' : [a.nounou],
|
|
'disconnect_shell':[a.nounou],
|
|
}
|
|
shell = os.path.basename(str(proprio['loginShell'][0])).lower()
|
|
shells = config.shells_gest_crans
|
|
shells_order = config.shells_gest_crans_order
|
|
def box():
|
|
# liste des shell éditables par l'utilisateur
|
|
editable_sheels = [s for s in shells_order if self.has_right(shells_droits.get(s, shells_droits['default']), proprio)]
|
|
# Si l'utilisateur de gest_crans peut éditer le shell courant
|
|
# il peut le changer pour un autre shell qu'il peut éditer
|
|
if shell in editable_sheels:
|
|
choices=[(s, shells[s]['desc'], 1 if s == shell else 0) for s in editable_sheels]
|
|
return self.dialog.radiolist(
|
|
text="",
|
|
height=0, width=0, list_height=0,
|
|
choices=choices_values if choices_values else choices,
|
|
title="Shell de %s %s" % (proprio.get('prenom', [""])[0], proprio['nom'][0]),
|
|
timeout=self.timeout
|
|
)
|
|
# Sinon, on affiche un message d'erreur et on annule
|
|
else:
|
|
self.dialog.msgbox("Vous ne pouvez pas changer le shell de cet utilisateur",
|
|
title="Édition du shell impossible", timeout=self.timeout, width=0, height=0)
|
|
return (self.dialog.DIALOG_CANCEL, None)
|
|
def todo(output, shell, shells, proprio, self_cont, cont):
|
|
loginShell = shells[output]['path']
|
|
if shell and shell != output:
|
|
with self.conn.search(dn=proprio.dn, scope=0, mode='rw')[0] as proprio:
|
|
proprio['loginShell']=unicode(loginShell)
|
|
proprio.validate_changes()
|
|
proprio.history_gen()
|
|
proprio.save()
|
|
self.dialog.msgbox("Shell modifié avec succès.\nLa modification peut prendre une quainzaine de minute avant d'être effective.",
|
|
title="Shell de %s %s" % (proprio.get('prenom', [""])[0], proprio['nom'][0]),
|
|
width=50, timeout=self.timeout,
|
|
)
|
|
raise Continue(cont(proprio=proprio))
|
|
|
|
(code, output) = self.handle_dialog(cont, box)
|
|
self_cont = TailCall(self.proprio_compte_shell, proprio=proprio, cont=cont, choices_values=[(s, shells[s]['desc'], 1 if s == output else 0) for s in shells_order])
|
|
return self.handle_dialog_result(
|
|
code=code,
|
|
output=output,
|
|
cancel_cont=cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], todo, [output, shell, shells, proprio, self_cont, cont])]
|
|
)
|
|
|
|
def proprio_compte(self, proprio, cont, default_item=None, update_obj='proprio'):
|
|
"""Menu de gestion du compte crans d'un proprio"""
|
|
has_compte = 'cransAccount' in proprio['objectClass']
|
|
disabled_compte = has_compte and 0 in proprio['shadowExpire']
|
|
a = attributs
|
|
menu_droits = {
|
|
"Password": [a.cableur, a.nounou],
|
|
'MailAlias': [a.cableur, a.nounou],
|
|
"Activer" : [a.nounou],
|
|
"Désactiver" : [a.nounou],
|
|
"Créer" : [a.cableur, a.nounou],
|
|
"Supprimer" : [a.cableur, a.nounou],
|
|
"Shell" : [a.nounou, a.soi, a.cableur],
|
|
}
|
|
menu = {
|
|
"Password" : {"text":"Changer le mot de passe du compte", "help":"", "callback":self.proprio_compte_password},
|
|
'MailAlias' : {'text': 'Créer ou supprimer des alias mail', "help":"", 'attribut':attributs.mailAlias},
|
|
"Shell" : {"text" : "Changer le shell de cet utilisateur", "help":'', "callback":self.proprio_compte_shell},
|
|
"Activer" : {"text" : "Activer le compte pour la connexion mail/serveur", "help":"Permet d'autoriser les connexions smtp, imap, ssh, etc… avec le compte", "callback":TailCall(self.proprio_compte_etat, disable=False)},
|
|
"Désactiver" : {"text" : "Désactiver le compte pour la connexion mail/serveur", "help":"Permet d'interdire les connexions smtp, imap, ssh, etc… avec le compte", "callback":TailCall(self.proprio_compte_etat, disable=True)},
|
|
"Créer" : {"text": "Créer un compte", "help":'', "callback":self.proprio_compte_create},
|
|
"Supprimer" : {"text": "Supprimer le compte", "help":"Le home sera archivé dans le cimetière", "callback":self.proprio_compte_delete},
|
|
}
|
|
menu_order = []
|
|
tag_translate = {
|
|
"Créer":"Password",
|
|
"Password":"Password",
|
|
"Supprimer":"Créer",
|
|
"Activer":"Désactiver",
|
|
"Désactiver":"Activer",
|
|
"Shell":"Shell",
|
|
'MailAlias':'MailAlias',
|
|
'':'',
|
|
}
|
|
if has_compte:
|
|
if disabled_compte:
|
|
menu_order.append("Activer")
|
|
else:
|
|
menu_order.append("Désactiver")
|
|
menu_order.extend(['MailAlias', "Shell", "Password", "Supprimer"])
|
|
else:
|
|
menu_order.append("Créer")
|
|
def box(default_item=None):
|
|
return self.dialog.menu(
|
|
"Quel action effectuer sur le compte ?",
|
|
width=0,
|
|
height=0,
|
|
menu_height=0,
|
|
timeout=self.timeout,
|
|
item_help=1,
|
|
default_item=str(default_item),
|
|
title="Gestion du compte de %s %s" % (proprio.get('prenom', [''])[0], proprio["nom"][0]),
|
|
scrollbar=True,
|
|
cancel_label="Retour",
|
|
backtitle=self._connected_as(),
|
|
choices=[(k, menu[k]['text'], menu[k]['help']) for k in menu_order if self.has_right(menu_droits[k], proprio)])
|
|
|
|
def todo(tag, menu, proprio, self_cont):
|
|
if not tag in menu_order:
|
|
raise Continue(self_cont)
|
|
elif 'callback' in menu[tag]:
|
|
raise Continue(TailCall(menu[tag]['callback'], cont=self_cont, proprio=proprio))
|
|
elif 'attribut' in menu[tag]:
|
|
raise Continue(TailCall(self.modif_proprio_attributs, proprio=proprio, cont=self_cont, attr=menu[tag]['attribut'].ldap_name))
|
|
else:
|
|
raise EnvironmentError("Il n'y a ni champ 'attribut' ni 'callback' pour le tag %s" % tag)
|
|
|
|
|
|
cont(**{update_obj:proprio})
|
|
(code, tag) = self.handle_dialog(cont, box, default_item)
|
|
self_cont = TailCall(self.proprio_compte, proprio=proprio, cont=cont, default_item=tag_translate.get(tag, tag), update_obj=update_obj)
|
|
return self.handle_dialog_result(
|
|
code=code,
|
|
output=tag,
|
|
cancel_cont=cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, menu, proprio, self_cont])]
|
|
)
|
|
|
|
@tailcaller
|
|
def proprio_vente_set(self, article, cont):
|
|
"""Permet de définir la quantité de l'article à vendre"""
|
|
def box():
|
|
if article['pu'] == '*':
|
|
return self.dialog.inputbox(title="Montant pour %s ?" % article['designation'],
|
|
text="", init=str(article.get('nombre','')), timeout=self.timeout, width=70)
|
|
else:
|
|
return self.dialog.inputbox(title="Nombre de %s ?" % article['designation'],
|
|
text="", timeout=self.timeout, init=str(article.get('nombre','1')), width=70)
|
|
def todo(article, output, cont):
|
|
article['nombre']=output
|
|
# Il faut entrer quelque chose
|
|
if not output:
|
|
raise ValueError("Merci d'entrer une valeur")
|
|
# Vérification des centimes
|
|
if article['pu'] == '*' and '.' in output:
|
|
if len(output.split('.', 1)[1])>2:
|
|
raise ValueError("Les centimes, c'est seulement deux chiffres après la virgule")
|
|
typ = float if article['pu'] == '*' else int
|
|
# Vérification du type de l'entré
|
|
try:
|
|
output=typ(output)
|
|
except ValueError:
|
|
raise ValueError("Merci d'entrez seulement des nombres")
|
|
# On définis le nombre d'entrée. Pour pu=* il y aura du trairement à faire
|
|
# avant de générer la facture : mettre pu à nombre et nombre à 1
|
|
# on le met comme ça pour pouvoir naviger aisément entre les écrans dialog
|
|
article['nombre'] = output
|
|
return article
|
|
|
|
(code, output) = self.handle_dialog(cont, box)
|
|
self_cont = TailCall(self.proprio_vente_set, article=article, cont=cont)
|
|
return self.handle_dialog_result(
|
|
code=code,
|
|
output=output,
|
|
cancel_cont=cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], todo, [article, output, cont])]
|
|
)
|
|
|
|
@tailcaller
|
|
def proprio_choose_paiement(self, proprio, cont, cancel_cont, articles=[], tag_paiment=None, comment=True, text=""):
|
|
"""Pour choisir un mode de paiement.
|
|
Si `articles` est donné, empêche le paiement par solde si soldes est dans articles
|
|
La continuation `cont` doit accepter en paramètre `tag_paiment` pour le mode de paiement
|
|
et `comment_paiement` pour un commentaire.
|
|
"""
|
|
box_paiement = {
|
|
"liquide" : "Espèces",
|
|
"cheque" : "Chèque",
|
|
"carte" : "Par carte bancaire",
|
|
"solde" : "Solde Crans (actuel : %s€)",
|
|
"note" : "Note kfet (attention, moins tracable...)",
|
|
"arbitraire" : "Création ou destruction magique d'argent.",
|
|
}
|
|
|
|
def box_choose_paiment(tag, articles):
|
|
box_paiement_order = ["liquide", "cheque", "carte","note"]
|
|
if "cransAccount" in proprio['objectClass']:
|
|
if not "SOLDE" in [art['code'] for art in articles] and proprio["solde"]:
|
|
box_paiement_order.append("solde")
|
|
box_paiement["solde"] = box_paiement["solde"] % proprio["solde"][0]
|
|
if len(articles) == 1 and "SOLDE" in [art['code'] for art in articles]:
|
|
box_paiement_order.append("arbitraire")
|
|
|
|
choices = []
|
|
for key in box_paiement_order:
|
|
choices.append((key, box_paiement[key], 1 if key == tag else 0))
|
|
return self.dialog.radiolist(
|
|
text=text,
|
|
title="Choix d'un mode de paiement pour %s %s" % (proprio.get("prenom", [''])[0], proprio["nom"][0]),
|
|
choices=choices,
|
|
timeout=self.timeout)
|
|
|
|
def choose_paiment(tag_paiement, proprio, self_cont, cont):
|
|
if not tag_paiement:
|
|
raise ValueError("Il faut choisir un moyen de paiement")
|
|
if comment:
|
|
code, comment_paiement = self.dialog.inputbox(text="Détail pour les espèce, nom de la note ou banque du chèque", title="Commentaire", width=70, timeout=self.timeout)
|
|
if code != self.dialog.DIALOG_OK:
|
|
raise Continue(self_cont)
|
|
if not comment_paiement:
|
|
raise ValueError("Commentaire nécessaire")
|
|
raise Continue(cont(tag_paiment=tag_paiement, comment_paiement=comment_paiement))
|
|
|
|
self_cont=TailCall(self.proprio_choose_paiement, proprio=proprio, cont=cont, cancel_cont=cancel_cont, tag_paiment=tag_paiment, comment=comment)
|
|
(code, tag) = self.handle_dialog(cancel_cont, box_choose_paiment, tag_paiment, articles)
|
|
self_cont=self_cont(tag_paiment=tag)
|
|
return self.handle_dialog_result(
|
|
code=code,
|
|
output=tag,
|
|
cancel_cont=cancel_cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], choose_paiment, [tag, proprio, self_cont, cont])]
|
|
)
|
|
|
|
|
|
@tailcaller
|
|
def proprio_vente(self, proprio, cont, tags=[], tag_paiment=None, to_set=[], have_set=[], comment_paiement=None):
|
|
"""Menu de vente du crans. Permet également de recharger le solde crans"""
|
|
|
|
def box_choose_item(tags):
|
|
choices = []
|
|
gestion.config.factures.ITEMS.update(gestion.config.factures.ITEM_SOLDE)
|
|
for code, article in gestion.config.factures.ITEMS.items():
|
|
choices.append((code, u"%s%s" % (article['designation'], (u' (%s€)' % article['pu']) if article['pu'] != '*' else ""), 1 if code in tags else 0))
|
|
return self.dialog.checklist(
|
|
text="",
|
|
title="Vente de truc à %s %s" % (proprio.get("prenom", [''])[0], proprio["nom"][0]),
|
|
choices=choices,
|
|
timeout=self.timeout)
|
|
|
|
def choose_item(proprio, tags, articles, self_cont):
|
|
to_set=[]
|
|
for tag in tags:
|
|
articles[tag]['code']=tag
|
|
to_set.append(articles[tag])
|
|
raise Continue(self_cont(to_set=to_set, have_set=[]))
|
|
|
|
def number_of_items(to_set, have_set, self_cont):
|
|
# Où faut-il aller si l'utilisateur appuis sur annuler
|
|
if not have_set:
|
|
lcont = self_cont(to_set=[])
|
|
else:
|
|
lcont = self_cont(to_set=[have_set[-1]] + to_set, have_set=have_set[:-1])
|
|
art = self.proprio_vente_set(to_set[0], cont=lcont)
|
|
if not to_set[1:]:
|
|
total = 0
|
|
line=1
|
|
text=u"Résumé :\n"
|
|
for article in have_set + [art]:
|
|
if article['pu'] == '*':
|
|
total += article['nombre']
|
|
text+=u" * %s pour %s€\n" % (article['designation'], article['nombre'])
|
|
else:
|
|
total += article['nombre']*article['pu']
|
|
text+=u" * %dx %s à %s€\n" % (article['nombre'], article['designation'], article['pu'])
|
|
line+=1
|
|
text+=u"Total à payer : %s€" % total
|
|
self.dialog.msgbox(text=text,
|
|
title="Résumé de la facture à payer",
|
|
width=70, height=5+line, timeout=self.timeout)
|
|
return self_cont(to_set=to_set[1:], have_set=have_set + [art])
|
|
|
|
def paiement(have_set, tag, proprio, comment, cancel_cont, cont):
|
|
articles = copy.deepcopy(have_set)
|
|
|
|
# On formate les articles
|
|
for article in articles:
|
|
if article['pu'] == '*':
|
|
article['pu'] = article['nombre']
|
|
article['nombre'] = 1
|
|
|
|
# En arbitraire, on accepte que le solde
|
|
if tag == u"arbitraire":
|
|
if len(articles) > 1 or "SOLDE" not in [art['code'] for art in articles]:
|
|
raise ValueError("Il n'est possible que de faire une opération de solde en mode arbitraire")
|
|
|
|
# Les articles classiques on facture
|
|
with self.conn.newFacture(proprio.dn, {}) as facture:
|
|
facture['modePaiement']=unicode(tag, 'utf-8')
|
|
facture['article']=articles
|
|
facture['info']=unicode(comment, 'utf-8')
|
|
if self.confirm_item(item=facture,
|
|
text=u"Le paiement de %s€ a-t-il bien été reçu (mode : %s) ?\n" % (facture.total(), tag),
|
|
title=u"Validation du paiement",
|
|
timeout=self.timeout):
|
|
# Appeler créditer va créditer ou débiter le solde, sauver le proprio et créer la facture
|
|
facture.crediter()
|
|
arts = ["%s %s" % (art['nombre'], art['designation']) for art in facture['article'] if art['code'] != 'SOLDE']
|
|
if arts:
|
|
self.dialog.msgbox(
|
|
text=u"Vous pouvez remettre à l'adherent les articles (si ce sont des articles) suivant :\n * %s" % '\n * '.join(arts),
|
|
title=u"Vente terminée",
|
|
width=0, height=0, timeout=self.timeout)
|
|
if tag == "solde":
|
|
self.dialog.msgbox(text=u"Le solde de l'adhérent a bien été débité", title="Solde débité", width=0, height=0, timeout=self.timeout)
|
|
if [a for a in facture['article'] if art['code'] == 'SOLDE']:
|
|
self.dialog.msgbox(text=u"Le solde de l'adhérent a bien été crédité", title="Solde crédité", width=0, height=0, timeout=self.timeout)
|
|
else:
|
|
if not self.confirm(text=u"Le paiement n'a pas été reçue.\n Annuler la vente ?", title="Annulation de la vente", defaultno=True):
|
|
raise Continue(cancel_cont)
|
|
raise Continue(cont)
|
|
|
|
self_cont=TailCall(self.proprio_vente, proprio=proprio, cont=cont, tags=tags, tag_paiment=tag_paiment, to_set=to_set, have_set=have_set, comment_paiement=comment_paiement)
|
|
# S'il y a des article dont il faut définir la quantité
|
|
if to_set:
|
|
return self.handle_dialog_result(
|
|
code=self.dialog.DIALOG_OK,
|
|
output=None,
|
|
cancel_cont=None,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], number_of_items, [to_set, have_set, self_cont])]
|
|
)
|
|
# Sinon, si tous les quantités de tous les articles sont définis
|
|
elif have_set and (not comment_paiement or not tag_paiment):
|
|
lcont = self_cont.copy()
|
|
lcont(to_set=[have_set[-1]] + to_set, have_set=have_set[:-1])
|
|
return self.proprio_choose_paiement(proprio=proprio, cont=self_cont, cancel_cont=lcont, articles=have_set, tag_paiment=tag_paiment)
|
|
# Et si on a choisit le mode de paiement
|
|
elif have_set and comment_paiement:
|
|
cancel_cont = self_cont(comment_paiement=None)
|
|
return self.handle_dialog_result(
|
|
code=self.dialog.DIALOG_OK,
|
|
output=[],
|
|
cancel_cont=cancel_cont,
|
|
error_cont=cancel_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], paiement, [have_set, tag_paiment, proprio, comment_paiement, cancel_cont, cont])]
|
|
)
|
|
|
|
# Sinon, on propose des articles à chosir
|
|
else:
|
|
(code, tags) = self.handle_dialog(cont, box_choose_item, tags)
|
|
self_cont=self_cont(tags=tags, have_set=[], to_set=[], tag_paiment=None)
|
|
gestion.config.factures.ITEMS.update(gestion.config.factures.ITEM_SOLDE)
|
|
return self.handle_dialog_result(
|
|
code=code,
|
|
output=tags,
|
|
cancel_cont=cont,
|
|
error_cont=self_cont,
|
|
codes_todo=[([self.dialog.DIALOG_OK], choose_item, [proprio, tags, copy.deepcopy(gestion.config.factures.ITEMS), self_cont])]
|
|
)
|