summaryrefslogtreecommitdiff
path: root/archaeological_finds
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2024-11-28 17:58:55 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2025-02-19 14:45:56 +0100
commitd0760b4da854e1dadb4ff130468a4c6d185b1abc (patch)
tree464aab890c5d8ea8b51f6ad3558446b3c8018036 /archaeological_finds
parentf10b03c55ece933e4277cdf1e7d4acfba9fdd7ed (diff)
downloadIshtar-d0760b4da854e1dadb4ff130468a4c6d185b1abc.tar.bz2
Ishtar-d0760b4da854e1dadb4ff130468a4c6d185b1abc.zip
✨ exhibition: forms/sheets
Diffstat (limited to 'archaeological_finds')
-rw-r--r--archaeological_finds/fixtures/initial_data-fr.json1
-rw-r--r--archaeological_finds/forms_treatments.py232
-rw-r--r--archaeological_finds/migrations/0133_exhibition.py32
-rw-r--r--archaeological_finds/models_treatments.py48
-rw-r--r--archaeological_finds/templates/ishtar/sheet_exhibition.html133
-rw-r--r--archaeological_finds/templates/ishtar/sheet_exhibition_pdf.html14
-rw-r--r--archaeological_finds/templates/ishtar/sheet_exhibition_window.html3
-rw-r--r--archaeological_finds/templates/ishtar/wizard/exhibition.html13
-rw-r--r--archaeological_finds/templates/ishtar/wizard/wizard_find_creation.html13
-rw-r--r--archaeological_finds/urls.py41
-rw-r--r--archaeological_finds/views.py96
-rw-r--r--archaeological_finds/wizards.py13
12 files changed, 614 insertions, 25 deletions
diff --git a/archaeological_finds/fixtures/initial_data-fr.json b/archaeological_finds/fixtures/initial_data-fr.json
index b969d15ca..09017e750 100644
--- a/archaeological_finds/fixtures/initial_data-fr.json
+++ b/archaeological_finds/fixtures/initial_data-fr.json
@@ -7089,6 +7089,7 @@
"label": "Exposition",
"txt_idx": "exhibition",
"comment": "",
+ "treatment_file_type": ["loan-for-exhibition"],
"available": true
}
}
diff --git a/archaeological_finds/forms_treatments.py b/archaeological_finds/forms_treatments.py
index 88b26905d..c624ebfc6 100644
--- a/archaeological_finds/forms_treatments.py
+++ b/archaeological_finds/forms_treatments.py
@@ -25,6 +25,7 @@ from bootstrap_datepicker.widgets import DateField
from django import forms
from django.core import validators
from ishtar_common.utils import ugettext_lazy as _
+from ishtar_common.forms import FormHeader
from archaeological_finds import models
from archaeological_operations.forms import AdministrativeActForm, \
@@ -705,16 +706,6 @@ class TreatmentFileFormSelectionMultiple(MultiSearchForm):
validators=[valid_ids(models.TreatmentFile)])
-class ExhibitionFormSelection(TreatmentFileFormSelection):
- pk = forms.CharField(
- label="", required=False,
- widget=widgets.DataTable(
- reverse_lazy('get-exhibition'),
- TreatmentFileSelect, models.TreatmentFile,
- ),
- validators=[valid_ids(models.TreatmentFile)])
-
-
class TreatmentFileForm(CustomForm, ManageOldType):
form_label = _("Treatment request")
base_models = ['treatment_type_type']
@@ -903,3 +894,224 @@ class AdministrativeActTreatmentFileModifForm(
AdministrativeActModifForm, AdministrativeActTreatmentFileForm):
pk = forms.IntegerField(required=False, widget=forms.HiddenInput)
index = forms.IntegerField(label=_("Index"), required=False)
+
+
+# Exhibitions
+
+
+class ExhibitionSelect(DocumentItemSelect):
+ _model = models.Exhibition
+ form_admin_name = _("Exhibition - 001 - Search")
+ form_slug = "exhibition-001-search"
+
+ search_vector = forms.CharField(
+ label=_("Full text search"), widget=widgets.SearchWidget(
+ 'archaeological-finds', 'Exhibition'
+ ))
+ name = forms.CharField(label=_("Name"))
+ exhibition_type = forms.ChoiceField(label=_("Type"), choices=[])
+ year = forms.IntegerField(label=_("Year"))
+ reference = forms.CharField(label=_("Reference"))
+ in_charge = forms.IntegerField(
+ label=_("In charge"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-person'),
+ associated_model=Person),
+ validators=[valid_id(Person)])
+ TYPES = [
+ FieldType("exhibition_type", models.ExhibitionType),
+ ]
+
+
+class ExhibitionFormSelection(CustomForm, forms.Form):
+ SEARCH_AND_SELECT = True
+ form_label = _("Exhibition search")
+ associated_models = {'pk': models.Exhibition}
+ currents = {'pk': models.Exhibition}
+ pk = forms.CharField(
+ label="", required=False,
+ widget=widgets.DataTable(
+ reverse_lazy('get-exhibition'),
+ ExhibitionSelect, models.Exhibition,
+ ),
+ validators=[valid_ids(models.Exhibition)])
+
+
+class ExhibitionForm(forms.ModelForm, CustomForm, ManageOldType):
+ form_label = _("Exhibition")
+ form_admin_name = _("Exhibition - 020 - Main form")
+ form_slug = "exhibition-20-general"
+ extra_form_modals = ["person"]
+
+ pk = forms.IntegerField(label="", required=False, widget=forms.HiddenInput)
+ name = forms.CharField(label=_("Name"), max_length=500)
+ exhibition_type = forms.ChoiceField(label=_("Type"), choices=[])
+ year = forms.IntegerField(label=_("Year"),
+ initial=lambda: datetime.datetime.now().year,
+ validators=[validators.MinValueValidator(1000),
+ validators.MaxValueValidator(2100)])
+ reference = forms.CharField(
+ label=_("Reference"), max_length=500, required=False)
+ in_charge = forms.IntegerField(
+ label=_("Responsible"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-person'), associated_model=Person,
+ new=True),
+ validators=[valid_id(Person)], required=False)
+ associated_basket_id = forms.IntegerField(
+ label=_("Associated basket"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-findbasket'),
+ associated_model=models.FindBasket), required=False)
+ comment = forms.CharField(label=_("Comment"),
+ widget=forms.Textarea, required=False)
+
+ class Meta:
+ model = models.Exhibition
+ fields = [
+ "pk",
+ "name",
+ "exhibition_type",
+ "year",
+ "reference",
+ "in_charge",
+ "comment",
+ "associated_basket_id",
+ ]
+
+ HEADERS = {
+ "name": FormHeader(_("General")),
+ }
+ TYPES = [
+ FieldType("exhibition_type", models.ExhibitionType, empty_first=False),
+ ]
+
+ def __init__(self, *args, **kwargs):
+ self.user = kwargs.pop("user")
+ super().__init__(*args, **kwargs)
+ type_field = self.fields["exhibition_type"]
+ if len(type_field.choices) == 1:
+ type_field.initial = type_field.choices[0][0]
+ type_field.widget.attrs["readonly"] = True
+
+ def clean_in_charge(self):
+ return self._clean_model_field("in_charge", Person)
+
+ def clean_exhibition_type(self):
+ return self._clean_model_field("exhibition_type", models.ExhibitionType)
+
+ def save(self, *args, **kwargs):
+ obj = super().save(*args, **kwargs)
+ obj = models.Exhibition.objects.get(pk=obj.pk)
+ if self.user and not obj.history_creator:
+ obj.history_creator = self.user
+ obj.history_modifier = self.user
+ obj.skip_history_when_saving = True
+ obj.save()
+ return obj
+
+
+class QANewExhibitionLoanForm(IshtarForm):
+ extra_form_modals = [
+ "applicant_organisation", "exhibition_location",
+ "insurance_provider", "in_charge"
+ ]
+ qa_exhibition_start = DateField(label=_("Exhibition start date"))
+ qa_exhibition_end = DateField(label=_("Exhibition end date"))
+ qa_applicant_organisation = forms.IntegerField(
+ label=_("Beneficiary of the loan"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-organization'),
+ associated_model=Organization, # new=True
+ ),
+ validators=[valid_id(Organization)])
+ qa_in_charge = forms.IntegerField(
+ label=_("Scientific manager of the exhibition"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-person'),
+ associated_model=Person, # new=True
+ ),
+ validators=[valid_id(Person)])
+ qa_exhibition_location = forms.IntegerField(
+ label=_("Exhibition location"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-warehouse'),
+ associated_model=Warehouse, # new=True
+ ),
+ validators=[valid_id(Warehouse)],
+ help_text=_("The exhibition location must have an organization attached.")
+ )
+ qa_insurance_provider = forms.IntegerField(
+ label=_("Insurance provider"),
+ widget=widgets.JQueryAutoComplete(
+ reverse_lazy('autocomplete-organization'),
+ associated_model=Organization, # new=True
+ ),
+ validators=[valid_id(Organization)], required=False)
+
+ def __init__(self, *args, **kwargs):
+ self.user = None
+ if 'user' in kwargs:
+ self.user = kwargs.pop('user')
+ if hasattr(self.user, 'ishtaruser'):
+ self.user = self.user.ishtaruser
+ self.exhibition = kwargs.pop('items')[0]
+ super().__init__(*args, **kwargs)
+
+ def clean(self):
+ data = self.cleaned_data
+ if not self.exhibition.associated_basket_id:
+ raise forms.ValidationError(
+ _("Cannot create loan when no basket is associated to this"
+ " exhibition."))
+ return data
+
+ def save(self):
+ basket = self.exhibition.associated_basket
+ if not basket:
+ return
+ values = {
+ "year": self.cleaned_data["qa_exhibition_start"].year,
+ "type": self.exhibition.exhibition_type.treatment_file_type,
+ "exhibition_name": self.exhibition.name,
+ "exhibition_start_date": self.cleaned_data["qa_exhibition_start"],
+ "exhibition_end_date": self.cleaned_data["qa_exhibition_end"],
+ "history_creator": self.user.user_ptr,
+ "history_modifier": self.user.user_ptr,
+ }
+ try:
+ exhibition_location = Warehouse.objects.get(
+ pk=self.cleaned_data["qa_exhibition_location"]
+ )
+ except Warehouse.DoesNotExist:
+ return
+ loan_name = f"{self.exhibition.name} | {exhibition_location}"
+ values["name"] = loan_name
+ values["exhibition_location"] = exhibition_location
+ new_basket = basket.duplicate()
+ basket_label = f"{_('Exhibition')} | {loan_name}"
+ new_basket.label = basket_label
+ new_basket.save()
+ values["associated_basket_id"] = new_basket.id
+ try:
+ values["in_charge"] = Person.objects.get(
+ pk=self.cleaned_data["qa_in_charge"]
+ )
+ except Person.DoesNotExist:
+ return
+ if self.cleaned_data.get("qa_insurance_provider", None):
+ try:
+ values["insurance_provider"] = Organization.objects.get(
+ pk=self.cleaned_data["qa_insurance_provider"]
+ )
+ except Organization.DoesNotExist:
+ return
+ try:
+ values["applicant_organisation"] = Organization.objects.get(
+ pk=self.cleaned_data["qa_applicant_organisation"]
+ )
+ except Organization.DoesNotExist:
+ return
+ obj = models.TreatmentFile.objects.create(**values)
+ self.exhibition.treatment_files.add(obj)
+ return obj
diff --git a/archaeological_finds/migrations/0133_exhibition.py b/archaeological_finds/migrations/0133_exhibition.py
index a4a29168e..9ef1aa7e8 100644
--- a/archaeological_finds/migrations/0133_exhibition.py
+++ b/archaeological_finds/migrations/0133_exhibition.py
@@ -17,9 +17,18 @@ import simple_history.models
def create_default(apps, __):
+ TreatmentFileType = apps.get_model(
+ "archaeological_finds", "TreatmentFileType")
+ loan, __ = TreatmentFileType.objects.get_or_create(
+ txt_idx="loan-for-exhibition",
+ defaults={"label": "Exposition"}
+ )
+ loan.is_exhibition = True
+ loan.save()
ExhibitionType = apps.get_model("archaeological_finds", "ExhibitionType")
ExhibitionType.objects.get_or_create(
- txt_idx="exhibition", defaults={"label": "Exposition"}
+ txt_idx="exhibition",
+ defaults={"label": "Exposition", "treatment_file_type_id": loan.id}
)
@@ -40,6 +49,7 @@ class Migration(migrations.Migration):
('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')], verbose_name='Textual ID')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('available', models.BooleanField(default=True, verbose_name='Available')),
+ ('treatment_file_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='archaeological_finds.TreatmentFileType', verbose_name='Treatment request type')),
],
options={
'verbose_name': 'Exhibition type',
@@ -128,4 +138,24 @@ class Migration(migrations.Migration):
unique_together={('year', 'name')},
),
migrations.RunPython(create_default),
+ migrations.AddField(
+ model_name='historicaltreatmentfile',
+ name='exhibition_location',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_warehouse.Warehouse', verbose_name='Exhibition location'),
+ ),
+ migrations.AddField(
+ model_name='treatmentfile',
+ name='exhibition_location',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_warehouse.Warehouse', verbose_name='Exhibition location'),
+ ),
+ migrations.AddField(
+ model_name='historicaltreatmentfile',
+ name='insurance_provider',
+ field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='ishtar_common.Organization', verbose_name='Insurance provider'),
+ ),
+ migrations.AddField(
+ model_name='treatmentfile',
+ name='insurance_provider',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='insurance_provider_of', to='ishtar_common.Organization', verbose_name='Insurance provider'),
+ ),
]
diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py
index 933df0fa8..60a4440fd 100644
--- a/archaeological_finds/models_treatments.py
+++ b/archaeological_finds/models_treatments.py
@@ -1304,6 +1304,21 @@ class TreatmentFile(
exhibition_end_date = models.DateField(
_("Exhibition end date"), blank=True, null=True
)
+ exhibition_location = models.ForeignKey(
+ Warehouse,
+ verbose_name=_("Exhibition location"),
+ blank=True,
+ null=True,
+ on_delete=models.SET_NULL,
+ )
+ insurance_provider = models.ForeignKey(
+ Organization,
+ related_name="insurance_provider_of",
+ verbose_name=_("Insurance provider"),
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ )
comment = models.TextField(_("Comment"), blank=True, default="")
documents = models.ManyToManyField(
@@ -1457,6 +1472,12 @@ post_save.connect(cached_label_changed, sender=TreatmentFile)
class ExhibitionType(GeneralType):
+ treatment_file_type = models.ForeignKey(
+ TreatmentFileType,
+ verbose_name=_("Treatment request type"),
+ on_delete=models.PROTECT,
+ )
+
class Meta:
verbose_name = _("Exhibition type")
verbose_name_plural = _("Exhibition types")
@@ -1474,11 +1495,11 @@ class Exhibition(
AssociatedFindBasket,
):
SLUG = "exhibition"
- APP = "archaeological_finds"
+ APP = "archaeological-finds"
MODEL = SLUG
SHOW_URL = "show-exhibition"
- DELETE_URL = "delete-exhibition"
- TABLE_COLS = ["year", "reference", "name"]
+ # DELETE_URL = "delete-exhibition"
+ TABLE_COLS = ["year", "name", "reference"]
BASE_SEARCH_VECTORS = [
SearchVectorConfig("exhibition_type__label"),
SearchVectorConfig("reference"),
@@ -1552,3 +1573,24 @@ class Exhibition(
]
ADMIN_SECTION = _("Treatments")
+ def get_extra_actions(self, request):
+ """
+ For sheet template:
+ """
+ actions = super().get_extra_actions(request)
+ can_add_tf = self.can_do(
+ request, "archaeological_finds.add_treatmentfile"
+ )
+ if can_add_tf:
+
+ actions += [
+ (
+ reverse("exhibition-qa-add-loan", args=[self.pk]),
+ _("Add exhibition loan"),
+ "fa fa-plus",
+ _("exhibition loan"),
+ "",
+ True,
+ ),
+ ]
+ return actions
diff --git a/archaeological_finds/templates/ishtar/sheet_exhibition.html b/archaeological_finds/templates/ishtar/sheet_exhibition.html
new file mode 100644
index 000000000..cd37bac4b
--- /dev/null
+++ b/archaeological_finds/templates/ishtar/sheet_exhibition.html
@@ -0,0 +1,133 @@
+{% extends "ishtar/sheet.html" %}
+{% load i18n l10n ishtar_helpers window_field from_dict link_to_window window_tables window_ope_tables window_header humanize %}
+
+{% block head_title %}<strong><i class="fa fa-users" aria-hidden="true"></i>&nbsp; {% trans "Exhibition" %}</strong> - {{ item.name|default:"" }} [{{item.year|unlocalize}}]{% endblock %}
+
+{% block toolbar %}
+{% window_nav item window_id 'show-exhibition' 'exhibition-modify' 'exhibition-show-historized' 'exhibition-revert' previous next 1 %}
+{% endblock %}
+
+{% block content %}
+{% with perm_documents=permission_view_own_document|or_:permission_view_document %}
+{% with perm_change_basket=permission_view_own_find|or_:permission_view_find %}
+{% with has_documents=item|safe_or:"documents.count|documents_list" %}
+{% with display_documents=perm_documents|and_:has_documents %}
+
+{% if output != "ODT" and output != "PDF"%}
+<ul class="nav nav-tabs" id="{{window_id}}-tabs" role="tablist">
+ <li class="nav-item">
+ <a class="nav-link active" id="{{window_id}}-exhibition-tab"
+ data-toggle="tab" href="#{{window_id}}-exhibition" role="tab"
+ aria-controls="{{window_id}}-exhibition" aria-selected="true">
+ {% trans "Exhibition" %}
+ </a>
+ </li>
+ {% for loan in item.treatment_files.all %}
+ <li class="nav-item">
+ <a class="nav-link" id="{{window_id}}-loan{{forloop.counter}}-tab"
+ data-toggle="tab" href="#{{window_id}}-loan{{forloop.counter}}" role="tab"
+ aria-controls="{{window_id}}-loan{{forloop.counter}}" aria-selected="true">
+ {{loan.name}}
+ </a>
+ </li>
+ {% endfor %}
+</ul>
+{% endif %}
+
+<div class="tab-content" id="{{window_id}}-tab-content">
+ <div class="tab-pane fade show active" id="{{window_id}}-exhibition"
+ role="tabpanel" aria-labelledby="{{window_id}}-exhibition-tab">
+
+ {% with has_image=item.images.count %}
+ {% if has_image %}
+ <div class="clearfix">
+ <div class="card float-left col-12 col-md-6 col-lg-4">
+ {% include "ishtar/blocks/window_image.html" %}
+ </div>
+ {% endif %}
+ <div class="row">
+ {% field_flex _("Name") item.name %}
+ {% field_flex _("Type") item.exhibition_type %}
+ {% field_flex _("Year") item.year %}
+ {% field_flex_detail _("Responsible") item.in_charge %}
+ {% field_flex_full "Comment" item.comment "<pre>" "</pre>" %}
+ {% include "ishtar/blocks/sheet_json.html" %}
+ </div>
+ {% if has_image %}
+ </div>
+ {% endif %}
+ {% endwith %}
+
+ {% if item.associated_basket %}
+ <h3>{% trans "Associated basket" %}</h3>
+ <div class="row">
+ {% field_flex_detail _("Associated basket") item.associated_basket %}
+
+ {% if perm_change_basket %}
+ <p class="col-12 col-md-6 col-lg-3 flex-wrap">
+ <a class="wait-button btn btn-success" href="/find_basket_modification_add/{{item.associated_basket.pk}}/?back_url={% url 'display-exhibition' item.pk %}" title="{% trans 'Manage basket' %}">
+ <i class="fa fa-shopping-basket"></i>&nbsp; {% trans "manage items of this basket" %}
+ </a>
+ </p>
+ {% endif %}
+
+ </div>
+ {% dynamic_table_document finds 'finds' 'basket_id' item.associated_basket.pk 'TABLE_COLS' output %}
+ {% endif %}
+
+
+ {% if display_documents %}
+ {% trans "Associated documents" as associated_docs %}
+ {% dynamic_table_document associated_docs 'documents' 'treatment_files' item.pk '' output %}
+ {% endif %}
+
+ {% if item.administrative_act.count %}
+ {% trans "Administrative acts" as admact_lbl %}
+ {% table_administrativact admact_lbl item.administrative_act.all %}
+ {% endif %}
+
+ {% if not is_external %}
+ {% if item.history_creator or item.last_edition_date or item.created %}
+ <h3>{% trans "Sheet"%}</h3>
+ <div class="row">
+ {% include "ishtar/blocks/sheet_creation_section.html" %}
+ </div>
+ {% endif %}
+ {% endif %}
+ </div>
+ {% for loan in item.treatment_files.all %}
+ <div class="tab-pane fade" id="{{window_id}}-loan{{forloop.counter}}"
+ role="tabpanel" aria-labelledby="{{window_id}}-loan{{forloop.counter}}-tab">
+ <div class="row">
+ {% with loan.exhibition_start_date|date:"SHORT_DATE_FORMAT" as exhibition_start_date %}
+ <dl class="col-12 col-md-6 col-lg-3 flex-wrap">
+ <dt>{% trans "Dates" %}</dt>
+ <dd>{{loan.exhibition_start_date|date:"SHORT_DATE_FORMAT"}} / {{loan.exhibition_end_date|date:"SHORT_DATE_FORMAT"}}</dd>
+ </dl>
+ {% endwith %}
+ {% field_flex_detail _("Beneficiary of the loan") loan.applicant_organisation %}
+ {% field_flex_detail _("Scientific manager of the exhibition") loan.in_charge %}
+ {% field_flex_detail _("Exhibition location") loan.exhibition_location %}
+ {% field_flex_detail _("Insurance provider") loan.insurance_provider %}
+ </div>
+ <h3>{% trans "Associated basket" %}</h3>
+ <div class="row">
+ {% field_flex_detail _("Associated basket") loan.associated_basket %}
+
+ {% if perm_change_basket %}
+ <p class="col-12 col-md-6 col-lg-3 flex-wrap">
+ <a class="wait-button btn btn-success" href="/find_basket_modification_add/{{loan.associated_basket.pk}}/?back_url={% url 'display-exhibition' loan.pk %}" title="{% trans 'Manage basket' %}">
+ <i class="fa fa-shopping-basket"></i>&nbsp; {% trans "manage items of this basket" %}
+ </a>
+ </p>
+ {% endif %}
+
+ </div>
+ {% dynamic_table_document finds 'finds' 'basket_id' loan.associated_basket.pk 'TABLE_COLS' output %}
+
+ </div>
+ {% endfor %}
+</div>
+
+{% endwith %}{% endwith %}{% endwith %}{% endwith %}
+{% endblock %}
diff --git a/archaeological_finds/templates/ishtar/sheet_exhibition_pdf.html b/archaeological_finds/templates/ishtar/sheet_exhibition_pdf.html
new file mode 100644
index 000000000..6540e1d58
--- /dev/null
+++ b/archaeological_finds/templates/ishtar/sheet_exhibition_pdf.html
@@ -0,0 +1,14 @@
+{% extends "ishtar/sheet_exhibition.html" %}
+{% block header %}
+{% endblock %}
+{% block main_head %}
+{{ block.super }}
+<div id="pdfheader">
+ Ishtar &ndash; {{APP_NAME}} &ndash; {{item}}
+</div>
+{% endblock %}
+{%block head_sheet%}{%endblock%}
+{%block main_foot%}
+</body>
+</html>
+{%endblock%}
diff --git a/archaeological_finds/templates/ishtar/sheet_exhibition_window.html b/archaeological_finds/templates/ishtar/sheet_exhibition_window.html
new file mode 100644
index 000000000..e6a8dcc5b
--- /dev/null
+++ b/archaeological_finds/templates/ishtar/sheet_exhibition_window.html
@@ -0,0 +1,3 @@
+{% extends "ishtar/sheet_exhibition.html" %}
+{% block main_head %}{%endblock%}
+{% block main_foot %}{%endblock%}
diff --git a/archaeological_finds/templates/ishtar/wizard/exhibition.html b/archaeological_finds/templates/ishtar/wizard/exhibition.html
new file mode 100644
index 000000000..e60126632
--- /dev/null
+++ b/archaeological_finds/templates/ishtar/wizard/exhibition.html
@@ -0,0 +1,13 @@
+{% extends "ishtar/wizard/search.html" %}
+{% load i18n %}
+{% block wizard_top_button %}
+{% if permission_add_exhibition %}
+<div class="row mb-3">
+ <div class="col">
+ <a href="{% url "exhibition-create" %}" class="btn btn-success">
+ <i class="fa fa-plus" aria-hidden="true"></i>&nbsp; {% trans "new exhibition" %}
+ </a>
+ </div>
+</div>
+{% endif %}
+{% endblock %}
diff --git a/archaeological_finds/templates/ishtar/wizard/wizard_find_creation.html b/archaeological_finds/templates/ishtar/wizard/wizard_find_creation.html
new file mode 100644
index 000000000..65806e303
--- /dev/null
+++ b/archaeological_finds/templates/ishtar/wizard/wizard_find_creation.html
@@ -0,0 +1,13 @@
+{% extends "ishtar/wizard/default_wizard.html" %}
+{% load i18n l10n %}
+{% block wizard_top_button %}
+{% if no_context_cr %}
+<div class="row mb-3">
+ <div class="col">
+ <a href="/find_create/{{no_context_cr|unlocalize}}/" class="btn btn-success">
+ <i class="fa fa-plus" aria-hidden="true"></i> {% trans "find without context" %}
+ </a>
+ </div>
+</div>
+{% endif %}
+{% endblock %}
diff --git a/archaeological_finds/urls.py b/archaeological_finds/urls.py
index 467df9770..6e6f6ff0a 100644
--- a/archaeological_finds/urls.py
+++ b/archaeological_finds/urls.py
@@ -18,16 +18,19 @@
# See the file COPYING for details.
from django.conf.urls import url
-from django.urls import path
+from django.urls import path, register_converter
from ishtar_common.utils import check_permissions, get_urls_for_model
+from ishtar_common import urls_converters
from ishtar_common.views import QALinkView
from archaeological_finds import views
from archaeological_finds import views_api
from archaeological_operations.views import administrativeactfile_document
from archaeological_finds import models
+register_converter(urls_converters.DateTimeConverter, "datetime")
+
# be careful: each check_permissions must be relevant with ishtar_menu
# forms
@@ -455,6 +458,36 @@ urlpatterns = [
name="exhibition-create",
),
path(
+ "exhibition/qa/add-loan/<int:pks>/",
+ check_permissions(
+ ["archaeological_finds.add_treatmentfile"]
+ )(views.QAExhibitionLoanFormView.as_view()),
+ name="exhibition-qa-add-loan",
+ ),
+ path(
+ "exhibition/modify/<int:pk>/",
+ check_permissions(
+ ["archaeological_finds.change_exhibition",
+ "archaeological_finds.change_own_exhibition"]
+ )(views.ExhibitionEditView.as_view()),
+ name="exhibition-modify",
+ ),
+ path(
+ "exhibition/show/<int:pk>/",
+ views.show_exhibition,
+ name="exhibition-show-historized",
+ ),
+ path(
+ "exhibition/show/<int:pk>/<datetime:date>/",
+ views.show_exhibition,
+ name="exhibition-show-historized",
+ ),
+ path(
+ "exhibition/revert/<int:pk>/<datetime:date>/",
+ views.revert_exhibition,
+ name="exhibition-revert"
+ ),
+ path(
"exhibition/<step>/",
check_permissions(
["archaeological_finds.view_exhibition",
@@ -462,6 +495,12 @@ urlpatterns = [
)(views.exhibition_wizard),
name="exhibition-search",
),
+ path("show-exhibition/<int:pk>/<slug:type>",
+ views.show_exhibition, name="show-exhibition"),
+ path("show-exhibition/<int:pk>/",
+ views.show_exhibition, name="show-exhibition"),
+ path("display-exhibition/<int:pk>/",
+ views.display_exhibition, name="display-exhibition"),
url(
r"^treatmentfle_search/(?P<step>.+)?$",
check_permissions(
diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py
index 777b064c5..52085f2ef 100644
--- a/archaeological_finds/views.py
+++ b/archaeological_finds/views.py
@@ -27,11 +27,11 @@ from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.shortcuts import redirect
from django.urls import reverse
-from ishtar_common.utils import ugettext_lazy as _
+from ishtar_common.utils import ugettext_lazy as _, BSMessage
from django.views.generic import TemplateView
-from django.views.generic.edit import CreateView, FormView
+from django.views.generic.edit import CreateView, FormView, UpdateView
-from ishtar_common.models import IshtarUser, get_current_profile
+from ishtar_common.models import get_current_profile, IshtarUser, QuickAction
from archaeological_operations.models import AdministrativeAct, Operation
from archaeological_context_records.models import ContextRecord
from archaeological_finds import models
@@ -142,12 +142,21 @@ get_treatmentfile = get_item(
search_form=forms.TreatmentFileSelect,
)
+show_exhibition = show_item(models.Exhibition, "exhibition")
+revert_exhibition = revert_item(models.Exhibition)
get_exhibition = get_item(
- models.TreatmentFile,
- "get_treatmentfile",
- "treatmentfile",
- search_form=forms.TreatmentFileSelect,
- base_request={"type__is_exhibition": True},
+ models.Exhibition, "get_exhibition", "exhibition",
+ search_form=forms_treatments.ExhibitionSelect
+)
+display_exhibition = display_item(models.Exhibition)
+
+autocomplete_exhibition = get_autocomplete_item(model=models.Exhibition)
+
+get_exhibition = get_item(
+ models.Exhibition,
+ "get_exhibition",
+ "exhibition",
+ search_form=forms_treatments.ExhibitionSelect,
)
get_administrativeacttreatmentfile = get_item(
@@ -553,6 +562,7 @@ class SelectItemsInBasket(OwnBasket, IshtarMixin, LoginRequiredMixin, TemplateVi
context["form"] = forms.MultipleFindFormSelectionWarehouseModule()
else:
context["form"] = forms.MultipleFindFormSelection()
+ context["back_url"] = self.request.GET.get("back_url", None)
context["add_url"] = reverse("add_iteminbasket")
context["list_url"] = reverse(
"list_iteminbasket", kwargs={"pk": self.basket.pk}
@@ -630,12 +640,40 @@ get_downstreamtreatment = get_item(
)
-exhibition_wizard = wizards.TreatmentFileSearch.as_view(
+exhibition_wizard = wizards.ExhibitionSearch.as_view(
[("search", forms_treatments.ExhibitionFormSelection)],
- label=_("Exhibition: search"),
+ label=_("Exhibition"),
url_name="exhibition-search",
)
+
+class ExhibitionFormMixin(IshtarMixin, LoginRequiredMixin):
+ form_class = forms_treatments.ExhibitionForm
+ template_name = "ishtar/forms/base_form.html"
+ model = models.Exhibition
+
+ def get_form_kwargs(self):
+ kwargs = super().get_form_kwargs()
+ kwargs["user"] = self.request.user
+ return kwargs
+
+ def get_success_url(self):
+ return f"{reverse('exhibition-search')}?open_item={self.object.pk}"
+
+
+class ExhibitionCreateView(ExhibitionFormMixin, CreateView):
+ page_name = _("Exhibition creation")
+
+ def get_context_data(self, **kwargs):
+ data = super().get_context_data(**kwargs)
+ # data["extra_form_modals"] = self.form_class.extra_form_modals
+ return data
+
+
+class ExhibitionEditView(ExhibitionFormMixin, UpdateView):
+ page_name = _("Exhibition modification")
+
+
treatment_wizard_steps = [
("selecfind-treatment_creation", forms.UpstreamFindFormSelection),
("file-treatment_creation", forms.TreatmentFormFileChoice),
@@ -1368,3 +1406,41 @@ def get_geo_items(request, current_right=None):
geo = item.get_geo_items()
return HttpResponse(json.dumps(geo).encode("utf-8"))
+
+class QAExhibitionLoanFormView(QAItemForm):
+ model = models.Exhibition
+ form_class = forms_treatments.QANewExhibitionLoanForm
+ page_name = _("Add loan for exhibition")
+ modal_size = "large"
+ base_url = "exhibition-qa-add-loan"
+ icon = "fa fa-plus"
+ action_name = _("Create")
+
+ def get_quick_action(self):
+ return QuickAction(
+ url=self.base_url,
+ icon_class=self.icon,
+ text=self.page_name,
+ rights=[
+ "archaeological_finds.add_treatmentfile",
+ ],
+ )
+
+ def get_form_kwargs(self):
+ kwargs = super().get_form_kwargs()
+ kwargs["user"] = self.request.user
+ return kwargs
+
+ def get_context_data(self, **kwargs):
+ data = super().get_context_data(**kwargs)
+ if not self.items[0].associated_basket_id:
+ data["messages"] = [
+ BSMessage(
+ _("No basket associted to the exhibition."),
+ "danger", "fa fa-exclamation-triangle")
+ ]
+ return data
+
+ def form_valid(self, form):
+ form.save()
+ return HttpResponseRedirect(reverse("success"))
diff --git a/archaeological_finds/wizards.py b/archaeological_finds/wizards.py
index c43f65356..dd2e65ff8 100644
--- a/archaeological_finds/wizards.py
+++ b/archaeological_finds/wizards.py
@@ -43,6 +43,9 @@ class FindWizard(Wizard):
model = models.Find
wizard_done_window = reverse_lazy("show-find")
redirect_url = "find_modification"
+ wizard_templates = {
+ "selecrecord-find_creation": "ishtar/wizard/wizard_find_creation.html"
+ }
def get_current_contextrecord(self):
step = self.steps.current
@@ -626,3 +629,13 @@ class FindBasketDeletionWizard(DeletionWizard):
model = models.FindBasket
redirect_url = "find_basket_deletion"
wizard_confirm = "ishtar/wizard/wizard_findbasket_deletion.html"
+
+
+class ExhibitionSearch(SearchWizard):
+ model = models.Exhibition
+ template_name = "ishtar/wizard/exhibition.html"
+
+ def get_context_data(self, form, **kwargs):
+ data = super().get_context_data(form, **kwargs)
+ data["permission_add_exhibition"] = self.request.user.has_perm("ishtar_common.add_exhibition")
+ return data