diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-07-09 19:20:12 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-07-29 11:56:34 +0200 |
commit | cc6d9d22d724fb39a5b7d30255e6bab2adc78763 (patch) | |
tree | 8f2cb2984a7ececc7f683df42c02daa689f6644b | |
parent | 2836cae57302f572bf3993a53fc61d4ffe56f16e (diff) | |
download | Ishtar-cc6d9d22d724fb39a5b7d30255e6bab2adc78763.tar.bz2 Ishtar-cc6d9d22d724fb39a5b7d30255e6bab2adc78763.zip |
Preventive file: work on inlines
-rw-r--r-- | archaeological_files/admin.py | 26 | ||||
-rw-r--r-- | archaeological_files/forms.py | 98 | ||||
-rw-r--r-- | archaeological_files/migrations/0106_auto_20210720_1203.py (renamed from archaeological_files/migrations/0106_auto_20210708_1044.py) | 121 | ||||
-rw-r--r-- | archaeological_files/models.py | 110 | ||||
-rw-r--r-- | archaeological_files/templates/ishtar/forms/preventive_detail.html | 30 | ||||
-rw-r--r-- | archaeological_files/views.py | 64 | ||||
-rw-r--r-- | ishtar_common/templates/blocks/bs_field_snippet.html | 17 |
7 files changed, 243 insertions, 223 deletions
diff --git a/archaeological_files/admin.py b/archaeological_files/admin.py index 9ba3d17d5..6ac40a69b 100644 --- a/archaeological_files/admin.py +++ b/archaeological_files/admin.py @@ -20,6 +20,7 @@ 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 @@ -65,13 +66,30 @@ admin_site.register(models.File, FileAdmin) general_models = [ - models.FileType, models.PermitType, models.Job, models.GenericEquipmentType, - models.EquipmentType, models.TechnicalService, + models.FileType, models.PermitType, models.Job, + models.GenericEquipmentServiceType, ] if settings.COUNTRY == "fr": general_models.append(models.SaisineType) for model in general_models: admin_site.register(model, GeneralTypeAdmin) -admin_site.register(models.EquipmentCost) -admin_site.register(models.TechnicalServiceCost) + +class EquipmentServiceTypeAdmin(GeneralTypeAdmin): + list_filter = ("available", 'generic_equipment_type') + + +admin_site.register(models.EquipmentServiceType, EquipmentServiceTypeAdmin) + + +class EquipmentServiceCostAdmin(admin.ModelAdmin): + search_fields = ( + "equipment_service_type__label", + "service_provider", + ) + list_filter = ("available",) + list_display = ["equipment_service_type", "specificity", "unitary_cost", "unit", + "flat_rate", "available"] + + +admin_site.register(models.EquipmentServiceCost, EquipmentServiceCostAdmin) diff --git a/archaeological_files/forms.py b/archaeological_files/forms.py index 8b5b3d00d..3c113c665 100644 --- a/archaeological_files/forms.py +++ b/archaeological_files/forms.py @@ -26,7 +26,7 @@ import datetime from django import forms from django.conf import settings from django.core import validators -from django.forms.formsets import formset_factory, BaseFormSet +from django.forms.formsets import formset_factory from django.utils.functional import lazy from django.utils.safestring import mark_safe from ishtar_common.utils import ugettext_lazy as _ @@ -592,6 +592,12 @@ class FileBaseFormset(forms.BaseModelFormSet): if self.instance: self.queryset = self.model.objects.filter(file_id=self.instance.pk) + def get_form_kwargs(self, index): + kwargs = super(FileBaseFormset, self).get_form_kwargs(index) + if self.instance: + kwargs['file_id'] = self.instance.pk + return kwargs + INLINE_JOB_FIELDS = ["man_by_day_planned", "days_planned", "man_by_day_worked", "days_worked"] @@ -607,12 +613,25 @@ JOB_LABELS = { "days_planned": _("Days"), "man_by_day_worked": _("Man by day"), "days_worked": _("Days"), + "Job": _("Job") } -class PreventiveFileJobForm(forms.ModelForm): +class PreventiveFileForm(forms.ModelForm): file_id = forms.IntegerField(widget=forms.HiddenInput) + def __init__(self, *args, **kwargs): + self.file_id = kwargs.pop("file_id") + super().__init__(*args, **kwargs) + self.fields["file_id"].initial = self.file_id + + def save(self, commit=True): + if not self.errors: + self.instance.file_id = self.file_id + return super().save(commit=commit) + + +class PreventiveFileJobForm(PreventiveFileForm): class Meta: model = models.PreventiveFileJob fields = ["job"] + INLINE_JOB_FIELDS @@ -633,9 +652,7 @@ PreventiveFileJobFormSet.form_slug = "preventive-030-post-excavation" PreventiveFileJobFormSet.dynamic_add = True -class PreventiveFileGroundJobForm(forms.ModelForm): - file_id = forms.IntegerField(widget=forms.HiddenInput) - +class PreventiveFileGroundJobForm(PreventiveFileForm): class Meta: model = models.PreventiveFileGroundJob fields = ["job"] + INLINE_JOB_FIELDS @@ -663,67 +680,54 @@ COST_LABELS = { "days_planned": _("Days / weeks / months"), "quantity_by_day_worked": _("Quantity"), "days_worked": _("Days / weeks / months"), + "equipment_service_cost": _("Equipment / service") } INLINE_COST_FIELDS = [ "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"}), + "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_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"}), + "equipment_service_cost": forms.Select( + attrs={"class":"form-cost", "bs_col_width": "col-12"}) } -class PreventiveFileEquipmentForm(forms.ModelForm): - file_id = forms.IntegerField(widget=forms.HiddenInput) - - class Meta: - model = models.PreventiveFileEquipmentCost - fields = ["equipment_cost"] + INLINE_COST_FIELDS - widgets = COST_WIDGETS - labels = COST_LABELS - - -class PreventiveFileEquipmentBaseFormSet(FileBaseFormset): - model = models.PreventiveFileEquipmentCost - - -PreventiveFileEquipmentFormSet = formset_factory( - PreventiveFileEquipmentForm, formset=PreventiveFileEquipmentBaseFormSet, - can_delete=True -) -PreventiveFileEquipmentFormSet.form_label = _("Equipment") -PreventiveFileEquipmentFormSet.form_admin_name = _("Preventive file - 050 - Equipments") -PreventiveFileEquipmentFormSet.form_slug = "preventive-050-equipments" -PreventiveFileEquipmentFormSet.dynamic_add = True - - -class PreventiveFileTechnicalServiceForm(forms.ModelForm): - file_id = forms.IntegerField(widget=forms.HiddenInput) +class PreventiveFileEquipmentServiceForm(PreventiveFileForm): + type_filter = None class Meta: - model = models.PreventiveFileTechnicalServiceCost - fields = ["technical_service_cost"] + INLINE_COST_FIELDS + model = models.PreventiveFileEquipmentServiceCost + fields = ["equipment_service_cost"] + INLINE_COST_FIELDS widgets = COST_WIDGETS labels = COST_LABELS + def __init__(self, *args, **kwargs): + 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 + ) -class PreventiveFileTechnicalServiceBaseFormSet(FileBaseFormset): - model = models.PreventiveFileTechnicalServiceCost + self.unities = {} + unit_dict = dict(models.ES_UNITS) + choices = [("", "--")] + for cost in q.all(): + choices.append((cost.pk, str(cost))) + if cost.unit: + self.unities[cost.pk] = unit_dict[cost.unit] + self.fields["equipment_service_cost"].choices = choices + self.fields["quantity_by_day_planned"].initial = 1 + self.fields["days_planned"].unit = "..." -PreventiveFileTechnicalServiceFormSet = formset_factory( - PreventiveFileTechnicalServiceForm, - formset=PreventiveFileTechnicalServiceBaseFormSet, can_delete=True -) -PreventiveFileTechnicalServiceFormSet.form_label = _("Technical Services") -PreventiveFileTechnicalServiceFormSet.form_admin_name = _( - "Preventive file - 060 - TechnicalServices" -) -PreventiveFileTechnicalServiceFormSet.form_slug = "preventive-060-technical_services" -PreventiveFileTechnicalServiceFormSet.dynamic_add = True +class PreventiveFileEquipmentServiceBaseFormSet(FileBaseFormset): + model = models.PreventiveFileEquipmentServiceCost class AdministrativeActFileModifySelect(TableSelect): diff --git a/archaeological_files/migrations/0106_auto_20210708_1044.py b/archaeological_files/migrations/0106_auto_20210720_1203.py index c09f9338f..86ff78ec6 100644 --- a/archaeological_files/migrations/0106_auto_20210708_1044.py +++ b/archaeological_files/migrations/0106_auto_20210720_1203.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.28 on 2021-07-08 10:44 +# Generated by Django 1.11.27 on 2021-07-20 12:03 from __future__ import unicode_literals import django.core.validators @@ -17,53 +17,54 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='EquipmentCost', + name='EquipmentServiceCost', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('service_provider', models.CharField(default='-', max_length=200, verbose_name='Service provider')), + ('service_provider', models.CharField(blank=True, default='', max_length=200, verbose_name='Service provider')), ('flat_rate', models.BooleanField(default=False, verbose_name='Flat rate')), - ('daily_cost', models.FloatField(blank=True, null=True, verbose_name='Daily cost')), - ('monday', models.BooleanField(default=True, verbose_name='Monday')), - ('tuesday', models.BooleanField(default=True, verbose_name='Tuesday')), - ('wednesday', models.BooleanField(default=True, verbose_name='Wednesday')), - ('thursday', models.BooleanField(default=True, verbose_name='Thursday')), - ('friday', models.BooleanField(default=True, verbose_name='Friday')), + ('unitary_cost', models.FloatField(blank=True, null=True, verbose_name='Unitary cost')), + ('unit', models.CharField(blank=True, choices=[('D', 'days'), ('W', 'weeks'), ('M', 'months'), ('L', 'linear meter')], max_length=1, null=True, verbose_name='Unit')), + ('specificity', models.CharField(blank=True, default='', max_length=200, verbose_name='Specificity')), ('order', models.IntegerField(default=10, verbose_name='Order')), + ('available', models.BooleanField(default=True, verbose_name='Available')), ], options={ - 'verbose_name': 'Equipment cost', - 'verbose_name_plural': 'Equipment costs', + 'verbose_name': 'Equipment/service cost', + 'verbose_name_plural': 'Equipment/service costs', + 'ordering': ('order', 'equipment_service_type__label'), }, ), migrations.CreateModel( - name='EquipmentType', + name='EquipmentServiceType', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('label', models.TextField(verbose_name='Label')), ('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', 32), "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')), + ('order', models.IntegerField(default=10, verbose_name='Order')), ], options={ - 'verbose_name': 'Equipment type', - 'verbose_name_plural': 'Equipment types', - 'ordering': ('label',), + 'verbose_name': 'Equipment/service type', + 'verbose_name_plural': 'Equipment/service types', + 'ordering': ('order', 'label'), }, bases=(ishtar_common.models_common.Cached, models.Model), ), migrations.CreateModel( - name='GenericEquipmentType', + name='GenericEquipmentServiceType', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('label', models.TextField(verbose_name='Label')), ('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', 32), "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')), + ('order', models.IntegerField(default=10, verbose_name='Order')), ], options={ 'verbose_name': 'Generic equipment type', 'verbose_name_plural': 'Generic equipment types', - 'ordering': ('label',), + 'ordering': ('order', 'label'), }, bases=(ishtar_common.models_common.Cached, models.Model), ), @@ -86,22 +87,22 @@ class Migration(migrations.Migration): options={ 'verbose_name': 'Job', 'verbose_name_plural': 'Jobs', - 'ordering': ('label',), + 'ordering': ('order', 'label'), }, bases=(ishtar_common.models_common.Cached, models.Model), ), migrations.CreateModel( - name='PreventiveFileEquipmentCost', + name='PreventiveFileEquipmentServiceCost', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('quantity_by_day_planned', models.FloatField(blank=True, null=True, verbose_name='Quantity by day - planned')), ('days_planned', models.FloatField(blank=True, null=True, verbose_name='Days - planned')), ('quantity_by_day_worked', models.FloatField(blank=True, null=True, verbose_name='Quantity by day - worked')), ('days_worked', models.FloatField(blank=True, null=True, verbose_name='Days - worked')), - ('equipment_cost', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentCost')), + ('equipment_service_cost', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentServiceCost')), ], options={ - 'abstract': False, + 'ordering': ('equipment_service_cost',), }, ), migrations.CreateModel( @@ -114,7 +115,7 @@ class Migration(migrations.Migration): ('days_worked', models.FloatField(blank=True, null=True, verbose_name='Days - worked')), ], options={ - 'abstract': False, + 'ordering': ('job',), }, ), migrations.CreateModel( @@ -127,53 +128,7 @@ class Migration(migrations.Migration): ('days_worked', models.FloatField(blank=True, null=True, verbose_name='Days - worked')), ], options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='PreventiveFileTechnicalServiceCost', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity_by_day_planned', models.FloatField(blank=True, null=True, verbose_name='Quantity by day - planned')), - ('days_planned', models.FloatField(blank=True, null=True, verbose_name='Days - planned')), - ('quantity_by_day_worked', models.FloatField(blank=True, null=True, verbose_name='Quantity by day - worked')), - ('days_worked', models.FloatField(blank=True, null=True, verbose_name='Days - worked')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='TechnicalService', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('label', models.TextField(verbose_name='Label')), - ('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', 32), "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')), - ], - options={ - 'verbose_name': 'Technical service', - 'verbose_name_plural': 'Technical services', - 'ordering': ('label',), - }, - bases=(ishtar_common.models_common.Cached, models.Model), - ), - migrations.CreateModel( - name='TechnicalServiceCost', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('service_provider', models.CharField(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')), - ('unit', models.CharField(blank=True, choices=[('D', 'Days'), ('M', 'Linear meter')], max_length=1, null=True, verbose_name='Unit')), - ('order', models.IntegerField(default=10, verbose_name='Order')), - ('parents', models.ManyToManyField(blank=True, help_text='Auto-add this cost when a parent is added', related_name='_technicalservicecost_parents_+', to='archaeological_files.TechnicalServiceCost', verbose_name='Parents')), - ('technical_service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.TechnicalService', verbose_name='Technical service')), - ], - options={ - 'verbose_name': 'Technical service cost', - 'verbose_name_plural': 'Technical service costs', + 'ordering': ('job',), }, ), migrations.AddField( @@ -247,16 +202,6 @@ class Migration(migrations.Migration): field=models.DateField(blank=True, null=True, verbose_name='End date'), ), migrations.AddField( - model_name='preventivefiletechnicalservicecost', - name='file', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='technical_service_costs', to='archaeological_files.File'), - ), - migrations.AddField( - model_name='preventivefiletechnicalservicecost', - name='technical_service_cost', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.TechnicalServiceCost'), - ), - migrations.AddField( model_name='preventivefilejob', name='file', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='jobs', to='archaeological_files.File'), @@ -277,23 +222,23 @@ class Migration(migrations.Migration): field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.Job'), ), migrations.AddField( - model_name='preventivefileequipmentcost', + model_name='preventivefileequipmentservicecost', name='file', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='equipment_costs', to='archaeological_files.File'), ), migrations.AddField( - model_name='equipmenttype', + model_name='equipmentservicetype', name='generic_equipment_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.GenericEquipmentType', verbose_name='Generic type'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.GenericEquipmentServiceType', verbose_name='Generic type'), ), migrations.AddField( - model_name='equipmentcost', - name='equipment_type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='archaeological_files.EquipmentType', verbose_name='Equipment'), + 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='equipmentcost', - name='parents', - field=models.ManyToManyField(blank=True, help_text='Auto-add this cost when a parent is added', related_name='_equipmentcost_parents_+', to='archaeological_files.EquipmentCost', verbose_name='Parents'), + 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'), ), ] diff --git a/archaeological_files/models.py b/archaeological_files/models.py index 87abd4083..334e8faa4 100644 --- a/archaeological_files/models.py +++ b/archaeological_files/models.py @@ -88,84 +88,72 @@ class Job(GeneralType): class Meta: verbose_name = _("Job") verbose_name_plural = _("Jobs") - ordering = ("label",) + ordering = ("order", "label",) + +class GenericEquipmentServiceType(GeneralType): + order = models.IntegerField(_("Order"), default=10) -class GenericEquipmentType(GeneralType): class Meta: verbose_name = _("Generic equipment type") verbose_name_plural = _("Generic equipment types") - ordering = ("label",) + ordering = ("order", "label",) -class EquipmentType(GeneralType): +class EquipmentServiceType(GeneralType): generic_equipment_type = models.ForeignKey( - GenericEquipmentType, verbose_name=_("Generic type")) - - class Meta: - verbose_name = _("Equipment type") - verbose_name_plural = _("Equipment types") - ordering = ("label",) - - -class EquipmentCost(models.Model): - service_provider = models.CharField(_("Service provider"), max_length=200, - default="-") - equipment_type = models.ForeignKey( - EquipmentType, verbose_name=_("Equipment")) - flat_rate = models.BooleanField(_("Flat rate"), default=False) - daily_cost = models.FloatField(_("Daily cost"), blank=True, null=True) - monday = models.BooleanField(_("Monday"), default=True) - tuesday = models.BooleanField(_("Tuesday"), default=True) - wednesday = models.BooleanField(_("Wednesday"), default=True) - thursday = models.BooleanField(_("Thursday"), default=True) - friday = models.BooleanField(_("Friday"), default=True) + GenericEquipmentServiceType, verbose_name=_("Generic type")) order = models.IntegerField(_("Order"), default=10) - parents = models.ManyToManyField( + parent = models.ForeignKey( "self", blank=True, - verbose_name=_("Parents"), - help_text=_("Auto-add this cost when a parent is added") + 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 cost") - verbose_name_plural = _("Equipment costs") - - -class TechnicalService(GeneralType): - class Meta: - verbose_name = _("Technical service") - verbose_name_plural = _("Technical services") - ordering = ("label",) + verbose_name = _("Equipment/service type") + verbose_name_plural = _("Equipment/service types") + ordering = ("order", "label",) -TECH_UNITS = ( - ("D", _("Days")), - ("M", _("Linear meter")), +ES_UNITS = ( + ("D", _("days")), + ("W", _("weeks")), + ("M", _("months")), + ("L", _("linear meter")), ) -class TechnicalServiceCost(models.Model): - service_provider = models.CharField(_("Service provider"), max_length=200, - default="-") - technical_service = models.ForeignKey( - TechnicalService, verbose_name=_("Technical service")) +class EquipmentServiceCost(models.Model): + equipment_service_type = models.ForeignKey( + EquipmentServiceType, verbose_name=_("Equipment/Service")) + service_provider = models.CharField( + _("Service provider"), max_length=200, blank=True, default="") flat_rate = models.BooleanField(_("Flat rate"), default=False) unitary_cost = models.FloatField(_("Unitary cost"), blank=True, null=True) - unit = models.CharField(_("Unit"), max_length=1, choices=TECH_UNITS, + unit = models.CharField(_("Unit"), max_length=1, choices=ES_UNITS, blank=True, null=True) + specificity = models.CharField(_("Specificity"), blank=True, + max_length=200, default="") order = models.IntegerField(_("Order"), default=10) - parents = models.ManyToManyField( - "self", - blank=True, - verbose_name=_("Parents"), - help_text=_("Auto-add this cost when a parent is added") - ) + available = models.BooleanField(_("Available"), default=True) class Meta: - verbose_name = _("Technical service cost") - verbose_name_plural = _("Technical service costs") + verbose_name = _("Equipment/service cost") + verbose_name_plural = _("Equipment/service costs") + ordering = ("order", "equipment_service_type__label",) + + def __str__(self): + lbl = str(self.equipment_service_type) + if self.specificity: + lbl += " - " + self.specificity + if self.service_provider: + lbl += f" ({self.service_provider})" + return lbl class FileType(GeneralType): @@ -1205,11 +1193,17 @@ class PreventiveFileGroundJob(ManDays): file = models.ForeignKey(File, related_name="ground_jobs") job = models.ForeignKey(Job) + class Meta: + ordering = ("job",) + class PreventiveFileJob(ManDays): file = models.ForeignKey(File, related_name="jobs") job = models.ForeignKey(Job) + class Meta: + ordering = ("job",) + class TechDays(models.Model): quantity_by_day_planned = models.FloatField( @@ -1237,11 +1231,9 @@ class TechDays(models.Model): return self.days_worked * self.quantity_by_day_worked -class PreventiveFileEquipmentCost(TechDays): +class PreventiveFileEquipmentServiceCost(TechDays): file = models.ForeignKey(File, related_name="equipment_costs") - equipment_cost = models.ForeignKey(EquipmentCost) + equipment_service_cost = models.ForeignKey(EquipmentServiceCost) - -class PreventiveFileTechnicalServiceCost(TechDays): - file = models.ForeignKey(File, related_name="technical_service_costs") - technical_service_cost = models.ForeignKey(TechnicalServiceCost) + class Meta: + ordering = ("equipment_service_cost",) diff --git a/archaeological_files/templates/ishtar/forms/preventive_detail.html b/archaeological_files/templates/ishtar/forms/preventive_detail.html index 20adcccfb..5b6f7f9ed 100644 --- a/archaeological_files/templates/ishtar/forms/preventive_detail.html +++ b/archaeological_files/templates/ishtar/forms/preventive_detail.html @@ -17,12 +17,15 @@ </div> </div> <div class="w-100 pb-3 text-center"> - <button class="btn btn-secondary btn-sm form-planned" type="button">{% trans "Add default costs" %}</button> + <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"> <button class="btn btn-secondary btn-sm form-worked" type="button">{% trans "Copy planned costs" %}</button> </div> - {{block.super}} + {% for inline in inline_forms %} + <h4>{{inline.form_label}}</h4> + {% bs_formset inline 0 True %} + {% endfor %} </div> </div> {% endblock %} @@ -31,7 +34,7 @@ {% block end_js %} {{block.super}} var check_planned_value = function() { - if (this.value == 'false'){ + if (this.value === 'false'){ $(".form-planned").parent().hide() $(".form-worked").parent().show() } else { @@ -39,7 +42,24 @@ $(".form-worked").parent().hide() } }; - $('input[type=radio][name=planned-toggle]').change(check_planned_value); - $(document).ready(check_planned_value); + + const cost_units = {{% for unit_pk, value in form_unities %}{% if forloop.counter0 %},{% endif %} + {{unit_pk}}: "{{value}}"{% endfor %} + }; + var update_units = function() { + $(".form-cost").each(function(){ + var unit = cost_units[$(this).val()]; + if (!unit){ + unit = "..."; + } + $(this).parent().parent().find(".unit-label").html(unit); + }); + }; + $(document).ready(function(){ + $('input[type=radio][name=planned-toggle]').change(check_planned_value); + $(".form-cost").change(update_units); + check_planned_value(); + update_units(); + }); {% endblock %} {# </script> #} diff --git a/archaeological_files/views.py b/archaeological_files/views.py index 366ef3754..90d41c68b 100644 --- a/archaeological_files/views.py +++ b/archaeological_files/views.py @@ -23,6 +23,7 @@ import re from django.core.urlresolvers import reverse from django.db.models import Q from django.http import HttpResponse, Http404, HttpResponseRedirect +from django.forms.formsets import formset_factory from django.views.generic.edit import UpdateView from django.shortcuts import redirect, render from ishtar_common.utils import ugettext_lazy as _ @@ -361,37 +362,41 @@ def reset_wizards(request): class MixFormFormsetUpdateView(UpdateView): form_inlines_class = [] + def get_form_inlines_class(self): + return self.form_inlines_class + def get(self, request, *args, **kwargs): self.object = self.get_object() self.inline_forms = [ inline(instance=self.object, prefix=getattr(inline, "form_slug", "form")) - for inline in self.form_inlines_class + for inline in self.get_form_inlines_class() ] return super(MixFormFormsetUpdateView, self).get(request, *args, **kwargs) def post(self, request, *args, **kwargs): self.object = self.get_object() - attrs = {"instance": self.object} form = self.form_class(data=request.POST, instance=self.object) - inline_forms = [ + self.inline_forms = [ inline(instance=self.object, data=request.POST, prefix=getattr(inline, "form_slug", "form")) - for inline in self.form_inlines_class + for inline in self.get_form_inlines_class() ] - if form.is_valid() and all((inline.is_valid() for inline in inline_forms)): - return self.form_valid(form, inline_forms) + if form.is_valid() and all((inline.is_valid() for inline in self.inline_forms)): + return self.form_valid(form, self.inline_forms) else: - return self.form_invalid(form, inline_forms) + return self.form_invalid(form, self.inline_forms) def form_valid(self, form, inline_forms): self.object = form.save() + print("OK") for inline in inline_forms: - # save inlines... + 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) ) @@ -405,15 +410,35 @@ class MixFormFormsetUpdateView(UpdateView): class PreventiveEditView(IshtarMixin, LoginRequiredMixin, MixFormFormsetUpdateView): page_name = _("Preventive modification") form_class = forms.FileFormPreventiveDetail - form_inlines_class = [ - forms.PreventiveFileJobFormSet, - forms.PreventiveFileGroundJobFormSet, - forms.PreventiveFileEquipmentFormSet, - forms.PreventiveFileTechnicalServiceFormSet - ] template_name = "ishtar/forms/preventive_detail.html" model = models.File + def get_form_inlines_class(self): + inlines = [ + forms.PreventiveFileGroundJobFormSet, + forms.PreventiveFileJobFormSet, + ] + self.inline_types = list( + models.GenericEquipmentServiceType.objects.filter(available=True).all()) + + for inline_type in self.inline_types: + form_class_name = "".join([ + t.capitalize() + for t in inline_type.txt_idx.replace( + '-', ' ').replace('_', ' ').split(" ")]) + "Formset" + form = type(form_class_name, + (forms.PreventiveFileEquipmentServiceForm,), + {"type_filter": inline_type.txt_idx}) + formset = formset_factory( + form, formset=forms.PreventiveFileEquipmentServiceBaseFormSet, + can_delete=True + ) + formset.form_label = str(inline_type) + formset.form_slug = inline_type.txt_idx + formset.dynamic_add = True + inlines.append(formset) + return inlines + def get_success_url(self): return reverse("file_modification") + "?open_item={}".format(self.object.pk) @@ -438,4 +463,13 @@ class PreventiveEditView(IshtarMixin, LoginRequiredMixin, MixFormFormsetUpdateVi kwargs["initial"] = initial kwargs["user"] = self.request.user self.file = file - return kwargs
\ No newline at end of file + return kwargs + + def get_context_data(self, **kwargs): + context = super(PreventiveEditView, self).get_context_data(**kwargs) + unities = {} + 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) + context["form_unities"] = unities.items() + return context diff --git a/ishtar_common/templates/blocks/bs_field_snippet.html b/ishtar_common/templates/blocks/bs_field_snippet.html index beecf624f..86ec89e84 100644 --- a/ishtar_common/templates/blocks/bs_field_snippet.html +++ b/ishtar_common/templates/blocks/bs_field_snippet.html @@ -1,5 +1,5 @@ {% load i18n %} - <div id="main_div-{{field.auto_id}}" class="form-group{% if not field.label %} no-label{% endif %} {% if bs_col_width %}{{bs_col_width}}{% elif field.field.widget.attrs.bs_col_width %}{{field.field.widget.attrs.bs_col_width}}{% else %}{% if field.field.widget.attrs.cols or force_large_col %}col-lg-12{% else %}col-lg-6{% endif %}{% endif %}{% if field.errors %} is-invalid{% endif %}{% if field.field.required %} required{% endif %}{% if force_large_col %} full-width{% endif %}" + <div id="main_div-{{field.auto_id}}" class="form-group{% if not field.label %} no-label{% endif %} {% if field.field.widget.attrs.bs_col_width %}{{field.field.widget.attrs.bs_col_width}}{% elif bs_col_width %}{{bs_col_width}}{% else %}{% if field.field.widget.attrs.cols or force_large_col %}col-lg-12{% else %}col-lg-6{% endif %}{% endif %}{% if field.errors %} is-invalid{% endif %}{% if field.field.required %} required{% endif %}{% if force_large_col %} full-width{% endif %}" data-alt-name="{{field.field.alt_name}}"> {% if field.label %}{{ field.label_tag }}{% endif %} {% if extra_field_label %}<label><em>{{extra_field_label}}</em></label>{% endif %} @@ -7,17 +7,24 @@ {% if field.field.order_number %}<span class="badge badge-pill badge-success field-tip"> {{field.field.order_number}} </span>{% endif %}{% endif %} - {% if field.help_text %} + {% if field.help_text or field.field.unit %} <div class="input-group"> {% endif %} {{field|safe}} - {% if field.help_text %} + {% if field.help_text or field.field.unit %} <span class="input-group-append"> + {% if field.field.unit %} + <a class="input-group-text unit-label"> + {{field.field.unit}} + </a> + {% endif %} + {% if field.help_text %} <a class="input-group-text" data-toggle="collapse" href="#{{field.auto_id}}_help" aria-expanded="false" aria-controls="{{field.auto_id}}_help"> <i class="fa fa-question-circle" aria-hidden="true"></i> </a> + {% endif %} </span> {% if field.errors %}<div class="invalid"> {{ field.errors }} @@ -25,11 +32,11 @@ </div> <div class="collapse" id="{{field.auto_id}}_help"> <div class="card card-body help-text"> - {{field.help_text}} + {{field.hep_text}} </div> </div> {% endif %} - {% if field.errors and not field.help_text %} + {% if field.errors and not field.help_text and not field.unit %} <div class="invalid"> {{ field.errors }} </div>{% endif %} |