diff --git a/gestion/gest_crans.py b/gestion/gest_crans.py index 9b370334..c298f031 100755 --- a/gestion/gest_crans.py +++ b/gestion/gest_crans.py @@ -1653,6 +1653,62 @@ def del_club(club): arg += u'--msgbox "Club détruit\n\n\n" 0 0' dialog(arg) +################################################################## +## Fonctions de modif des factures + +def set_facture_mode(facture): + """Change le mode de paiement + + """ + + arg = u'--title "Mode de paiement pour la facture fid=%s (%s)" ' % (facture.id(), facture.proprietaire().Nom(),) + arg += u'--menu "Quel moyen de paiement enregistrer ?" 0 0 0 ' + 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 facture.proprietaire().solde() - facture.total() > 0: + arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." ' + annul, res = dialog(arg) + if annul: + return 1 + facture.modePaiement(res[0].lower()) + +def set_facture_recu(facture): + """Change le reçu d'une facture + + """ + arg = u'--title "Reçu pour la facture fid=%s (%s)" ' % (facture.id(), facture.proprietaire().Nom()) + arg += u'--separate-output ' + arg += u'--checklist "État du paiement\n" 0 0 0 ' + arg += u'"Pmt" "Paiement fourni." "%s"' % (on_off(facture.recuPaiement() is not None),) + + annul, res = dialog(arg) + if annul: + return 1 + if "Pmt\n" in res: + facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S")) + else: + facture.recuPaiement(False) + +def set_facture_controle(facture): + """Change le contrôle de la facture + + """ + arg = u'--title "Contrôle pour la facture fid=%s (%s)" ' % (facture.id(), facture.proprietaire().Nom()) + arg += u'--separate-output ' + arg += u'--checklist "État du contrôle\n" 0 0 0 ' + arg += u'"Ctl" "Contrôle OK." "%s" ' % (on_off(facture.controle() == "TRUE"),) + arg += u'"NCtl" "Contrôle pas OK." "%s" ' % (on_off(facture.controle() == "FALSE"),) + + annul, res = dialog(arg) + if annul: + return 1 + if "Ctl\n" in res: + facture.controle(True) + elif "NCtl\n" in res: + facture.controle(False) + else: + facture.controle("") + ################################################################## ## Fonctions de remplissage ou modification des paramètres machine @@ -1906,6 +1962,31 @@ def del_machine(machine): arg += u'--msgbox "Machine détruite\n\n\n" 0 0' dialog(arg) +def del_facture(facture): + u""" + Destruction facture + """ + while 1: + arg = u'--title "Destruction facture fid=%s" --colors ' % facture.id() + arg += u'--inputbox "\Zr\Z1ATTENTION\Zn : la destruction est définitive, et aucun remboursement automatique n\'aura lieu.\nCommentaire à insérer ?" 0 0' + annul, res = dialog(arg) + if annul: return 1 + if res[0]: break + arg = u'--title "Destruction facture" ' + arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0' + dialog(arg) + + try: + facture.delete(res[0]) + except EnvironmentError, c: + arg = u'--title "Destruction facture" ' + arg += u'--msgbox "ERREUR n°%s\n\n\n" 0 0' % to_unicode(c.args[0]) + dialog(arg) + return 1 + + arg = u'--title "Destruction facture" ' + arg += u'--msgbox "Facture détruite\n\n\n" 0 0' + dialog(arg) #################################### ## Fonctions principales d'interface @@ -2174,6 +2255,36 @@ def modif_machine(machine): if machine.modifs: return confirm(machine) +def modif_facture(facture): + """Modifie la facture sélectionnée + + """ + + if facture.controle() and not iscontroleur and not isadm: + return 1 + + arg = u'--title "Modification de la facture %s (%s)" ' % (facture.id(), facture.proprietaire().Nom(),) + arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 ' + if not (facture.controle() == "TRUE" or facture.controle() == "FALSE") and not facture.recuPaiement(): + arg += u'"Mode" "Mode de paiement" ' + if not (facture.controle() == "TRUE" or facture.controle() == "FALSE") and facture.modePaiement(): + arg += u'"Recu" "Valider le reçu du paiement ou non" ' + if facture.recuPaiement() and (iscontroleur or isadm): + arg += u'"Controle" "Valider ou non le contrôle de la facture." ' + + annul, res = dialog(arg) + + if annul: return 1 + + if res[0] == 'Mode': + set_facture_mode(facture) + elif res[0] == 'Recu': + set_facture_recu(facture) + elif res[0] == 'Controle': + set_facture_controle(facture) + + if facture.modifs: + return confirm(facture) ######################################################################## ## Fonction de sélection (adhérent ou machine) @@ -2268,7 +2379,7 @@ def select(clas, quoi, mde=''): # Affichage if quoi[-1] == 'a': valid = res['adherent'] - if not valid and res['machine']: + if not valid and (res['machine'] or res['facture']): # On va récupérer les adhérents correspondants aux machines trouvés deja= [] for m in res['machine']: @@ -2276,6 +2387,11 @@ def select(clas, quoi, mde=''): if a.id() in deja: continue deja.append(a.id()) valid.append(a) + for f in res['facture']: + a = f.proprietaire() + if a.id() in deja: continue + deja.append(a.id()) + valid.append(a) elif quoi[-1] == 'm': valid = res['machine'] if not valid and res['adherent']: @@ -2283,6 +2399,13 @@ def select(clas, quoi, mde=''): for a in res['adherent']: for m in a.machines(): valid.append(m) + elif quoi[-1] == 'f': + valid = res['facture'] + if not valid and res['adherent']: + # On va récupérer les machines des adhérents trouvés + for a in res['adherent']: + for f in a.factures(): + valid.append(f) else: raise TypeError('Argument fonction invalide') @@ -2382,6 +2505,9 @@ def menu_principal(): arg += u'"mM" "Modifier une machine existante" "Changer le nom ou la MAC d\'une machine." ' arg += u'"dM" "Détruire une machine" "" ' arg += u'"" "---------------------------------------" "" ' + arg += u'"mF" "Modifier une facture existante" "Modifier les données relatives à la facture…" ' + arg += u'"dF" "Détruire une facture" "" ' + arg += u'"" "---------------------------------------" "" ' arg += u'"aC" "Inscrire un nouveau club" "" ' arg += u'"mC" "Modifier un club" "" ' arg += u'"aMC" "Ajouter une machine à un club" "" ' @@ -2438,6 +2564,12 @@ def menu_principal(): if not becane: continue choix= 'mMc' + elif choix == "mF": + # Modif d'une facture. Choisir facture. + facture = select(db, u'Facture à modifier f') + if not facture: continue + choix = "mFc" + elif choix == 'aC': # Ajout d'un club proprio = Club() @@ -2474,6 +2606,17 @@ def menu_principal(): del(proprio) ; proprio= None del(becane) ; becane= None + elif choix == 'dF': + # Destruction machine + facture = select(db, u'facture à détruire f') + if not facture: continue + proprio = facture.proprietaire() + if del_facture(facture): continue + del(facture) + facture = None + proprio.update_adhesion() + proprio.update_connexion() + elif choix == 'aKM': # Ajout machine au crans becane = MachineCrans(AssociationCrans(db.conn)) @@ -2583,6 +2726,12 @@ def menu_principal(): # Annulation des modifs becane.restore() + elif choix == 'mFc': + # Modif machine courante + if modif_facture(facture): + # Annulation des modifs + facture.restore() + elif choix == 'mCc': # Modif club courant if modif_club(proprio): diff --git a/gestion/ldap_crans.py b/gestion/ldap_crans.py index 0d093da9..947d38f4 100755 --- a/gestion/ldap_crans.py +++ b/gestion/ldap_crans.py @@ -1503,7 +1503,7 @@ class BaseProprietaire(BaseClasseCrans): # On récupère sur les factures l'ensemble de celles comportant une adhésion. adh_factures = self.factures_adh() - finAdh = max([0.0] + [fromGeneralizedTimeFormat(facture._data.get('finAdhesion', ["19700101000000Z"])[0]) for facture in adh_factures if facture.controle() != "FALSE"]) + finAdh = max([0.0] + [fromGeneralizedTimeFormat(facture._data.get('finAdhesion', ["19700101000000Z"])[0]) for facture in adh_factures if facture.controle() != "FALSE" and facture.recuPaiement() is not None]) if update == False: return finAdh @@ -1530,6 +1530,19 @@ class BaseProprietaire(BaseClasseCrans): self._set("debutAdhesion", self._data.get("debutAdhesion", []) + [generalizedTimeFormat(thetime)]) return f + def update_adhesion(self): + """Récupère les dates de début et fin d'adhésion dans les factures + de l'adhérent, et met à jour ses données.""" + + debutAdh = [] + finAdh = [] + for facture in self.factures_adh(): + debutAdh.append(generalizedTimeFormat(facture._data['debutAdhesion'][0])) + finAdh.append(generalizedTimeFormat(facture._data['finAdhesion'][0])) + self._set('debutAdhesion', debutAdh) + self._set('finAdhesion', finAdh) + self._save() + def droits(self, droits=None, light=False): """ Renvoie les droits courants. Non modifiable (sauf si surchargée dans classe enfant)""" if droits <> None: @@ -2378,7 +2391,7 @@ class Adherent(BaseProprietaire): # On récupère sur les factures l'ensemble de celles comportant une connexion. conn_factures = self.factures_conn() - finConn = max([0.0] + [fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0]) for facture in conn_factures if facture.controle() != "FALSE"]) + finConn = max([0.0] + [fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0]) for facture in conn_factures if facture.controle() != "FALSE" and facture.recuPaiement() is not None]) if mois is None: return finConn @@ -2408,6 +2421,19 @@ class Adherent(BaseProprietaire): self._set("debutConnexion", self._data.get("debutConnexion", []) + [generalizedTimeFormat(thetime)]) return f + def update_connexion(self): + """Récupère les dates de début et fin de connexion dans les factures + de l'adhérent, et met à jour ses données.""" + + debutConn = [] + finConn = [] + for facture in self.factures_adh(): + debutConn.append(generalizedTimeFormat(facture._data['debutConnexion'][0])) + finConn.append(generalizedTimeFormat(facture._data['finConnexion'][0])) + self._set('debutConnexion', debutConn) + self._set('finConnexion', finConn) + self._save() + def adherentPayant(self, valeur = None): """ L'adhérent paie sa cotisation (a droit au WiFi, à un compte Crans, ... True par défaut @@ -3946,7 +3972,7 @@ class Facture(BaseClasseCrans): raise ValueError, u'Facture déja payée' # modification de la valeur - if new != None: + if new is not None and new != False: # on vérifie que la facture est modifiable if not self._modifiable and new: raise NotImplementedError, "La facture n'est pas modifiable" @@ -3960,6 +3986,17 @@ class Facture(BaseClasseCrans): # modifie la base ldap self._set("recuPaiement", [new]) + elif new == False: + # on vérifie que la facture est modifiable + if not self._modifiable and new: + raise NotImplementedError, "La facture n'est pas modifiable" + + # on crédite les articles, si c'est pas possible, la metode + # levera une exeption + self._vider() + + # modifie la base ldap + self._set("recuPaiement", []) # renvoie la valeur trouvée dans la base return self._data.get("recuPaiement", [None])[0] @@ -3997,14 +4034,37 @@ class Facture(BaseClasseCrans): # on crédite les articles for art in self._articles(): # solde impression (on débite d'abord si jamais quelqu'un s'amuse à recharger son solde avec son solde) - if self.modePaiement() == 'solde': - proprio = self.proprietaire() - proprio.solde(operation=0.0 - self.total(), comment="Facture n°%s" % self.numero()) - proprio.save() if art["code"] == "SOLDE": proprio = self.proprietaire() proprio.solde(operation=art['nombre']*art["pu"], comment="Facture n°%s : %s" % (self.numero(), art['designation'])) proprio.save() + if self.modePaiement() == 'solde': + proprio = self.proprietaire() + proprio.solde(operation=0.0 - self.total(), comment="Facture n°%s" % self.numero()) + proprio.save() + + def _vider(self): + """ + Retire les articles à son propriétaire + """ + + # on vérifie que le propriétaire est modifiable + if not self.proprietaire()._modifiable: + raise SystemError, u"Impossible de créditer les articles, le proprietaire n'est pas modifiable" + + # on crédite les articles + for art in self._articles(): + # solde impression (on débite d'abord si jamais quelqu'un s'amuse à recharger son solde avec son solde) + if art["code"] == "SOLDE": + proprio = self.proprietaire() + proprio.solde(operation=-art['nombre']*art["pu"], comment="Revert facture n°%s : %s" % (self.numero(), art['designation'])) + proprio.save() + if art["code"] == "FRAIS": + self.supprime(art) + if self.modePaiement() == 'solde': + proprio = self.proprietaire() + proprio.solde(operation=0.0 + self.total(), comment="Revert facture n°%s" % self.numero()) + proprio.save() def _frais(self): """ diff --git a/gestion/whos.py b/gestion/whos.py index 8aa13584..b326afe0 100755 --- a/gestion/whos.py +++ b/gestion/whos.py @@ -295,11 +295,11 @@ def factures_brief(factures) : # Contrôle controle = facture.controle() if controle == "TRUE": - controle = coul("Validée", "vert") + controle = coul(u"Validée", "vert") elif controle == "FALSE": - controle = coul("Invalide", "rouge") + controle = coul(u"Invalide", "rouge") else: - controle = "N/A" + controle = u"N/A" # Données data.append([ @@ -423,11 +423,11 @@ def list_factures(factures) : for f in factures : controle = f.controle() if controle == "TRUE": - controle = coul("Validée", "vert") + controle = coul(u"Validée", "vert") elif controle == "FALSE": - controle = coul("Invalide", "rouge") + controle = coul(u"Invalide", "rouge") else: - controle = "N/A" + controle = u"N/A" data.append([ f.id(), f.modePaiement(), @@ -702,11 +702,11 @@ def facture_details(facture) : controle = facture.controle() if controle == "TRUE": - controle = coul("Validée", "vert") + controle = coul(u"Validée", "vert") elif controle == "FALSE": - controle = coul("Invalide", "rouge") + controle = coul(u"Invalide", "rouge") else: - controle = "N/A" + controle = u"N/A" f=u'' # Fid