From 589e9bf4d071bf701d870cdc698361d937ff918f Mon Sep 17 00:00:00 2001 From: chove Date: Sat, 10 Jun 2006 23:10:36 +0200 Subject: [PATCH] premier jet pour l'intgration des factures la base de gestion du crans darcs-hash:20060610211036-4ec08-dc1e583c484f17ad06dee3f2b33fd135782bf416.gz --- gestion/ldap_crans.py | 350 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 341 insertions(+), 9 deletions(-) diff --git a/gestion/ldap_crans.py b/gestion/ldap_crans.py index 8af4ec16..292e51c6 100755 --- a/gestion/ldap_crans.py +++ b/gestion/ldap_crans.py @@ -258,7 +258,8 @@ class CransLdap: 'machineFixe': auto_search_machines_champs, 'machineWifi': auto_search_machines_champs, 'machineCrans': auto_search_machines_champs, - 'borneWifi': auto_search_machines_champs } + 'borneWifi': auto_search_machines_champs, + 'facture' : [] } # Champs de recherche pour la recherche manuelle (en plus de la recherche auto) non_auto_search_machines_champs = \ @@ -278,7 +279,8 @@ class CransLdap: 'machineCrans': non_auto_search_machines_champs + ['prise'], 'borneWifi': non_auto_search_machines_champs + \ ['prise', 'puissance', 'canal', 'hotspot', 'positionBorne', 'nvram'], - 'machineWifi': non_auto_search_machines_champs + ['ipsec'] } + 'machineWifi': non_auto_search_machines_champs + ['ipsec'], + 'facture': ['fid']} # tous les champs de recherche search_champs = {} @@ -291,7 +293,8 @@ class CransLdap: 'machineFixe': 2, 'machineWifi': 2, 'machineCrans': 2, - 'borneWifi': 2 } + 'borneWifi': 2, + 'facture': 2} def __init__(self, readonly=False): self.connect(readonly) @@ -550,6 +553,9 @@ class CransLdap: # On récupère la bonne classe nom_classe = (entry[1].get('objectClass') or ['none'])[0] nom_classe = nom_classe[0].upper() + nom_classe[1:] + classe = eval(nom_classe) + # On crée l'objet + return classe(entry, mode, self.conn) try: classe = eval(nom_classe) # On crée l'objet @@ -1012,7 +1018,10 @@ class BaseClasseCrans(CransLdap): # Construction de l'historique if not self._init_data: - modif = 'inscription' + if self.idn=='fid': + modif = 'création' + else: + modif = 'inscription' else: ### ON NE TOUCHE PAS A SELF.MODIFS, IL EST UTILISÉ PLUS LOIN !!!!!!! # Dictionnaire local des modifs @@ -1127,9 +1136,13 @@ class BaseClasseCrans(CransLdap): r = r[0].split(',')[0] if r[:4] != '%s=' % self.idn: continue vidns.append(int(r[4:])) - # On prend le premier libre - while vidn in vidns: - vidn += 1 + if self.idn=='fid': + # Pour une facture on prend un nouveau numéro + vidn = max(vidns)+1 + else: + # Sinon on prend le premier libre + while vidn in vidns: + vidn += 1 self.dn = '%s=%s,%s' % (self.idn, vidn, self.dn) self._data[self.idn]= [ '%d' % vidn ] @@ -1350,6 +1363,18 @@ class BaseProprietaire(BaseClasseCrans): else: return [] + def factures(self): + """ Retourne les factures (instances) appartenant à la classe """ + # Le champ id n'est pas initialisé lorsque le proprio est en cours + # de création + if self.id(): + res = [] + for r in self.conn.search_s('%s=%s,%s' % (self.idn, self.id(), self.base_dn), 1, Facture.filtre_idn): + res.append(self.make(r, self._modifiable)) + return res + else: + return [] + def solde(self, operation=None, comment=None): """ Retourne ou modifie le solde d'un propriétaire operation doit être un nombre positif ou négatif @@ -2643,7 +2668,6 @@ class Machine(BaseClasseCrans): ret += coul(u'Clef IPsec de la machine : %s\n' % self.ipsec(), 'cyan') self.services_to_restart('conf_wifi_ng') - # Reconfiguration firewalls et dhcps if reconf_ip: self.services_to_restart('macip', reconf_ip) @@ -2667,7 +2691,7 @@ class Machine(BaseClasseCrans): # Reconfiguration clients wifi ? if isinstance(self, MachineWifi) or isinstance(self, BorneWifi) \ - and ('ipHostNumber' in self.modifs or 'host' in self.modifs or 'macAddress' in self.modifs or 'nvram' in self.modifs or 'hotspot' in self.modifs): + and ('ipHostNumber' in self.modifs or 'host' in self.modifs or 'macAddress' in self.modifs): self.services_to_restart('conf_wifi_ng') # Regénération blackliste nécessaire ? @@ -2976,6 +3000,314 @@ class BorneWifi(Machine): self._set('nvram', current) return new +class Facture(BaseClasseCrans): + """ Classe de définition d'une facture """ + objectClass = 'facture' + idn = 'fid' + filtre_idn = 'objectClass=facture' + + def __init__(self, parent_or_tuple, mode='', conn=None): + """ + parent_or_tuple est : + * soit une instance d'une classe pouvant posséder une facture + (Adherent, Club), la nouvelle facture lui sera alors associée. + * soit directement le tuple définissant une facture (tel que + retourné par les fonctions de recherche ldap) + + Pour l'édition d'une facture, mode devra être égal à 'w' + Attention, si mode='w' mais si l'objet est déja locké il n'y a + pas d'erreur, vérifier l'obtention du lock grâce à la valeur de + _modifiable (si ='w' c'est bon) + + conn est une instance de la classe de connexion à la base LDAP + """ + + # Initialisation de la connexion + self.conn = conn + if not self.conn: + self.connect() + + self.modifs = {} + t = parent_or_tuple.__class__ + if t == tuple: + # Initialisation avec données fournies + self.dn = parent_or_tuple[0] + if mode == 'w': + try: + self.lock(self.idn, self.id()) + self._modifiable = 'w' + except EnvironmentError , c: + self._modifiable = 0 + else: + self._modifiable = 0 + # Utile pour construire l'instruction LDAP de modif + self._init_data = parent_or_tuple[1].copy() + self._data = parent_or_tuple[1] + + # Propriéraire inconnu mais ce n'est pas grave + self.__proprietaire = None + + elif t in [Adherent, Club] and mode != 'w': + # Facture vide + self.__proprietaire = parent_or_tuple + self.dn = parent_or_tuple.dn + self._data = {'objectClass': [self.objectClass], 'modePaiement':['paypal']} + self._init_data = {} + self._modifiable = 'w' + + else: + raise TypeError(u'Arguments invalides') + + def numero(self): + """ Retourne le numéro de facture """ + fid = self._data.get('fid',[None])[0] + if fid == None: + raise NotImplementedError, u'Il faut enregistrer une facture pour connaitre son numero' + return fid + + def proprietaire(self): + """ + retroune le propriétaire de la facture (classe Adherent ou Club) + """ + # si la facture est en mode w mais pas le proprio, on tente de prendre + # le proprio en w + if self.__proprietaire and self.__proprietaire._modifiable != self._modifiable: + self.__proprietaire = None + + # récupère le proprio si ce n'est pas encore fait + if not self.__proprietaire: + res = self.conn.search_s(','.join(self.dn.split(',')[1:]), 0) + if 'adherent' in res[0][1]['objectClass']: + self.__proprietaire = Adherent(res[0], self._modifiable, self.conn) + elif 'club' in res[0][1]['objectClass']: + self.__proprietaire = Club(res[0], self._modifiable, self.conn) + else: + raise ValueError, u'Propriétaire inconnu' + + return self.__proprietaire + + def modePaiement(self, new=None): + """ + Définit ou retourne le mode de paiement. + Le mode de paiement doit être une chaine de caractère + """ + + # modification du mode de paiement + if new != None: + if self.recuPaiement(): + raise ValueError, u'Facture déja payée' + + if not self._modifiable: + raise NotImplementedError, "La facture n'est pas modifiable" + + if new not in ['liquide','cheque','paypal']: + raise ValueError, u'Mode de paiement non accepté' + + self._set('modePaiement', [new]) + + return decode(self._data.get('modePaiement', [None])[0]) + + def recuPaiement(self, new=None): + """ + Définit ou retourne qui a recu le paiement + """ + + # on vérifie que la facture n'est pas déja payéee + if new and self._data.get('recuPaiement', []): + raise ValueError, u'Facture déja payée' + + # modification de la valeur + if new != None: + # 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._crediter() + + # ajout des frais à la liste d'articles + self.ajoute(self._frais()) + + # modifie la base ldap + self._set("recuPaiement",[new]) + + # renvoie la valeur trouvée dans la base + return self._data.get("recuPaiement",[None])[0] + + def _del_recu_paiement(self): + """ Pour test """ + self._set("recuPaiement",[]) + + def _crediter(self): + """ + Credite les articles à son propriétaire + """ + + # si la facture n'existe pas encore, on la sauve pour générer un numéro + if not self._data.has_key('fid'): + self.save() + + # 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(): + print art + # solde impression + if art["code"] == "SOLDE": + print 'coucou' + proprio = self.proprietaire() + proprio.solde(operation=art['nombre']*art["pu"], comment="Facture n°%s : %s" % (self.numero(),art['designation'])) + proprio.save() + + def _frais(self): + """ + Retourne une liste d'articles correspondants aux divers frais + """ + + arts = [] + + # aucun frais pour une facture payée, ils sont intégrés aux articles + if self.recuPaiement(): + return [] + + # frais de paiement par paypal + if self.modePaiement() == 'paypal': + # 25 centimes pour le paiement paypal + s = 0.25 + + # et on ajoute 3.5% du montant + for art in self._articles(): + s += 0.035 * art['nombre'] * art['pu'] + + # ajoute à la liste d'articles de frais + arts.append( {'code':'FRAIS','designation':'Frais de tansaction PayPal','nombre':1,'pu':round(s,2)} ) + + return arts + + def _articles(self, arts = None): + """ Retourne ou modifie la liste des articles de la base """ + + # modifie la liste des articles + if arts != None: + self._set('article',['%s~~%s~~%s~~%s' % (art['code'],art['designation'],str(art['nombre']),str(art['pu'])) for art in arts]) + + # charge la liste des articles + arts = [] + for art in self._data.get("article",[]): + art = art.split('~~') + art = { 'code' : art[0], + 'designation' : art[1], + 'nombre' : float(art[2]), + 'pu' : float(art[3]) } + arts.append(art) + + return arts + + def ajoute(self, ajoute): + """ Ajoute un/des article(s) à la facture + ajoute est un article ou une liste d'articles + """ + # on ne eut pas modifier une facture payée + if self.recuPaiement(): + raise ValueError, u'On ne peut pas modifier une facture payée' + + # charge la liste des articles + arts = self._articles() + + # ajoute les articles + if type(ajoute)==dict: + ajoute = [ajoute] + if type(ajoute)==list: + for art in ajoute: + if '~~' in ' '.join([str(x) for x in art.values()]): + raise ValueError, u'Ne pas mettre de ~~ dans les champs' + arts.append(art) + + # enregistre la nouvelle liste + self._articles(arts) + + def supprime(self, supprime): + """ Supprime un/des article(s) à la facture + arts est un article ou une liste d'articles + """ + # on ne eut pas modifier une facture payée + if self.recuPaiement(): + raise ValueError, u'On ne peut pas modifier une facture payée' + + # charge la liste des articles + arts = self._articles() + + # on supprime les anciens articles + if type(supprime)==dict: + supprime = [supprime] + if type(supprime)==list: + for art in supprime: + arts.remove(art) + + # enregistre la nouvelle liste + self._articles(arts) + + def articles(self): + """ + Retourne la liste des articles. + ajoute et supprime sont : + un article ou + une liste d'articles + Un article est un dictionnaire de la forme : + { 'code' : string, + 'designation' : string, + 'nombre' : int/float, + 'pu' : int/float } + """ + return self._articles() + self._frais() + + def total(self): + """ + Calcule le total de la facture, frais compris + """ + s = 0 + for art in self.articles(): + s += art['nombre'] * art['pu'] + return s + + def urlPaypal(self): + """ + Retourne l'url paypal pour le paiement de cette facture + """ + url = "https://www.paypal.com/cgi-bin/webscr?cmd=_cart" + url += "&upload=1" + url += "&business=paypal@crans.org" + url += "¤cy_code=EUR" + url += "&no_shipping=1" + #url += "&address_override=1" + url += "&no_note=1" + url += "&return=http://factures.crans.org/return" + url += "&cancel_return=http://factures.crans.org/cancel" + url += "&invoice=12345" # num de facture + url += "&item_name_1=coucou" + url += "&amount_1=12" + url += "&quantity_1=2" + return url + + def save(self): + """ + Enregistre la facture dans la base LDAP + Retourne une chaîne indiquant les opération effectuées. + """ + # Enregistrement + self._save() + # Remise à zéro + self.modifs = {} + # Message de sortie + return coul(u"Facture n°%s enregistrée avec succès." % self.numero(), 'vert') + + def delete(self, comment=''): + """ Suppression de la facture """ + self.__proprietaire = None + self._delete(self.dn, comment) class _FakeProprio(CransLdap): """ Définitions de base d'un propriétaire virtuel """