X-Git-Url: http://git.auf.org/?p=auf_rh_dae.git;a=blobdiff_plain;f=project%2Frh%2Fmodels.py;h=e5bcfd450df3bac2bf851b82c8c01238c85282cd;hp=4156a817cb98da5c9dff6f441c9873f5911cd3c5;hb=c41b7fcceb16fdd98d7f41f7ef88e3c714422988;hpb=769a0755c02e103ef210781af73dcd746bd139d4 diff --git a/project/rh/models.py b/project/rh/models.py index 4156a81..e5bcfd4 100644 --- a/project/rh/models.py +++ b/project/rh/models.py @@ -1,16 +1,15 @@ # -=- encoding: utf-8 -=- -import datetime - from django.core.files.storage import FileSystemStorage from django.db import models -import settings - +from django.conf import settings +from auf.django.metadata.models import AUFMetadata +from auf.django.metadata.managers import NoDeleteManager import datamaster_modeles.models as ref # Constantes -HELP_TEXT_DATE = u"format: aaaa-mm-jj" +HELP_TEXT_DATE = "format: aaaa-mm-jj" REGIME_TRAVAIL_DEFAULT = 100.00 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT = 35.00 @@ -27,30 +26,8 @@ def dossier_piece_dispatch(instance, filename): path = "dossier/%s/%s" % (instance.dossier_id, filename) return path -# Abstracts -class Metadata(models.Model): - """Méta-données AUF. - Metadata.actif = flag remplaçant la suppression. - actif == False : objet réputé supprimé. - """ - actif = models.BooleanField(default=True) - date_creation = models.DateField(auto_now_add=True) - user_creation = models.ForeignKey('auth.User', - db_column='user_creation', related_name='+', - null=True, blank=True) - date_modification = models.DateField(auto_now=True) - user_modification = models.ForeignKey('auth.User', - db_column='user_modification', related_name='+', - null=True, blank=True) - date_desactivation = models.DateField(null=True, blank=True) - user_desactivation = models.ForeignKey('auth.User', - db_column='user_desactivation', related_name='+', - null=True, blank=True) - - class Meta: - abstract = True -class Commentaire(Metadata): +class Commentaire(AUFMetadata): texte = models.TextField() owner = models.ForeignKey('auth.User', db_column='owner', related_name='+') @@ -69,51 +46,58 @@ POSTE_APPEL_CHOICES = ( ('externe', 'Externe'), ) -class Poste_(Metadata): +class PosteManager(NoDeleteManager): + def get_query_set(self): + return super(PosteManager, self).get_query_set().select_related('implantation') + +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éé. Si on veut recruter 2 jardiniers, 2 Postes distincts existent. """ + + objects = PosteManager() + # Identification nom = models.CharField(max_length=255, - verbose_name=u"Titre du poste", ) + verbose_name="Titre du poste", ) nom_feminin = models.CharField(max_length=255, - verbose_name=u"Titre du poste (au féminin)", + verbose_name="Titre du poste (au féminin)", null=True) implantation = models.ForeignKey(ref.Implantation, db_column='implantation', related_name='+') type_poste = models.ForeignKey('TypePoste', db_column='type_poste', related_name='+', null=True) - service = models.ForeignKey('Service', db_column='service', + service = models.ForeignKey('Service', db_column='service', null=True, related_name='+', - verbose_name=u"Direction/Service/Pôle support", + verbose_name="Direction/Service/Pôle support", default=1) # default = Rectorat responsable = models.ForeignKey('Poste', db_column='responsable', - related_name='+', - verbose_name=u"Poste du responsable", + related_name='+', null=True, + verbose_name="Poste du responsable", default=149) # default = Recteur # Contrat regime_travail = models.DecimalField(max_digits=12, decimal_places=2, - default=REGIME_TRAVAIL_DEFAULT, - verbose_name=u"Temps de travail", - help_text=u"% du temps complet") + default=REGIME_TRAVAIL_DEFAULT, null=True, + verbose_name="Temps de travail", + help_text="% du temps complet") regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12, - decimal_places=2, + decimal_places=2, null=True, default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT, - verbose_name=u"Nb. heures par semaine") + verbose_name="Nb. heures par semaine") # Recrutement - local = models.BooleanField(verbose_name=u"Local", default=True, - blank=True) - expatrie = models.BooleanField(verbose_name=u"Expatrié", default=False, - blank=True) - mise_a_disposition = models.BooleanField( - verbose_name=u"Mise à disposition", - default=False) - appel = models.CharField(max_length=10, - verbose_name=u"Appel à candidature", + local = models.NullBooleanField(verbose_name="Local", default=True, + null=True, blank=True) + expatrie = models.NullBooleanField(verbose_name="Expatrié", default=False, + null=True, blank=True) + mise_a_disposition = models.NullBooleanField( + verbose_name="Mise à disposition", + null=True, default=False) + appel = models.CharField(max_length=10, null=True, + verbose_name="Appel à candidature", choices=POSTE_APPEL_CHOICES, default='interne') @@ -130,25 +114,25 @@ class Poste_(Metadata): valeur_point_max = models.ForeignKey('ValeurPoint', db_column='valeur_point_max', related_name='+', null=True, blank=True) - devise_min = models.ForeignKey('Devise', db_column='devise_min', + devise_min = models.ForeignKey('Devise', db_column='devise_min', null=True, related_name='+', default=5) - devise_max = models.ForeignKey('Devise', db_column='devise_max', + devise_max = models.ForeignKey('Devise', db_column='devise_max', null=True, related_name='+', default=5) salaire_min = models.DecimalField(max_digits=12, decimal_places=2, - default=0) + null=True, default=0) salaire_max = models.DecimalField(max_digits=12, decimal_places=2, - default=0) + null=True, default=0) indemn_min = models.DecimalField(max_digits=12, decimal_places=2, - default=0) + null=True, default=0) indemn_max = models.DecimalField(max_digits=12, decimal_places=2, - default=0) + null=True, default=0) autre_min = models.DecimalField(max_digits=12, decimal_places=2, - default=0) + null=True, default=0) autre_max = models.DecimalField(max_digits=12, decimal_places=2, - default=0) + null=True, default=0) # Comparatifs de rémunération - devise_comparaison = models.ForeignKey('Devise', + devise_comparaison = models.ForeignKey('Devise', null=True, db_column='devise_comparaison', related_name='+', default=5) @@ -178,20 +162,33 @@ class Poste_(Metadata): # Autres Metadata date_validation = models.DateTimeField(null=True, blank=True) # de dae - date_debut = models.DateField(verbose_name=u"Date de début", + date_debut = models.DateField(verbose_name="Date de début", null=True, help_text=HELP_TEXT_DATE) - date_fin = models.DateField(verbose_name=u"Date de fin", + date_fin = models.DateField(verbose_name="Date de fin", help_text=HELP_TEXT_DATE, null=True, blank=True) class Meta: abstract = True ordering = ['implantation__nom', 'nom'] + verbose_name = "Poste" + verbose_name_plural = "Postes" def __unicode__(self): - # TODO : gérer si poste est vacant ou non dans affichage - # TODO : gérer le nom_feminin (autre méthode appelée par __unicode__ ?) - return u'%s - %s [%s]' % (self.implantation, self.nom, self.id) + representation = u'%s - %s [%s]' % (self.implantation, self.nom, + self.id) + if self.is_vacant(): + representation = representation + u' (vacant)' + return representation + + def is_vacant(self): + # TODO : si existe un dossier actif pour ce poste, return False + # self.dossier_set.all() fonctionne pas + return False + + +class Poste(Poste_): + __doc__ = Poste_.__doc__ class Poste(Poste_): @@ -204,32 +201,39 @@ POSTE_FINANCEMENT_CHOICES = ( ('C', 'C - Autre') ) -class PosteFinancement(models.Model): + +class PosteFinancement_(models.Model): """Pour un Poste, structure d'informations décrivant comment on prévoit financer ce Poste. """ poste = models.ForeignKey('Poste', db_column='poste', - related_name='financements') + 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=u"ex.: 33.33 % (décimale avec point)") + help_text="ex.: 33.33 % (décimale avec point)") commentaire = models.TextField( - help_text=u"Spécifiez la source de financement.") + help_text="Spécifiez la source de financement.") class Meta: + abstract = True ordering = ['type'] def __unicode__(self): return u'%s : %s %' % (self.type, self.pourcentage) + +class PosteFinancement(PosteFinancement_): + __doc__ = PosteFinancement_.__doc__ + + class PostePiece(models.Model): """Documents relatifs au Poste. Ex.: Description de poste """ - poste = models.ForeignKey("Poste", db_column='poste', + poste = models.ForeignKey('Poste', db_column='poste', related_name='pieces') - nom = models.CharField(verbose_name=u"Nom", max_length=255) - fichier = models.FileField(verbose_name=u"Fichier", + nom = models.CharField(verbose_name="Nom", max_length=255) + fichier = models.FileField(verbose_name="Fichier", upload_to=poste_piece_dispatch, storage=storage_prive) @@ -239,8 +243,20 @@ class PostePiece(models.Model): def __unicode__(self): return u'%s' % (self.nom) +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') + implantation = models.ForeignKey(ref.Implantation, null=True, blank=True, related_name="+") + nom = models.CharField(verbose_name="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) + montant_euros = models.IntegerField(null=True) + + class PosteCommentaire(Commentaire): - poste = models.ForeignKey("Poste", db_column='poste', related_name='+') + poste = models.ForeignKey('Poste', db_column='poste', related_name='+') ### EMPLOYÉ/PERSONNE @@ -255,7 +271,7 @@ SITUATION_CHOICES = ( ('M', 'Marié'), ) -class Employe(Metadata): +class Employe(AUFMetadata): """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de Dossiers qu'il occupe ou a occupé de Postes. @@ -264,29 +280,35 @@ class Employe(Metadata): """ # Identification nom = models.CharField(max_length=255) - prenom = models.CharField(max_length=255, verbose_name=u"Prénom") - # TODO : nom_affichage doit être obligatoire, pas nom et prenom + prenom = models.CharField(max_length=255, verbose_name="Prénom") nom_affichage = models.CharField(max_length=255, - verbose_name=u"Nom d'affichage", + verbose_name="Nom d'affichage", null=True, blank=True) nationalite = models.ForeignKey(ref.Pays, to_field='code', db_column='nationalite', - related_name='employes_nationalite') + related_name='employes_nationalite', + verbose_name="Nationalité") date_naissance = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de naissance", null=True, blank=True) genre = models.CharField(max_length=1, choices=GENRE_CHOICES) # Infos personnelles situation_famille = models.CharField(max_length=1, choices=SITUATION_CHOICES, + verbose_name="Situation familiale", null=True, blank=True) - date_entree = models.DateField(verbose_name=u"Date d'entrée à l'AUF", + date_entree = models.DateField(verbose_name="Date d'entrée à l'AUF", help_text=HELP_TEXT_DATE, null=True, blank=True) # Coordonnées - tel_domicile = models.CharField(max_length=255, null=True, blank=True) - tel_cellulaire = models.CharField(max_length=255, null=True, blank=True) + tel_domicile = models.CharField(max_length=255, + verbose_name="Tél. domicile", + null=True, blank=True) + tel_cellulaire = models.CharField(max_length=255, + verbose_name="Tél. cellulaire", + null=True, blank=True) adresse = models.CharField(max_length=255, null=True, blank=True) ville = models.CharField(max_length=255, null=True, blank=True) province = models.CharField(max_length=255, null=True, blank=True) @@ -296,20 +318,27 @@ class Employe(Metadata): null=True, blank=True) class Meta: - ordering = ['nom_affichage'] + ordering = ['nom_affichage','nom','prenom'] + verbose_name = "Employé" + verbose_name_plural = "Employés" def __unicode__(self): - # TODO : gérer nom d'affichage - return u'%s' % (self.nom_affichage) + return u'%s' % (self.get_nom()) + + def get_nom(self): + nom_affichage = self.nom_affichage + if not nom_affichage: + nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom) + return nom_affichage class EmployePiece(models.Model): """Documents relatifs à un employé. Ex.: CV... """ - employe = models.ForeignKey("Employe", db_column='employe', + employe = models.ForeignKey('Employe', db_column='employe', related_name='+') - nom = models.CharField(verbose_name=u"Nom", max_length=255) - fichier = models.FileField(verbose_name=u"Fichier", + nom = models.CharField(verbose_name="Nom", max_length=255) + fichier = models.FileField(verbose_name="Fichier", upload_to=dossier_piece_dispatch, storage=storage_prive) @@ -320,7 +349,7 @@ class EmployePiece(models.Model): return u'%s' % (self.nom) class EmployeCommentaire(Commentaire): - employe = models.ForeignKey("Employe", db_column='employe', + employe = models.ForeignKey('Employe', db_column='employe', related_name='+') @@ -331,38 +360,50 @@ LIEN_PARENTE_CHOICES = ( ('Fils', 'Fils'), ) -class AyantDroit(Metadata): +class AyantDroit(AUFMetadata): """Personne en relation avec un Employe. """ # Identification nom = models.CharField(max_length=255) - prenom = models.CharField(max_length=255) - # TODO : nom_affichage doit être obligatoire, pas nom et prenom + prenom = models.CharField(max_length=255, + verbose_name="Prénom",) nom_affichage = models.CharField(max_length=255, - verbose_name=u"Nom d'affichage", + verbose_name="Nom d'affichage", null=True, blank=True) nationalite = models.ForeignKey(ref.Pays, to_field='code', db_column='nationalite', - related_name='ayantdroits_nationalite') + related_name='ayantdroits_nationalite', + verbose_name="Nationalité") date_naissance = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de naissance", null=True, blank=True) genre = models.CharField(max_length=1, choices=GENRE_CHOICES) # Relation employe = models.ForeignKey('Employe', db_column='employe', - related_name='ayantdroits') + related_name='ayantdroits', + verbose_name="Employé") lien_parente = models.CharField(max_length=10, choices=LIEN_PARENTE_CHOICES, + verbose_name="Lien de parenté", null=True, blank=True) class Meta: ordering = ['nom_affichage'] + verbose_name = "Ayant droit" + verbose_name_plural = "Ayants droit" + def __unicode__(self): - # TODO : gérer nom d'affichage - return u'%s %s' % (self.prenom, self.nom.upper()) + return u'%s' % (self.get_nom()) + + def get_nom(self): + nom_affichage = self.nom_affichage + if not nom_affichage: + nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom) + return nom_affichage class AyantDroitCommentaire(Commentaire): - ayant_droit = models.ForeignKey("AyantDroit", db_column='ayant_droit', + ayant_droit = models.ForeignKey('AyantDroit', db_column='ayant_droit', related_name='+') @@ -379,7 +420,7 @@ COMPTE_COMPTA_CHOICES = ( ('aucun', 'Aucun'), ) -class Dossier_(Metadata): +class Dossier_(AUFMetadata): """Le Dossier regroupe les informations relatives à l'occupation d'un Poste par un Employe. Un seul Dossier existe par Poste occupé par un Employe. @@ -390,15 +431,16 @@ class Dossier_(Metadata): """ # Identification employe = models.ForeignKey('Employe', db_column='employe', - related_name='+') - poste = models.ForeignKey('Poste', db_column='poste', - related_name='+', editable=False) - statut = models.ForeignKey('Statut', related_name='+', default=3) + related_name='+', + verbose_name="Employé") + poste = models.ForeignKey('Poste', db_column='poste', related_name='+') + statut = models.ForeignKey('Statut', related_name='+', default=3, + null=True) organisme_bstg = models.ForeignKey('OrganismeBstg', db_column='organisme_bstg', related_name='+', - verbose_name=u"Organisme", - help_text=u"Si détaché (DET) ou \ + verbose_name="Organisme", + help_text="Si détaché (DET) ou \ mis à disposition (MAD), \ préciser l'organisme.", null=True, blank=True) @@ -406,28 +448,28 @@ class Dossier_(Metadata): # Recrutement remplacement = models.BooleanField(default=False) statut_residence = models.CharField(max_length=10, default='local', - verbose_name=u"Statut", + verbose_name="Statut", null=True, choices=STATUT_RESIDENCE_CHOICES) # Rémunération classement = models.ForeignKey('Classement', db_column='classement', related_name='+', null=True, blank=True) - regime_travail = models.DecimalField(max_digits=12, + regime_travail = models.DecimalField(max_digits=12, null=True, decimal_places=2, default=REGIME_TRAVAIL_DEFAULT, - verbose_name=u"Régime de travail", - help_text=u"% du temps complet") + verbose_name="Régime de travail", + help_text="% du temps complet") regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12, - decimal_places=2, + decimal_places=2, null=True, default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT, - verbose_name=u"Nb. heures par semaine") + verbose_name="Nb. heures par semaine") # Occupation du Poste par cet Employe (anciennement "mandat") - date_debut = models.DateField(verbose_name=u"Date de début d'occupation \ + date_debut = models.DateField(verbose_name="Date de début d'occupation \ de poste", help_text=HELP_TEXT_DATE) - date_fin = models.DateField(verbose_name=u"Date de fin d'occupation \ + date_fin = models.DateField(verbose_name="Date de fin d'occupation \ de poste", help_text=HELP_TEXT_DATE, null=True, blank=True) @@ -437,10 +479,20 @@ class Dossier_(Metadata): class Meta: abstract = True - ordering = ['poste__nom', 'employe__nom_affichage'] + ordering = ['employe__nom_affichage', 'employe__nom', 'poste__nom'] + verbose_name = "Dossier" + verbose_name_plural = "Dossiers" def __unicode__(self): - return u'%s - %s' % (self.poste.nom, self.employe) + poste = self.poste.nom + if self.employe.genre == 'F': + poste = self.poste.nom_feminin + return u'%s - %s' % (self.employe, poste) + + +class Dossier(Dossier_): + __doc__ = Dossier_.__doc__ + class Dossier(Dossier_): @@ -451,10 +503,10 @@ 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', + dossier = models.ForeignKey('Dossier', db_column='dossier', related_name='+') - nom = models.CharField(verbose_name=u"Nom", max_length=255) - fichier = models.FileField(verbose_name=u"Fichier", + nom = models.CharField(verbose_name="Nom", max_length=255) + fichier = models.FileField(verbose_name="Fichier", upload_to=dossier_piece_dispatch, storage=storage_prive) @@ -465,20 +517,23 @@ class DossierPiece(models.Model): return u'%s' % (self.nom) class DossierCommentaire(Commentaire): - dossier = models.ForeignKey("Dossier", db_column='dossier', + dossier = models.ForeignKey('Dossier', db_column='dossier', related_name='+') ### RÉMUNÉRATION -class RemunerationMixin(Metadata): +class RemunerationMixin(AUFMetadata): # Identification - dossier = models.ForeignKey('Dossier', db_column='dossier') + dossier = models.ForeignKey('Dossier', db_column='dossier', + related_name='%(app_label)s_%(class)s_remunerations') type = models.ForeignKey('TypeRemuneration', db_column='type', - related_name='+') + related_name='+', + verbose_name="Type de rémunération") type_revalorisation = models.ForeignKey('TypeRevalorisation', db_column='type_revalorisation', related_name='+', + verbose_name="Type de revalorisation", null=True, blank=True) montant = models.FloatField(null=True, blank=True, default=0) @@ -489,9 +544,11 @@ class RemunerationMixin(Metadata): # commentaire = precision commentaire = models.CharField(max_length=255, null=True, blank=True) # date_debut = anciennement date_effectif - date_debut = models.DateField(help_text=HELP_TEXT_DATE, + date_debut = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de début", null=True, blank=True) date_fin = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de fin", null=True, blank=True) class Meta: @@ -501,7 +558,7 @@ class RemunerationMixin(Metadata): def __unicode__(self): return u'%s %s (%s)' % (self.montant, self.devise.code, self.type.nom) -class Remuneration(RemunerationMixin): +class Remuneration_(RemunerationMixin): """Structure de rémunération (données budgétaires) en situation normale pour un Dossier. Si un Evenement existe, utiliser la structure de rémunération EvenementRemuneration de cet événement. @@ -526,34 +583,56 @@ class Remuneration(RemunerationMixin): devise = "???" return "%s %s" % (self.montant, devise) + class Meta: + abstract = True + verbose_name = "Rémunération" + verbose_name_plural = "Rémunérations" + + +class Remuneration(Remuneration_): + __doc__ = Remuneration_.__doc__ + ### CONTRATS + +class ContratManager(NoDeleteManager): + def get_query_set(self): + return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste') + -class Contrat(Metadata): +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='+') type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat', - related_name='+') - date_debut = models.DateField(help_text=HELP_TEXT_DATE) + related_name='+', + verbose_name="Type de contrat") + date_debut = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de début") date_fin = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de fin", null=True, blank=True) class Meta: ordering = ['dossier__employe__nom_affichage'] + verbose_name = "Contrat" + verbose_name_plural = "Contrats" def __unicode__(self): - return u'%s - %s' % (self.dossier.employe.nom_affichage, self.id) + return u'%s - %s' % (self.dossier, self.id) # TODO? class ContratPiece(models.Model): ### ÉVÉNEMENTS -class Evenement(Metadata): +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). @@ -565,54 +644,94 @@ class Evenement(Metadata): 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', + dossier = models.ForeignKey('Dossier', db_column='dossier', related_name='+') nom = models.CharField(max_length=255) - date_debut = models.DateField(help_text=HELP_TEXT_DATE) - date_fin = models.DateField(help_text=HELP_TEXT_DATE, + date_debut = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de début") + date_fin = models.DateField(help_text=HELP_TEXT_DATE, + verbose_name="Date de fin", null=True, blank=True) + class Meta: + abstract = True ordering = ['nom'] + verbose_name = "Évènement" + verbose_name_plural = "Évènements" def __unicode__(self): return u'%s' % (self.nom) + + +class Evenement(Evenement_): + __doc__ = Evenement_.__doc__ + -class EvenementRemuneration(RemunerationMixin): +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='+') + related_name='+', + verbose_name="É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 = "Évènement - rémunération" + verbose_name_plural = "Évènements - rémunérations" + + +class EvenementRemuneration(EvenementRemuneration_): + __doc__ = EvenementRemuneration_.__doc__ + + class Meta: + abstract = True + + +class EvenementRemuneration(EvenementRemuneration_): + __doc__ = EvenementRemuneration_.__doc__ ### RÉFÉRENCES RH -class FamilleEmploi(Metadata): +class FamilleEmploi(AUFMetadata): """Catégorie utilisée dans la gestion des Postes. Catégorie supérieure à TypePoste. """ nom = models.CharField(max_length=255) + class Meta: + ordering = ['nom'] + verbose_name = "Famille d'emploi" + verbose_name_plural = "Familles d'emploi" + def __unicode__(self): return u'%s' % (self.nom) -class TypePoste(Metadata): +class TypePoste(AUFMetadata): """Catégorie de Poste. """ nom = models.CharField(max_length=255) - nom_feminin = models.CharField(max_length=255) + nom_feminin = models.CharField(max_length=255, + verbose_name="Nom féminin") - is_responsable = models.BooleanField(default=False) + is_responsable = models.BooleanField(default=False, + verbose_name="Poste de responsabilité") famille_emploi = models.ForeignKey('FamilleEmploi', db_column='famille_emploi', - related_name='+') + related_name='+', + verbose_name="Famille d'emploi") class Meta: ordering = ['nom'] + verbose_name = "Type de poste" + verbose_name_plural = "Types de poste" def __unicode__(self): - # TODO : gérer nom féminin return u'%s' % (self.nom) @@ -629,34 +748,48 @@ NATURE_REMUNERATION_CHOICES = ( ('Traitement', 'Traitement'), ) -class TypeRemuneration(Metadata): +class TypeRemuneration(AUFMetadata): """Catégorie de Remuneration. """ nom = models.CharField(max_length=255) type_paiement = models.CharField(max_length=30, - choices=TYPE_PAIEMENT_CHOICES) + choices=TYPE_PAIEMENT_CHOICES, + verbose_name="Type de paiement") nature_remuneration = models.CharField(max_length=30, - choices=NATURE_REMUNERATION_CHOICES) + choices=NATURE_REMUNERATION_CHOICES, + verbose_name="Nature de la rémunération") + + class Meta: + ordering = ['nom'] + verbose_name = "Type de rémunération" + verbose_name_plural = "Types de rémunération" def __unicode__(self): return u'%s' % (self.nom) -class TypeRevalorisation(Metadata): +class TypeRevalorisation(AUFMetadata): """Justification du changement de la Remuneration. (Actuellement utilisé dans aucun traitement informatique.) """ nom = models.CharField(max_length=255) + + class Meta: + ordering = ['nom'] + verbose_name = "Type de revalorisation" + verbose_name_plural = "Types de revalorisation" def __unicode__(self): return u'%s' % (self.nom) -class Service(Metadata): +class Service(AUFMetadata): """Unité administrative où les Postes sont rattachés. """ nom = models.CharField(max_length=255) class Meta: ordering = ['nom'] + verbose_name = "Service" + verbose_name_plural = "Services" def __unicode__(self): return u'%s' % (self.nom) @@ -667,7 +800,7 @@ TYPE_ORGANISME_CHOICES = ( ('DET', 'Détachement'), ) -class OrganismeBstg(Metadata): +class OrganismeBstg(AUFMetadata): """Organisation d'où provient un Employe mis à disposition (MAD) de ou détaché (DET) à l'AUF à titre gratuit. @@ -682,11 +815,13 @@ class OrganismeBstg(Metadata): class Meta: ordering = ['type', 'nom'] + verbose_name = "Organisme BSTG" + verbose_name_plural = "Organismes BSTG" def __unicode__(self): - return u'%s (%s)' % (self.nom, self.type) + return u'%s (%s)' % (self.nom, self.get_type_display()) -class Statut(Metadata): +class Statut(AUFMetadata): """Statut de l'Employe dans le cadre d'un Dossier particulier. """ # Identification @@ -695,6 +830,8 @@ class Statut(Metadata): class Meta: ordering = ['code'] + verbose_name = "Statut d'employé" + verbose_name_plural = "Statuts d'employé" def __unicode__(self): return u'%s : %s' % (self.code, self.nom) @@ -710,7 +847,8 @@ TYPE_CLASSEMENT_CHOICES = ( ('HG', 'HG - Hors grille [direction]'), ) -class Classement(Metadata): + +class Classement_(AUFMetadata): """Éléments de classement de la "Grille générique de classement hiérarchique". @@ -721,74 +859,97 @@ class Classement(Metadata): """ # Identification type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES) - echelon = models.IntegerField() - degre = models.IntegerField() - coefficient = models.FloatField(default=0) + echelon = models.IntegerField(verbose_name="Échelon") + degre = models.IntegerField(verbose_name="Degré") + coefficient = models.FloatField(default=0, verbose_name="Coéfficient", + null=True) # Méta # annee # au lieu de date_debut et date_fin commentaire = models.TextField(null=True, blank=True) class Meta: + abstract = True ordering = ['type','echelon','degre','coefficient'] + verbose_name = "Classement" + verbose_name_plural = "Classements" def __unicode__(self): return u'%s.%s.%s (%s)' % (self.type, self.echelon, self.degre, self.coefficient) -class TauxChange(Metadata): +class Classement(Classement_): + __doc__ = Classement_.__doc__ + + +class TauxChange_(AUFMetadata): """Taux de change de la devise vers l'euro (EUR) pour chaque année budgétaire. """ # Identification - devise = models.ForeignKey('Devise', to_field='code', db_column='devise', + devise = models.ForeignKey('Devise', db_column='devise', related_name='+') - annee = models.IntegerField() - taux = models.FloatField() - + annee = models.IntegerField(verbose_name="Année") + taux = models.FloatField(verbose_name="Taux vers l'euro") + class Meta: - ordering = ['annee', 'devise__code'] + abstract = True + ordering = ['-annee', 'devise__code'] + verbose_name = "Taux de change" + verbose_name_plural = "Taux de change" def __unicode__(self): - return u'%s : %s €' % (self.devise.code, self.taux) + return u'%s : %s € (%s)' % (self.devise, self.taux, self.annee) -class ValeurPoint(Metadata): + +class TauxChange(TauxChange_): + __doc__ = TauxChange_.__doc__ + + +class ValeurPoint_(AUFMetadata): """Utile pour connaître, pour un Dossier, le salaire de base théorique lié au classement dans la grille. La ValeurPoint s'obtient par l'implantation - du POste de ce Dossier : dossier.poste.implantation (pseudo code). + du Poste de ce Dossier : dossier.poste.implantation (pseudo code). salaire de base = coefficient * valeur du point de l'Implantation du Poste """ - valeur = models.FloatField() - devise = models.ForeignKey('Devise', db_column='devise', + valeur = models.FloatField(null=True) + devise = models.ForeignKey('Devise', db_column='devise', null=True, related_name='+', default=5) implantation = models.ForeignKey(ref.Implantation, - db_column='implantation', - related_name='valeur_point') + db_column='implantation', + related_name='%(app_label)s_valeur_point') # Méta annee = models.IntegerField() - # Stockage de tous les taux de change - # pour optimiser la recherche de la devise associée - annee_courante = datetime.datetime.now().year - tauxchange = TauxChange.objects.select_related('devise') \ - .filter(annee=annee_courante) - class Meta: ordering = ['annee', 'implantation__nom'] - + abstract = True + ordering = ['annee'] + verbose_name = "Valeur du point" + verbose_name_plural = "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): - tx = self.get_tauxchange_courant() - if tx: - devise_code = tx.devise.code - else: - devise_code = "??" - return u'%s %s (%s-%s)' % (self.valeur, devise_code, - self.implantation_id, self.annee) - - class Meta: - ordering = ['valeur'] + return u'%s %s (%s)' % (self.valeur, self.devise, self.annee) + + +class ValeurPoint(ValeurPoint_): + __doc__ = ValeurPoint_.__doc__ + -class Devise(Metadata): +class Devise(AUFMetadata): """Devise monétaire. """ code = models.CharField(max_length=10, unique=True) @@ -796,23 +957,30 @@ class Devise(Metadata): class Meta: ordering = ['code'] + verbose_name = "Devise" + verbose_name_plural = "Devises" def __unicode__(self): return u'%s - %s' % (self.code, self.nom) -class TypeContrat(Metadata): +class TypeContrat(AUFMetadata): """Type de contrat. """ nom = models.CharField(max_length=255) nom_long = models.CharField(max_length=255) + class Meta: + ordering = ['nom'] + verbose_name = "Type de contrat" + verbose_name_plural = "Types de contrat" + def __unicode__(self): return u'%s' % (self.nom) ### AUTRES -class ResponsableImplantation(Metadata): +class ResponsableImplantation(AUFMetadata): """Le responsable d'une implantation. Anciennement géré sur le Dossier du responsable. """ @@ -828,3 +996,5 @@ class ResponsableImplantation(Metadata): class Meta: ordering = ['implantation__nom'] + verbose_name = "Responsable d'implantation" + verbose_name_plural = "Responsables d'implantation"