premier jet pour l'intgration des factures la base de gestion du crans
darcs-hash:20060610211036-4ec08-dc1e583c484f17ad06dee3f2b33fd135782bf416.gz
This commit is contained in:
parent
d3da8dc087
commit
589e9bf4d0
1 changed files with 341 additions and 9 deletions
|
@ -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,7 +1018,10 @@ class BaseClasseCrans(CransLdap):
|
||||||
|
|
||||||
# Construction de l'historique
|
# Construction de l'historique
|
||||||
if not self._init_data:
|
if not self._init_data:
|
||||||
modif = 'inscription'
|
if self.idn=='fid':
|
||||||
|
modif = 'création'
|
||||||
|
else:
|
||||||
|
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 !!!!!!!
|
||||||
# Dictionnaire local des modifs
|
# Dictionnaire local des modifs
|
||||||
|
@ -1127,9 +1136,13 @@ 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':
|
||||||
while vidn in vidns:
|
# Pour une facture on prend un nouveau numéro
|
||||||
vidn += 1
|
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.dn = '%s=%s,%s' % (self.idn, vidn, self.dn)
|
||||||
self._data[self.idn]= [ '%d' % vidn ]
|
self._data[self.idn]= [ '%d' % vidn ]
|
||||||
|
@ -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 += "¤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):
|
class _FakeProprio(CransLdap):
|
||||||
""" Définitions de base d'un propriétaire virtuel """
|
""" Définitions de base d'un propriétaire virtuel """
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue