On the way there...
authorBenoit C. Sirois <benoit.clennett-sirois@auf.org>
Tue, 19 Feb 2013 15:27:04 +0000 (10:27 -0500)
committerBenoit C. Sirois <benoit.clennett-sirois@auf.org>
Tue, 19 Feb 2013 15:27:04 +0000 (10:27 -0500)
project/assets/js/dae-embauche.js
project/dae/forms.py
project/dae/templates/dae/embauche-remun.html
project/dae/templates/dae/embauche.html
project/dae/templates/dae/table_remuneration.html [new file with mode: 0644]
project/dae/urls.py
project/dae/views.py
project/dae/widgets.py
project/settings.py

index fbc7c99..18b46a6 100644 (file)
@@ -17,6 +17,32 @@ function activateDossierDropDowns() {
     $('#id_classement, #id_devise').change(loadSalaire);
 }
 
+
+var namePatt = new RegExp(/-(\d+)-/);
+formCount = null;
+
+function ajouterLigne(addLnk) {
+    if (formCount == null) {
+       formCount = $('.remunform').length;
+    }
+    var prev = addLnk.parent('td').parent('tr').prev('tr');
+    var copyOfLastInGroup = prev.clone();
+    copyOfLastInGroup.find('input, select, td.cumulable').each(function(i, e){
+       var origName = $(e).attr('id');
+       var origId = $(e).attr('id');
+       $(e).attr('name', origName.replace(namePatt, '-' + formCount + '-'))
+       $(e).attr('id', origId.replace(namePatt, '-' + formCount + '-'))
+       $(e).val('');
+    });
+
+    prev.after(copyOfLastInGroup);
+
+    copyOfLastInGroup.find('input, select').change(function(e){
+       remun_totaux();
+    });
+    formCount++;
+}
+
 function loadSalaire() {
     var implantation = $('#implantation').val();
     var devise = $('#id_devise').val();
@@ -60,7 +86,6 @@ function remun_line(input) {
   var taux = parseFloat(DEVISES[devise]);
   if (isNaN(taux)) taux = 0;
   montant_annuel_euros.text(roundNumber((value * taux), 0))
-  
 }
 
 function totalByIndex(selector, accessor) {
@@ -87,7 +112,7 @@ function totalByIndex(selector, accessor) {
 }
 
 function remun_totaux() {
-    $('#global-cost input[type="text"]').each(function() {
+    $('#global-cost tr td.monnaie.cumulable input[type="text"], #global-cost tr td.monnaie.cumulable input[type="hidden"]').each(function() {
         remun_line($(this));
     });
     totalByIndex('#global-cost td.sous-total',
@@ -166,6 +191,12 @@ $(document).ready(function() {
     */
     remun_totaux();
 
+    $('#global-cost .addlink').click(function(e){
+       e.preventDefault();
+       el = $(this);
+       ajouterLigne(el);
+    });
+
     /* Traitement de la recherche AJAX pour les dossiers de comparaison */
     $(".results_on_deck").bind('added', function() {
 
index 916f3ac..01aee78 100644 (file)
@@ -4,6 +4,7 @@ import datetime
 from ordereddict import OrderedDict
 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,
@@ -23,7 +24,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
+from .widgets import ReadOnlyChoiceWidget, ReadOnlyWidget
 from project.dae.workflow import POSTE_ETATS_BOUTONS, POSTE_ETAT_FINALISE
 
 
@@ -263,7 +264,7 @@ class FlexibleRemunForm(forms.ModelForm):
 
     def __init__(self, *a, **kw):
         super(FlexibleRemunForm, self).__init__(*a, **kw)
-        self.fields['type'].widget = ReadOnlyChoiceWidget(choices=self.fields['type'].choices)
+        # self.fields['type'].widget = ReadOnlyChoiceWidget(choices=self.fields['type'].choices)
 
     def clean_devise(self):
         devise = self.cleaned_data['devise']
@@ -307,43 +308,105 @@ class FlexibleRemunForm(forms.ModelForm):
         return bool(changed_data)
 
 
+class ReadOnlyRemunForm(FlexibleRemunForm):
+    def __init__(self, *a, **kw):
+        super(FlexibleRemunForm, 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, group_accessor, 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 
-        """
+    def set_groups(self,
+                   groups,
+                   group_accessor,
+                   choice_overrides=[]):
+
 
-        # Build group list.
+        # Create pre-defined groups.
         self.groups = OrderedDict()
-        temp_groups = {}
-        # self.groups_and_forms = []
+        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:
-            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
+            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(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,
@@ -354,11 +417,13 @@ def remun_formset_factory(parent_model,
                           exclude=None,
                           can_order=False,
                           can_delete=True,
+                          extra=2,
                           max_num=None,
                           formfield_callback=None,
-                          group_order=None):
+                          groups=None,
+                          choice_overrides=[]):
     trs = rh.TypeRemuneration.objects.all()
-    extra = max_num = trs.count()
+    # 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:
@@ -378,40 +443,112 @@ def remun_formset_factory(parent_model,
     FormSet.fk = fk
 
     def grouper(form):
-        if 'type' in form.initial and form.initial['type']:
-            return (form.initial['type'].nature_remuneration,
-                    form.initial['type'].nature_remuneration,
-                    )
+        rtype = form.initial['type']
+        if not isinstance(rtype, rh.TypeRemuneration):
+            rtype = rh.TypeRemuneration.objects.get(id=rtype)
+        return (rtype.nature_remuneration,
+                rtype.nature_remuneration
+                )
 
     def __init__(inst, *a, **kw):
         super(FormSet, inst).__init__(*a, **kw)
 
-        # Set initial data.
-        for form, tr in zip(inst.forms, trs):
-            form.initial = {
-                'type': tr,
-                }
+        # Warning, this constructor can be improved and made more
+        # efficient.. it's a bit of a hack that will help creating a
+        # "grouped" formset that works inside Django. Although the
+        # code makes several iterations, I've timed it at 1ms to
+        # for it's execution.
+
+        # # Set initial data.
+        # d_inst = kw.get('instance', None)
 
-        # Set form grouping.
-        inst.set_groups(grouper, group_order)
+        # Set initial for all form type.
+        # for form, tr in zip(inst.forms, trs):
+        #     form.initial['type'] = tr
 
+        # # Set form grouping.
+        inst.set_groups(groups, grouper, choice_overrides)
+
+        # # Create groups list of choices for type field.
+        # inst.group_type_choices = {}
+        # for group_key in inst.groups:
+        #     inst.group_type_choices[group_key] = []
+
+        # # Add choices based on form.initial which have been set above.
+        # for form in inst.forms:
+        #     inst.group_type_choices[
+        #         form.initial['type'].nature_remuneration].append(
+        #         (form.initial['type'].id, form.initial['type'].nom)
+        #         )
+
+        # # Finally... change the choices of each form in the formset:
+        # for form in inst.forms:
+        #     form.fields['type'].choices = inst.group_type_choices[
+        #         form.initial['type'].nature_remuneration]
+
+        
     FormSet.__init__ = __init__
 
     return FormSet
 
 
+ReadOnlyRemunForm = remun_formset_factory(
+    dae.Dossier,
+    dae.Remuneration,
+    form=ReadOnlyRemunForm,
+    groups = [
+        (u'Traitement', u'Traitement',),
+        (u'Indemnité', u'Indemnité',),
+        (u'Charges', u'Charges',),
+        (u'Accessoire', u'Accessoire',),
+        ]
+    )
+
+null_choice = ('', '-' * 10)
 RemunForm = remun_formset_factory(
     dae.Dossier,
     dae.Remuneration,
     form=FlexibleRemunForm,
-    group_order = [
-        u'Traitement',
-        u'Indemnité',
-        u'Charges',
-        u'Accessoire',
-        ]
+    groups = [
+        (u'Traitement', u'Traitement',),
+        (u'Indemnité', u'Indemnité',),
+        (u'Charges', u'Charges',),
+        (u'Accessoire', u'Accessoire',),
+        ],
+    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')
+                )
+            },
+        }
     )
 
+RemunForm = ReadOnlyRemunForm
+
 
 class PosteForm(forms.ModelForm):
     """ Formulaire des postes. """
index 622c038..73ae79d 100644 (file)
   <th>Supprimer</th>
 </tr>
 {% for group in remunForm.group_list %}
-{% if group.name != 'RAS' %}
+{% if group.key != 'RAS' %}
 <tr>
   <th colspan="6">{{ group.name }}</th>
 </tr>
 {% for f in group.forms %}
-<tr class="calculable">
+<tr class="calculable remunform">
     {{ f.id }}
     <td>{{ f.type.errors }} {{ f.type }}</td>
     <td>{{ f.devise.errors }} {{ f.devise }}</td>
     <td class="monnaie cumulable">{{ f.montant.errors }} {{ f.montant }}</td>
-    <td class="euro cumulable" id="id_{{ f.prefix }}-montant_annuel_euros"></td>
+    <td class="euro cumulable" id="{{ f.prefix }}-montant_annuel_euros"></td>
     <td>{{ f.commentaire.errors }} {{ f.commentaire }}</td>
     <td>{{ f.DELETE }}</td>
 </tr>
 {% endfor %}
 <tr class="sous-totaux">
-    <td>Sous-total</td>
+    <td>Sous-total -
+      <a class="addlink" href="/admin/dae/poste/add/">
+       [<span class="icon">Ajouter une ligne</span>]
+      </a>
+    </td>
     <td><!-- Laisser ce td pour que le javascript fonctionne bien. --></td>
     <td class="sous-total"></td>
     <td class="sous-total"></td>
@@ -51,7 +55,7 @@
     <th colspan="2"></th>
 </tr>
 {% for f in remunForm.groups.RAS.forms %}
-<tr class="calculable">
+<tr class="calculable remunform">
     {{ f.id }}
     <td>{{ f.type.errors }} {{ f.type }}</td>
     <td>{{ f.devise.errors }} {{ f.devise }}</td>
 </tr>
 {% endfor %}
 <tr class="sous-totaux">
-    <td>Sous-total</td>
+    <td>Sous-total - 
+      <a class="addlink" href="#">
+       [<span class="icon">Ajouter une ligne</span>]
+      </a>
+    </td>
     <td><!-- Laisser ce td pour que le javascript fonctionne bien. --></td>
     <td class="sous-total"></td>
     <td class="sous-total"></td>
index 43aed1c..edc902a 100644 (file)
 
 
       <div id="form-dossier">
-        {% comment %}Wrapper du formulaire de dossier{% endcomment %}
+        <!-- Wrapper du formulaire de dossier -->
         {% with forms.dossier as form %}
             {% include "dae/embauche-dossier.html" %}
         {% endwith %}
         {% include 'dae/embauche-remun.html' %}
       </table>
 
-      {% comment %}
-      <select id="type-remun" name="type-remun">
-        <option value="">(Ajouter une ligne)</option>
-        {% for tr in type_remun %}
-          <option value="{{ tr.id }}">{{ tr.nom }}</option>
-        {% endfor %}
-      </select>
-      {% endcomment %}
+      <!-- <select id="type-remun" name="type-remun"> -->
+      <!--   <option value="">(Ajouter une ligne)</option> -->
+      <!--   {% for tr in type_remun %} -->
+      <!--     <option value="{{ tr.id }}">{{ tr.nom }}</option> -->
+      <!--   {% endfor %} -->
+      <!-- </select> -->
 
       </fieldset>
 
       <input type="submit" name="save" value="Sauvegarder" />
     </form>
 {% endblock %}
-
diff --git a/project/dae/templates/dae/table_remuneration.html b/project/dae/templates/dae/table_remuneration.html
new file mode 100644 (file)
index 0000000..2de99af
--- /dev/null
@@ -0,0 +1 @@
+{{ form.as_p }}
index e866c25..2dee710 100644 (file)
@@ -58,6 +58,8 @@ urlpatterns = patterns(
         name='poste_resume'),
     url(r'^salaire$', 'salaire', name='salaire'),
     url(r'^salaire/(.*)/(.*)/(.*)$', 'salaire', name='salaire'),
+    # url(r'^table_remuneration/(?P<dossier_id>.*)$', name='table_remuneration'),
+    url(r'^tableau_remuneration/(?P<dossier_id>.*)$', 'tableau_remuneration', name='table_remuneration'),
     url(r'^coefficient$', 'coefficient', name='dae_coefficient'),
     url(r'^liste_valeurs_point$', 'liste_valeurs_point',
         name='liste_valeurs_point'),
index 53faea2..b924683 100644 (file)
@@ -30,7 +30,8 @@ from project.dae.forms import \
         DossierWorkflowForm, ChoosePosteForm, \
         EmployeForm, DossierForm, DossierPieceForm, \
         DossierComparaisonFormSet, RemunForm, ContratForm, DAENumeriseeForm, \
-        label_poste_display, DAEFinaliseesSearchForm
+        label_poste_display, DAEFinaliseesSearchForm, \
+        remun_formset_factory, ReadOnlyRemunForm
 from project.dae.mail import send_drh_finalisation_mail
 from project.dae.workflow import \
         DOSSIER_ETAT_FINALISE, DOSSIER_ETAT_REGION_FINALISATION, \
@@ -43,6 +44,18 @@ from project.rh import models as rh
 from project import groups
 
 
+@login_required
+@drh_or_admin_required
+def tableau_remuneration(request, dossier_id):
+    dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
+    form = RemunForm(instance=dossier)
+    return render(
+        request,
+        'dae/table_remuneration.html', {
+            'form': form,
+            })
+
+
 # Helpers
 
 def devises():
index 59568f0..1ceda29 100644 (file)
@@ -1,5 +1,5 @@
 from django.utils.safestring import mark_safe
-from django.forms.widgets import Select
+from django.forms.widgets import Select, TextInput
 
 
 class ReadOnlyChoiceWidget(Select):
@@ -9,9 +9,20 @@ class ReadOnlyChoiceWidget(Select):
         except ValueError:
             key = ''
         return mark_safe(
-            '<span>%(display)s</span><input type="hidden" '
-            'name="%(name)s" value="%(value)s" />' % {
+            '%(display)s</span><input type="hidden" '
+            'name="%(name)s" id="%(name)s" value="%(value)s" />' % {
                 'display': dict(self.choices)[key],
                 'name': name,
                 'value': value,
             })
+
+
+class ReadOnlyWidget(TextInput):
+    def render(self, name, value, attrs=None):
+        return mark_safe(
+            '%(display)s<input id="%(name)s" type="hidden" '
+            'name="%(name)s" value="%(value)s" />' % {
+                'display': value,
+                'name': name,
+                'value': value,
+            })
index bd4769b..802ad13 100644 (file)
@@ -225,7 +225,7 @@ LOGGING = {
     },
     'loggers': {
         'django.db.backends': {
-            'level': 'ERROR',
+            'level': 'DEBUG',
             'handlers': ['console'],
             'propagate': False,
         },