summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@proxience.com>2014-12-28 01:15:07 +0100
committerÉtienne Loks <etienne.loks@proxience.com>2015-05-06 15:38:32 +0200
commit086cb36acde4343f5e4dc8dc18a70cdbd8512c33 (patch)
treecf7782840ce79aee720cf3b6c55571ab0b2e7c8e
parentee6a1eb3588da3c6c1c3f0e75691541cea2d37ab (diff)
downloadIshtar-086cb36acde4343f5e4dc8dc18a70cdbd8512c33.tar.bz2
Ishtar-086cb36acde4343f5e4dc8dc18a70cdbd8512c33.zip
Work on dynamic (too much of ajax and JS...) person/organization widget
-rw-r--r--archaeological_files/models.py2
-rw-r--r--archaeological_files_pdl/forms.py89
-rw-r--r--archaeological_files_pdl/templates/ishtar/blocks/JQueryCorporationPerson.js2
-rw-r--r--archaeological_files_pdl/templates/ishtar/blocks/JQueryNaturalPerson.js2
-rw-r--r--archaeological_files_pdl/templates/ishtar/blocks/JQueryPersonOrga.js65
-rw-r--r--archaeological_files_pdl/templates/ishtar/wizard/wizard_generalcontractor.html32
-rw-r--r--archaeological_files_pdl/views.py8
-rw-r--r--archaeological_files_pdl/wizards.py9
-rw-r--r--ishtar_common/forms_common.py40
-rw-r--r--ishtar_common/models.py12
-rw-r--r--ishtar_common/static/media/style.css9
-rw-r--r--ishtar_common/templates/blocks/JQueryPersonOrganization.js50
-rw-r--r--ishtar_common/templates/blocks/PersonOrganization.html9
-rw-r--r--ishtar_common/templates/ishtar/organization_form.html30
-rw-r--r--ishtar_common/templates/ishtar/organization_person_form.html131
-rw-r--r--ishtar_common/templates/ishtar/person_form.html113
-rw-r--r--ishtar_common/urls.py14
-rw-r--r--ishtar_common/views.py58
-rw-r--r--ishtar_common/widgets.py103
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>&nbsp;</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: