diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-06-27 17:20:43 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-06-27 17:20:43 +0200 |
commit | 3d766fae8dd27b097eadd66993a091aa32af1aec (patch) | |
tree | 9201a1616398d4145fe3836821c3ccca788d817d | |
parent | 7186a3adae39105729e31d0c7b594fcbcbdfd091 (diff) | |
download | Ishtar-3d766fae8dd27b097eadd66993a091aa32af1aec.tar.bz2 Ishtar-3d766fae8dd27b097eadd66993a091aa32af1aec.zip |
Warehouse: link warehouse to an organization - manage address dependencies
-rw-r--r-- | archaeological_warehouse/forms.py | 56 | ||||
-rw-r--r-- | archaeological_warehouse/migrations/0036_auto_20190627_1321.py | 77 | ||||
-rw-r--r-- | archaeological_warehouse/models.py | 33 | ||||
-rw-r--r-- | archaeological_warehouse/templates/ishtar/sheet_warehouse.html | 5 | ||||
-rw-r--r-- | archaeological_warehouse/views.py | 4 | ||||
-rw-r--r-- | archaeological_warehouse/wizards.py | 13 | ||||
-rw-r--r-- | ishtar_common/forms.py | 19 | ||||
-rw-r--r-- | ishtar_common/models.py | 54 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/blocks/sheet_address_section.html | 8 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/sheet_organization.html | 19 |
10 files changed, 266 insertions, 22 deletions
diff --git a/archaeological_warehouse/forms.py b/archaeological_warehouse/forms.py index 1679c9d0b..58d856844 100644 --- a/archaeological_warehouse/forms.py +++ b/archaeological_warehouse/forms.py @@ -26,7 +26,8 @@ from django.conf import settings from django.forms.formsets import formset_factory from django.utils.translation import ugettext_lazy as _ -from ishtar_common.models import Person, valid_id, Town, SpatialReferenceSystem +from ishtar_common.models import Person, valid_id, Town, \ + SpatialReferenceSystem, Organization, OrganizationType from archaeological_operations.models import ArchaeologicalSite from archaeological_context_records.models import ContextRecord from archaeological_finds.models import TreatmentType, FindBasket, \ @@ -129,6 +130,7 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form): associated_models = { 'warehouse_type': models.WarehouseType, 'person_in_charge': Person, + 'organization': Organization, 'precise_town': Town, 'spatial_reference_system': SpatialReferenceSystem } @@ -137,15 +139,33 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form): validators=[name_validator]) warehouse_type = forms.ChoiceField(label=_(u"Warehouse type"), choices=[]) + organization = forms.IntegerField( + label=_("Organization"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-organization'), + associated_model=Organization, new=True), + validators=[valid_id(Organization)], + required=False) person_in_charge = forms.IntegerField( - label=_(u"Person in charge"), + label=_("Person in charge"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-person'), associated_model=Person, new=True), validators=[valid_id(Person)], required=False) + create_organization = forms.BooleanField( + label=_("Create a new organization from this warehouse"), + required=False + ) comment = forms.CharField(label=_(u"Comment"), widget=forms.Textarea, required=False) + HEADERS['address'] = FormHeader( + _(u"Address"), collapse=True, + help_message=_( + "Only fill the following fields if no organization is provided or " + "if the address of the warehouse is different from the one of the " + "organization. If a new organization is created from this " + "warehouse, the following fields are used for the organization.")) address = forms.CharField(label=_(u"Address"), widget=forms.Textarea, required=False) address_complement = forms.CharField(label=_(u"Address complement"), @@ -160,11 +180,11 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form): phone = forms.CharField(label=_(u"Phone"), max_length=18, required=False) mobile_phone = forms.CharField(label=_(u"Mobile phone"), max_length=18, required=False) - HEADERS['x'] = FormHeader(_(u"Coordinates")) - x = forms.FloatField(label=_(u"X"), required=False) - y = forms.FloatField(label=_(u"Y"), required=False) + HEADERS['x'] = FormHeader(_("Coordinates")) + x = forms.FloatField(label=_("X"), required=False) + y = forms.FloatField(label=_("Y"), required=False) spatial_reference_system = forms.ChoiceField( - label=_(u"Spatial Reference System"), required=False, choices=[]) + label=_("Spatial Reference System"), required=False, choices=[]) TYPES = [ FieldType('warehouse_type', models.WarehouseType), @@ -176,21 +196,43 @@ class WarehouseForm(CustomForm, ManageOldType, forms.Form): kwargs.pop('limits') super(WarehouseForm, self).__init__(*args, **kwargs) + def clean(self): + if self.cleaned_data.get("organization", None) and \ + self.cleaned_data.get("create_organization", None): + raise forms.ValidationError( + _("A new organization is not created if an organization is " + "selected.")) + return self.cleaned_data + def save(self, user): dct = self.cleaned_data dct['history_modifier'] = user dct['warehouse_type'] = models.WarehouseType.objects.get( pk=dct['warehouse_type']) if 'person_in_charge' in dct and dct['person_in_charge']: - dct['person_in_charge'] = models.Person.objects.get( + dct['person_in_charge'] = Person.objects.get( pk=dct['person_in_charge']) + if 'organization' in dct and dct['organization']: + dct['organization'] = Organization.objects.get( + pk=dct['organization']) if not dct.get("spatial_reference_system", None): dct.pop("spatial_reference_system") + create_orga = dct.pop("create_organization") new_item = models.Warehouse(**dct) new_item.save() + if not create_orga: + return new_item + + new_item.create_attached_organization() return new_item +class WarehouseModifyForm(WarehouseForm): + def __init__(self, *args, **kwargs): + super(WarehouseModifyForm, self).__init__(*args, **kwargs) + self.fields.pop("create_organization") + + class WarehouseDeletionForm(FinalForm): confirm_msg = _(u"Would you like to delete this warehouse?") confirm_end_msg = _(u"Would you like to delete this warehouse?") diff --git a/archaeological_warehouse/migrations/0036_auto_20190627_1321.py b/archaeological_warehouse/migrations/0036_auto_20190627_1321.py new file mode 100644 index 000000000..c293d60e4 --- /dev/null +++ b/archaeological_warehouse/migrations/0036_auto_20190627_1321.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.18 on 2019-06-27 13:21 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('ishtar_common', '0096_tinyurl'), + ('archaeological_warehouse', '0035_auto_20190225_1637'), + ] + + operations = [ + migrations.AddField( + model_name='warehouse', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='warehouse', to='ishtar_common.Organization', verbose_name='Organisation'), + ), + migrations.AlterField( + model_name='container', + name='multi_polygon_source', + field=models.CharField(blank=True, choices=[('T', 'Commune'), ('P', 'Précis'), ('M', 'Polygone')], max_length=1, null=True, verbose_name='Source du multi-polygone'), + ), + migrations.AlterField( + model_name='container', + name='multi_polygon_source_item', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Élément source du multi-polygone'), + ), + migrations.AlterField( + model_name='container', + name='point_source', + field=models.CharField(blank=True, choices=[('T', 'Commune'), ('P', 'Précis'), ('M', 'Polygone')], max_length=1, null=True, verbose_name='Source du point'), + ), + migrations.AlterField( + model_name='container', + name='point_source_item', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Élément source du point'), + ), + migrations.AlterField( + model_name='containerlocalisation', + name='reference', + field=models.CharField(default='', max_length=200, verbose_name='Référence'), + ), + migrations.AlterField( + model_name='warehouse', + name='multi_polygon_source', + field=models.CharField(blank=True, choices=[('T', 'Commune'), ('P', 'Précis'), ('M', 'Polygone')], max_length=1, null=True, verbose_name='Source du multi-polygone'), + ), + migrations.AlterField( + model_name='warehouse', + name='multi_polygon_source_item', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Élément source du multi-polygone'), + ), + migrations.AlterField( + model_name='warehouse', + name='point_source', + field=models.CharField(blank=True, choices=[('T', 'Commune'), ('P', 'Précis'), ('M', 'Polygone')], max_length=1, null=True, verbose_name='Source du point'), + ), + migrations.AlterField( + model_name='warehouse', + name='point_source_item', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Élément source du point'), + ), + migrations.AlterField( + model_name='warehouse', + name='precise_town', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.Town', verbose_name='Commune (précis)'), + ), + migrations.AlterField( + model_name='warehouse', + name='town', + field=models.CharField(blank=True, max_length=150, null=True, verbose_name='Commune (saisie libre)'), + ), + ] diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index 97898e5c1..3dd3f08e8 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -31,7 +31,7 @@ from ishtar_common.data_importer import post_importer_action from ishtar_common.model_managers import ExternalIdManager from ishtar_common.models import Document, GeneralType, get_external_id, \ LightHistorizedItem, OwnPerms, Address, Person, post_save_cache, \ - DashboardFormItem, ShortMenuItem, \ + DashboardFormItem, ShortMenuItem, Organization, OrganizationType, \ document_attached_changed, SearchAltName, DynamicRequest, GeoItem, \ QRCodeItem, SearchVectorConfig, DocumentItem from ishtar_common.model_merging import merge_model_objects @@ -89,6 +89,9 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, person_in_charge = models.ForeignKey( Person, on_delete=models.SET_NULL, related_name='warehouse_in_charge', verbose_name=_(u"Person in charge"), null=True, blank=True) + organization = models.ForeignKey( + Organization, blank=True, null=True, related_name='warehouses', + verbose_name=_("Organization"), on_delete=models.SET_NULL) comment = models.TextField(_(u"Comment"), null=True, blank=True) associated_divisions = models.ManyToManyField( 'WarehouseDivision', verbose_name=_("Divisions"), blank=True, @@ -104,6 +107,7 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, external_id = models.TextField(_(u"External ID"), blank=True, null=True) auto_external_id = models.BooleanField( _(u"External ID is set automatically"), default=False) + SUB_ADDRESSES = ["organization", "person_in_charge"] class Meta: verbose_name = _(u"Warehouse") @@ -125,6 +129,33 @@ class Warehouse(Address, DocumentItem, GeoItem, QRCodeItem, DashboardFormItem, def _get_base_image_path(self): return u"{}/{}".format(self.SLUG, self.external_id) + def create_attached_organization(self): + """ + Create an attached organization from warehouse fields + """ + dct_orga = {} + for k in Address.FIELDS: + dct_orga[k] = getattr(self, k) + + q = OrganizationType.objects.filter(txt_idx="warehouse") + if q.count(): + orga_type = q.all()[0] + else: + orga_type, __ = OrganizationType.objects.get_or_create( + txt_idx="undefined", + defaults={"label": _("Undefined")} + ) + dct_orga["organization_type"] = orga_type + dct_orga["name"] = self.name + orga = Organization.objects.create(**dct_orga) + self.organization = orga + for k in Address.FIELDS: + if k == "alt_address_is_prefered": + setattr(self, k, False) + else: + setattr(self, k, None) + self.save() + @property def location_types(self): return [ diff --git a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html index 2d22df0fc..30d48d6bd 100644 --- a/archaeological_warehouse/templates/ishtar/sheet_warehouse.html +++ b/archaeological_warehouse/templates/ishtar/sheet_warehouse.html @@ -29,15 +29,16 @@ <div class='row'> - {% field_flex "Person in charge" item.person_in_charge %} {% include "ishtar/blocks/sheet_creation_section.html" %} + {% field_flex_detail "Person in charge" item.person_in_charge %} + {% field_flex_detail "Organization" item.organization %} {% field_flex "Divisions" item.location_types|join:", " %} {% field_flex_full "Comment" item.comment "<pre>" "</pre>" %} {% include "ishtar/blocks/sheet_json.html" %} </div> -{% if item.point_2d or item.multi_polygon or item.address or item.address_complement or item.postal_code or item.town %} +{% if item.point_2d or item.multi_polygon or item.get_address or item.get_address_complement or item.get_postal_code or item.get_town %} <h3>{% trans "Localisation"%}</h3> <div class='row'> {% with geo_item=item %} diff --git a/archaeological_warehouse/views.py b/archaeological_warehouse/views.py index a4fb30bf4..85b5511ae 100644 --- a/archaeological_warehouse/views.py +++ b/archaeological_warehouse/views.py @@ -29,7 +29,7 @@ from archaeological_warehouse import models from archaeological_warehouse.forms import WarehouseForm, ContainerForm, \ ContainerFormSelection, BasePackagingForm, WarehouseFormSelection, \ - SelectedDivisionFormset, WarehouseDeletionForm, \ + WarehouseModifyForm, SelectedDivisionFormset, WarehouseDeletionForm, \ MainContainerFormSelection, ContainerModifyForm, LocalisationForm, \ ContainerDeletionForm, ContainerSelect, WarehouseSelect from ishtar_common.forms import FinalForm @@ -137,7 +137,7 @@ warehouse_creation_wizard = WarehouseWizard.as_view( warehouse_modification_wizard = WarehouseModificationWizard.as_view([ ('selec-warehouse_modification', WarehouseFormSelection), - ("warehouse-warehouse_modification", WarehouseForm), + ("warehouse-warehouse_modification", WarehouseModifyForm), ('divisions-warehouse_modification', SelectedDivisionFormset), ('final-warehouse_modification', FinalForm)], label=_(u"Warehouse modification"), diff --git a/archaeological_warehouse/wizards.py b/archaeological_warehouse/wizards.py index 3d762ceb6..7476eb2b7 100644 --- a/archaeological_warehouse/wizards.py +++ b/archaeological_warehouse/wizards.py @@ -76,6 +76,19 @@ class WarehouseWizard(Wizard): model = models.Warehouse wizard_done_window = reverse_lazy('show-warehouse') + def save_model(self, dct, m2m, whole_associated_models, form_list, + return_object): + create_organization = False + if 'create_organization' in dct: + create_organization = dct.pop('create_organization') + obj, res = super(WarehouseWizard, self).save_model( + dct, m2m, whole_associated_models, form_list, True + ) + if self.modification or not create_organization: + return return_object and (obj, res) or res + obj.create_attached_organization() + return return_object and (obj, res) or res + class WarehouseModificationWizard(Wizard): model = models.Warehouse diff --git a/ishtar_common/forms.py b/ishtar_common/forms.py index 6cfef1595..823adf811 100644 --- a/ishtar_common/forms.py +++ b/ishtar_common/forms.py @@ -512,16 +512,23 @@ class FieldType(object): class FormHeader(object): - def __init__(self, label, level=4, collapse=False): + def __init__(self, label, level=4, collapse=False, help_message=""): self.label = label self.collapse = collapse self.level = level + self.help_message = help_message def render(self): + help_message = "" + if self.help_message: + help_message = """ + <div class="alert alert-info" role="alert">{}</div>""".format( + self.help_message) if not self.collapse: - return mark_safe(u"<h{level}>{label}</h{level}>".format( - label=self.label, level=self.level - )) + return mark_safe( + "<h{level}>{label}</h{level}>{help_message}".format( + label=self.label, level=self.level, + help_message=help_message)) html = u"""<div id="collapse-parent-{slug}" class="collapse-form"> <div class="card"> <div class="card-header" id="collapse-head-{slug}"> @@ -540,7 +547,9 @@ class FormHeader(object): aria-labelledby="collapse-head-{slug}" data-parent="#colapse-parent-{slug}"> <div class="card-body"> -""".format(label=self.label, slug=slugify(self.label), level=self.level) + {help_message} +""".format(label=self.label, slug=slugify(self.label), level=self.level, + help_message=help_message) return mark_safe(html) def render_end(self): diff --git a/ishtar_common/models.py b/ishtar_common/models.py index a70399ba7..a03f9f387 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -93,7 +93,13 @@ from ishtar_common.utils import get_cache, disable_for_loaddata, create_slug, \ __all__ = [ 'ImporterModel', 'ImporterType', 'ImporterDefault', 'ImporterDefaultValues', 'ImporterColumn', 'ImporterDuplicateField', 'Regexp', 'ImportTarget', - 'TargetKey', 'FormaterType', 'Import', 'TargetKeyGroup', 'ValueFormater' + 'TargetKey', 'FormaterType', 'Import', 'TargetKeyGroup', 'ValueFormater', + 'Organization', 'Person', 'valid_id', 'Town', 'SpatialReferenceSystem', + 'OrganizationType', 'Document', 'GeneralType', 'get_external_id', + 'LightHistorizedItem', 'OwnPerms', 'Address', 'post_save_cache', + 'DashboardFormItem', 'ShortMenuItem', 'document_attached_changed', + 'SearchAltName', 'DynamicRequest', 'GeoItem', 'QRCodeItem', + 'SearchVectorConfig', 'DocumentItem' ] logger = logging.getLogger(__name__) @@ -3565,6 +3571,14 @@ class Area(HierarchicalType): class Address(BaseHistorizedItem): + FIELDS = ( + "address", "address_complement", "postal_code", "town", + "precise_town", "country", + "alt_address", "alt_address_complement", "alt_postal_code", "alt_town", + "alt_country", + "phone", "phone_desc", "phone2", "phone_desc2", "phone3", "phone_desc3", + "raw_phone", "mobile_phone", "email", "alt_address_is_prefered" + ) address = models.TextField(_("Address"), null=True, blank=True) address_complement = models.TextField(_("Address complement"), null=True, blank=True) @@ -3605,6 +3619,7 @@ class Address(BaseHistorizedItem): alt_address_is_prefered = models.BooleanField( _("Alternative address is prefered"), default=False) history = HistoricalRecords() + SUB_ADDRESSES = [] class Meta: abstract = True @@ -3612,10 +3627,47 @@ class Address(BaseHistorizedItem): def get_town_centroid(self): if self.precise_town: return self.precise_town.center, self._meta.verbose_name + for sub_address in self.SUB_ADDRESSES: + sub_item = getattr(self, sub_address) + if sub_item and sub_item.precise_town: + return sub_item.precise_town.center, sub_item._meta.verbose_name def get_town_polygons(self): if self.precise_town: return self.precise_town.limit, self._meta.verbose_name + for sub_address in self.SUB_ADDRESSES: + sub_item = getattr(self, sub_address) + if sub_item and sub_item.precise_town: + return sub_item.precise_town.limit, sub_item._meta.verbose_name + + def get_attribute(self, attr): + if self.town or self.precise_town: + return getattr(self, attr) + for sub_address in self.SUB_ADDRESSES: + sub_item = getattr(self, sub_address) + if not sub_item: + continue + if sub_item.town or sub_item.precise_town: + return getattr(sub_item, attr) + return getattr(self, attr) + + def get_address(self): + return self.get_attribute("address") + + def get_address_complement(self): + return self.get_attribute("address_complement") + + def get_postal_code(self): + return self.get_attribute("postal_code") + + def get_town(self): + return self.get_attribute("town") + + def get_precise_town(self): + return self.get_attribute("precise_town") + + def get_country(self): + return self.get_attribute("country") def simple_lbl(self): return str(self) diff --git a/ishtar_common/templates/ishtar/blocks/sheet_address_section.html b/ishtar_common/templates/ishtar/blocks/sheet_address_section.html index a42cd6cca..80dbc07a4 100644 --- a/ishtar_common/templates/ishtar/blocks/sheet_address_section.html +++ b/ishtar_common/templates/ishtar/blocks/sheet_address_section.html @@ -1,10 +1,10 @@ {% load i18n %} -{% if item.address or item.address_complement or item.postal_code or item.town or item.precise_town%} +{% if item.get_address or item.get_address_complement or item.get_postal_code or item.get_town or item.get_precise_town%} <dl class="{% if full %}col-12 col-lg-6{% else %}col-12 col-md-6 col-lg-4 d-flex row{% endif %} flex-wrap"> <dt>{% trans "Address" %}</dt> <dd> - <pre>{% if item.address %}{{item.address}}{% endif %}{% if item.address_complement %} -{{item.address_complement}}{% endif %}{% if item.postal_code or item.town or item.precise_town %} -{{item.postal_code}} {% if item.precise_town %}{{item.precise_town}}{% else %}{{item.town}}{% endif %}{% endif %}</pre> + <pre>{% if item.get_address %}{{item.get_address}}{% endif %}{% if item.get_address_complement %} +{{item.get_address_complement}}{% endif %}{% if item.get_postal_code or item.get_town or item.get_precise_town %} +{{item.get_postal_code}} {% if item.get_precise_town %}{{item.get_precise_town}}{% else %}{{item.get_town}}{% endif %}{% endif %}</pre> </dd> </dl>{% endif %} diff --git a/ishtar_common/templates/ishtar/sheet_organization.html b/ishtar_common/templates/ishtar/sheet_organization.html index 37f7a76ce..798ae7a9b 100644 --- a/ishtar_common/templates/ishtar/sheet_organization.html +++ b/ishtar_common/templates/ishtar/sheet_organization.html @@ -37,6 +37,25 @@ {% endfor %} </table> +{% if item.warehouses.count %} +<h3>{%trans "Warehouses"%}</h3> + +<table class='table table-striped'> + <tr> + <th class='link'> </th> + <th>{% trans "Name" %}</th> + <th>{% trans "Type" %}</th> + </tr> + {% for warehouse in item.warehouses.all %} + <tr> + <td class='link'><a class='display_details' href="#" onclick='load_window("{% url "show-warehouse" warehouse.pk "" %}")'><i class="fa fa-info-circle" aria-hidden="true"></i></a></td> + <td class='string'>{{warehouse.name|default:""}}</td> + <td class='string'>{{warehouse.warehouse_type}}</td> + </tr> + {% endfor %} +</table> +{% endif %} + {% trans "General contractor organization of archaeological files" as af %} {% if item.general_contractor_files.count %} {% dynamic_table_document af 'files' 'corporation_general_contractor' item.pk '' output %} |