premier jet pour l'intgration des factures la base de gestion du crans

darcs-hash:20060610211036-4ec08-dc1e583c484f17ad06dee3f2b33fd135782bf416.gz
This commit is contained in:
chove 2006-06-10 23:10:36 +02:00
parent d3da8dc087
commit 589e9bf4d0

View file

@ -258,7 +258,8 @@ class CransLdap:
'machineFixe': auto_search_machines_champs, 'machineFixe': auto_search_machines_champs,
'machineWifi': auto_search_machines_champs, 'machineWifi': auto_search_machines_champs,
'machineCrans': 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) # Champs de recherche pour la recherche manuelle (en plus de la recherche auto)
non_auto_search_machines_champs = \ non_auto_search_machines_champs = \
@ -278,7 +279,8 @@ class CransLdap:
'machineCrans': non_auto_search_machines_champs + ['prise'], 'machineCrans': non_auto_search_machines_champs + ['prise'],
'borneWifi': non_auto_search_machines_champs + \ 'borneWifi': non_auto_search_machines_champs + \
['prise', 'puissance', 'canal', 'hotspot', 'positionBorne', 'nvram'], ['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 # tous les champs de recherche
search_champs = {} search_champs = {}
@ -291,7 +293,8 @@ class CransLdap:
'machineFixe': 2, 'machineFixe': 2,
'machineWifi': 2, 'machineWifi': 2,
'machineCrans': 2, 'machineCrans': 2,
'borneWifi': 2 } 'borneWifi': 2,
'facture': 2}
def __init__(self, readonly=False): def __init__(self, readonly=False):
self.connect(readonly) self.connect(readonly)
@ -550,6 +553,9 @@ class CransLdap:
# On récupère la bonne classe # On récupère la bonne classe
nom_classe = (entry[1].get('objectClass') or ['none'])[0] nom_classe = (entry[1].get('objectClass') or ['none'])[0]
nom_classe = nom_classe[0].upper() + nom_classe[1:] 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: try:
classe = eval(nom_classe) classe = eval(nom_classe)
# On crée l'objet # On crée l'objet
@ -1012,6 +1018,9 @@ class BaseClasseCrans(CransLdap):
# Construction de l'historique # Construction de l'historique
if not self._init_data: if not self._init_data:
if self.idn=='fid':
modif = 'création'
else:
modif = 'inscription' modif = 'inscription'
else: else:
### ON NE TOUCHE PAS A SELF.MODIFS, IL EST UTILISÉ PLUS LOIN !!!!!!! ### ON NE TOUCHE PAS A SELF.MODIFS, IL EST UTILISÉ PLUS LOIN !!!!!!!
@ -1127,7 +1136,11 @@ class BaseClasseCrans(CransLdap):
r = r[0].split(',')[0] r = r[0].split(',')[0]
if r[:4] != '%s=' % self.idn: continue if r[:4] != '%s=' % self.idn: continue
vidns.append(int(r[4:])) vidns.append(int(r[4:]))
# On prend le premier libre 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: while vidn in vidns:
vidn += 1 vidn += 1
@ -1350,6 +1363,18 @@ class BaseProprietaire(BaseClasseCrans):
else: else:
return [] 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): def solde(self, operation=None, comment=None):
""" Retourne ou modifie le solde d'un propriétaire """ Retourne ou modifie le solde d'un propriétaire
operation doit être un nombre positif ou négatif 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') ret += coul(u'Clef IPsec de la machine : %s\n' % self.ipsec(), 'cyan')
self.services_to_restart('conf_wifi_ng') self.services_to_restart('conf_wifi_ng')
# Reconfiguration firewalls et dhcps # Reconfiguration firewalls et dhcps
if reconf_ip: if reconf_ip:
self.services_to_restart('macip', reconf_ip) self.services_to_restart('macip', reconf_ip)
@ -2667,7 +2691,7 @@ class Machine(BaseClasseCrans):
# Reconfiguration clients wifi ? # Reconfiguration clients wifi ?
if isinstance(self, MachineWifi) or isinstance(self, BorneWifi) \ 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') self.services_to_restart('conf_wifi_ng')
# Regénération blackliste nécessaire ? # Regénération blackliste nécessaire ?
@ -2976,6 +3000,314 @@ class BorneWifi(Machine):
self._set('nvram', current) self._set('nvram', current)
return new 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 += "&currency_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): class _FakeProprio(CransLdap):
""" Définitions de base d'un propriétaire virtuel """ """ Définitions de base d'un propriétaire virtuel """