# -=- encoding: utf-8 -=-
+import datetime
from datetime import date
from decimal import Decimal
from auf.django.metadata.managers import NoDeleteManager
import auf.django.references.models as ref
from validators import validate_date_passee
-from dae.managers import SecurityManager
+from managers import PosteManager, DossierManager, DossierComparaisonManager, PosteComparaisonManager
+
+
+
+# Gruick hack pour déterminer d'ou provient l'instanciation d'une classe pour l'héritage.
+# Cela permet de faire du dynamic loading par app sans avoir à redéfinir dans DAE la FK
+def app_context():
+ import inspect;
+ models_stack = [s[1].split('/')[-2] for s in inspect.stack() if s[1].endswith('models.py')]
+ return models_stack[-1]
+
# Constantes
REGIME_TRAVAIL_DEFAULT = Decimal('100.00')
REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT = Decimal('35.00')
-
# Upload de fichiers
storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
base_url=settings.PRIVE_MEDIA_URL)
('externe', 'Externe'),
)
-class PosteManager(SecurityManager):
- """
- Chargement de tous les objets FK existants sur chaque QuerySet.
- """
- prefixe_implantation = "implantation__region"
-
- def get_query_set(self):
- fkeys = (
- 'implantation',
- 'type_poste',
- )
- return super(PosteManager, self).get_query_set().select_related(*fkeys).all()
-
class Poste_(AUFMetadata):
"""Un Poste est un emploi (job) à combler dans une implantation.
Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
null=True)
service = models.ForeignKey('Service', db_column='service',
related_name='+',
- verbose_name = u"Direction/Service/Pôle support",
- default=1) # default = Rectorat
- responsable = models.ForeignKey('Poste', db_column='responsable',
- related_name='+',
- verbose_name = u"Poste du responsable",
- default=149) # default = Recteur
-
+ verbose_name = u"Direction/Service/Pôle support", )
+ responsable = models.ForeignKey('Poste', db_column='responsable',
+ related_name='+', null=True,
+ verbose_name = u"Poste du responsable", )
+
# Contrat
regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
default=REGIME_TRAVAIL_DEFAULT, null=True,
Généralement, retourne une liste d'un élément.
Si poste inoccupé, retourne liste vide.
"""
- return [d.employe for d in self.dossiers.filter(actif=True, supprime=False) \
+ return [d.employe for d in self.rh_dossiers.filter(actif=True, supprime=False) \
.exclude(date_fin__lt=date.today())]
prefix_implantation = "implantation__region"
"""Pour un Poste, structure d'informations décrivant comment on prévoit
financer ce Poste.
"""
- poste = models.ForeignKey('Poste', db_column='poste',
- related_name='%(app_label)s_financements')
+ poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='%(app_label)s_financements')
type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
help_text="ex.: 33.33 % (décimale avec point)")
ordering = ['type']
def __unicode__(self):
- return u'%s : %s %' % (self.type, self.pourcentage)
+ return u'%s : %s %%' % (self.type, self.pourcentage)
class PosteFinancement(PosteFinancement_):
- __doc__ = PosteFinancement_.__doc__
+ pass
-class PostePiece(models.Model):
+class PostePiece_(models.Model):
"""Documents relatifs au Poste.
Ex.: Description de poste
"""
- poste = models.ForeignKey('Poste', db_column='poste',
- related_name='pieces')
+ poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='%(app_label)s_pieces')
nom = models.CharField(verbose_name = u"Nom", max_length=255)
fichier = models.FileField(verbose_name = u"Fichier",
upload_to=poste_piece_dispatch,
storage=storage_prive)
class Meta:
+ abstract = True
ordering = ['nom']
def __unicode__(self):
return u'%s' % (self.nom)
-class PosteComparaison(models.Model):
+class PostePiece(PostePiece_):
+ pass
+
+class PosteComparaison_(models.Model):
"""
De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
"""
- poste = models.ForeignKey('Poste', related_name='comparaisons_internes')
+ poste = models.ForeignKey('%s.Poste' % app_context(), related_name='%(app_label)s_comparaisons_internes')
+ objects = PosteComparaisonManager()
+
implantation = models.ForeignKey(ref.Implantation, null=True, blank=True, related_name="+")
nom = models.CharField(verbose_name = u"Poste", max_length=255, null=True, blank=True)
montant = models.IntegerField(null=True)
devise = models.ForeignKey("Devise", default=5, related_name='+', null=True, blank=True)
+ class Meta:
+ abstract = True
+
def taux_devise(self):
- liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.implantation)
- if len(liste_taux) == 0:
- raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.implantation))
+ if self.devise.code == "EUR":
+ return 1
+ annee = self.poste.date_debut.year
+ taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise, annee=annee)]
+ taux = set(taux)
+ if len(taux) != 1:
+ raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
else:
- return liste_taux[0].taux
+ return list(taux)[0]
def montant_euros(self):
return round(float(self.montant) * float(self.taux_devise()), 2)
+class PosteComparaison(PosteComparaison_):
+ pass
+
+class PosteCommentaire_(Commentaire):
+ poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='+')
-class PosteCommentaire(Commentaire):
- poste = models.ForeignKey('Poste', db_column='poste', related_name='+')
+ class Meta:
+ abstract = True
+class PosteCommentaire(PosteCommentaire_):
+ pass
### EMPLOYÉ/PERSONNE
Une structure de Remuneration est rattachée au Dossier. Un Poste pour
lequel aucun Dossier n'existe est un poste vacant.
"""
- # Identification
- employe = models.ForeignKey('Employe', db_column='employe',
- related_name='dossiers',
- verbose_name=u"Employé")
+
+ objects = DossierManager()
+
# TODO: OneToOne ??
- poste = models.ForeignKey('Poste', db_column='poste', related_name='dossiers')
statut = models.ForeignKey('Statut', related_name='+', default=3,
null=True)
organisme_bstg = models.ForeignKey('OrganismeBstg',
def remunerations(self):
- return self.rh_remuneration_remunerations.all().order_by('date_debut')
+ return self.rh_remunerations.all().order_by('date_debut')
def get_salaire(self):
try:
class Dossier(Dossier_):
__doc__ = Dossier_.__doc__
+ poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='%(app_label)s_dossiers')
+ employe = models.ForeignKey('Employe', db_column='employe',
+ related_name='%(app_label)s_dossiers',
+ verbose_name=u"Employé")
-class DossierPiece(models.Model):
+class DossierPiece_(models.Model):
"""Documents relatifs au Dossier (à l'occupation de ce poste par employé).
Ex.: Lettre de motivation.
"""
- dossier = models.ForeignKey('Dossier', db_column='dossier',
- related_name='+')
+ dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='+')
nom = models.CharField(verbose_name = u"Nom", max_length=255)
fichier = models.FileField(verbose_name = u"Fichier",
upload_to=dossier_piece_dispatch,
storage=storage_prive)
class Meta:
+ abstract = True
ordering = ['nom']
def __unicode__(self):
return u'%s' % (self.nom)
-class DossierCommentaire(Commentaire):
- dossier = models.ForeignKey('Dossier', db_column='dossier',
- related_name='+')
+class DossierPiece(DossierPiece_):
+ pass
+
+class DossierCommentaire_(Commentaire):
+ dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='+')
+ class Meta:
+ abstract = True
+
+class DossierCommentaire(DossierCommentaire_):
+ pass
-class DossierComparaison(models.Model):
+class DossierComparaison_(models.Model):
"""
Photo d'une comparaison salariale au moment de l'embauche.
"""
- dossier = models.ForeignKey('Dossier', related_name='comparaisons')
+ dossier = models.ForeignKey('%s.Dossier' % app_context(), related_name='%(app_label)s_comparaisons')
+ objects = DossierComparaisonManager()
+
implantation = models.ForeignKey(ref.Implantation, related_name="+", null=True, blank=True)
poste = models.CharField(max_length=255, null=True, blank=True)
personne = models.CharField(max_length=255, null=True, blank=True)
montant = models.IntegerField(null=True)
devise = models.ForeignKey('Devise', default=5, related_name='+', null=True, blank=True)
+ class Meta:
+ abstract = True
+
def taux_devise(self):
- liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.dossier.poste.implantation)
- if len(liste_taux) == 0:
- raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.dossier.poste.implantation))
+ annee = self.dossier.poste.date_debut.year
+ taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise, annee=annee)]
+ taux = set(taux)
+ if len(taux) != 1:
+ raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
else:
- return liste_taux[0].taux
+ return list(taux)[0]
def montant_euros(self):
return round(float(self.montant) * float(self.taux_devise()), 2)
+class DossierComparaison(DossierComparaison_):
+ pass
### RÉMUNÉRATION
class RemunerationMixin(AUFMetadata):
+ dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='%(app_label)s_remunerations')
# Identification
- dossier = models.ForeignKey('Dossier', db_column='dossier',
- related_name='%(app_label)s_%(class)s_remunerations')
- type = models.ForeignKey('TypeRemuneration', db_column='type',
+ type = models.ForeignKey('TypeRemuneration', db_column='type',
related_name='+',
verbose_name = u"Type de rémunération")
type_revalorisation = models.ForeignKey('TypeRevalorisation',
montant = models.FloatField(null=True, blank=True,
default=0)
# Annuel (12 mois, 52 semaines, 364 jours?)
- devise = models.ForeignKey('Devise', to_field='id',
- db_column='devise', related_name='+',
- default=5)
+ devise = models.ForeignKey('Devise', db_column='devise', related_name='+', default=5)
# commentaire = precision
commentaire = models.CharField(max_length=255, null=True, blank=True)
# date_debut = anciennement date_effectif
return round(self.montant / 12, 2)
def taux_devise(self):
- return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
+ if self.devise.code == "EUR":
+ return 1
+
+ annee = datetime.datetime.now().year
+ if self.date_debut is not None:
+ annee = self.date_debut.year
+ if self.dossier.poste.date_debut is not None:
+ annee = self.dossier.poste.date_debut
+
+ taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise_id, annee=annee)]
+ taux = set(taux)
+ if len(taux) != 1:
+ raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
+ else:
+ return list(taux)[0]
def montant_euro(self):
- return round(float(self.montant) / float(self.taux_devise()), 2)
+ return round(float(self.montant) * float(self.taux_devise()), 2)
def montant_euro_mois(self):
return round(self.montant_euro() / 12, 2)
class Remuneration(Remuneration_):
- __doc__ = Remuneration_.__doc__
+ pass
### CONTRATS
def get_query_set(self):
return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste')
-
-class Contrat(AUFMetadata):
+
+class Contrat_(AUFMetadata):
"""Document juridique qui encadre la relation de travail d'un Employe
pour un Poste particulier. Pour un Dossier (qui documente cette
relation de travail) plusieurs contrats peuvent être associés.
"""
-
objects = ContratManager()
-
- dossier = models.ForeignKey('Dossier', db_column='dossier',
- related_name='contrats')
- type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
+ dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='%(app_label)s_contrats')
+ type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
related_name='+',
verbose_name = u"Type de contrat")
date_debut = models.DateField(verbose_name = u"Date de début")
null=True, blank=True)
class Meta:
+ abstract = True
ordering = ['dossier__employe__nom_affichage']
verbose_name = u"Contrat"
verbose_name_plural = u"Contrats"
def __unicode__(self):
return u'%s - %s' % (self.dossier, self.id)
-# TODO? class ContratPiece(models.Model):
-
+class Contrat(Contrat_):
+ pass
+
### ÉVÉNEMENTS
-class Evenement_(AUFMetadata):
- """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
- d'un Dossier qui vient altérer des informations normales liées à un Dossier
- (ex.: la Remuneration).
-
- Ex.: congé de maternité, maladie...
-
- Lors de ces situations exceptionnelles, l'Employe a un régime de travail
- différent et une rémunération en conséquence. On souhaite toutefois
- conserver le Dossier intact afin d'éviter une re-saisie des données lors
- du retour à la normale.
- """
- dossier = models.ForeignKey('Dossier', db_column='dossier',
- related_name='+')
- nom = models.CharField(max_length=255)
- date_debut = models.DateField(verbose_name = u"Date de début")
- date_fin = models.DateField(verbose_name = u"Date de fin",
- null=True, blank=True)
-
- class Meta:
- abstract = True
- ordering = ['nom']
- verbose_name = u"Évènement"
- verbose_name_plural = u"Évènements"
-
- def __unicode__(self):
- return u'%s' % (self.nom)
-
-
-class Evenement(Evenement_):
- __doc__ = Evenement_.__doc__
-
-
-class EvenementRemuneration_(RemunerationMixin):
- """Structure de rémunération liée à un Evenement qui remplace
- temporairement la Remuneration normale d'un Dossier, pour toute la durée
- de l'Evenement.
- """
- evenement = models.ForeignKey("Evenement", db_column='evenement',
- related_name='+',
- verbose_name = u"Évènement")
- # TODO : le champ dossier hérité de Remuneration doit être dérivé
- # de l'Evenement associé
-
- class Meta:
- abstract = True
- ordering = ['evenement', 'type__nom', '-date_fin']
- verbose_name = u"Évènement - rémunération"
- verbose_name_plural = u"Évènements - rémunérations"
-
-
-class EvenementRemuneration(EvenementRemuneration_):
- __doc__ = EvenementRemuneration_.__doc__
-
- class Meta:
- abstract = True
-
-
-class EvenementRemuneration(EvenementRemuneration_):
- __doc__ = EvenementRemuneration_.__doc__
+#class Evenement_(AUFMetadata):
+# """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
+# d'un Dossier qui vient altérer des informations normales liées à un Dossier
+# (ex.: la Remuneration).
+#
+# Ex.: congé de maternité, maladie...
+#
+# Lors de ces situations exceptionnelles, l'Employe a un régime de travail
+# différent et une rémunération en conséquence. On souhaite toutefois
+# conserver le Dossier intact afin d'éviter une re-saisie des données lors
+# du retour à la normale.
+# """
+# dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier',
+# related_name='+')
+# nom = models.CharField(max_length=255)
+# date_debut = models.DateField(verbose_name = u"Date de début")
+# date_fin = models.DateField(verbose_name = u"Date de fin",
+# null=True, blank=True)
+#
+# class Meta:
+# abstract = True
+# ordering = ['nom']
+# verbose_name = u"Évènement"
+# verbose_name_plural = u"Évènements"
+#
+# def __unicode__(self):
+# return u'%s' % (self.nom)
+#
+#
+#class Evenement(Evenement_):
+# __doc__ = Evenement_.__doc__
+#
+#
+#class EvenementRemuneration_(RemunerationMixin):
+# """Structure de rémunération liée à un Evenement qui remplace
+# temporairement la Remuneration normale d'un Dossier, pour toute la durée
+# de l'Evenement.
+# """
+# evenement = models.ForeignKey("Evenement", db_column='evenement',
+# related_name='+',
+# verbose_name = u"Évènement")
+# # TODO : le champ dossier hérité de Remuneration doit être dérivé
+# # de l'Evenement associé
+#
+# class Meta:
+# abstract = True
+# ordering = ['evenement', 'type__nom', '-date_fin']
+# verbose_name = u"Évènement - rémunération"
+# verbose_name_plural = u"Évènements - rémunérations"
+#
+#
+#class EvenementRemuneration(EvenementRemuneration_):
+# __doc__ = EvenementRemuneration_.__doc__
+#
+# class Meta:
+# abstract = True
+#
+#
+#class EvenementRemuneration(EvenementRemuneration_):
+# __doc__ = EvenementRemuneration_.__doc__
+# TODO? class ContratPiece(models.Model):
### RÉFÉRENCES RH
verbose_name = u"Valeur du point"
verbose_name_plural = u"Valeurs du point"
- # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
- def get_tauxchange_courant(self):
- """
- Recherche le taux courant associé à la valeur d'un point.
- Tous les taux de l'année courante sont chargés, pour optimiser un
- affichage en liste. (On pourrait probablement améliorer le manager pour
- lui greffer le taux courant sous forme de JOIN)
- """
- for tauxchange in self.tauxchange:
- if tauxchange.implantation_id == self.implantation_id:
- return tauxchange
- return None
-
def __unicode__(self):
return u'%s %s (%s)' % (self.valeur, self.devise, self.annee)