Merge branch 'release/1.6.5' into dev
[auf_rh_dae.git] / project / dae / forms.py
index 2a931c1..498f308 100644 (file)
@@ -1,10 +1,17 @@
 # -*- encoding: utf-8 -*-
 
 import datetime
+from ordereddict import OrderedDict
+from dateutil.relativedelta import relativedelta
 from django import forms
 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
+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
@@ -18,6 +25,7 @@ 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
 
 
@@ -210,6 +218,10 @@ class DossierComparaisonForm(forms.ModelForm):
     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
@@ -224,6 +236,11 @@ class PosteComparaisonForm(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',)
@@ -248,13 +265,17 @@ PosteComparaisonFormSet = inlineformset_factory(
 
 
 class FlexibleRemunForm(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':
@@ -271,10 +292,278 @@ class FlexibleRemunForm(forms.ModelForm):
         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.
+        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)
+    
+        # Add extra forms (n extra for each grop).
+        tmp_extras = []
+        for i in xrange(len(self.groups) * self.extra):
+            tmp_extras.insert(0,
+        self._construct_form(self.initial_form_count() + i))
+
+        for g in self.groups:
+            for i in xrange(self.extra):
+                self.groups[g]['forms'].append(tmp_extras.pop())
+            
+
+        # 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 set_groups(self, group_accessor, groups, group_order=[]):
+    #     """
+    #     group_accessor: A function that will get the key and name from
+    #     each form.
+    #     group_order: list the group keys here in a list and
+    #     GroupedInlineFormset.groups will be ordered (ordereddict) by
+    #     the key sequence provided here. Any missing key from the
+    #     sequence will 
+    #     """
+
+    #     # Build group list.
+    #     self.groups = OrderedDict()
+    #     temp_groups = {}
+    #     # self.groups_and_forms = []
+    #     for form in self.forms:
+    #         group_key, group_name = group_accessor(form)
+    #         if not temp_groups.has_key(group_key):
+    #             temp_groups[group_key] = {
+    #                 'name': group_name,
+    #                 'key': group_key,
+    #                 'forms': [],
+    #                 }
+    #         temp_groups[group_key]['forms'].append(form)
+
+    #     for order_key in group_order:
+    #         if temp_groups.has_key(order_key):
+    #             self.groups[order_key] = temp_groups.pop(order_key)
+
+    #     for key in temp_groups:
+    #         self.groups[key] = temp_groups[key]
+
+    #     del temp_groups
+            
+    #     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):
+    """
+    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
+
+    return remun_formset_factory(
+        parent_model,
+        model,
+        form=form_class,
+        extra=extras,
+        can_delete=can_delete,
+        read_only=read_only,
+        groups = [
+            (u'Traitement', u'Traitement',),
+            (u'Indemnité', u'Indemnité',),
+            (u'Charges', u'Charges',),
+            (u'Accessoire', u'Accessoire',),
+            (u'RAS', u'Rémunération autre source',),
+            ],
+        choice_overrides = {
+            u'Traitement': {
+                'type': [null_choice] + list(
+                    rh.TypeRemuneration.objects.filter(
+                        nature_remuneration=u'Traitement').values_list(
+                        'id', 'nom')
+                    )
+                },
+            u'Indemnité': {
+                'type': [null_choice] + list(
+                    rh.TypeRemuneration.objects.filter(
+                        nature_remuneration=u'Indemnité').values_list(
+                        'id', 'nom')
+                    )
+                },
+            u'Charges': {
+                'type': [null_choice] + list(
+                    rh.TypeRemuneration.objects.filter(
+                        nature_remuneration=u'Charges').values_list(
+                        'id', 'nom')
+                    )
+                },
+            u'Accessoire': {
+                'type': [null_choice] + list(
+                    rh.TypeRemuneration.objects.filter(
+                        nature_remuneration=u'Accessoire').values_list(
+                        'id', 'nom')
+                    )
+                },
+            u'RAS': {
+                'type': [null_choice] + list(
+                    rh.TypeRemuneration.objects.filter(
+                        nature_remuneration=u'RAS').values_list(
+                        'id', 'nom')
+                    )
+                },
+            }
+        )
+
+RemunForm = remun_formset_factory_factory(
+    read_only=False,
+    parent_model=dae.Dossier,
+    model=dae.Remuneration,
 )
 
+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(forms.ModelForm):
     """ Formulaire des postes. """