#!/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€)",
        }

        def box_choose_paiment(tag, articles):
            box_paiement_order = ["liquide", "cheque", "carte"]
            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]
            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 = []
            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)
            for article in articles:
                if article['pu'] == '*':
                    article['pu'] = article['nombre']
                    article['nombre'] = 1

            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 se 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 à 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 à 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)
            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])]
          )
