# -*- encoding: utf-8 -*-
-from django.db.models import Q
+import datetime
+from ordereddict import OrderedDict
+from dateutil.relativedelta import relativedelta
from django import forms
-from django.forms.models import inlineformset_factory
-from django.contrib.admin import widgets as admin_widgets
+from django.core.urlresolvers import reverse
+from django.core.exceptions import MultipleObjectsReturned
+from django.forms.models import BaseInlineFormSet
+from django.forms.models import (
+ inlineformset_factory,
+ modelformset_factory,
+ _get_foreign_key,
+ )
+from django.db.models import Q, Max, Count
+from django.shortcuts import redirect
+from django.contrib.admin import widgets as admin_widgets
+
from ajax_select.fields import AutoCompleteSelectField
+
+from auf.django.references import models as ref
from auf.django.workflow.forms import WorkflowFormMixin
-from datamaster_modeles import models as ref
-from dae import models as dae
-from utils import get_employe_from_user, is_user_dans_service
-from rh_v1 import models as rh
-from workflow import grp_drh
+from auf.django.workflow.models import WorkflowCommentaire
+
+from project import groups
+from project.rh import models as rh
+from project.dae import models as dae
+from .widgets import ReadOnlyChoiceWidget, ReadOnlyWidget
+from project.dae.workflow import POSTE_ETATS_BOUTONS, POSTE_ETAT_FINALISE
+
+
+def filtered_archived_fields_form_factory(*fields):
+ """
+ Retourne un model form
+ """
+ class FilterArchivedFields(object):
+ def __init__(self, *a, **kw):
+ super(FilterArchivedFields, self).__init__(*a, **kw)
+ for f in fields:
+ self.fields[f].queryset = (
+ self.fields[f]._queryset.filter(archive=False)
+ )
+ return FilterArchivedFields
+
+
+class BaseInlineFormSetWithInitial(BaseInlineFormSet):
+ """
+ Cette classe permet de fournir l'option initial aux inlineformsets.
+ Elle devient désuette en django 1.4.
+ """
+ def __init__(self, data=None, files=None, instance=None,
+ save_as_new=False, prefix=None, queryset=None, **kwargs):
+
+ self.initial_extra = kwargs.pop('initial', None)
+
+ from django.db.models.fields.related import RelatedObject
+ if instance is None:
+ self.instance = self.fk.rel.to()
+ else:
+ self.instance = instance
+ self.save_as_new = save_as_new
+ # is there a better way to get the object descriptor?
+ self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
+ if queryset is None:
+ queryset = self.model._default_manager
+ qs = queryset.filter(**{self.fk.name: self.instance})
+ super(BaseInlineFormSetWithInitial, self).__init__(data, files, prefix=prefix,
+ queryset=qs, **kwargs)
+
+ def _construct_form(self, i, **kwargs):
+ if self.is_bound and i < self.initial_form_count():
+ # Import goes here instead of module-level because importing
+ # django.db has side effects.
+ from django.db import connections
+ pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
+ pk = self.data[pk_key]
+ pk_field = self.model._meta.pk
+ pk = pk_field.get_db_prep_lookup('exact', pk,
+ connection=connections[self.get_queryset().db])
+ if isinstance(pk, list):
+ pk = pk[0]
+ kwargs['instance'] = self._existing_object(pk)
+ if i < self.initial_form_count() and not kwargs.get('instance'):
+ kwargs['instance'] = self.get_queryset()[i]
+ if i >= self.initial_form_count() and self.initial_extra:
+ # Set initial values for extra forms
+ try:
+ kwargs['initial'] = self.initial_extra[i-self.initial_form_count()]
+ except IndexError:
+ pass
+
+ defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
+ if self.is_bound:
+ defaults['data'] = self.data
+ defaults['files'] = self.files
+ if self.initial:
+ try:
+ defaults['initial'] = self.initial[i]
+ except IndexError:
+ pass
+ # Allow extra forms to be empty.
+ if i >= self.initial_form_count():
+ defaults['empty_permitted'] = True
+ defaults.update(kwargs)
+ form = self.form(**defaults)
+ self.add_fields(form, i)
+ return form
+
def _implantation_choices(obj, request):
# TRAITEMENT NORMAL
- employe = get_employe_from_user(request.user)
- # SERVICE
- if is_user_dans_service(request.user):
- q = Q(**{ 'id' : employe.implantation_id })
- # REGION
- else:
- q = Q(**{ 'region' : employe.implantation.region })
+ employe = groups.get_employe_from_user(request.user)
+ q = Q(**{'zone_administrative__in': groups.get_zones_from_user(request.user)})
# TRAITEMENT DRH
- if grp_drh in request.user.groups.all():
+ user_groupes = [g.name for g in request.user.groups.all()]
+ if groups.DRH_NIVEAU_1 in user_groupes or \
+ groups.DRH_NIVEAU_2 in user_groupes:
q = Q()
- return [('', '----------')] + [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
+ return [('', '----------')] + \
+ [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
-def _employe_choices(obj, request):
- q = Q(id_rh__isnull=True) & Q(id_rh__isnull=True)
+def _employe_choices(obj, request):
# TRAITEMENT NORMAL
- employe = get_employe_from_user(request.user)
- # SERVICE
- if is_user_dans_service(request.user):
- q_region_service = Q(implantation1=employe.implantation) | Q(implantation2=employe.implantation)
- # REGION
- else:
- q_region_service = Q(implantation1__region=employe.implantation.region) | Q(implantation2__region=employe.implantation.region)
+ employe = groups.get_employe_from_user(request.user)
+ q_dae_region_service = Q(
+ poste__implantation__zone_administrative__in=(
+ groups.get_zones_from_user(request.user)
+ )
+ )
+ q_rh_region_service = Q(
+ poste__implantation__zone_administrative__in=(
+ groups.get_zones_from_user(request.user)
+ )
+ )
# TRAITEMENT DRH
- if grp_drh in request.user.groups.all():
- q_region_service = Q()
-
- # Construction de la liste des employés en puisant dans DAE (pas d'info) et dans rh_v1
- # Pour le filtrage par région/service, on est obligé d'aller regarder le dossier rh_v1
- # car l'information dans le modèle rh_v1.Employe n'existe pas.
- dae_ = dae.Employe.objects.filter(id_rh__isnull=True)
- copies = dae.Employe.objects.filter(Q(id_rh__isnull=False))
+ user_groupes = [g.name for g in request.user.groups.all()]
+ if groups.DRH_NIVEAU_1 in user_groupes or \
+ groups.DRH_NIVEAU_2 in user_groupes:
+ q_dae_region_service = Q()
+ q_rh_region_service = Q()
+
+ # On filtre les employes avec les droits régionaux et on s'assure que
+ # c'est bien le dernier dossier en date pour sortir l'employe. On retient
+ # un employé qui travaille présentement dans la même région que le user
+ # connecté.
+ dossiers_regionaux_ids = [
+ d.id for d in dae.Dossier.objects.filter(q_dae_region_service)
+ ]
+ employes_ids = [
+ d['employe']
+ for d in dae.Dossier.objects
+ .values('employe')
+ .annotate(dernier_dossier=Max('id'))
+ if d['dernier_dossier'] in dossiers_regionaux_ids
+ ]
+ dae_employe = dae.Employe.objects.filter(id__in=employes_ids)
+ dae_ = dae_employe.filter(id_rh__isnull=True)
+ copies = dae_employe.filter(Q(id_rh__isnull=False))
id_copies = [p.id_rh_id for p in copies.all()]
- employes_ids = list(set([d.employe_id for d in rh.Dossier.objects.filter(q_region_service)]))
- rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies)
-
- def option_label(employe):
- return "%s %s" % (employe.nom.upper(), employe.prenom.title())
- return [('', 'Nouvel employé')] + \
- sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies] +
- [('rh-%s' % p.id, option_label(p)) for p in rhv1],
- key=lambda t: t[1])
+ dossiers_regionaux_ids = [
+ d.id for d in rh.Dossier.objects.filter(q_rh_region_service)
+ ]
+ employes_ids = [
+ d['employe']
+ for d in rh.Dossier.objects
+ .values('employe')
+ .annotate(dernier_dossier=Max('id'))
+ if d['dernier_dossier'] in dossiers_regionaux_ids
+ ]
+ rhv1 = rh.Employe.objects \
+ .filter(id__in=employes_ids) \
+ .exclude(id__in=id_copies)
+
+ # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont
+ # pas de Dossier associés
+ employes_avec_dae = [d.employe_id for d in dae.Dossier.objects.all()]
+ employes_orphelins = dae.Employe.objects.exclude(id__in=employes_avec_dae)
+
+ def option_label(employe, extra=""):
+ if extra:
+ extra = " [%s]" % extra
+ return "%s %s %s" % (employe.nom.upper(), employe.prenom.title(), extra)
+
+ lbl_rh = sorted([('rh-%s' % p.id, option_label(p, "existant dans rh")) for p in rhv1],
+ key=lambda t: t[1])
+ lbl_dae = sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins],
+ key=lambda t: t[1])
+ return [('', 'Nouvel employé')] + lbl_rh + lbl_dae
def label_poste_display(poste):
"""Formate un visuel pour un poste dans une liste déroulante"""
- label = u"%s - %s [%s]" %(poste.type_poste, poste.type_poste.famille_emploi.nom, poste.id)
+ annee = ""
+ if poste.date_debut:
+ annee = poste.date_debut.year
+
+ nom = poste.nom
+ label = u"%s (%s) %s [%s]" % (
+ annee,
+ poste.implantation.nom_court,
+ nom,
+ #poste.type_poste.categorie_emploi.nom,
+ poste.id,
+ )
return label
-class PostePieceForm(inlineformset_factory(dae.Poste, dae.PostePiece)):
- pass
-class DossierPieceForm(inlineformset_factory(dae.Dossier, dae.DossierPiece)):
- pass
+PostePieceFormSet = inlineformset_factory(dae.Poste, dae.PostePiece,)
+DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
+
+# Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
+# données de RH
+FinancementFormSetInitial = inlineformset_factory(
+ dae.Poste,
+ dae.PosteFinancement,
+ formset=BaseInlineFormSetWithInitial,
+ extra=2
+)
+FinancementFormSet = inlineformset_factory(
+ dae.Poste,
+ dae.PosteFinancement,
+ extra=2
+)
+
+
+class DossierComparaisonForm(
+ filtered_archived_fields_form_factory(
+ 'classement',
+ ),
+ forms.ModelForm):
+
+ recherche = AutoCompleteSelectField('dossiers', required=False)
+ poste = forms.CharField(
+ max_length=255, widget=forms.TextInput(attrs={'size': '60'})
+ )
+ cmp_dossier = forms.IntegerField(
+ widget=forms.widgets.HiddenInput,
+ required=False
+ )
-class FinancementForm(inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=1)):
- pass
+ class Meta:
+ model = dae.DossierComparaison
+ exclude = ('dossier',)
-class JustificationNouvelEmployeForm(inlineformset_factory(dae.Dossier,
- dae.JustificationNouvelEmploye,
- extra=0,
- can_delete=False,
- exclude=('question',))):
- """
- Formulaire de justification d'un nouvel employé.
- Le dossier a besoin d'être enregistré une première fois afin de prépopuler les questions.
- """
- def __init__(self, *args, **kwargs):
- instance = kwargs['instance']
- if instance.id:
- q_ids = [j.question.id for j in instance.justificationnouvelemploye_set.filter(dossier=instance)]
- for q in dae.JustificationQuestion.objects.filter(type="N"):
- if q.id in q_ids:
- continue
- j = dae.JustificationNouvelEmploye()
- j.dossier = instance
- j.question = q
- j.save()
- super(self.__class__, self).__init__(*args, **kwargs)
-
-class JustificationAutreEmployeForm(inlineformset_factory(dae.Dossier,
- dae.JustificationAutreEmploye,
- extra=0,
- can_delete=False,
- exclude=('question',))):
+DossierComparaisonFormSet = modelformset_factory(
+ dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
+)
+
+
+class PosteComparaisonForm(
+ filtered_archived_fields_form_factory('classement'),
+ forms.ModelForm):
+
+ recherche = AutoCompleteSelectField('dae_postes', required=False)
+
+ cmp_poste = forms.IntegerField(
+ widget=forms.widgets.HiddenInput,
+ required=False,
+ )
+
+ class Meta:
+ model = dae.PosteComparaison
+ exclude = ('poste',)
+
+# Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
+# données de RH
+PosteComparaisonFormSetInitial = inlineformset_factory(
+ dae.Poste,
+ dae.PosteComparaison,
+ extra=3,
+ max_num=3,
+ form=PosteComparaisonForm,
+ formset=BaseInlineFormSetWithInitial,
+)
+PosteComparaisonFormSet = inlineformset_factory(
+ dae.Poste,
+ dae.PosteComparaison,
+ extra=3,
+ max_num=3,
+ form=PosteComparaisonForm,
+)
+
+
+class FlexibleRemunForm(
+ filtered_archived_fields_form_factory(
+ 'type',
+ ),
+ forms.ModelForm):
+ # Utilisé dans templats.
+ montant_mensuel = forms.DecimalField(required=False)
+ montant = forms.DecimalField(required=True, label='Montant annuel')
+
+ class Meta:
+ model = dae.Remuneration
+
+ def __init__(self, *a, **kw):
+ super(FlexibleRemunForm, self).__init__(*a, **kw)
+ # self.fields['type'].widget = ReadOnlyChoiceWidget(choices=self.fields['type'].choices)
+
+ def clean_devise(self):
+ devise = self.cleaned_data['devise']
+ if devise.code == 'EUR':
+ return devise
+ implantation = ref.Implantation.objects.get(
+ id=self.data['implantation']
+ )
+ liste_taux = devise.tauxchange_set.order_by('-annee')
+ if len(liste_taux) == 0:
+ raise forms.ValidationError(
+ u"La devise %s n'a pas de taux pour l'implantation %s" %
+ (devise, implantation)
+ )
+ else:
+ return devise
+
+ def has_changed(self):
+ """
+ Modification de has_changed pour qu'il ignore les montant a 0
+ et les 'types'.
+ """
+
+ changed_data = self.changed_data
+
+ # Type is set in hidden fields, it shouldn't be changed by the
+ # user; ignore when checking if data has changed.
+ if 'type' in changed_data:
+ changed_data.pop(changed_data.index('type'))
+
+ # Montant is set to 0 in javascript, ifnore 'montant' data if
+ # its value is 0.
+
+ # Generer le key tel qu'identifié dans self.data:
+ montant_key = '-'.join((self.prefix, 'montant'))
+
+ if ('montant' in changed_data and
+ self.data.get(montant_key, '0') == '0'):
+ changed_data.pop(changed_data.index('montant'))
+
+ return bool(changed_data)
+
+
+class ReadOnlyRemunForm(FlexibleRemunForm):
+ # Utilisé dans templats.
+
+ def __init__(self, *a, **kw):
+ super (ReadOnlyRemunForm, self).__init__(*a, **kw)
+ for field in self.fields:
+ field = self.fields[field]
+ if not isinstance(field.widget, (
+ forms.widgets.HiddenInput,
+ forms.widgets.Select)):
+ field.widget = ReadOnlyWidget()
+ elif isinstance(field.widget, forms.widgets.Select):
+ field.widget = ReadOnlyChoiceWidget(choices=field.choices)
+
+
+class GroupedInlineFormset(BaseInlineFormSet):
+
+ def set_groups(self,
+ groups,
+ group_accessor,
+ choice_overrides=[]):
+
+
+ # Create pre-defined groups.
+ self.groups = OrderedDict()
+ for group in groups:
+ self.groups[group[0]] = {
+ 'name': group[1],
+ 'key': group[0],
+ 'forms': [],
+ }
+
+ # Assign each form to a group.
+ ungrouped_forms = []
+ for form in self.forms:
+ if bool(form.initial):
+ grp = group_accessor(form)
+ if grp[0] not in self.groups:
+ self.groups[grp[0]] = {
+ 'name': grp[1],
+ 'key': grp[0],
+ 'forms': [],
+ }
+ self.groups[grp[0]]['forms'].append(form)
+ else:
+ ungrouped_forms.append(form)
+
+
+ # Distribuer les extras de django dans les groupes, et ajouter
+ # des extras pour les groupes en nécessitant.
+ f_count = len(self.forms)
+ for g in self.groups:
+ for i in xrange(f_count, f_count + self.extra):
+ if len(ungrouped_forms) == 0:
+ f_count += 1
+
+ if len(ungrouped_forms) > 0:
+ new_form = ungrouped_forms.pop()
+ else:
+ new_form = self._construct_form(i)
+ self.forms.append(new_form)
+
+ self.groups[g]['forms'].append(new_form)
+
+
+ # Override form choices with the data provided in
+ # choice_overrides
+ for key in choice_overrides:
+ for form in self.groups.get(key, {'forms': []})['forms']:
+ for field_key in choice_overrides[key]:
+ form.fields[field_key].choices = choice_overrides[
+ key][field_key]
+
+
+ # Create an iterable for easier access in template.
+ self.group_list = self.groups.values()
+
+
+def remun_formset_factory(parent_model,
+ model,
+ form=forms.ModelForm,
+ formset=GroupedInlineFormset,
+ fk_name=None,
+ fields=None,
+ exclude=None,
+ can_order=False,
+ can_delete=True,
+ read_only=False,
+ extra=2,
+ max_num=None,
+ formfield_callback=None,
+ groups=None,
+ choice_overrides=[]):
+ trs = rh.TypeRemuneration.objects.all()
+ # extra = max_num = trs.count()
+ fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
+ # enforce a max_num=1 when the foreign key to the parent model is unique.
+ if fk.unique:
+ max_num = 1
+ kwargs = {
+ 'form': form,
+ 'formfield_callback': formfield_callback,
+ 'formset': formset,
+ 'extra': extra,
+ 'can_delete': can_delete,
+ 'can_order': can_order,
+ 'fields': fields,
+ 'exclude': exclude,
+ 'max_num': max_num,
+ }
+ FormSet = modelformset_factory(model, **kwargs)
+ FormSet.fk = fk
+ FormSet.read_only = read_only
+
+ def grouper(form):
+ rtype = form.initial['type']
+ if not isinstance(rtype, rh.TypeRemuneration):
+ rtype = rh.TypeRemuneration.objects.get(id=rtype)
+ return (rtype.nature_remuneration,
+ rtype.nature_remuneration
+ )
+
+
+
+ # Monkey patch FormSet.
+ def __init__(inst, *a, **kw):
+ super(inst.__class__, inst).__init__(*a, **kw)
+ inst.set_groups(groups, grouper, choice_overrides)
+
+ FormSet.__init__ = __init__
+
+ return FormSet
+
+
+def remun_formset_factory_factory(
+ read_only=False,
+ parent_model=dae.Dossier,
+ model=dae.Remuneration,
+ exclude_archived=False):
"""
- Formulaire de justification d'un nouvel employé.
- Le dossier a besoin d'être enregistré une première fois afin de prépopuler les questions.
+ Don't we love factory factories?
"""
- def __init__(self, *args, **kwargs):
- instance = kwargs['instance']
- if instance.id:
- q_ids = [j.question.id for j in instance.justificationautreemploye_set.filter(dossier=instance)]
- for q in dae.JustificationQuestion.objects.filter(type="R"):
- if q.id in q_ids:
- continue
- j = dae.JustificationAutreEmploye()
- j.dossier = instance
- j.question = q
- j.save()
- super(self.__class__, self).__init__(*args, **kwargs)
-
-class PosteForm(forms.ModelForm):
- """ Formulaire des postes. """
+ null_choice = ('', '-' * 10)
+ extras = 2 if not read_only else 0
+ can_delete = False if read_only else True
+ form_class = ReadOnlyRemunForm if read_only else FlexibleRemunForm
+
+ choice_override_extra_q = {}
+
+ if exclude_archived:
+ choice_override_extra_q.update({
+ 'archive': False
+ })
+ return remun_formset_factory(
+ parent_model,
+ model,
+ form=form_class,
+ extra=extras,
+ can_delete=can_delete,
+ read_only=read_only,
+ groups = rh.NATURE_REMUNERATION_CHOICES,
+ choice_overrides = {
+ u'Traitement': {
+ 'type': [null_choice] + list(
+ rh.TypeRemuneration.objects.filter(
+ nature_remuneration=u'Traitement',
+ **choice_override_extra_q).values_list(
+ 'id', 'nom')
+ )
+ },
+ u'Indemnité': {
+ 'type': [null_choice] + list(
+ rh.TypeRemuneration.objects.filter(
+ nature_remuneration=u'Indemnité',
+ **choice_override_extra_q).values_list(
+ 'id', 'nom')
+ )
+ },
+ u'Charges': {
+ 'type': [null_choice] + list(
+ rh.TypeRemuneration.objects.filter(
+ nature_remuneration=u'Charges',
+ **choice_override_extra_q).values_list(
+ 'id', 'nom')
+ )
+ },
+ u'Accessoire': {
+ 'type': [null_choice] + list(
+ rh.TypeRemuneration.objects.filter(
+ nature_remuneration=u'Accessoire',
+ **choice_override_extra_q).values_list(
+ 'id', 'nom')
+ )
+ },
+ u'RAS': {
+ 'type': [null_choice] + list(
+ rh.TypeRemuneration.objects.filter(
+ nature_remuneration=u'RAS',
+ **choice_override_extra_q).values_list(
+ 'id', 'nom')
+ )
+ },
+ },
+ )
+
+
+RemunForm = remun_formset_factory_factory(
+ read_only=False,
+ parent_model=dae.Dossier,
+ model=dae.Remuneration,
+ exclude_archived=True,
+)
+
+ReadOnlyRemunFormSet = remun_formset_factory_factory(
+ read_only=True,
+ parent_model=dae.Dossier,
+ model=dae.Remuneration,
+ )
+
+PosteCompReadOnlyRemunFormSet = remun_formset_factory_factory(
+ read_only=True,
+ parent_model=dae.PosteComparaison,
+ model=dae.PosteComparaisonRemuneration,
+ )
+
+DossierCompReadOnlyRemunFormSet = remun_formset_factory_factory(
+ read_only=True,
+ parent_model=dae.DossierComparaison,
+ model=dae.DossierComparaisonRemuneration,
+ )
+
+
+class PosteForm(filtered_archived_fields_form_factory(
+ 'classement_min',
+ 'classement_max',),
+ forms.ModelForm):
+ """ Formulaire des postes. """
+
+ # On ne propose que les services actifs
+ service = forms.ModelChoiceField(
+ queryset=rh.Service.objects.all(), required=True
+ )
+
+ responsable = AutoCompleteSelectField('responsables', required=True)
+ #responsable = forms.ModelChoiceField(
+ # queryset=rh.Poste.objects.select_related(depth=1))
+
+ # La liste des choix est laissée vide. Voir __init__ pour la raison.
+ poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
+ choices=(), required=False)
+
+ valeur_point_min = forms.ModelChoiceField(
+ queryset=rh.ValeurPoint.actuelles.all(), required=False
+ )
+ valeur_point_max = forms.ModelChoiceField(
+ queryset=rh.ValeurPoint.actuelles.all(), required=False
+ )
+
class Meta:
model = dae.Poste
-
- fields = ('poste', 'implantation', 'type_poste', 'service', 'nom',
+ fields = ('type_intervention',
+ 'poste', 'implantation', 'type_poste', 'service', 'nom',
'responsable', 'local', 'expatrie', 'mise_a_disposition',
- 'appel', 'date_debut', 'date_fin', 'actif',
+ 'appel', 'date_debut', 'date_fin',
'regime_travail', 'regime_travail_nb_heure_semaine',
'classement_min', 'classement_max',
'valeur_point_min', 'valeur_point_max',
'devise_min', 'devise_max',
- 'salaire_min', 'salaire_max', 'indemn_min', 'indemn_max',
+ 'salaire_min', 'salaire_max',
+ 'indemn_expat_min', 'indemn_expat_max',
+ 'indemn_fct_min', 'indemn_fct_max',
+ 'charges_patronales_min', 'charges_patronales_max',
'autre_min', 'autre_max', 'devise_comparaison',
'comp_locale_min', 'comp_locale_max',
'comp_universite_min', 'comp_universite_max',
'comp_autre_min', 'comp_autre_max',
'justification',
)
- widgets = dict(appel=forms.RadioSelect(),
+ widgets = dict(type_intervention=forms.RadioSelect(),
+ appel=forms.RadioSelect(),
nom=forms.TextInput(attrs={'size': 60},),
date_debut=admin_widgets.AdminDateWidget(),
date_fin=admin_widgets.AdminDateWidget(),
#devise_max=forms.Select(attrs={'disabled':'disabled'}),
)
- responsable=AutoCompleteSelectField('responsables', required=True)
- #responsable = forms.ModelChoiceField(
- # queryset=rh.Poste.objects.select_related(depth=1))
-
- # La liste des choix est laissée vide. Voir __init__ pour la raison.
- poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
- choices=(), required=False)
-
- valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
- valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
-
def __init__(self, *args, **kwargs):
""" Mise à jour dynamique du contenu du menu des postes.
request = kwargs.pop('request')
super(PosteForm, self).__init__(*args, **kwargs)
self.fields['poste'].choices = self._poste_choices(request)
- self.fields['implantation'].choices = _implantation_choices(self, request)
+
+ self.fields['implantation'].choices = \
+ _implantation_choices(self, request)
# Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
if self.instance and self.instance.id is None:
dossiers = self.instance.get_dossiers()
if len(dossiers) > 0:
- self.initial['service'] = dossiers[0].service_id
- self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
-
+ self.initial['service'] = dossiers[0].poste.service
def _poste_choices(self, request):
""" Menu déroulant pour les postes.
-
- Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
- d'équivalent dans dae.
-
+ Constitué des postes de RH
"""
- dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
- copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
- id_copies = [p.id_rh_id for p in copies.all()]
- rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
- # Optimisation de la requête
- rhv1 = rhv1.select_related(depth=1)
+ postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).all()
+ postes_rh = postes_rh.select_related(depth=1)
return [('', 'Nouveau poste')] + \
- sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
- [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
+ sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
+ postes_rh],
key=lambda t: t[1])
def clean(self):
"""
Validation conditionnelles de certains champs.
"""
- cleaned_data = self.cleaned_data
-
- # Gestion de la mise à disposition
- mise_a_disposition = cleaned_data.get("mise_a_disposition")
- valeur_point_min = cleaned_data.get("valeur_point_min")
- valeur_point_max = cleaned_data.get("valeur_point_max")
- if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
- msg = u"Ce champ est obligatoire."
- self._errors["valeur_point_min"] = self.error_class([msg])
- self._errors["valeur_point_max"] = self.error_class([msg])
- raise forms.ValidationError("Les valeurs de point sont vides")
-
- return cleaned_data
-
+ cleaned_data = self.cleaned_data
+ if cleaned_data.get("local") is False \
+ and cleaned_data.get("expatrie") is False:
+ msg = "Le poste doit au moins être ouvert localement " \
+ "ou aux expatriés"
+ self._errors["local"] = self.error_class([msg])
+ self._errors["expatrie"] = ''
+ raise forms.ValidationError(msg)
- def save(self, *args, **kwargs):
- kwargs2 = kwargs.copy()
- kwargs2['commit'] = False
- poste = super(PosteForm, self).save(*args, **kwargs2)
- # id_rh
- if 'commit' not in kwargs or kwargs['commit']:
- poste.save()
- return poste
+ return cleaned_data
-class ChoosePosteForm(forms.ModelForm):
+class ChoosePosteForm(forms.Form):
class Meta:
- model = dae.Poste
fields = ('poste',)
# La liste des choix est laissée vide. Voir PosteForm.__init__.
- poste = forms.ChoiceField(choices=(), required=False)
+ postes_dae = forms.ChoiceField(choices=(), required=False)
+ postes_rh = forms.ChoiceField(choices=(), required=False)
def __init__(self, request=None, *args, **kwargs):
super(ChoosePosteForm, self).__init__(*args, **kwargs)
- self.fields['poste'].choices = self._poste_choices(request)
+ self.fields['postes_dae'].choices = self._poste_dae_choices(request)
+ self.fields['postes_rh'].choices = self._poste_rh_choices(request)
- def _poste_choices(self, request):
- """ Menu déroulant pour les postes. """
- dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
- copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
- id_copies = [p.id_rh_id for p in copies.all()]
+ def _poste_dae_choices(self, request):
+ """ Menu déroulant pour les postes."""
+ postes_dae = dae.Poste.objects.ma_region_ou_service(request.user) \
+ .exclude(etat__in=(POSTE_ETAT_FINALISE, )) \
+ .annotate(num_dae=Count('dae_dossiers')) \
+ .filter(num_dae=0) \
+ .order_by('implantation', '-date_debut', )
return [('', '----------')] + \
- sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
- key=lambda t: t[1])
+ [('dae-%s' % p.id, label_poste_display(p)) for p in postes_dae]
+
+ def _poste_rh_choices(self, request):
+ """ Menu déroulant pour les postes."""
+ postes_dae = dae.Poste.objects.exclude(etat__in=(POSTE_ETAT_FINALISE, ))
+ today = datetime.date.today()
+ id_poste_dae_commences = [p.id_rh_id for p in postes_dae if p.id_rh is not None]
+ postes_rh = rh.Poste.objects.ma_region_ou_service(request.user) \
+ .exclude(id__in=id_poste_dae_commences) \
+ .filter(Q(date_debut__lte=today) &
+ (Q(date_fin__gte=today) |
+ Q(date_fin__isnull=True))
+ ) \
+ .order_by('implantation', '-date_debut', )
+
+ return [('', '----------')] + \
+ [('rh-%s' % p.id, label_poste_display(p)) for p in postes_rh]
+
+ def clean(self):
+ cleaned_data = super(ChoosePosteForm, self).clean()
+ postes_dae = cleaned_data.get("postes_dae")
+ postes_rh = cleaned_data.get("postes_rh")
+ if (postes_dae is u"" and postes_rh is u"") or \
+ (postes_dae is not u"" and postes_rh is not u""):
+ raise forms.ValidationError("Choisissez un poste DAE ou un poste RH")
+ return cleaned_data
+ def redirect(self):
+ poste_dae_key = self.cleaned_data.get("postes_dae")
+ if poste_dae_key is not u"":
+ return redirect(reverse('embauche', args=(poste_dae_key,)))
+ poste_rh_key = self.cleaned_data.get("postes_rh")
+ if poste_rh_key is not u"":
+ return redirect("%s?creer_dossier_dae='M'" % reverse('poste', args=(poste_rh_key,)))
class EmployeForm(forms.ModelForm):
""" Formulaire des employés. """
self.fields['employe'].choices = _employe_choices(self, request)
-
-class DossierForm(forms.ModelForm):
+class DossierForm(
+ filtered_archived_fields_form_factory(
+ 'classement',
+ 'classement_anterieur',
+ 'classement_titulaire_anterieur',
+ ),
+ forms.ModelForm):
""" Formulaire des dossiers. """
class Meta:
- exclude= ('etat', )
+ exclude = ('etat', 'employe', 'poste', 'date_debut',)
model = dae.Dossier
widgets = dict(statut_residence=forms.RadioSelect(),
contrat_date_debut=admin_widgets.AdminDateWidget(),
contrat_date_fin=admin_widgets.AdminDateWidget(),
)
+WF_HELP_TEXT = ""
+
+
class PosteWorkflowForm(WorkflowFormMixin):
-
+ bouton_libelles = POSTE_ETATS_BOUTONS
+
class Meta:
fields = ('etat', )
model = dae.Poste
+ def __init__(self, *args, **kwargs):
+ super(PosteWorkflowForm, self).__init__(*args, **kwargs)
+ self.fields['etat'].help_text = WF_HELP_TEXT
+
+
class DossierWorkflowForm(WorkflowFormMixin):
-
+ bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
+
class Meta:
fields = ('etat', )
model = dae.Dossier
+
+ def __init__(self, *args, **kwargs):
+ super(DossierWorkflowForm, self).__init__(*args, **kwargs)
+ self.fields['etat'].help_text = WF_HELP_TEXT
+ self._etat_initial = self.instance.etat
+
+ def save(self):
+ super(DossierWorkflowForm, self).save()
+ poste = self.instance.poste
+
+ if poste.etat == self._etat_initial:
+ poste.etat = self.instance.etat
+ poste.save()
+
+ # créer le commentaire automatique pour le poste associé
+ commentaire = WorkflowCommentaire()
+ commentaire.content_object = poste
+ texte = u"Validation automatique à travers le dossier [%s] de %s\n%s" %(
+ self.instance.id,
+ self.instance,
+ self.data.get('commentaire', ''),
+ )
+ commentaire.texte = texte
+ commentaire.etat_initial = self.instance._etat_courant
+ commentaire.etat_final = self.instance.etat
+ commentaire.owner = self.request.user
+ commentaire.save()
+
+
+class ContratForm(forms.ModelForm):
+
+ class Meta:
+ fields = ('type_contrat', 'fichier', )
+ model = dae.Contrat
+
+
+class DAENumeriseeForm(forms.ModelForm):
+
+ class Meta:
+ model = dae.Dossier
+ fields = ('dae_numerisee',)
+
+
+class DAEFinaliseesSearchForm(forms.Form):
+ q = forms.CharField(
+ label='Recherche', required=False,
+ widget=forms.TextInput(attrs={'size': 40})
+ )
+ importees = forms.ChoiceField(
+ label='Importation', required=False, choices=(
+ ('', ''),
+ ('oui', 'DAE importées seulement'),
+ ('non', 'DAE non-importées seulement'),
+ )
+ )