summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commitcc6d9d22d724fb39a5b7d30255e6bab2adc78763 (patch)
tree8f2cb2984a7ececc7f683df42c02daa689f6644b
parent2836cae57302f572bf3993a53fc61d4ffe56f16e (diff)
downloadIshtar-cc6d9d22d724fb39a5b7d30255e6bab2adc78763.tar.bz2
Ishtar-cc6d9d22d724fb39a5b7d30255e6bab2adc78763.zip
Preventive file: work on inlines
-rw-r--r--archaeological_files/admin.py26
-rw-r--r--archaeological_files/forms.py98
-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.py110
-rw-r--r--archaeological_files/templates/ishtar/forms/preventive_detail.html30
-rw-r--r--archaeological_files/views.py64
-rw-r--r--ishtar_common/templates/blocks/bs_field_snippet.html17
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 %}