diff options
| author | Étienne Loks <etienne.loks@iggdrasil.net> | 2021-07-09 19:20:12 +0200 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2022-07-08 09:58:47 +0200 | 
| commit | c3d97de7bdec3f7899340c606f46276836eb2a8b (patch) | |
| tree | f65f72441311e7378b90674a31e3109034aca890 | |
| parent | 3142d9d08e88dfd58a02a10821bfd26b49a35f79 (diff) | |
| download | Ishtar-c3d97de7bdec3f7899340c606f46276836eb2a8b.tar.bz2 Ishtar-c3d97de7bdec3f7899340c606f46276836eb2a8b.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 %}  | 
