X-Git-Url: http://git.auf.org/?p=auf_rh_dae.git;a=blobdiff_plain;f=project%2Fdae%2Fforms.py;h=373099fa80f012ecc4a8be598e7c45741881f94e;hp=29ff2147eb61f037f894d2c5d0cad1302af6cf7d;hb=892a50fd99f2506664f1699024d673fd39765c65;hpb=aa5121221e674480798e423f68c333d1bc5ffa5e diff --git a/project/dae/forms.py b/project/dae/forms.py index 29ff214..373099f 100644 --- a/project/dae/forms.py +++ b/project/dae/forms.py @@ -1,91 +1,245 @@ # -*- encoding: utf-8 -*- import datetime -from django.db.models import Q, Max +from ordereddict import OrderedDict +from dateutil.relativedelta import relativedelta from django import forms -from django.forms.models import inlineformset_factory, modelformset_factory +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.workflow.forms import WorkflowFormMixin + from auf.django.references import models as ref -from dae import models as dae -from utils import get_employe_from_user, is_user_dans_services_centraux -from rh import models as rh -from workflow import grp_drh, POSTE_ETATS_BOUTONS, DOSSIER_ETAT_FINALISE, POSTE_ETAT_FINALISE +from auf.django.workflow.forms import WorkflowFormMixin +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_services_centraux(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): # TRAITEMENT NORMAL - employe = get_employe_from_user(request.user) - # SERVICE - if is_user_dans_services_centraux(request.user): - q_dae_region_service = Q(poste__implantation=employe.implantation) - q_rh_region_service = Q(poste__implantation=employe.implantation) - # REGION - else: - q_dae_region_service = Q(poste__implantation__region=employe.implantation.region) - q_rh_region_service = Q(poste__implantation__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(): + 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] + # 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()] - 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 + 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) - def option_label(employe): - return "%s %s" % (employe.nom.upper(), employe.prenom.title()) + 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 - return [('', 'Nouvel employé')] + \ - sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins] + - [('rh-%s' % p.id, option_label(p)) for p in rhv1], - key=lambda t: t[1]) def label_poste_display(poste): """Formate un visuel pour un poste dans une liste déroulante""" annee = "" if poste.date_debut: annee = poste.date_debut.year - label = u"%s %s - %s [%s]" %(annee, poste.type_poste, poste.type_poste.famille_emploi.nom, poste.id) + + 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 -PostePieceForm = inlineformset_factory(dae.Poste, dae.PostePiece) + +PostePieceFormSet = inlineformset_factory(dae.Poste, dae.PostePiece,) DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece) -FinancementForm = inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2) -class DossierComparaisonForm(forms.ModelForm): +# 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'})) + poste = forms.CharField( + max_length=255, widget=forms.TextInput(attrs={'size': '60'}) + ) + cmp_dossier = forms.IntegerField( + widget=forms.widgets.HiddenInput, + required=False + ) class Meta: model = dae.DossierComparaison @@ -95,48 +249,345 @@ DossierComparaisonFormSet = modelformset_factory( dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm ) -class PosteComparaisonForm(forms.ModelForm): + +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',) -PosteComparaisonFormSet = modelformset_factory( - dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm +# 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(forms.ModelForm): +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']) + 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)) + raise forms.ValidationError( + u"La devise %s n'a pas de taux pour l'implantation %s" % + (devise, implantation) + ) else: return devise -RemunForm = inlineformset_factory( - dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm + 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): + """ + Don't we love factory factories? + """ + + 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, ) -class PosteForm(forms.ModelForm): +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.filter(archive=False), required=True) + service = forms.ModelChoiceField( + queryset=rh.Service.objects.all(), required=True + ) - responsable=AutoCompleteSelectField('responsables', required=True) + responsable = AutoCompleteSelectField('responsables', required=True) #responsable = forms.ModelChoiceField( # queryset=rh.Poste.objects.select_related(depth=1)) @@ -144,9 +595,12 @@ class PosteForm(forms.ModelForm): 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) - + 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 @@ -195,7 +649,9 @@ class PosteForm(forms.ModelForm): 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: @@ -203,32 +659,28 @@ class PosteForm(forms.ModelForm): if len(dossiers) > 0: 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 """ - copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True).filter(etat=POSTE_ETAT_FINALISE) - id_copies = [p.id_rh_id for p in copies.all()] - rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).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([('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 + 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" + 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) @@ -236,39 +688,62 @@ class PosteForm(forms.ModelForm): return cleaned_data - 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']: - if poste.id is None: - poste.date_creation = datetime.datetime.now() - poste.save() - return poste - - -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) + 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. """ @@ -286,27 +761,28 @@ class EmployeForm(forms.ModelForm): 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', 'employe', 'poste', 'date_debut', ) + 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(), ) - def save(self, *args, **kwargs): - dossier = super(PosteForm, self).save(*args, **kwargs) - if dossier.id is None: - dossier.date_creation = datetime.datetime.now() - dossier.save() - return dossier - WF_HELP_TEXT = "" + class PosteWorkflowForm(WorkflowFormMixin): bouton_libelles = POSTE_ETATS_BOUTONS + class Meta: fields = ('etat', ) model = dae.Poste @@ -317,7 +793,8 @@ class PosteWorkflowForm(WorkflowFormMixin): class DossierWorkflowForm(WorkflowFormMixin): - bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste... + bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste... + class Meta: fields = ('etat', ) model = dae.Dossier @@ -330,42 +807,49 @@ class DossierWorkflowForm(WorkflowFormMixin): 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', ) + fields = ('type_contrat', 'fichier', ) model = dae.Contrat + class DAENumeriseeForm(forms.ModelForm): class Meta: model = dae.Dossier fields = ('dae_numerisee',) -class DAEImportableForm(forms.Form): - qs_poste = dae.Poste.objects.filter(etat=POSTE_ETAT_FINALISE) - qs_dossier = dae.Dossier.objects.filter(etat=DOSSIER_ETAT_FINALISE) - poste = forms.ModelChoiceField(queryset=qs_poste, label="Poste finalisé", required=False) - dossier = forms.ModelChoiceField(queryset=qs_dossier, label="DAE finalisée", required=False) - - def clean_poste(self): - poste = self.cleaned_data['poste'] - if poste is not None and poste.est_importe(): - raise forms.ValidationError("Ce poste a déjà été importé") - return poste - - def clean_dossier(self): - dossier = self.cleaned_data['dossier'] - if dossier is not None and not dossier.poste.est_importe(): - raise forms.ValidationError("Le poste de ce dossier doit être importé avant de pouvoir importer le dossier.") - return dossier - - def importer_poste(self): - poste = self.cleaned_data['poste'] - if poste is not None and not poste.est_importe(): - poste.importer() - + +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'), + ) + )