diff options
19 files changed, 765 insertions, 13 deletions
| diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 7dc10d49c..289aa4166 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -46,7 +46,7 @@ class FileType(GeneralType):          try:              preventive = FileType.objects.get(txt_idx=key).pk              return file_type_id == preventive -        except ObjectDoesNotExist: +        except FileType.DoesNotExist:              return False  class PermitType(GeneralType): diff --git a/archaeological_files_pdl/forms.py b/archaeological_files_pdl/forms.py index aedf60ba1..fc4efff73 100644 --- a/archaeological_files_pdl/forms.py +++ b/archaeological_files_pdl/forms.py @@ -23,11 +23,13 @@ from django import forms  from django.core import validators  from django.utils.translation import ugettext_lazy as _ -from ishtar_common.models import Person +from ishtar_common.models import Person, valid_id  from archaeological_files import models -from ishtar_common.forms import get_now +from ishtar_common.forms import get_now, reverse_lazy  from ishtar_common.forms_common import get_town_field +from archaeological_files.forms import GENERAL_CONTRACTOR, \ +                                       RESPONSIBLE_PLANNING_SERVICE  from ishtar_common import widgets @@ -65,7 +67,6 @@ class FileFormPreventiveType(forms.Form):                                                                default='NP')          self.fields['permit_type'].help_text = models.PermitType.get_help() -  class FileFormPlanning(forms.Form):      form_label = _(u"Planning")      associated_models = {'town':models.Town} @@ -87,3 +88,85 @@ class FileFormPlanning(forms.Form):             label=_(u"Total developed surface (m²)"),             required=False, validators=[validators.MinValueValidator(0),                                         validators.MaxValueValidator(999999999)]) + +class FileFormGeneralContractor(forms.Form): +    form_label = _(u"General contractor") +    associated_models = {'general_contractor':models.Person} + +    def __init__(self, *args, **kwargs): + +        # get the status: natural person or corporation +        DEFAULT_STATUS = 'natural' +        current_status = '' +        if 'data' in kwargs: +            current_item_key = ((kwargs['prefix'] + '-') if kwargs.get('prefix')\ +                                                 else '') + 'general_contractor' +            if kwargs['data'] and kwargs['data'].get(current_item_key): +                model = self.associated_models['general_contractor'] +                try: +                    item = model.objects.get(pk=kwargs['data'][current_item_key]) +                    current_status = 'natural' if item.is_natural() \ +                                     else 'corporation' +                except (model.DoesNotExist, ValueError): +                    pass + +        status = '' +        if 'status' in kwargs: +            status = kwargs.pop('status') +            if current_status != status: +                if kwargs.get('data'): +                    # status is different from the existing - clear fields +                    kwargs.pop('data') +        elif current_status: +            status = current_status +        else: +            status = DEFAULT_STATUS + +        if status not in ('natural', 'corporation'): +            status = DEFAULT_STATUS +        super(FileFormGeneralContractor, self).__init__(*args, **kwargs) + +        # distinct widget for natural and corporation +        if status == 'natural': +            self.fields['general_contractor'] = forms.IntegerField( +             label=_(u"General contractor"), +             widget=widgets.JQueryPersonOrganization( +                reverse_lazy('autocomplete-person', +                             args=[GENERAL_CONTRACTOR.pk]), +                reverse_lazy('person_create'), +                model=Person, +                limit={'person_types':[GENERAL_CONTRACTOR.pk], +                       'attached_to__isnull':True}, +                js_template='ishtar/blocks/JQueryNaturalPerson.js', +                new=True), +             validators=[valid_id(Person)]) +        else: +            self.fields['general_contractor'] = forms.IntegerField( +             label=_(u"General contractor"), +             widget=widgets.JQueryPersonOrganization( +                reverse_lazy('autocomplete-person', +                             args=[GENERAL_CONTRACTOR.pk]), +                reverse_lazy('organization_person_create'), +                model=Person, +                limit={'person_types':[GENERAL_CONTRACTOR.pk]}, +                js_template='ishtar/blocks/JQueryCorporationPerson.js', +                new=True), +             validators=[valid_id(Person)]) + +class FileFormPlanningService(forms.Form): +    form_label = _(u"Town planning service") +    associated_models = {'responsible_planning_service':models.Person} + +    def __init__(self, *args, **kwargs): +        super(FileFormPlanningService, self).__init__(*args, **kwargs) +        self.fields['responsible_planning_service'] = forms.IntegerField( +            label=_(u"Responsible town planning service"), +            widget=widgets.JQueryPersonOrganization( +                reverse_lazy('autocomplete-person', +                             args=[RESPONSIBLE_PLANNING_SERVICE.pk]), +                reverse_lazy('person_create'), +                model=Person, +                limit={'person_types':[RESPONSIBLE_PLANNING_SERVICE.pk]}, +                new=True), +            validators=[valid_id(Person)]) + diff --git a/archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js b/archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js new file mode 100644 index 000000000..3eb375167 --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js @@ -0,0 +1,2 @@ +var current_status = 'corporation'; +{% include "ishtar/blocks/JQueryPersonOrga.js" %} diff --git a/archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js b/archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js new file mode 100644 index 000000000..fc4b9a90c --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js @@ -0,0 +1,2 @@ +var current_status = 'natural'; +{% include "ishtar/blocks/JQueryPersonOrga.js" %} diff --git a/archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js b/archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js new file mode 100644 index 000000000..c151a5e4d --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js @@ -0,0 +1,65 @@ +person_save_callback = function(item_id, lbl){ +    var url =  {{edit_source}}; +    $('#id_{{field_id}}').val(null); +    $('#id_select_{{field_id}}').val(lbl); +    if (item_id){ +        url = {{edit_source}}+item_id; +        $('#id_{{field_id}}').val(item_id); +    } +    $("#id_select_{{field_id}}").trigger('autocompletechange'); +    $.get(url , function( data ) { +        $( "#div-{{field_id}}" ).html( data ); +    }); +}; + +edit_url =  {{edit_source}}; +parent_id = "{{field_id}}"; + +person_new_callback = function(){ +    var url =  {{edit_source}}; +    $('#id_{{field_id}}').val(null); +    $('#id_select_{{field_id}}').val(null); +} + +$(function() { +    var $radios = $('input:radio[name=person_type]'); +    if($radios.is(':checked') === false) { +        $radios.filter('[value='+ current_status +']').prop('checked', true); +    } + +    $radios.change(function(){ +        var loc = window.location; +        window.location = loc.protocol + '//' + loc.host + loc.pathname + "?status=" + $('input:radio[name=person_type]:checked').val(); +    }); + +    $("#id_select_{{field_id}}").autocomplete({ +        source: {{source}}, +        select: function( event, ui ) { +                var url =  {{edit_source}}; +                if(ui.item){ +                    url = {{edit_source}}+ui.item.id; +                    $('#id_{{field_id}}').val(ui.item.id); +                } else { +                    $('#id_{{field_id}}').val(null); +                } +                $.get(url , function( data ) { +                    $( "#div-{{field_id}}" ).html( data ); +                }); +            }, +        minLength: 2{% if options %}, +        {{options}} +        {% endif %} +    }); + +    $.get( {{edit_source}}{% if selected %}+'{{selected}}'{% endif %}, function( data ) { +        $( "#div-{{field_id}}" ).html( data ); +    }); + +    $('#id_select_{{field_id}}').live('click', function(){ +        $('#id_{{field_id}}').val(null); +        $('#id_select_{{field_id}}').val(null); +        $.get( {{edit_source}}, function( data ) { +            $( "#div-{{field_id}}" ).html( data ); +        }); +    }); +}); diff --git a/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html b/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html new file mode 100644 index 000000000..4788a41fc --- /dev/null +++ b/archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html @@ -0,0 +1,32 @@ +{% extends "ishtar/wizard/default_wizard.html" %} +{% load i18n range table_form %} +{% block wizard_form %} +<form action="." method="post" name='wizard'{% if wizard.form.file_upload %} enctype="multipart/form-data"{% endif %}>{% csrf_token %} +<div class='form'> +{{ wizard.form.media }} +{{ wizard.management_form }} + +<table class='formset'> +  <caption>Statut</caption> +  <tr> +    <th><label>{% trans "Corporation" %}</label></th> +    <td><input type='radio' name='person_type' value='corporation'/></td> +  </tr> +  <tr> +    <th><label>{% trans "Natural person" %}</label></th> +    <td><input type='radio' name='person_type' value='natural'/></td> +  </tr> +</table> + +<table> +{% table_form wizard.form %} +</table> +<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> +{{ previous_fields|safe }} +<div id='validation-bar'> +  <input type="submit" id="submit_form" name='validate' value="{% trans "Validate" %}"/> +  {% if next_steps %}<input type="submit" id="submit_end_form" name='validate_and_end' value="{% trans "Validate and end" %}"/>{% endif %} +</div> +</div> +</form> +{% endblock %} diff --git a/archaeological_files_pdl/views.py b/archaeological_files_pdl/views.py index 1dd69b50c..139da223d 100644 --- a/archaeological_files_pdl/views.py +++ b/archaeological_files_pdl/views.py @@ -33,7 +33,10 @@ file_creation_wizard = FileWizard.as_view([                  ('preventivetype-file_creation', forms.FileFormPreventiveType),                  ('preventiveplanning-file_creation', forms.FileFormPlanning),                  ('parcelspdl-file_creation', ParcelFormSet), -                ('preventive-file_creation', ref_forms.FileFormPreventive), +                ('generalcontractor-file_creation', +                                            forms.FileFormGeneralContractor), +                ('planningservice-file_creation', +                                            forms.FileFormPlanningService),                  ('research-file_creation', ref_forms.FileFormResearch),                  ('final-file_creation', ref_forms.FinalForm)],               label=_(u"New file"), @@ -41,9 +44,6 @@ file_creation_wizard = FileWizard.as_view([          'preventivetype-file_creation':\              is_preventive('general-file_creation', models.FileType,                            type_key='file_type'), -        'preventive-file_creation':\ -            is_preventive('general-file_creation', models.FileType, -                          type_key='file_type'),          'research-file_creation':\              is_not_preventive('general-file_creation', models.FileType,                                type_key='file_type'), diff --git a/archaeological_files_pdl/wizards.py b/archaeological_files_pdl/wizards.py index 679b0823f..4abe5f0d5 100644 --- a/archaeological_files_pdl/wizards.py +++ b/archaeological_files_pdl/wizards.py @@ -24,4 +24,13 @@ class FileWizard(BaseFileWizard):      town_step_key = 'preventiveplanning-'      town_input_id = 'main_town'      multi_towns = False +    wizard_templates = { +        'generalcontractor-file_creation':\ +                    'ishtar/wizard/wizard_generalcontractor.html',} +    def get_form_kwargs(self, *args, **kwargs): +        returned = super(FileWizard, self).get_form_kwargs(*args, **kwargs) +        if args and args[0].startswith('generalcontractor-file_creation'): +            if 'status' in self.request.GET: +                returned['status'] = self.request.GET['status'] +        return returned diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index d8740b68c..e1a0e9063 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -191,6 +191,13 @@ class OrganizationFormSelection(forms.Form):                                       OrganizationSelect, models.Organization),           validators=[models.valid_id(models.Organization)]) +class BaseOrganizationForm(forms.ModelForm): +    form_prefix = "orga" +    class Meta: +        model = models.Organization +        fields = ['name', 'organization_type', 'address', 'address_complement', +                  'town', 'postal_code',] +  class PersonSelect(TableSelect):      name = forms.CharField(label=_(u"Name"), max_length=30)      surname = forms.CharField(label=_(u"Surname"), max_length=20) @@ -242,6 +249,39 @@ class SimplePersonForm(NewItemForm):                      associated_model=models.Organization, new=True),          validators=[models.valid_id(models.Organization)], required=False) +class BasePersonForm(forms.ModelForm): +    class Meta: +        model = models.Person +        fields = ['title', 'name', 'surname', 'address', 'address_complement', +                  'town', 'postal_code'] + +class BaseOrganizationPersonForm(forms.ModelForm): +    class Meta: +        model = models.Person +        fields = ['attached_to', 'title', 'name', 'surname',] +        widgets = {'attached_to':widgets.JQueryPersonOrganization( +                reverse_lazy('autocomplete-organization'), +                reverse_lazy('organization_create'), +                model=models.Organization, +                attrs={'hidden':True}, +                new=True), +        } + +    def __init__(self, *args, **kwargs): +        super(BaseOrganizationPersonForm, self).__init__(*args, **kwargs) + +    def save(self, *args, **kwargs): +        person = super(BaseOrganizationPersonForm, self).save(*args, **kwargs) +        instance = person.attached_to +        form = BaseOrganizationForm(self.data, instance=instance, +                                    prefix=BaseOrganizationForm.form_prefix) +        if form.is_valid(): +            orga = form.save() +            if not person.attached_to: +                person.attached_to = orga +                person.save() +        return person +  class PersonForm(SimplePersonForm):      person_types = forms.MultipleChoiceField(label=_("Person type"),                               choices=[], widget=forms.CheckboxSelectMultiple) diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 23c2f3ac7..fe907f014 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -1540,10 +1540,13 @@ class Person(Address, Merge, OwnPerms, ValueGetter) :          values = [unicode(getattr(self, attr))                     for attr in ('surname', 'name')                                 if getattr(self, attr)] -        if not values: -            values = [self.raw_name or ""] +        if not values and self.raw_name: +            values = [self.raw_name]          if self.attached_to: -            values.append(u"- " + unicode(self.attached_to)) +            attached_to = unicode(self.attached_to) +            if values: +                values.append(u'-') +            values.append(attached_to)          return u" ".join(values)      def get_values(self, prefix=''): @@ -1577,6 +1580,9 @@ class Person(Address, Merge, OwnPerms, ValueGetter) :              self.merge_key += "-" + self.attached_to.merge_key          self.merge_key = self.merge_key[:300] +    def is_natural(self): +        return not self.attached_to +      def has_right(self, right_name):          if '.' in right_name:              right_name = right_name.split('.')[-1] diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 5168cba6e..55bb95e30 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -120,6 +120,15 @@ td{      text-align:left;  } +div.selected{ +    border:1px solid #922; +} + +textarea:disabled, +input[type="text"]:disabled{ +    background-color: #eee; +} +  button{      background-color:#f1f2f6;      border:1px solid #AAA; diff --git a/ishtar_common/templates/blocks/JQueryPersonOrganization.js b/ishtar_common/templates/blocks/JQueryPersonOrganization.js new file mode 100644 index 000000000..b13a2c28e --- /dev/null +++ b/ishtar_common/templates/blocks/JQueryPersonOrganization.js @@ -0,0 +1,50 @@ + +$("#id_select_{{field_id}}").autocomplete({ +    source: {{source}}, +    select: function( event, ui ) { +            var url =  {{edit_source}}; +            if(ui.item){ +                url = {{edit_source}}+ui.item.id; +                $('#id_{{field_id}}').val(ui.item.id); +            } else { +                $('#id_{{field_id}}').val(null); +            } +            $.get(url , function( data ) { +                $( "#div-{{field_id}}" ).html( data ); +            }); +        }, +    minLength: 2{% if options %}, +    {{options}} +    {% endif %} +}); + +$.get( {{edit_source}}{% if selected %}+'{{selected}}'{% endif %}, function( data ) { +    $( "#div-{{field_id}}" ).html( data ); +}); + +$('#id_select_{{field_id}}').live('click', function(){ +    $('#id_{{field_id}}').val(null); +    $('#id_select_{{field_id}}').val(null); +    $.get( {{edit_source}}, function( data ) { +        $( "#div-{{field_id}}" ).html( data ); +    }); +}); + +person_save_callback = function(item_id, lbl){ +    var url =  {{edit_source}}; +    $('#id_{{field_id}}').val(null); +    $('#id_select_{{field_id}}').val(lbl); +    if (item_id){ +        url = {{edit_source}}+item_id; +        $('#id_{{field_id}}').val(item_id); +    } +    $("#id_select_{{field_id}}").trigger('autocompletechange'); +    $.get(url , function( data ) { +        $( "#div-{{field_id}}" ).html( data ); +    }); +}; +person_new_callback = function(){ +    var url =  {{edit_source}}; +    $('#id_{{field_id}}').val(null); +    $('#id_select_{{field_id}}').val(null); +} diff --git a/ishtar_common/templates/blocks/PersonOrganization.html b/ishtar_common/templates/blocks/PersonOrganization.html new file mode 100644 index 000000000..6e7264c8e --- /dev/null +++ b/ishtar_common/templates/blocks/PersonOrganization.html @@ -0,0 +1,9 @@ +{% load i18n %}<input{{attrs_select}}/> +</table> + +<div id='div-{{name}}'></div> + +<input type="hidden"{{attrs_hidden}}/> +<script type="text/javascript"><!--// +{{js}} +//--></script> diff --git a/ishtar_common/templates/ishtar/organization_form.html b/ishtar_common/templates/ishtar/organization_form.html new file mode 100644 index 000000000..207116c21 --- /dev/null +++ b/ishtar_common/templates/ishtar/organization_form.html @@ -0,0 +1,30 @@ +{% load i18n %}<table class='organization'> +<tr> +    <th><label>{{form.name.label}}</label></th> +    <td>{{form.name}}</td> +</tr> +</table> +{% for hidden in form.hidden_fields %}{{hidden}}{% endfor %} +<table class='organization-address'> +<tr> +    <th><label>{{form.organization_type.label}}</label></th> +    <td>{{form.organization_type}}</td> +</tr> +<tr> +    <th><label>{{form.address.label}}</label></th> +    <td>{{form.address}}</td> +</tr> +<tr> +    <th><label>{{form.address_complement.label}}</label></th> +    <td>{{form.address_complement}}</td> +</tr> +<tr> +    <th><label>{{form.postal_code.label}}</label></th> +    <td>{{form.postal_code}}</td> +</tr> +<tr> +    <th><label>{{form.town.label}}</label></th> +    <td>{{form.town}}</td> +</tr> +</table> + diff --git a/ishtar_common/templates/ishtar/organization_person_form.html b/ishtar_common/templates/ishtar/organization_person_form.html new file mode 100644 index 000000000..ccc442197 --- /dev/null +++ b/ishtar_common/templates/ishtar/organization_person_form.html @@ -0,0 +1,131 @@ +{% load i18n %}{% load url from future %} +<div id='orga-person-form'> +<form id='dyn-form-person' method='post'> +{% csrf_token %} +{% for hidden_field in form.hidden_fields %}{{hidden_field}}{% endfor %} +<input type='hidden' name='hidden_person_pk' id='hidden_person_pk' value='{{object.pk}}'/> +<input type='hidden' name='hidden_person_lbl' id='hidden_person_lbl' value="{{object}}"/> +<table class='organization'> +<caption>{% trans "Identification" %}</caption> +<tr> +    <th> </th> +    <td>{{form.attached_to}}</td> +</tr> +</table> + +<table class='person'> +<caption>{% trans "Corporation manager" %}</caption> +<tr> +    <th><label>{{form.title.label}}</label></th> +    <td>{{form.title}}</td> +</tr> +<tr> +    <th><label>{{form.name.label}}</label></th> +    <td>{{form.name}}</td> +</tr> +<tr> +    <th><label>{{form.surname.label}}</label></th> +    <td>{{form.surname}}</td> +</tr> +</table> +<div> +<input type='submit' id='btn-modify-person' value="{% trans "Modify"%}"/> +<input type='submit' id='btn-new-person' value="{% trans "New"%}"/> +<input type='submit' id='btn-save-person' value="{% trans "Save"%}"/> +<input type='submit' id='btn-cancel-person' value="{% trans "Cancel"%}"/> +</div> +</form> +</div> +<script type="text/javascript"><!--// + +function checkPendingRequest(todo) { +    window.setTimeout(function () { +        if ($.active > 0) { +            window.setTimeout(checkPendingRequest(todo), 250); +        } else { +            todo();  +        } +    }, 250); +}; + +person_save_callback = function(){ +    var item_id = $('#hidden_person_pk').val(); + +    var url = edit_url; +    $('#id_' + parent_id).val(null); +    if (item_id){ +        url = edit_url+item_id; +        $('#id_'+parent_id).val(item_id); +    } +    $("#id_select_"+parent_id).trigger('autocompletechange'); +    $.get(url , function( data ) { +        $( "#div-"+parent_id ).html( data ); +        var lbl = $('#hidden_person_lbl').val(); +        $('#id_select_' + parent_id).val(lbl); +    }); +}; + +person_new_callback = function(){ +    var url = edit_url; +    $('#id_'+parent_id).val(null); +    $('#id_select_'+parent_id).val(null); +} +$(function() { +    disable_person = function(){ +        $("#orga-person-form :input[type='text'], #orga-person-form textarea, #orga-person-form select").prop("disabled", true); +        $("#btn-save-person").hide(); +        $("#btn-cancel-person").hide(); +        $("#btn-modify-person").show(); +        $("#btn-new-person").show(); +        $("#orga-person-form").removeClass('selected'); +    } +    enable_person = function(){ +        $("#orga-person-form :input[type='text'], #orga-person-form textarea, #orga-person-form select").prop("disabled", false); +        $("#btn-save-person").show(); +        $("#btn-cancel-person").show(); +        $("#btn-modify-person").hide(); +        $("#btn-new-person").hide(); +        $("#orga-person-form").addClass('selected'); +    } + +    checkPendingRequest(disable_person); + +    $("#btn-modify-person").on('click', function(){ +        {% if object %}submit_url = "{% url 'organization_person_edit' object.pk %}"; +        {% else %}submit_url = "{% url 'organization_person_create' %}";{% endif %} +        enable_person(); +        return false; +    }); +    $("#btn-new-person").on('click', function(){ +        submit_url = "{% url 'organization_person_create' %}"; +        $("#orga-person-form").find("input[type=text], textarea, input[type=hidden]").val(""); +        enable_person(); +        person_new_callback(); +        return false; +    }); +    $("#btn-cancel-person").on('click', function(){ +        person_save_callback(); +        disable_person(); +        return false; +    }); +    $("#btn-save-person").on('click', function(){ +        var form = $("#dyn-form-person"); +        $.ajax({ +            url: submit_url, +            type: 'post', +            data: form.serialize(), +            dataType: 'html', +            success: function(data) { +                $("#orga-person-form").parent().html(data); +                person_save_callback(); +                disable_person(); +                return false; +            } +        }); +        return false; +    }); + + +}); +//--></script> + diff --git a/ishtar_common/templates/ishtar/person_form.html b/ishtar_common/templates/ishtar/person_form.html new file mode 100644 index 000000000..555aa1a5f --- /dev/null +++ b/ishtar_common/templates/ishtar/person_form.html @@ -0,0 +1,113 @@ +{% load i18n %}{% load url from future %} +<div id='person-form'> +<form id='dyn-form-person' method='post'> +{% csrf_token %} +{% for hidden_field in form.hidden_fields %}{{hidden_field}}{% endfor %} +<input type='hidden' name='hidden_person_pk' id='hidden_person_pk' value='{{object.pk}}'/> +<input type='hidden' name='hidden_person_lbl' id='hidden_person_lbl' value="{{object}}"/> +<table class='person'> +<caption>{% trans "Identification" %}</caption> +<tr> +    <th><label>{{form.title.label}}</label></th> +    <td>{{form.title}}</td> +</tr> +<tr> +    <th><label>{{form.name.label}}</label></th> +    <td>{{form.name}}</td> +</tr> +<tr> +    <th><label>{{form.surname.label}}</label></th> +    <td>{{form.surname}}</td> +</tr> +</table> +<table class='person-address'> +<caption>{% trans "Contact informations" %}</caption> +<tr> +    <th><label>{{form.address.label}}</label></th> +    <td>{{form.address}}</td> +</tr> +<tr> +    <th><label>{{form.address_complement.label}}</label></th> +    <td>{{form.address_complement}}</td> +</tr> +<tr> +    <th><label>{{form.postal_code.label}}</label></th> +    <td>{{form.postal_code}}</td> +</tr> +<tr> +    <th><label>{{form.town.label}}</label></th> +    <td>{{form.town}}</td> +</tr> +</table> +<div> +<input type='submit' id='btn-modify-person' value="{% trans "Modify"%}"/> +<input type='submit' id='btn-new-person' value="{% trans "New"%}"/> +<input type='submit' id='btn-save-person' value="{% trans "Save"%}"/> +<input type='submit' id='btn-cancel-person' value="{% trans "Cancel"%}"/> +</div> +</form> +</div> +<script type="text/javascript"><!--// +$(function() { +    // person_save_callback and person_new_callback must have been defined +    disable_person = function(){ +        $("#person-form :input[type='text'], #person-form textarea, #person-form select").prop("disabled", true); +        $("#btn-save-person").hide(); +        $("#btn-cancel-person").hide(); +        $("#btn-modify-person").show(); +        $("#btn-new-person").show(); +        $("#person-form").removeClass('selected'); +    } + +    enable_person = function(){ +        $("#person-form :input[type='text'], #person-form textarea, #person-form select").prop("disabled", false); +        $("#btn-save-person").show(); +        $("#btn-cancel-person").show(); +        $("#btn-modify-person").hide(); +        $("#btn-new-person").hide(); +        $("#person-form").addClass('selected'); +    } + +    disable_person(); + +    var submit_url; +    $("#btn-modify-person").on('click', function(){ +        {% if object %}submit_url = "{% url 'person_edit' object.pk %}"; +        {% else %}submit_url = "{% url 'person_create' %}";{% endif %} +        enable_person(); +        return false; +    }); +    $("#btn-new-person").on('click', function(){ +        submit_url = "{% url 'person_create' %}"; +        $("#person-form").find("input[type=text], textarea").val(""); +        enable_person(); +        person_new_callback(); +        return false; +    }); +    $("#btn-cancel-person").on('click', function(){ +        person_save_callback($('#hidden_person_pk').val(), +                             $('#hidden_person_lbl').val()); +        disable_person(); +        return false; +    }); +    $("#btn-save-person").on('click', function(){ +        var form = $("#dyn-form-person"); +        $.ajax({ +            url: submit_url, +            type: 'post', +            data: form.serialize(), +            dataType: 'html', +            success: function(data) { +                $("#person-form").parent().html(data); +                person_save_callback($('#hidden_person_pk').val(), +                                     $('#hidden_person_lbl').val()); +                disable_person(); +                return false; +            } +        }); +        return false; +    }); + + +}); +//--></script> diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py index 97fe4f81a..d31d0d359 100644 --- a/ishtar_common/urls.py +++ b/ishtar_common/urls.py @@ -39,6 +39,10 @@ urlpatterns = patterns('',             views.person_modification_wizard, name='person_modification'),         url(r'person_deletion/(?P<step>.+)?$',             views.person_deletion_wizard, name='person_deletion'), +       url(r'^person-edit/$', +           views.PersonCreate.as_view(), name='person_create'), +       url(r'^person-edit/(?P<pk>\d+)$', +           views.PersonEdit.as_view(), name='person_edit'),         url(r'organization_creation/(?P<step>.+)?$',             views.organization_creation_wizard, name='organization_creation'),         url(r'organization_modification/(?P<step>.+)?$', @@ -46,6 +50,16 @@ urlpatterns = patterns('',             name='organization_modification'),         url(r'organization_deletion/(?P<step>.+)?$',             views.organization_deletion_wizard, name='organization_deletion'), +       url(r'organization-edit/$', +           views.OrganizationCreate.as_view(), name='organization_create'), +       url(r'organization-edit/(?P<pk>\d+)$', +           views.OrganizationEdit.as_view(), name='organization_edit'), +       url(r'organization-person-edit/$', +           views.OrganizationPersonCreate.as_view(), +           name='organization_person_create'), +       url(r'organization-person-edit/(?P<pk>\d+)$', +           views.OrganizationPersonEdit.as_view(), +           name='organization_person_edit'),         url(r'account_management/(?P<step>.+)?$',             views.account_management_wizard, name='account_management'),         url(r'import/$', views.NewImportView.as_view(), name='new_import'), diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 1b27bcf43..3c6b796db 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -44,7 +44,7 @@ from django.shortcuts import render_to_response, redirect  from django.template import RequestContext, loader  from django.utils.decorators import method_decorator  from django.utils.translation import ugettext, ugettext_lazy as _ -from django.views.generic import ListView +from django.views.generic import ListView, UpdateView, CreateView  from django.views.generic.edit import CreateView, DeleteView  from xhtml2odt import xhtml2odt @@ -943,3 +943,59 @@ class ImportDeleteView(IshtarMixin, LoginRequiredMixin, DeleteView):      def get_success_url(self):          return reverse('current_imports') + +class PersonCreate(LoginRequiredMixin, CreateView): +    model = models.Person +    form_class = forms.BasePersonForm +    template_name = 'ishtar/person_form.html' + +    def get_success_url(self): +        return reverse('person_edit', args=[self.object.pk]) + +class PersonEdit(LoginRequiredMixin, UpdateView): +    model = models.Person +    form_class = forms.BasePersonForm +    template_name = 'ishtar/person_form.html' + +    def get_success_url(self): +        return reverse('person_edit', args=[self.object.pk]) + +class OrganizationCreate(LoginRequiredMixin, CreateView): +    model = models.Organization +    form_class = forms.BaseOrganizationForm +    template_name = 'ishtar/organization_form.html' +    form_prefix = "orga" + +    def get_form_kwargs(self): +        kwargs = super(OrganizationCreate, self).get_form_kwargs() +        if hasattr(self.form_class, 'form_prefix'): +            kwargs.update({'prefix': self.form_class.form_prefix}) +        return kwargs + +class OrganizationEdit(LoginRequiredMixin, UpdateView): +    model = models.Organization +    form_class = forms.BaseOrganizationForm +    template_name = 'ishtar/organization_form.html' + +    def get_form_kwargs(self): +        kwargs = super(OrganizationEdit, self).get_form_kwargs() +        if hasattr(self.form_class, 'form_prefix'): +            kwargs.update({'prefix': self.form_class.form_prefix}) +        return kwargs + +class OrganizationPersonCreate(LoginRequiredMixin, CreateView): +    model = models.Person +    form_class = forms.BaseOrganizationPersonForm +    template_name = 'ishtar/organization_person_form.html' + +    def get_success_url(self): +        return reverse('organization_person_edit', args=[self.object.pk]) + +class OrganizationPersonEdit(LoginRequiredMixin, UpdateView): +    model = models.Person +    form_class = forms.BaseOrganizationPersonForm +    template_name = 'ishtar/organization_person_form.html' + +    def get_success_url(self): +        return reverse('organization_person_edit', args=[self.object.pk]) + diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index 0ba4061a8..91594edbd 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -26,6 +26,7 @@ from django.db.models import fields  from django.forms import ClearableFileInput  from django.forms.widgets import flatatt  from django.template import Context, loader +from django.template.defaultfilters import slugify  from django.utils.encoding import smart_unicode  from django.utils.functional import lazy  from django.utils.html import escape @@ -203,7 +204,7 @@ class JQueryAutoComplete(forms.TextInput):              except:                  raise ValueError('source type is not valid')          dct = {'source':mark_safe(source), -               'field_id':field_id} +               'field_id':field_id,}          if self.options:              dct['options'] = mark_safe('%s' % self.options) @@ -218,6 +219,7 @@ class JQueryAutoComplete(forms.TextInput):      def render(self, name, value=None, attrs=None):          attrs_hidden = self.build_attrs(attrs, name=name)          attrs_select = self.build_attrs(attrs) +        attrs_select['placeholder'] = _(u"Search...")          if value:              hiddens = []              selects = [] @@ -281,6 +283,105 @@ class JQueryAutoComplete(forms.TextInput):          }          return html +class JQueryPersonOrganization(forms.TextInput): +    """ +    Complex widget which manage: +     * link between person and organization +     * display addresses of the person and of the organization +     * create new person and new organization +    """ + +    def __init__(self, source, edit_source, model, options={}, +                 attrs={}, new=False, limit={}, +                 html_template = 'blocks/PersonOrganization.html', +                 js_template='blocks/JQueryPersonOrganization.js'): +        self.options = None +        self.attrs = {} +        self.model = model +        self.source = source +        self.edit_source = edit_source +        if len(options) > 0: +            self.options = JSONEncoder().encode(options) +        self.attrs.update(attrs) +        self.new = new +        self.limit = limit +        self.js_template = js_template +        self.html_template = html_template + +    @classmethod +    def encode_source(cls, source): +        encoded_src = '' +        if isinstance(source, list): +            encoded_src = JSONEncoder().encode(source) +        elif isinstance(source, str) \ +           or isinstance(source, unicode): +            encoded_src = "'%s'" % escape(source) +        else: +            try: +                encoded_src = "'" + unicode(source) + "'" +            except: +                raise ValueError('source type is not valid') +        return encoded_src + +    def render_js(self, field_id, selected=''): +        source = self.encode_source(self.source) +        edit_source = self.encode_source(self.edit_source) +        dct = {'source':mark_safe(source), +               'edit_source':mark_safe(edit_source), +               'selected':selected, +               'safe_field_id':slugify(field_id).replace('-', '_'), +               'field_id':field_id} +        if self.options: +            dct['options'] = mark_safe('%s' % self.options) +        js = loader.get_template(self.js_template).render(Context(dct)) +        return js + +    def render(self, name, value=None, attrs=None): +        attrs_hidden = self.build_attrs(attrs, name=name) +        attrs_select = self.build_attrs(attrs) +        attrs_select['placeholder'] = _(u"Search...") +        selected = '' +        if value: +            hiddens = [] +            selects = [] +            if type(value) not in (list, tuple): +                values = unicode(escape(smart_unicode(value))) +                values = values.replace('[', '').replace(']', '') +                values = values.split(',') +            else: +                values = [] +                for v in value: +                    values += v.split(',') +            for v in values: +                if not v: +                    continue +                hiddens.append(v) +                selects.append(v) +                if self.model: +                    try: +                        item = self.model.objects.get(pk=v) +                        selects[-1] = unicode(item) +                        selected = item.pk +                    except (self.model.DoesNotExist, ValueError): +                        selects.pop() +                        hiddens.pop() +            if hiddens and selects: +                attrs_hidden['value'] = hiddens[0] +                attrs_select['value'] = selects[0] +        if not self.attrs.has_key('id'): +            attrs_hidden['id'] = 'id_%s' % name +            attrs_select['id'] = 'id_select_%s' % name +        if 'class' not in attrs_select: +            attrs_select['class'] = 'autocomplete' +        new = '' +        dct = {'attrs_select':mark_safe(flatatt(attrs_select)), +               'attrs_hidden':mark_safe(flatatt(attrs_hidden)), +               'name':name, +               'js':self.render_js(name, selected), +               'new':mark_safe(new)} +        html = loader.get_template(self.html_template).render(Context(dct)) +        return html +  class JQueryJqGrid(forms.RadioSelect):      COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}"      class Media: | 
