diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-07-22 18:22:54 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-07-29 11:56:34 +0200 |
commit | 7e1b10f0645539698b3aad3512a5e865dd56522f (patch) | |
tree | 272169633833554b81ab51336c3ebe41bb714e8a | |
parent | cc6d9d22d724fb39a5b7d30255e6bab2adc78763 (diff) | |
download | Ishtar-7e1b10f0645539698b3aad3512a5e865dd56522f.tar.bz2 Ishtar-7e1b10f0645539698b3aad3512a5e865dd56522f.zip |
Preventive file: work on inlines - 2
-rw-r--r-- | archaeological_files/admin.py | 12 | ||||
-rw-r--r-- | archaeological_files/forms.py | 120 | ||||
-rw-r--r-- | archaeological_files/migrations/0106_auto_20210726_1230.py (renamed from archaeological_files/migrations/0106_auto_20210720_1203.py) | 13 | ||||
-rw-r--r-- | archaeological_files/models.py | 33 | ||||
-rw-r--r-- | archaeological_files/templates/ishtar/forms/preventive_detail.html | 23 | ||||
-rw-r--r-- | archaeological_files/views.py | 9 | ||||
-rw-r--r-- | ishtar_common/admin.py | 14 |
7 files changed, 157 insertions, 67 deletions
diff --git a/archaeological_files/admin.py b/archaeological_files/admin.py index 6ac40a69b..629a0031d 100644 --- a/archaeological_files/admin.py +++ b/archaeological_files/admin.py @@ -20,10 +20,10 @@ from ajax_select import make_ajax_form from django.conf import settings -from django.contrib import admin from ishtar_common.apps import admin_site -from ishtar_common.admin import HistorizedObjectAdmin, GeneralTypeAdmin +from ishtar_common.admin import HistorizedObjectAdmin, GeneralTypeAdmin, \ + export_as_csv_action, serialize_type_action, ImportActionAdmin from . import models @@ -82,14 +82,16 @@ class EquipmentServiceTypeAdmin(GeneralTypeAdmin): admin_site.register(models.EquipmentServiceType, EquipmentServiceTypeAdmin) -class EquipmentServiceCostAdmin(admin.ModelAdmin): +class EquipmentServiceCostAdmin(ImportActionAdmin): search_fields = ( "equipment_service_type__label", "service_provider", ) list_filter = ("available",) - list_display = ["equipment_service_type", "specificity", "unitary_cost", "unit", - "flat_rate", "available"] + list_display = ["equipment_service_type", "specificity", "parent", + "unitary_cost", "unit", "flat_rate", "available"] + actions = [export_as_csv_action(), serialize_type_action] + model = models.EquipmentServiceCost admin_site.register(models.EquipmentServiceCost, EquipmentServiceCostAdmin) diff --git a/archaeological_files/forms.py b/archaeological_files/forms.py index 3c113c665..4918fdaaa 100644 --- a/archaeological_files/forms.py +++ b/archaeological_files/forms.py @@ -522,25 +522,37 @@ class FileFormPreventiveDetail(forms.ModelForm, CustomForm, ManageOldType): class Meta: model = models.File - fields = ["start_date", "end_date", "ground_start_date", - "ground_end_date", "study_period", "execution_report_date", - "total_developed_surface", "total_surface", "linear_meter"] + fields = [ + "start_date", + "end_date", + "ground_start_date", + "ground_end_date", + "study_period", + "execution_report_date", + "total_developed_surface", + "total_surface", + "linear_meter", + ] pk = forms.IntegerField(label="", required=False, widget=forms.HiddenInput) start_date = forms.DateField( - label=_("Start date"), required=False, + label=_("Start date"), + required=False, widget=DatePicker(attrs={"bs_col_width": "col-6 col-lg-3"}), ) end_date = forms.DateField( - label=_("End date"), required=False, + label=_("End date"), + required=False, widget=DatePicker(attrs={"bs_col_width": "col-6 col-lg-3"}), ) ground_start_date = forms.DateField( - label=_("Ground start date"), required=False, + label=_("Ground start date"), + required=False, widget=DatePicker(attrs={"bs_col_width": "col-6 col-lg-3"}), ) ground_end_date = forms.DateField( - label=_("Ground end date"), required=False, + label=_("Ground end date"), + required=False, widget=DatePicker(attrs={"bs_col_width": "col-6 col-lg-3"}), ) study_period = forms.CharField( @@ -549,7 +561,8 @@ class FileFormPreventiveDetail(forms.ModelForm, CustomForm, ManageOldType): required=False, ) execution_report_date = forms.DateField( - label=_("Execution report date"), required=False, + label=_("Execution report date"), + required=False, widget=DatePicker(attrs={"bs_col_width": "col-6 col-lg-3"}), ) total_developed_surface = forms.FloatField( @@ -571,7 +584,8 @@ class FileFormPreventiveDetail(forms.ModelForm, CustomForm, ManageOldType): ], ) linear_meter = forms.IntegerField( - label=_("Linear meter (m)"), required=False, + label=_("Linear meter (m)"), + required=False, widget=widgets.MeterKilometerWidget(attrs={"bs_col_width": "col-6 col-lg-3"}), ) @@ -588,24 +602,32 @@ class FileBaseFormset(forms.BaseModelFormSet): if "instance" in kwargs: self.instance = kwargs.pop("instance") super().__init__(*args, **kwargs) - self.queryset = self.model.objects.filter(pk=None) + self.queryset = self.get_base_queryset() + + def get_base_queryset(self): + queryset = self.model.objects.filter(pk=None) if self.instance: - self.queryset = self.model.objects.filter(file_id=self.instance.pk) + queryset = self.model.objects.filter(file_id=self.instance.pk) + return queryset def get_form_kwargs(self, index): kwargs = super(FileBaseFormset, self).get_form_kwargs(index) if self.instance: - kwargs['file_id'] = self.instance.pk + kwargs["file_id"] = self.instance.pk return kwargs -INLINE_JOB_FIELDS = ["man_by_day_planned", "days_planned", "man_by_day_worked", - "days_worked"] +INLINE_JOB_FIELDS = [ + "man_by_day_planned", + "days_planned", + "man_by_day_worked", + "days_worked", +] JOB_WIDGETS = { "man_by_day_planned": forms.NumberInput(attrs={"class": "w-50 form-planned"}), "days_planned": forms.NumberInput(attrs={"class": "w-50 form-planned"}), "man_by_day_worked": forms.NumberInput(attrs={"class": "w-50 form-worked"}), - "days_worked": forms.NumberInput(attrs={"class": "w-50 form-worked"}) + "days_worked": forms.NumberInput(attrs={"class": "w-50 form-worked"}), } JOB_LABELS = { @@ -613,7 +635,7 @@ JOB_LABELS = { "days_planned": _("Days"), "man_by_day_worked": _("Man by day"), "days_worked": _("Days"), - "Job": _("Job") + "Job": _("Job"), } @@ -645,7 +667,8 @@ class PreventiveFileJobBaseFormSet(FileBaseFormset): PreventiveFileJobFormSet = formset_factory( - PreventiveFileJobForm, formset=PreventiveFileJobBaseFormSet, can_delete=True) + PreventiveFileJobForm, formset=PreventiveFileJobBaseFormSet, can_delete=True +) PreventiveFileJobFormSet.form_label = _("Post-excavation") PreventiveFileJobFormSet.form_admin_name = _("Preventive file - 030 - Post-excavation") PreventiveFileJobFormSet.form_slug = "preventive-030-post-excavation" @@ -665,8 +688,9 @@ class PreventiveFileGroundJobBaseFormSet(FileBaseFormset): PreventiveFileGroundJobFormSet = formset_factory( - PreventiveFileGroundJobForm, formset=PreventiveFileGroundJobBaseFormSet, - can_delete=True + PreventiveFileGroundJobForm, + formset=PreventiveFileGroundJobBaseFormSet, + can_delete=True, ) PreventiveFileGroundJobFormSet.form_label = _("Ground jobs") PreventiveFileGroundJobFormSet.form_admin_name = _( @@ -677,23 +701,27 @@ PreventiveFileGroundJobFormSet.dynamic_add = True COST_LABELS = { "quantity_by_day_planned": _("Quantity"), - "days_planned": _("Days / weeks / months"), + "days_planned": "", "quantity_by_day_worked": _("Quantity"), - "days_worked": _("Days / weeks / months"), - "equipment_service_cost": _("Equipment / service") + "days_worked": "", + "equipment_service_cost": _("Equipment / service"), } INLINE_COST_FIELDS = [ - "quantity_by_day_planned", "days_planned", "quantity_by_day_worked", "days_worked"] + "quantity_by_day_planned", + "days_planned", + "quantity_by_day_worked", + "days_worked", +] COST_WIDGETS = { - "quantity_by_day_planned": forms.NumberInput( - attrs={"class": "w-50 form-planned"}), - "days_planned": forms.NumberInput(attrs={"class": "w-50 form-planned"}), + "quantity_by_day_planned": forms.NumberInput(attrs={"class": "w-50 form-planned"}), + "days_planned": forms.NumberInput(attrs={"class": "w-50 form-planned unit-form"}), "quantity_by_day_worked": forms.NumberInput(attrs={"class": "w-50 form-worked"}), - "days_worked": forms.NumberInput(attrs={"class": "w-50 form-worked"}), + "days_worked": forms.NumberInput(attrs={"class": "w-50 form-worked unit-form"}), "equipment_service_cost": forms.Select( - attrs={"class":"form-cost", "bs_col_width": "col-12"}) + attrs={"class": "form-cost", "bs_col_width": "col-12"} + ), } @@ -710,25 +738,55 @@ class PreventiveFileEquipmentServiceForm(PreventiveFileForm): super(PreventiveFileEquipmentServiceForm, self).__init__(*args, **kwargs) q = models.EquipmentServiceCost.objects.filter( available=True, - equipment_service_type__parent__isnull=True, - equipment_service_type__generic_equipment_type__txt_idx=self.type_filter + parent__isnull=True, + equipment_service_type__generic_equipment_type__txt_idx=self.type_filter, ) self.unities = {} unit_dict = dict(models.ES_UNITS) choices = [("", "--")] - for cost in q.all(): + costs = list(q.all()) + if self.instance and self.instance.equipment_service_cost_id \ + and self.instance.equipment_service_cost not in costs: + costs.append(self.instance.equipment_service_cost) + self.flat_rates = [] + for cost in costs: choices.append((cost.pk, str(cost))) if cost.unit: self.unities[cost.pk] = unit_dict[cost.unit] + if cost.flat_rate: + self.flat_rates.append(cost.pk) self.fields["equipment_service_cost"].choices = choices self.fields["quantity_by_day_planned"].initial = 1 self.fields["days_planned"].unit = "..." + self.fields["days_worked"].unit = "..." + + def save(self, commit=True): + item = super().save(commit=True) + if not item: + return + for child in item.equipment_service_cost.equipment_service_type.children.all(): + if not self._meta.model.objects.filter( + file_id=item.file_id, equipment_service_cost=child).count(): + self._meta.model.objects.create( + file_id=item.file_id, equipment_service_cost=child, + quantity_by_day_planned=0 + ) + return item class PreventiveFileEquipmentServiceBaseFormSet(FileBaseFormset): model = models.PreventiveFileEquipmentServiceCost + def get_base_queryset(self): + queryset = super( + PreventiveFileEquipmentServiceBaseFormSet, self + ).get_base_queryset() + queryset = queryset.filter( + equipment_service_cost__equipment_service_type__generic_equipment_type__txt_idx=self.type_filter + ) + return queryset + class AdministrativeActFileModifySelect(TableSelect): _model = AdministrativeAct diff --git a/archaeological_files/migrations/0106_auto_20210720_1203.py b/archaeological_files/migrations/0106_auto_20210726_1230.py index 86ff78ec6..5f3163cf9 100644 --- a/archaeological_files/migrations/0106_auto_20210720_1203.py +++ b/archaeological_files/migrations/0106_auto_20210726_1230.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.27 on 2021-07-20 12:03 +# Generated by Django 1.11.28 on 2021-07-26 12:30 from __future__ import unicode_literals import django.core.validators @@ -20,6 +20,7 @@ class Migration(migrations.Migration): name='EquipmentServiceCost', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', max_length=300, unique=True, verbose_name='Textual ID')), ('service_provider', models.CharField(blank=True, default='', max_length=200, verbose_name='Service provider')), ('flat_rate', models.BooleanField(default=False, verbose_name='Flat rate')), ('unitary_cost', models.FloatField(blank=True, null=True, verbose_name='Unitary cost')), @@ -232,13 +233,13 @@ class Migration(migrations.Migration): field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.GenericEquipmentServiceType', verbose_name='Generic type'), ), migrations.AddField( - model_name='equipmentservicetype', - name='parent', - field=models.ForeignKey(blank=True, help_text='Auto-add this cost when a parent is added', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='archaeological_files.EquipmentServiceType', verbose_name='Parent'), - ), - migrations.AddField( model_name='equipmentservicecost', name='equipment_service_type', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentServiceType', verbose_name='Equipment/Service'), ), + migrations.AddField( + model_name='equipmentservicecost', + name='parent', + field=models.ForeignKey(blank=True, help_text='Auto-add this cost when a parent is added', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='archaeological_files.EquipmentServiceType', verbose_name='Parent'), + ), ] diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 334e8faa4..ab84be8cb 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -104,15 +104,6 @@ class EquipmentServiceType(GeneralType): generic_equipment_type = models.ForeignKey( GenericEquipmentServiceType, verbose_name=_("Generic type")) order = models.IntegerField(_("Order"), default=10) - parent = models.ForeignKey( - "self", - blank=True, - null=True, - on_delete=models.CASCADE, - verbose_name=_("Parent"), - help_text=_("Auto-add this cost when a parent is added"), - related_name="children" - ) class Meta: verbose_name = _("Equipment/service type") @@ -131,6 +122,16 @@ ES_UNITS = ( class EquipmentServiceCost(models.Model): equipment_service_type = models.ForeignKey( EquipmentServiceType, verbose_name=_("Equipment/Service")) + slug = models.SlugField( + _("Textual ID"), + unique=True, + max_length=300, + help_text=_( + "The slug is the standardized version of the name. It contains " + "only lowercase letters, numbers and hyphens. Each slug must " + "be unique." + ), + ) service_provider = models.CharField( _("Service provider"), max_length=200, blank=True, default="") flat_rate = models.BooleanField(_("Flat rate"), default=False) @@ -141,6 +142,15 @@ class EquipmentServiceCost(models.Model): max_length=200, default="") order = models.IntegerField(_("Order"), default=10) available = models.BooleanField(_("Available"), default=True) + parent = models.ForeignKey( + EquipmentServiceType, + blank=True, + null=True, + on_delete=models.CASCADE, + verbose_name=_("Parent"), + help_text=_("Auto-add this cost when a parent is added"), + related_name="children" + ) class Meta: verbose_name = _("Equipment/service cost") @@ -148,7 +158,10 @@ class EquipmentServiceCost(models.Model): ordering = ("order", "equipment_service_type__label",) def __str__(self): - lbl = str(self.equipment_service_type) + lbl = "" + if self.parent: + lbl = self.parent.label + " - " + lbl += str(self.equipment_service_type) if self.specificity: lbl += " - " + self.specificity if self.service_provider: diff --git a/archaeological_files/templates/ishtar/forms/preventive_detail.html b/archaeological_files/templates/ishtar/forms/preventive_detail.html index 5b6f7f9ed..cc27e26f3 100644 --- a/archaeological_files/templates/ishtar/forms/preventive_detail.html +++ b/archaeological_files/templates/ishtar/forms/preventive_detail.html @@ -16,10 +16,10 @@ </label> </div> </div> - <div class="w-100 pb-3 text-center"> + <div class="w-100 pb-3 text-center form-group"> <button class="btn btn-secondary btn-sm form-planned" type="button">{% trans "Add default costs" %}</button> </div> - <div class="w-100 pb-3 text-center"> + <div class="w-100 pb-3 text-center form-group"> <button class="btn btn-secondary btn-sm form-worked" type="button">{% trans "Copy planned costs" %}</button> </div> {% for inline in inline_forms %} @@ -34,18 +34,21 @@ {% block end_js %} {{block.super}} var check_planned_value = function() { - if (this.value === 'false'){ - $(".form-planned").parent().hide() - $(".form-worked").parent().show() + if ($('#planned-toggle-true').is(":checked") === false){ + $(".form-planned").closest("div.form-group").hide() + $(".form-worked").closest("div.form-group").show() } else { - $(".form-planned").parent().show() - $(".form-worked").parent().hide() + $(".form-planned").closest("div.form-group").show() + $(".form-worked").closest("div.form-group").hide() } }; const cost_units = {{% for unit_pk, value in form_unities %}{% if forloop.counter0 %},{% endif %} {{unit_pk}}: "{{value}}"{% endfor %} }; + const flat_rates = [{% for pk in form_flat_rates %}{% if forloop.counter0 %},{% endif %} + "{{pk}}"{% endfor %} + ]; var update_units = function() { $(".form-cost").each(function(){ var unit = cost_units[$(this).val()]; @@ -53,6 +56,12 @@ unit = "..."; } $(this).parent().parent().find(".unit-label").html(unit); + if (flat_rates.indexOf($(this).val()) != -1){ + $(this).parent().parent().find(".unit-form").prop("disabled", true); + } else { + $(this).parent().parent().find(".unit-form").prop("disabled", false); + } + check_planned_value(); }); }; $(document).ready(function(){ diff --git a/archaeological_files/views.py b/archaeological_files/views.py index 90d41c68b..805921016 100644 --- a/archaeological_files/views.py +++ b/archaeological_files/views.py @@ -389,14 +389,11 @@ class MixFormFormsetUpdateView(UpdateView): def form_valid(self, form, inline_forms): self.object = form.save() - print("OK") for inline in inline_forms: inline.save() - pass return HttpResponseRedirect(self.get_success_url()) def form_invalid(self, form, inline_forms): - print("NOP") return self.render_to_response( self.get_context_data(form=form, inlines=inline_forms) ) @@ -436,6 +433,7 @@ class PreventiveEditView(IshtarMixin, LoginRequiredMixin, MixFormFormsetUpdateVi formset.form_label = str(inline_type) formset.form_slug = inline_type.txt_idx formset.dynamic_add = True + formset.type_filter = inline_type.txt_idx inlines.append(formset) return inlines @@ -468,8 +466,13 @@ class PreventiveEditView(IshtarMixin, LoginRequiredMixin, MixFormFormsetUpdateVi def get_context_data(self, **kwargs): context = super(PreventiveEditView, self).get_context_data(**kwargs) unities = {} + flat_rates = set() for inline_formset in context["inline_forms"]: if inline_formset.forms and hasattr(inline_formset.forms[0], "unities"): unities.update(inline_formset.forms[0].unities) + if inline_formset.forms and hasattr(inline_formset.forms[0], "flat_rates"): + for form in inline_formset.forms: + flat_rates.update(form.flat_rates) context["form_unities"] = unities.items() + context["form_flat_rates"] = flat_rates return context diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index bbd61f14b..b21503c35 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -46,6 +46,7 @@ from django.contrib.gis.geos import GEOSGeometry, MultiPolygon from django.contrib.gis.gdal.error import GDALException from django.contrib.gis.geos.error import GEOSException from django.core.cache import cache +from django.core.exceptions import FieldError from django.core.serializers import serialize from django.core.urlresolvers import reverse from django.db.models.fields import ( @@ -575,11 +576,14 @@ class ImportActionAdmin(admin.ModelAdmin): elif isinstance(field, ForeignKey): if value: model = field.rel.to - try: - value = model.objects.get(**{slug_col: value}) - except model.DoesNotExist: - missing_parent.append(row.pop(k)) - continue + for slug_col2 in self.import_keys: + try: + value = model.objects.get(**{slug_col2: value}) + except FieldError: + continue + except model.DoesNotExist: + missing_parent.append(row.pop(k)) + break else: value = None row[k] = value |