summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commit3d766fae8dd27b097eadd66993a091aa32af1aec (patch)
tree9201a1616398d4145fe3836821c3ccca788d817d
parent7186a3adae39105729e31d0c7b594fcbcbdfd091 (diff)
downloadIshtar-3d766fae8dd27b097eadd66993a091aa32af1aec.tar.bz2
Ishtar-3d766fae8dd27b097eadd66993a091aa32af1aec.zip
Warehouse: link warehouse to an organization - manage address dependencies
-rw-r--r--archaeological_warehouse/forms.py56
-rw-r--r--archaeological_warehouse/migrations/0036_auto_20190627_1321.py77
-rw-r--r--archaeological_warehouse/models.py33
-rw-r--r--archaeological_warehouse/templates/ishtar/sheet_warehouse.html5
-rw-r--r--archaeological_warehouse/views.py4
-rw-r--r--archaeological_warehouse/wizards.py13
-rw-r--r--ishtar_common/forms.py19
-rw-r--r--ishtar_common/models.py54
-rw-r--r--ishtar_common/templates/ishtar/blocks/sheet_address_section.html8
-rw-r--r--ishtar_common/templates/ishtar/sheet_organization.html19
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'>&nbsp;</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 %}