From 769137fabd25f038f924b56771ee129bf7b1425f Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 01:44:54 +0100 Subject: [PATCH 01/29] Started work on improving asset form update and validation - requires de-spaghettifying of asset_update.html before continuing --- assets/forms.py | 9 ++ assets/templates/asset_update.html | 89 +++++--------------- assets/templates/partials/asset_buttons.html | 8 +- assets/urls.py | 2 +- assets/views.py | 17 +++- 5 files changed, 50 insertions(+), 75 deletions(-) diff --git a/assets/forms.py b/assets/forms.py index c727a9d1..a7d4d052 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.core.exceptions import ValidationError from assets import models @@ -7,7 +8,15 @@ class AssetForm(forms.ModelForm): class Meta: model = models.Asset fields = '__all__' + + def __init__(self, *args, **kwargs): + super(AssetForm, self).__init__(*args, **kwargs) + self.fields['asset_id'].disabled = True #You should not be able to change the asset ID, either in update or create + def clean_date_sold(self): + if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]: + raise ValidationError("Cannot sell an item before it is acquired") + return self.cleaned_data["date_sold"] class SupplierForm(forms.ModelForm): class Meta: diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 6c3de1e0..3b906aad 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -19,7 +19,11 @@ {% endif %} - +{% if create %} +
+{% else %} + +{% endif %}
@@ -27,7 +31,6 @@
- {% csrf_token %}
@@ -76,7 +79,7 @@ {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %}
- +
{% render_field form.comments|add_class:'form-control' %} @@ -143,16 +146,20 @@ {% if object.date_acquired %} - {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=object.date_acquired|date %} + {% with date_acq=object.date_acquired|date:"Y-m-d" %} + {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} + {% endwith %} {% else %} - {% endif %}
- {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=object.date_sold|date %} + {% with date_sol=object.form.date_sold|date:"Y-m-d" %} + {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} + {% endwith %}
{% else %}
@@ -242,71 +249,15 @@ +
+
+ {% include 'partials/asset_buttons.html' %} +
+
-
-
- {% include 'partials/asset_buttons.html' %} -
-
+ {% include 'partials/confirm_delete.html' with object=object %} -{% endblock %} - -{% block js %} - - -{# #} - - - - -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/assets/templates/partials/asset_buttons.html b/assets/templates/partials/asset_buttons.html index d752e254..25eeac9c 100644 --- a/assets/templates/partials/asset_buttons.html +++ b/assets/templates/partials/asset_buttons.html @@ -1,16 +1,16 @@
{% if edit and object %} - + Duplicate Delete {% elif duplicate %} - + Cancel - {% elif not object %} + {% elif create %} - + {% else %} Edit diff --git a/assets/urls.py b/assets/urls.py index dcdf5569..900f4c33 100644 --- a/assets/urls.py +++ b/assets/urls.py @@ -10,7 +10,7 @@ urlpatterns = [ path('', views.AssetList.as_view(), name='index'), path('asset/list/', views.AssetList.as_view(), name='asset_list'), path('asset//', views.AssetDetail.as_view(), name='asset_detail'), - path('asset/create/', views.AssetEdit.as_view(), name='asset_create'), + path('asset/create/', views.AssetCreate.as_view(), name='asset_create'), path('asset//edit/', views.AssetEdit.as_view(), name='asset_update'), path('asset/delete/', views.asset_delete, name='ajax_asset_delete'), path('asset/update/', views.asset_update, name='ajax_asset_update'), diff --git a/assets/views.py b/assets/views.py index 62402a95..037937f4 100644 --- a/assets/views.py +++ b/assets/views.py @@ -53,8 +53,10 @@ class AssetDetail(LoginRequiredMixin, generic.DetailView): # template_name = 'asset_update.html' # # success_url = reverse_lazy('asset_list') -class AssetEdit(LoginRequiredMixin, generic.TemplateView): +class AssetEdit(LoginRequiredMixin, generic.UpdateView): template_name = 'asset_update.html' + model = models.Asset + form_class = forms.AssetForm def get_context_data(self, **kwargs): context = super(AssetEdit, self).get_context_data(**kwargs) @@ -75,6 +77,19 @@ class AssetEdit(LoginRequiredMixin, generic.TemplateView): return context + def get_success_url(self): + return reverse("asset_detail", kwargs={"pk":self.object.id}) + +class AssetCreate(LoginRequiredMixin, generic.CreateView): + template_name = 'asset_update.html' + model = models.Asset + form_class = forms.AssetForm + + def get_context_data(self, **kwargs): + context = super(AssetCreate, self).get_context_data(**kwargs) + context["create"] = True + return context + @login_required() def asset_update(request): context = dict() From a4adc8bc7b2fd4ee1c7951e0d502491e20251ea5 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 14:54:19 +0100 Subject: [PATCH 02/29] Now asset_ids are going to be editable, removed that limitation from form --- assets/forms.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/assets/forms.py b/assets/forms.py index a7d4d052..d1a24ef0 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -8,10 +8,6 @@ class AssetForm(forms.ModelForm): class Meta: model = models.Asset fields = '__all__' - - def __init__(self, *args, **kwargs): - super(AssetForm, self).__init__(*args, **kwargs) - self.fields['asset_id'].disabled = True #You should not be able to change the asset ID, either in update or create def clean_date_sold(self): if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]: From 3aa3498d3a19fe8a8ef96a366e86e3807046481f Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 14:59:15 +0100 Subject: [PATCH 03/29] Split out create and duplicate templates, got both working --- assets/templates/asset_create.html | 187 +++++++++++++++++++ assets/templates/partials/asset_buttons.html | 2 +- assets/urls.py | 2 +- assets/views.py | 70 +++---- 4 files changed, 215 insertions(+), 46 deletions(-) create mode 100644 assets/templates/asset_create.html diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html new file mode 100644 index 00000000..640008e3 --- /dev/null +++ b/assets/templates/asset_create.html @@ -0,0 +1,187 @@ +{% extends 'base_assets.html' %} +{% load widget_tweaks %} +{% load asset_templatetags %} +{% block title %}Asset {{ object.asset_id }}{% endblock %} + + +{% block content %} + + +{% if duplicate %} +
+{% else %} + +{% endif %} +
+
+
+ {% include 'partials/asset_buttons.html' %} +
+
+
+ {% csrf_token %} + +
+
+
+
+ Asset Details +
+
+
Asset ID + {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} +
+
+ + {% render_field form.description|add_class:'form-control' value=object.description %} +
+
+ + +
+
+ + +
+
+ + {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %} +
+ +
+ + {% render_field form.comments|add_class:'form-control' %} +
+
+
+
+
+
+
+
+
+ Purchase Details +
+
+
+ + +
+ +
+ +
+ £ + {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} +
+
+ +
+ +
+ £ + {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} +
+
+ +
+ + {% if object.date_acquired%} + {% with date_acq=object.date_acquired|date:"Y-m-d" %} + {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} + {% endwith %} + {% else %} + + {% endif %} +
+ +
+ + {% with date_sol=object.form.date_sold|date:"Y-m-d" %} + {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} + {% endwith %} +
+
+
+
+
+
+
+ Collection Details +
+
+
+ + +
+ + + + +
+
+ +
+ +
+ + + + +
+
+
+
+
+
+ +
+
+
+
+
+
+ {% include 'partials/asset_buttons.html' %} +
+
+
+
+ + +{% include 'partials/confirm_delete.html' with object=object %} + +{% endblock %} \ No newline at end of file diff --git a/assets/templates/partials/asset_buttons.html b/assets/templates/partials/asset_buttons.html index 25eeac9c..606a9bd7 100644 --- a/assets/templates/partials/asset_buttons.html +++ b/assets/templates/partials/asset_buttons.html @@ -14,7 +14,7 @@ {% else %} Edit - Duplicate + Duplicate Delete {% endif %} diff --git a/assets/urls.py b/assets/urls.py index 900f4c33..5970d64b 100644 --- a/assets/urls.py +++ b/assets/urls.py @@ -12,8 +12,8 @@ urlpatterns = [ path('asset//', views.AssetDetail.as_view(), name='asset_detail'), path('asset/create/', views.AssetCreate.as_view(), name='asset_create'), path('asset//edit/', views.AssetEdit.as_view(), name='asset_update'), + path('asset//duplicate/', views.AssetDuplicate.as_view(), name='asset_duplicate'), path('asset/delete/', views.asset_delete, name='ajax_asset_delete'), - path('asset/update/', views.asset_update, name='ajax_asset_update'), path('supplier/list', views.SupplierList.as_view(), name='supplier_list'), path('supplier/', views.SupplierDetail.as_view(), name='supplier_detail'), diff --git a/assets/views.py b/assets/views.py index 037937f4..ff2d0a22 100644 --- a/assets/views.py +++ b/assets/views.py @@ -65,67 +65,49 @@ class AssetEdit(LoginRequiredMixin, generic.UpdateView): context['form'] = forms.AssetForm # context['asset_names'] = models.Asset.objects.values_list('asset_id', 'description').order_by('-date_acquired')[] - if self.request.GET.get('duplicate'): - context['duplicate'] = True - context['previous_asset_id'] = context['object'].asset_id - context['previous_asset_pk'] = context['object'].pk - context['object'].pk = 0 - context['object'].asset_id = '' - context['object'].serial_number = '' - else: - context['edit'] = True + context['edit'] = True return context + def form_invalid(self, form): + print(form.errors) + return super().form_invalid(form) + def get_success_url(self): return reverse("asset_detail", kwargs={"pk":self.object.id}) class AssetCreate(LoginRequiredMixin, generic.CreateView): - template_name = 'asset_update.html' + template_name = 'asset_create.html' model = models.Asset form_class = forms.AssetForm def get_context_data(self, **kwargs): context = super(AssetCreate, self).get_context_data(**kwargs) + context["create"] = True return context + + def get_success_url(self): + return reverse("asset_detail", kwargs={"pk":self.object.id}) -@login_required() -def asset_update(request): - context = dict() +class DuplicateMixin: + def get(self, request, *args, **kwargs): + self.object = self.get_object() + self.object.pk = None + return self.render_to_response(self.get_context_data()) - if request.method == 'POST' and request.is_ajax(): - defaults = QueryDict(request.POST['form'].encode('ASCII')).dict() - defaults.pop('csrfmiddlewaretoken') - - asset_pk = int(defaults.pop('id')) - - if defaults['date_acquired']: - defaults['date_acquired'] = parser.parse(defaults.pop('date_acquired')) - else: - defaults['date_acquired'] = None - - if defaults['date_sold']: - defaults['date_sold'] = parser.parse(defaults.pop('date_sold')) - else: - defaults['date_sold'] = None - - # if defaults['parent']: - # defaults['parent'] = models.Asset.objects.get(asset_id=defaults.pop('parent')) - - form = forms.AssetForm(defaults) - context['valid'] = form.is_valid() - context['errors'] = form.errors.as_json() - - if asset_pk == 0: - asset = models.Asset.objects.create(**form.cleaned_data) - else: - asset, created = models.Asset.objects.update_or_create(pk=asset_pk, defaults=form.cleaned_data) - - context['url'] = reverse('asset_detail', args=[asset.pk]) - - return HttpResponse(json.dumps(context), content_type='application/json') +class AssetDuplicate(DuplicateMixin, AssetCreate): + template_name = 'asset_create.html' + model = models.Asset + form_class = forms.AssetForm + def get_context_data(self, **kwargs): + context = super(AssetCreate, self).get_context_data(**kwargs) + context["create"] = None + context["duplicate"] = True + context['previous_asset_id'] = self.get_object().asset_id + context["previous_asset_pk"] = pk = self.kwargs.get(self.pk_url_kwarg) + return context @login_required() def asset_delete(request): From 1d253aa45272984f5a78aa03486c8385d00a62d4 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 15:07:41 +0100 Subject: [PATCH 04/29] Fixed edit always being invalid due to always failing form validation --- assets/templates/asset_update.html | 2 +- assets/views.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 3b906aad..1a976646 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -46,7 +46,7 @@ {% if duplicate %} {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} {% elif object.asset_id %} - {% render_field form.asset_id|attr:'readonly disabled'|add_class:'disabled_input form-control' value=object.asset_id %} + {% render_field form.asset_id|attr:'readonly'|add_class:'disabled_input form-control' value=object.asset_id %} {% else %} {% render_field form.asset_id|add_class:'form-control' %} {% endif %} diff --git a/assets/views.py b/assets/views.py index ff2d0a22..064ccfc1 100644 --- a/assets/views.py +++ b/assets/views.py @@ -60,8 +60,6 @@ class AssetEdit(LoginRequiredMixin, generic.UpdateView): def get_context_data(self, **kwargs): context = super(AssetEdit, self).get_context_data(**kwargs) - if self.kwargs: - context['object'] = get_object_or_404(models.Asset, pk=self.kwargs['pk']) context['form'] = forms.AssetForm # context['asset_names'] = models.Asset.objects.values_list('asset_id', 'description').order_by('-date_acquired')[] @@ -70,7 +68,6 @@ class AssetEdit(LoginRequiredMixin, generic.UpdateView): return context def form_invalid(self, form): - print(form.errors) return super().form_invalid(form) def get_success_url(self): From e3cbcbd1518e7ce51c0ab623f7ed9a35e5310d44 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 20:17:26 +0100 Subject: [PATCH 05/29] Fixed search and implemented an asset search api Added asset picker for parent attribute Co-authored-by: Panagiotis Petridis --- assets/templates/asset_create.html | 32 +--------- assets/templates/asset_list.html | 20 +++--- assets/templates/asset_update.html | 32 +--------- .../partials/asset_list_table_body.html | 3 +- assets/templates/partials/asset_picker.html | 63 +++++++++++++++++++ assets/urls.py | 2 + assets/views.py | 38 +++++++---- 7 files changed, 109 insertions(+), 81 deletions(-) create mode 100644 assets/templates/partials/asset_picker.html diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index 640008e3..4a908d79 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -20,6 +20,7 @@ {% else %}
{% endif %} + {% include 'form_errors.html' %}
@@ -142,35 +143,8 @@ Collection Details
-
- - -
- - - - -
-
- -
- -
- - - - -
-
-
-
-
-
- -
-
+ {% include 'partials/asset_picker.html' %} +
diff --git a/assets/templates/asset_list.html b/assets/templates/asset_list.html index a8a0da2a..3d1ed407 100644 --- a/assets/templates/asset_list.html +++ b/assets/templates/asset_list.html @@ -11,20 +11,20 @@ {% csrf_token %}
- +
- - -
- {% csrf_token %}
- + {% for name in statuses %} + {% if name == status_select %} + {% endfor %}
- +
diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 1a976646..a3a228a0 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -24,6 +24,7 @@ {% else %}
{% endif %} +{% include 'form_errors.html' %}
@@ -190,35 +191,8 @@
{% if edit or duplicate %} -
- - -
- - -
-
- -
- -
- - -
-
-
- -
-
-
- -
- {% else %} + {% include 'partials/asset_picker.html' %} + {% else%}
Parent
diff --git a/assets/templates/partials/asset_list_table_body.html b/assets/templates/partials/asset_list_table_body.html index 4972b993..db4a62a9 100644 --- a/assets/templates/partials/asset_list_table_body.html +++ b/assets/templates/partials/asset_list_table_body.html @@ -1,7 +1,6 @@ {% for item in object_list %} {#
  • {{ item.asset_id }} - {{ item.description }}
  • #} - - + {% if object.parent%} + + {% endif %} + + +{% load static %} +{% block css %} + + +{% endblock %} + +{% block preload_js %} + + +{% endblock %} + +{% block js %} + +{% endblock js %} \ No newline at end of file diff --git a/assets/urls.py b/assets/urls.py index 5970d64b..09c12047 100644 --- a/assets/urls.py +++ b/assets/urls.py @@ -15,6 +15,8 @@ urlpatterns = [ path('asset//duplicate/', views.AssetDuplicate.as_view(), name='asset_duplicate'), path('asset/delete/', views.asset_delete, name='ajax_asset_delete'), + path('asset/search/', views.AssetSearch.as_view(), name='asset_search_json'), + path('supplier/list', views.SupplierList.as_view(), name='supplier_list'), path('supplier/', views.SupplierDetail.as_view(), name='supplier_detail'), path('supplier/create', views.SupplierCreate.as_view(), name='supplier_create'), diff --git a/assets/views.py b/assets/views.py index 064ccfc1..02e51892 100644 --- a/assets/views.py +++ b/assets/views.py @@ -1,7 +1,7 @@ from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin -from django.http import HttpResponse, QueryDict +from django.http import HttpResponse, QueryDict, JsonResponse from django.core import serializers from django.views import generic from django.contrib.auth import views as auth_views @@ -22,27 +22,39 @@ class AssetList(LoginRequiredMixin, generic.ListView): def get_queryset(self): #TODO Feedback to user when search fails query = self.request.GET.get('query', "") + queryset = self.model.objects.all() if len(query) >= 3: - return self.model.objects.filter(Q(asset_id__exact=query) | Q(description__icontains=query)) - elif query != "": - return self.model.objects.filter(Q(asset_id__exact=query)) - else: - cat = self.request.GET.get('cat', "") - status = self.request.GET.get('status', "") - if cat != "None": - return self.model.objects.filter(category__name__exact=cat) - elif status != "None": - return self.model.objects.filter(status__name__exact=status) - else: - return self.model.objects.all() + queryset = self.model.objects.filter(Q(asset_id__exact=query) | Q(description__icontains=query)) + + cat = self.request.GET.get('cat', "") + status = self.request.GET.get('status', "") + if cat != "": + queryset = queryset.filter(category__name__exact=cat) + elif status != "": + queryset = queryset.filter(status__name__exact=status) + + return queryset def get_context_data(self, **kwargs): context = super(AssetList, self).get_context_data(**kwargs) context["search_name"] = self.request.GET.get('query', "") + context["categories"] = models.AssetCategory.objects.all() + context["category_select"] = self.request.GET.get('cat', "") + context["statuses"] = models.AssetStatus.objects.all() + context["status_select"] = self.request.GET.get('stats', "") return context; +class AssetSearch(AssetList): + def render_to_response(self, context, **response_kwargs): + result = [] + + for asset in context["object_list"]: + result.append({"id":asset.pk, "label":(asset.asset_id + " | " + asset.description)}) + + return JsonResponse(result, safe=False) + class AssetDetail(LoginRequiredMixin, generic.DetailView): model = models.Asset template_name = 'asset_update.html' From 366d9c4130fdd22572915eea352236fc21ca2a21 Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Sat, 5 Oct 2019 20:33:03 +0100 Subject: [PATCH 06/29] Label the collection 'set parent' field as such --- assets/templates/asset_create.html | 9 ++++++--- assets/templates/asset_update.html | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index 4a908d79..64ae9d84 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -143,8 +143,11 @@ Collection Details
    - {% include 'partials/asset_picker.html' %} -
    + + {% include 'partials/asset_picker.html' %} +
    +
    @@ -158,4 +161,4 @@ {% include 'partials/confirm_delete.html' with object=object %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index a3a228a0..a926d079 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -191,7 +191,10 @@
    {% if edit or duplicate %} - {% include 'partials/asset_picker.html' %} +
    + + {% include 'partials/asset_picker.html' %} +
    {% else%}
    Parent
    @@ -234,4 +237,4 @@ {% include 'partials/confirm_delete.html' with object=object %} -{% endblock %} \ No newline at end of file +{% endblock %} From 7359e05427e9c0ca9cb0a6e7cb5d30e96acd469e Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 21:16:03 +0100 Subject: [PATCH 07/29] Started adding functionality for assets and cables forms to be dynamically swapped --- assets/forms.py | 5 +++++ assets/views.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/assets/forms.py b/assets/forms.py index d1a24ef0..3b5ec6ea 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -14,6 +14,11 @@ class AssetForm(forms.ModelForm): raise ValidationError("Cannot sell an item before it is acquired") return self.cleaned_data["date_sold"] +class CableForm(AssetForm): + class Meta: + model = models.Cable + fields = '__all__' + class SupplierForm(forms.ModelForm): class Meta: model = models.Supplier diff --git a/assets/views.py b/assets/views.py index 02e51892..9549a5c4 100644 --- a/assets/views.py +++ b/assets/views.py @@ -13,6 +13,12 @@ from dateutil import parser import simplejson as json from assets import models, forms +class CableFormMixin: + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["cableForm"] = forms.CableForm + return context + class AssetList(LoginRequiredMixin, generic.ListView): model = models.Asset template_name = 'asset_list.html' From d5f08a8bffdd5db5041e4c9fc898df71e9fb41fe Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 21:16:42 +0100 Subject: [PATCH 08/29] Started adding functionality for assets and cables forms to be dynamically swapped --- assets/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/views.py b/assets/views.py index 9549a5c4..4f441dcc 100644 --- a/assets/views.py +++ b/assets/views.py @@ -61,7 +61,7 @@ class AssetSearch(AssetList): return JsonResponse(result, safe=False) -class AssetDetail(LoginRequiredMixin, generic.DetailView): +class AssetDetail(LoginRequiredMixin, CableFormMixin, generic.DetailView): model = models.Asset template_name = 'asset_update.html' @@ -71,7 +71,7 @@ class AssetDetail(LoginRequiredMixin, generic.DetailView): # template_name = 'asset_update.html' # # success_url = reverse_lazy('asset_list') -class AssetEdit(LoginRequiredMixin, generic.UpdateView): +class AssetEdit(LoginRequiredMixin, CableFormMixin, generic.UpdateView): template_name = 'asset_update.html' model = models.Asset form_class = forms.AssetForm @@ -91,7 +91,7 @@ class AssetEdit(LoginRequiredMixin, generic.UpdateView): def get_success_url(self): return reverse("asset_detail", kwargs={"pk":self.object.id}) -class AssetCreate(LoginRequiredMixin, generic.CreateView): +class AssetCreate(LoginRequiredMixin, CableFormMixin, generic.CreateView): template_name = 'asset_create.html' model = models.Asset form_class = forms.AssetForm From 3d5272d7229cde9eb0a34d65fa9122f90991ae37 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 21:58:49 +0100 Subject: [PATCH 09/29] Fixed incorrecty data being sent to the date_sold field in the asset_update template. Fixes last of dates not being passed around correctly --- assets/templates/asset_update.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index a926d079..e73eee3a 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -158,7 +158,7 @@
    - {% with date_sol=object.form.date_sold|date:"Y-m-d" %} + {% with date_sol=object.date_sold|date:"Y-m-d" %} {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} {% endwith %}
    From a22956cc6262b87a05bc6ad85841649d0fbe7a9a Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Sat, 5 Oct 2019 22:42:21 +0100 Subject: [PATCH 10/29] Initial work on cable template things --- assets/templates/asset_create.html | 269 ++++++++++++++++++----------- assets/templates/asset_update.html | 2 +- assets/views.py | 1 + 3 files changed, 169 insertions(+), 103 deletions(-) diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index 64ae9d84..013958ad 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -20,34 +20,34 @@ {% else %} {% endif %} - {% include 'form_errors.html' %} -
    -
    -
    - {% include 'partials/asset_buttons.html' %} + {% include 'form_errors.html' %} +
    +
    +
    + {% include 'partials/asset_buttons.html' %} +
    -
    - {% csrf_token %} - -
    -
    -
    -
    - Asset Details -
    -
    -
    Asset ID - {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} + {% csrf_token %} + +
    +
    +
    +
    + Asset Details
    -
    - - {% render_field form.description|add_class:'form-control' value=object.description %} -
    -
    - - {% for id, choice in form.category.field.choices %}
    -
    - - {% for id, choice in form.status.field.choices %} {% endfor %} -
    -
    - +
    +
    + {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %} -
    - -
    - +
    + +
    + {% render_field form.comments|add_class:'form-control' %} +
    -
    -
    -
    -
    -
    - Purchase Details -
    -
    -
    - - -
    - -
    - -
    - £ - {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} -
    -
    - -
    - -
    - £ - {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} -
    -
    - -
    - - {% if object.date_acquired%} - {% with date_acq=object.date_acquired|date:"Y-m-d" %} - {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} - {% endwith %} - {% else %} - - {% endif %} -
    - -
    - - {% with date_sol=object.form.date_sold|date:"Y-m-d" %} - {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} - {% endwith %} -
    -
    -
    -
    +
    - Collection Details + Purchase Details
    -
    - +
    + + +
    + +
    + +
    + £ + {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} +
    +
    + +
    + +
    + £ + {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} +
    +
    + +
    + + {% if object.date_acquired%} + {% with date_acq=object.date_acquired|date:"Y-m-d" %} + {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} + {% endwith %} + {% else %} + + {% endif %} +
    + +
    + + {% with date_sol=object.form.date_sold|date:"Y-m-d" %} + {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} + {% endwith %} +
    +
    +
    +
    + {#% if object.is_cable and object.category == "Power" %#} + +
    +
    +
    + Collection Details +
    +
    +
    + {% include 'partials/asset_picker.html' %} +
    @@ -155,10 +207,23 @@ {% include 'partials/asset_buttons.html' %}
    -
    -
    - + -{% include 'partials/confirm_delete.html' with object=object %} + {% include 'partials/confirm_delete.html' with object=object %} -{% endblock %} + {% endblock %} + +{% block js%} + +{%endblock%} diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index e73eee3a..286c740e 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -192,7 +192,7 @@
    {% if edit or duplicate %}
    - + {% include 'partials/asset_picker.html' %}
    {% else%} diff --git a/assets/views.py b/assets/views.py index 4f441dcc..341a0e44 100644 --- a/assets/views.py +++ b/assets/views.py @@ -100,6 +100,7 @@ class AssetCreate(LoginRequiredMixin, CableFormMixin, generic.CreateView): context = super(AssetCreate, self).get_context_data(**kwargs) context["create"] = True + context["connectors"] = models.Connector.objects.all() return context def get_success_url(self): From d944f99e1f3ffcdfded5faa110b8edb020bca83f Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sat, 5 Oct 2019 23:19:22 +0100 Subject: [PATCH 11/29] Fixed cable form fields not appearing, and made the 'cable details' group appearing more reliable --- assets/templates/asset_create.html | 46 ++++++++++++++---------------- assets/views.py | 2 +- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index 013958ad..ee6c9e60 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -47,7 +47,7 @@
    - {% for id, choice in form.category.field.choices %}
    - {% render_field form.is_cable|attr:'onchange=toggleBox()' value=object.is_cable %} + {% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' value=object.is_cable %}
    + +
    - -
    - - {% render_field cableform.length|add_class:'form-control' %} - {{ cableform.length.help_text }} + + {% render_field cable_form.length|add_class:'form-control' %} + {{ cable_form.length.help_text }}
    - - {% render_field cableform.csa|add_class:'form-control' value=object.csa %} - {{ cableform.csa.help_text }} + + {% render_field cable_form.csa|add_class:'form-control' value=object.csa %} + {{ cable_form.csa.help_text }}
    - - {% render_field cableform.circuits|add_class:'form-control' value=object.circuits %} + + {% render_field cable_form.circuits|add_class:'form-control' value=object.circuits %}
    - - {% render_field cableform.cores|add_class:'form-control' value=object.cores %} + + {% render_field cable_form.cores|add_class:'form-control' value=object.cores %}
    @@ -215,15 +215,13 @@ {% block js%} {%endblock%} diff --git a/assets/views.py b/assets/views.py index 341a0e44..daf81058 100644 --- a/assets/views.py +++ b/assets/views.py @@ -16,7 +16,7 @@ from assets import models, forms class CableFormMixin: def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["cableForm"] = forms.CableForm + context["cable_form"] = forms.CableForm(**self.get_form_kwargs()) return context class AssetList(LoginRequiredMixin, generic.ListView): From 7b795ac332c799b3583a660cea658e269888119d Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Sun, 6 Oct 2019 01:03:41 +0100 Subject: [PATCH 12/29] Fixed some bugs with search --- assets/templates/asset_list.html | 6 +++--- assets/views.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/templates/asset_list.html b/assets/templates/asset_list.html index 3d1ed407..e3854b58 100644 --- a/assets/templates/asset_list.html +++ b/assets/templates/asset_list.html @@ -8,7 +8,7 @@

    Asset List

    -
    + {% csrf_token %}
    @@ -20,7 +20,7 @@ {% for name in statuses %} - {% if name == status_select %} + {% if name.name == status_select %}
    - + {% csrf_token %} -
    - +
    +
    -
    - - -
    -
    - - -
    - - +
    +
    +
    + + +
    +
    + + +
    + + +
    From d30bd9850de5714026a686893b7822f2986817d2 Mon Sep 17 00:00:00 2001 From: Arona Jones Date: Mon, 7 Oct 2019 16:46:15 +0100 Subject: [PATCH 16/29] Align text against buttons (middle) in asset list --- assets/templates/partials/asset_list_table_body.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/templates/partials/asset_list_table_body.html b/assets/templates/partials/asset_list_table_body.html index db4a62a9..523e99e1 100644 --- a/assets/templates/partials/asset_list_table_body.html +++ b/assets/templates/partials/asset_list_table_body.html @@ -11,10 +11,10 @@ text-muted {% endif %} "> - - - - + + + + - + diff --git a/assets/urls.py b/assets/urls.py index 09c12047..d8d87ce1 100644 --- a/assets/urls.py +++ b/assets/urls.py @@ -2,25 +2,26 @@ from django.urls import path, include from rest_framework import routers from assets import views, api +from PyRIGS.decorators import permission_required_with_403 + router = routers.DefaultRouter() router.register(r'api/assets', api.AssetViewSet) urlpatterns = [ - # path('', views.Index.as_view(), name='index'), path('', views.AssetList.as_view(), name='index'), path('asset/list/', views.AssetList.as_view(), name='asset_list'), path('asset//', views.AssetDetail.as_view(), name='asset_detail'), - path('asset/create/', views.AssetCreate.as_view(), name='asset_create'), - path('asset//edit/', views.AssetEdit.as_view(), name='asset_update'), - path('asset//duplicate/', views.AssetDuplicate.as_view(), name='asset_duplicate'), - path('asset/delete/', views.asset_delete, name='ajax_asset_delete'), + path('asset/create/', permission_required_with_403('assets.create_asset')(views.AssetCreate.as_view()), name='asset_create'), + path('asset//edit/', permission_required_with_403('assets.change_asset')(views.AssetEdit.as_view()), name='asset_update'), + path('asset//duplicate/', permission_required_with_403('assets.create_asset')(views.AssetDuplicate.as_view()), name='asset_duplicate'), + path('asset/delete/', permission_required_with_403('assets.delete_asset')(views.asset_delete), name='ajax_asset_delete'), path('asset/search/', views.AssetSearch.as_view(), name='asset_search_json'), path('supplier/list', views.SupplierList.as_view(), name='supplier_list'), path('supplier/', views.SupplierDetail.as_view(), name='supplier_detail'), - path('supplier/create', views.SupplierCreate.as_view(), name='supplier_create'), - path('supplier//edit', views.SupplierUpdate.as_view(), name='supplier_update'), + path('supplier/create', permission_required_with_403('assets.create_supplier')(views.SupplierCreate.as_view()), name='supplier_create'), + path('supplier//edit', permission_required_with_403('assets.edit_supplier')(views.SupplierUpdate.as_view()), name='supplier_update'), path('', include(router.urls)), ] From aac900318eda3c79d1653c318c6e08bf4de648be Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Mon, 14 Oct 2019 00:05:18 +0100 Subject: [PATCH 27/29] Cables are now a thing, unfortunately by making a monolithic Asset model Co-authored-by: Arona Jones --- assets/admin.py | 5 -- assets/forms.py | 60 -------------- assets/migrations/0010_auto_20191013_2123.py | 67 +++++++++++++++ assets/migrations/0011_auto_20191013_2247.py | 24 ++++++ assets/models.py | 85 ++++++++++++++------ assets/templates/asset_update.html | 7 +- assets/templates/partials/asset_buttons.html | 2 +- assets/templates/partials/cable_form.html | 4 +- assets/views.py | 27 +------ 9 files changed, 162 insertions(+), 119 deletions(-) create mode 100644 assets/migrations/0010_auto_20191013_2123.py create mode 100644 assets/migrations/0011_auto_20191013_2247.py diff --git a/assets/admin.py b/assets/admin.py index bbd7b2d0..7044040f 100644 --- a/assets/admin.py +++ b/assets/admin.py @@ -32,11 +32,6 @@ class ConnectorAdmin(admin.ModelAdmin): list_display = ['id', '__str__', 'current_rating', 'voltage_rating', 'num_pins'] -@admin.register(assets.Cable) -class CableAdmin(admin.ModelAdmin): - pass - - admin.AdminSite.site_header = 'PyAssets - TEC\'s Asset System' admin.AdminSite.site_title = 'PyAssets Admin' admin.AdminSite.index_title = 'System Administration' diff --git a/assets/forms.py b/assets/forms.py index a6c33c8e..19e00b11 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -1,74 +1,14 @@ from django import forms from django.core.exceptions import ValidationError -import re from assets import models class AssetForm(forms.ModelForm): - - class Meta: model = models.Asset fields = '__all__' - widgets = { - 'is_cable' : forms.CheckboxInput() - } - def clean_date_sold(self): - if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]: - raise ValidationError("Cannot sell an item before it is acquired") - return self.cleaned_data["date_sold"] - - def clean_asset_id(self): - # If the asset ID has been changed - if self.instance.asset_id and self.instance.asset_id == self.cleaned_data["asset_id"]: #If the item was not changed - return self.cleaned_data["asset_id"] - else: - if re.search("^[a-zA-Z0-9]+$", self.cleaned_data["asset_id"]) == None: - raise ValidationError("An Asset ID can only consist of letters and numbers") - return self.cleaned_data["asset_id"] - - def clean_purchase_price(self): - purchase_price = self.cleaned_data["purchase_price"] - if purchase_price and purchase_price < 0: - raise ValidationError("A price cannot be negative") - return purchase_price - - def clean_salvage_value(self): - salvage_value = self.cleaned_data["salvage_value"] - if salvage_value and salvage_value < 0: - raise ValidationError("A price cannot be negative") - return salvage_value - - -class CableForm(AssetForm): - class Meta(AssetForm.Meta): - model = models.Cable - - def clean_length(self): - length = self.cleaned_data["length"] - if length <= 0: - raise ValidationError("The length of a cable must be more than 0") - return length - - def clean_csa(self): - csa = self.cleaned_data["csa"] - if csa <= 0: - raise ValidationError("The CSA of a cable must be more than 0") - return csa - - def clean_circuits(self): - circuits = self.cleaned_data["circuits"] - if circuits <= 0: - raise ValidationError("There must be at least one circuit") - return circuits - - def clean_cores(self): - cores = self.cleaned_data["cores"] - if cores <= 0: - raise ValidationError("There must be at least one core") - return cores class SupplierForm(forms.Form): class Meta: diff --git a/assets/migrations/0010_auto_20191013_2123.py b/assets/migrations/0010_auto_20191013_2123.py new file mode 100644 index 00000000..b56c40c4 --- /dev/null +++ b/assets/migrations/0010_auto_20191013_2123.py @@ -0,0 +1,67 @@ +# Generated by Django 2.0.13 on 2019-10-13 20:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0009_auto_20191008_2148'), + ] + + operations = [ + migrations.RemoveField( + model_name='cable', + name='asset_ptr', + ), + migrations.RemoveField( + model_name='cable', + name='plug', + ), + migrations.RemoveField( + model_name='cable', + name='socket', + ), + migrations.AlterModelOptions( + name='asset', + options={}, + ), + migrations.RemoveField( + model_name='asset', + name='polymorphic_ctype', + ), + migrations.AddField( + model_name='asset', + name='circuits', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='asset', + name='cores', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AddField( + model_name='asset', + name='csa', + field=models.DecimalField(blank=True, decimal_places=2, help_text='mm^2', max_digits=10, null=True), + ), + migrations.AddField( + model_name='asset', + name='length', + field=models.DecimalField(blank=True, decimal_places=1, help_text='m', max_digits=10, null=True), + ), + migrations.AddField( + model_name='asset', + name='plug', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector'), + ), + migrations.AddField( + model_name='asset', + name='socket', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector'), + ), + migrations.DeleteModel( + name='Cable', + ), + ] diff --git a/assets/migrations/0011_auto_20191013_2247.py b/assets/migrations/0011_auto_20191013_2247.py new file mode 100644 index 00000000..af49b124 --- /dev/null +++ b/assets/migrations/0011_auto_20191013_2247.py @@ -0,0 +1,24 @@ +# Generated by Django 2.0.13 on 2019-10-13 21:47 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0010_auto_20191013_2123'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='plug', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector'), + ), + migrations.AlterField( + model_name='asset', + name='socket', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector'), + ), + ] diff --git a/assets/models.py b/assets/models.py index adeb2ced..66646515 100644 --- a/assets/models.py +++ b/assets/models.py @@ -1,9 +1,10 @@ +from django.core.exceptions import ValidationError from django.db import models from django.urls import reverse from polymorphic.models import PolymorphicModel import datetime - +import re class AssetCategory(models.Model): class Meta: @@ -37,7 +38,17 @@ class Supplier(models.Model): return self.name -class Asset(PolymorphicModel): +class Connector(models.Model): + description = models.CharField(max_length=80) + current_rating = models.DecimalField(decimal_places=2, max_digits=10, help_text='Amps') + voltage_rating = models.IntegerField(help_text='Volts') + num_pins = models.IntegerField(blank=True, null=True) + + def __str__(self): + return self.description + + +class Asset(models.Model): parent = models.ForeignKey(to='self', related_name='asset_parent', blank=True, null=True, on_delete=models.SET_NULL) asset_id = models.CharField(max_length=10, unique=True) @@ -55,34 +66,56 @@ class Asset(PolymorphicModel): # Cable assets is_cable = models.BooleanField(default=False) - - def get_absolute_url(self): - return reverse('asset_detail', kwargs={'pk': self.pk}) - - def __str__(self): - return str(self.asset_id) + ' - ' + self.description - -class Connector(models.Model): - description = models.CharField(max_length=80) - current_rating = models.DecimalField(decimal_places=2, max_digits=10, help_text='Amps') - voltage_rating = models.IntegerField(help_text='Volts') - num_pins = models.IntegerField(blank=True, null=True) - - def __str__(self): - return self.description - - -class Cable(Asset): - plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='plug', null=True) - socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='socket', null=True) + plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='plug', blank=True, null=True) + socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='socket', blank=True, null=True) length = models.DecimalField(decimal_places=1, max_digits=10, blank=True, null=True, help_text='m') csa = models.DecimalField(decimal_places=2, max_digits=10, blank=True, null=True, help_text='mm^2') circuits = models.IntegerField(blank=True, null=True) cores = models.IntegerField(blank=True, null=True) - def cable_resistance(self): - rho = 0.0000000168 - return (rho * self.length) / (self.csa * 1000000) + def get_absolute_url(self): + return reverse('asset_detail', kwargs={'pk': self.pk}) def __str__(self): - return '{} - {}m - {}'.format(self.plug, self.length, self.socket) + out = str(self.asset_id) + ' - ' + self.description + if self.is_cable: + out += '{} - {}m - {}'.format(self.plug, self.length, self.socket) + return out + + def clean(self): + if self.date_sold and self.date_acquired > self.date_sold: + raise ValidationError({"date_sold": "Cannot sell an item before it is acquired"}) + + self.asset_id = self.asset_id.upper() + if re.search("^[a-zA-Z0-9]+$", self.asset_id) is None: + raise ValidationError({"asset_id": "An Asset ID can only consist of letters and numbers"}) + + if self.purchase_price and self.purchase_price < 0: + raise ValidationError({"purchase_price": "A price cannot be negative"}) + + if self.salvage_value and self.salvage_value < 0: + raise ValidationError({"purchase_price": "A price cannot be negative"}) + + if self.is_cable: + if self.length is None: + raise ValidationError({"length": "The length of a cable must be a number"}) + elif self.csa is None: + raise ValidationError({"csa": "The csa of a cable must be a number"}) + elif self.circuits is None: + raise ValidationError({"circuits": "The number of circuits in a cable must be a number"}) + elif self.cores is None: + raise ValidationError({"cores": "The number of cores in a cable must be a number"}) + elif self.socket is None: + raise ValidationError({"plug": "A cable must have a plug"}) + elif self.plug is None: + raise ValidationError({"socket": "A cable must have a socket"}) + + if self.length <= 0: + raise ValidationError({"length": "The length of a cable must be more than 0"}) + elif self.csa <= 0: + raise ValidationError({"csa": "The CSA of a cable must be more than 0"}) + elif self.circuits <= 0: + raise ValidationError({"circuits": "There must be at least one circuit in a cable"}) + elif self.cores <= 0: + raise ValidationError({"cores": "There must be at least one core in a cable"}) + \ No newline at end of file diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 63a220a7..6c0159bc 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -35,7 +35,8 @@
    {% include 'partials/purchasedetails_form.html' %}
    -
    {{ item.asset_id }}{{ item.description }}{{ item.category }}{{ item.status }}{{ item.asset_id }}{{ item.description }}{{ item.category }}{{ item.status }}
    View From fe1541acbf3eebe1a5f8dfd98cfa7ba1d48f4d09 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 8 Oct 2019 01:33:38 +0100 Subject: [PATCH 17/29] Cables can now be created. --- assets/forms.py | 16 +++++---- .../management/commands/createSampleData.py | 12 +++++++ .../management/commands/deleteSampleData.py | 2 +- assets/templates/asset_create.html | 32 ++++++++--------- assets/views.py | 36 ++++++++++--------- 5 files changed, 58 insertions(+), 40 deletions(-) diff --git a/assets/forms.py b/assets/forms.py index 3b5ec6ea..e33c87ec 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -5,21 +5,25 @@ from assets import models class AssetForm(forms.ModelForm): - class Meta: - model = models.Asset - fields = '__all__' + def clean_date_sold(self): if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]: raise ValidationError("Cannot sell an item before it is acquired") return self.cleaned_data["date_sold"] + + class Meta: + model = models.Asset + fields = '__all__' + widgets = { + 'is_cable' : forms.CheckboxInput() + } class CableForm(AssetForm): - class Meta: + class Meta(AssetForm.Meta): model = models.Cable - fields = '__all__' -class SupplierForm(forms.ModelForm): +class SupplierForm(forms.Form): class Meta: model = models.Supplier fields = '__all__' diff --git a/assets/management/commands/createSampleData.py b/assets/management/commands/createSampleData.py index 0d5beeb0..d054b17d 100644 --- a/assets/management/commands/createSampleData.py +++ b/assets/management/commands/createSampleData.py @@ -21,6 +21,7 @@ class Command(BaseCommand): self.create_statuses() self.create_suppliers() self.create_assets() + self.create_connectors() def create_categories(self): categories = ['Case', 'Video', 'General', 'Sound', 'Lighting', 'Rigging'] @@ -60,3 +61,14 @@ class Command(BaseCommand): asset.purchased_from = random.choice(suppliers) asset.save() + + def create_connectors(self): + connectors = [ + { "description":"13A UK", "current_rating":13, "voltage_rating":230, "num_pins":3 }, + { "description":"16A", "current_rating":16, "voltage_rating":230, "num_pins":3 }, + { "description":"32/3", "current_rating":32, "voltage_rating":400, "num_pins":5 }, + { "description":"Socapex", "current_rating":23, "voltage_rating":600, "num_pins":19 }, + ] + for connector in connectors: + conn = models.Connector.objects.create(**connector) + conn.save() \ No newline at end of file diff --git a/assets/management/commands/deleteSampleData.py b/assets/management/commands/deleteSampleData.py index d58bee5f..f0338faa 100644 --- a/assets/management/commands/deleteSampleData.py +++ b/assets/management/commands/deleteSampleData.py @@ -21,7 +21,7 @@ class Command(BaseCommand): self.delete_objects(models.AssetCategory) self.delete_objects(models.AssetStatus) self.delete_objects(models.Supplier) - self.delete_objects(models.Collection) + self.delete_objects(models.Connector) self.delete_objects(models.Asset) def delete_objects(self, model): diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index ee6c9e60..a29c3635 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -58,7 +58,7 @@ {% endfor %}
    - {% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' value=object.is_cable %} + {% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' %}
    + +
    - -
    - - {% render_field cable_form.length|add_class:'form-control' %} - {{ cable_form.length.help_text }} + + {% render_field form.length|add_class:'form-control' %} + {{ form.length.help_text }}
    - - {% render_field cable_form.csa|add_class:'form-control' value=object.csa %} - {{ cable_form.csa.help_text }} + + {% render_field form.csa|add_class:'form-control' value=object.csa %} + {{ form.csa.help_text }}
    - - {% render_field cable_form.circuits|add_class:'form-control' value=object.circuits %} + + {% render_field form.circuits|add_class:'form-control' value=object.circuits %}
    - - {% render_field cable_form.cores|add_class:'form-control' value=object.cores %} + + {% render_field form.cores|add_class:'form-control' value=object.cores %}
    @@ -216,7 +216,7 @@ {% block js%} {%endblock%} From bfd4a380f3c622425eca4daef8123700ac3fb27a Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Tue, 8 Oct 2019 13:56:56 +0100 Subject: [PATCH 19/29] Wrap cableform input groups --- assets/templates/asset_create.html | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index 881d4ff1..61a12e20 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -169,13 +169,17 @@
    - {% render_field form.length|add_class:'form-control' %} - {{ form.length.help_text }} +
    + {% render_field form.length|add_class:'form-control' %} + {{ form.length.help_text }} +
    - {% render_field form.csa|add_class:'form-control' value=object.csa %} - {{ form.csa.help_text }} +
    + {% render_field form.csa|add_class:'form-control' value=object.csa %} + {{ form.csa.help_text }} +
    From 1b124a9da9c48aabe01c0019cd7f8f1ecd2fcbe8 Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Tue, 8 Oct 2019 16:49:10 +0100 Subject: [PATCH 20/29] Partialize alllllll the things Split each form section into its own partial which handles both the form and display view of that section. --- assets/templates/asset_create.html | 214 ++-------------- assets/templates/asset_update.html | 239 +++--------------- assets/templates/partials/asset_form.html | 75 ++++++ assets/templates/partials/cable_form.html | 75 ++++++ assets/templates/partials/parent_form.html | 41 +++ .../partials/purchasedetails_form.html | 76 ++++++ 6 files changed, 327 insertions(+), 393 deletions(-) create mode 100644 assets/templates/partials/asset_form.html create mode 100644 assets/templates/partials/cable_form.html create mode 100644 assets/templates/partials/parent_form.html create mode 100644 assets/templates/partials/purchasedetails_form.html diff --git a/assets/templates/asset_create.html b/assets/templates/asset_create.html index 61a12e20..1e58c720 100644 --- a/assets/templates/asset_create.html +++ b/assets/templates/asset_create.html @@ -17,9 +17,9 @@
    {% if duplicate %}
    -{% else %} - -{% endif %} + {% else %} + + {% endif %} {% include 'form_errors.html' %}
    @@ -32,200 +32,40 @@
    -
    -
    - Asset Details -
    -
    -
    - - {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} -
    -
    - - {% render_field form.description|add_class:'form-control' value=object.description %} -
    -
    - - -
    - {% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' %} -
    - - -
    -
    - - {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %} -
    - -
    - - {% render_field form.comments|add_class:'form-control' %} -
    -
    -
    + {% include 'partials/asset_form.html' %}
    -
    -
    - Purchase Details -
    -
    -
    - - -
    - -
    - -
    - £ - {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} -
    -
    - -
    - -
    - £ - {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} -
    -
    - -
    - - {% if object.date_acquired%} - {% with date_acq=object.date_acquired|date:"Y-m-d" %} - {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} - {% endwith %} - {% else %} - - {% endif %} -
    - -
    - - {% with date_sol=object.form.date_sold|date:"Y-m-d" %} - {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} - {% endwith %} -
    -
    -
    -
    - {#% if object.is_cable and object.category == "Power" %#} - -
    -
    -
    - Collection Details -
    -
    -
    - - {% include 'partials/asset_picker.html' %} -
    -
    -
    -
    + {% include 'partials/purchasedetails_form.html' %}
    -
    -
    - {% include 'partials/asset_buttons.html' %} -
    + +
    + {% include 'partials/parent_form.html' %} +
    +
    +
    +
    + {% include 'partials/asset_buttons.html' %} +
    +
    {% include 'partials/confirm_delete.html' with object=object %} {% endblock %} -{% block js%} - -{%endblock%} + checkIfCableHidden(); + + {%endblock%} diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 286c740e..90e264e9 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -10,231 +10,58 @@

    {% if edit and object %} Edit Asset: {{ object.asset_id }} - {{ object.description }} - {% elif duplicate %} - Duplication of Asset: {{ previous_asset_id }} - {% elif not object %} - Create Asset {% else %} Asset: {{ object.asset_id }} - {{ object.description }} {% endif %}

    -{% if create %} -
    -{% else %} -{% endif %} -{% include 'form_errors.html' %} -
    -
    -
    - {% include 'partials/asset_buttons.html' %} + {% include 'form_errors.html' %} +
    +
    +
    + {% include 'partials/asset_buttons.html' %} +
    -
    {% csrf_token %}
    -
    -
    - Asset Details -
    -
    - {% if edit or duplicate %} -
    - - {% if duplicate %} - {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} - {% elif object.asset_id %} - {% render_field form.asset_id|attr:'readonly'|add_class:'disabled_input form-control' value=object.asset_id %} - {% else %} - {% render_field form.asset_id|add_class:'form-control' %} - {% endif %} -
    -
    - - {% render_field form.description|add_class:'form-control' value=object.description %} -
    -
    - - -
    -
    - - -
    -
    - - {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %} -
    - -
    - - {% render_field form.comments|add_class:'form-control' %} -
    - {% else %} -
    Asset ID
    -
    {{ object.asset_id }}
    - -
    Description
    -
    {{ object.description }}
    - -
    Category
    -
    {{ object.category }}
    - -
    Status
    -
    {{ object.status }}
    - -
    Serial Number
    -
    {{ object.serial_number|default:'-' }}
    - -
    Comments
    -
    {{ object.comments|default:'-'|linebreaksbr }}
    - {% endif %} -
    -
    + {% include 'partials/asset_form.html' %}
    -
    -
    - Purchase Details -
    -
    - {% if edit or duplicate %} -
    - - -
    - -
    - -
    - £ - {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} -
    -
    - -
    - -
    - £ - {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} -
    -
    - -
    - - {% if object.date_acquired %} - {% with date_acq=object.date_acquired|date:"Y-m-d" %} - {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} - {% endwith %} - {% else %} - - {% endif %} -
    - -
    - - {% with date_sol=object.date_sold|date:"Y-m-d" %} - {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} - {% endwith %} -
    - {% else %} -
    -
    Purchased From
    -
    {{ object.purchased_from|default_if_none:'-' }}
    - -
    Purchase Price
    -
    £{{ object.purchase_price|default_if_none:'-' }}
    - -
    Salvage Value
    -
    £{{ object.salvage_value|default_if_none:'-' }}
    - -
    Date Acquired
    -
    {{ object.date_acquired|default_if_none:'-' }}
    - {% if object.date_sold %} -
    Date Sold
    -
    {{ object.date_sold|default_if_none:'-' }}
    - {% endif %} -
    - {% endif %} -
    -
    -
    -
    -
    -
    - Collection Details -
    -
    - {% if edit or duplicate %} -
    - - {% include 'partials/asset_picker.html' %} -
    - {% else%} -
    -
    Parent
    -
    - {% if object.parent %} - - {{ object.parent.asset_id }} - {{ object.parent.description }} - - {% else %} - - - {% endif %} -
    - -
    Children
    - {% if object.asset_parent.all %} - {% for child in object.asset_parent.all %} -
    - - {{ child.asset_id }} - {{ child.description }} - -
    - {% endfor %} - {% else %} -
    -
    - {% endif %} -
    - {% endif %} -
    -
    -
    -
    -
    -
    - {% include 'partials/asset_buttons.html' %} -
    + {% include 'partials/purchasedetails_form.html' %}
    + +
    + {% include 'partials/parent_form.html' %} +
    +
    +
    +
    + {% include 'partials/asset_buttons.html' %} +
    +
    - - {% include 'partials/confirm_delete.html' with object=object %} {% endblock %} + +{% block js%} + +{%endblock%} diff --git a/assets/templates/partials/asset_form.html b/assets/templates/partials/asset_form.html new file mode 100644 index 00000000..c4b17ed1 --- /dev/null +++ b/assets/templates/partials/asset_form.html @@ -0,0 +1,75 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Asset Details +
    +
    + {% if edit or duplicate %} +
    + + {% if duplicate %} + {% render_field form.asset_id|add_class:'form-control' value=object.asset_id %} + {% elif object.asset_id %} + {% render_field form.asset_id|attr:'readonly'|add_class:'disabled_input form-control' value=object.asset_id %} + {% else %} + {% render_field form.asset_id|add_class:'form-control' %} + {% endif %} +
    +
    + + {% render_field form.description|add_class:'form-control' value=object.description %} +
    +
    + + +
    + {% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' %} +
    + + +
    +
    + + {% render_field form.serial_number|add_class:'form-control' value=object.serial_number %} +
    + +
    + + {% render_field form.comments|add_class:'form-control' %} +
    + {% else %} +
    Asset ID
    +
    {{ object.asset_id }}
    + +
    Description
    +
    {{ object.description }}
    + +
    Category
    +
    {{ object.category }}
    + +
    Status
    +
    {{ object.status }}
    + +
    Serial Number
    +
    {{ object.serial_number|default:'-' }}
    + +
    Comments
    +
    {{ object.comments|default:'-'|linebreaksbr }}
    + {% endif %} +
    +
    diff --git a/assets/templates/partials/cable_form.html b/assets/templates/partials/cable_form.html new file mode 100644 index 00000000..3d64fe42 --- /dev/null +++ b/assets/templates/partials/cable_form.html @@ -0,0 +1,75 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Cable Details +
    +
    + {% if edit or duplicate %} +
    + + +
    +
    + + +
    +
    + +
    + {% render_field form.length|add_class:'form-control' %} + {{ form.length.help_text }} +
    +
    +
    + +
    + {% render_field form.csa|add_class:'form-control' value=object.csa %} + {{ form.csa.help_text }} +
    +
    +
    + + {% render_field form.circuits|add_class:'form-control' value=object.circuits %} +
    +
    + + {% render_field form.cores|add_class:'form-control' value=object.cores %} +
    + {% else %} +
    +
    Socket
    +
    {{ object.socket|default_if_none:'-' }}
    + +
    Plug
    +
    {{ object.plug|default_if_none:'-' }}
    + +
    Length
    +
    {{ object.length|default_if_none:'-' }}m
    + +
    Cross Sectional Area
    +
    {{ object.csa|default_if_none:'-' }}m^2
    + +
    Circuits
    +
    {{ object.circuits|default_if_none:'-' }}
    + +
    Cores
    +
    {{ object.cores|default_if_none:'-' }}
    +
    + {% endif %} +
    +
    diff --git a/assets/templates/partials/parent_form.html b/assets/templates/partials/parent_form.html new file mode 100644 index 00000000..c5853c41 --- /dev/null +++ b/assets/templates/partials/parent_form.html @@ -0,0 +1,41 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Collection Details +
    +
    + {% if edit or duplicate %} +
    + + {% include 'partials/asset_picker.html' %} +
    + {% else %} +
    +
    Parent
    +
    + {% if object.parent %} + + {{ object.parent.asset_id }} - {{ object.parent.description }} + + {% else %} + - + {% endif %} +
    + +
    Children
    + {% if object.asset_parent.all %} + {% for child in object.asset_parent.all %} +
    + + {{ child.asset_id }} - {{ child.description }} + +
    + {% endfor %} + {% else %} +
    -
    + {% endif %} +
    + {% endif%} +
    +
    diff --git a/assets/templates/partials/purchasedetails_form.html b/assets/templates/partials/purchasedetails_form.html new file mode 100644 index 00000000..477b9e04 --- /dev/null +++ b/assets/templates/partials/purchasedetails_form.html @@ -0,0 +1,76 @@ +{% load widget_tweaks %} +{% load asset_templatetags %} +
    +
    + Purchase Details +
    +
    + {% if edit or duplicate %} +
    + + +
    + +
    + +
    + £ + {% render_field form.purchase_price|add_class:'form-control' value=object.purchase_price %} +
    +
    + +
    + +
    + £ + {% render_field form.salvage_value|add_class:'form-control' value=object.salvage_value %} +
    +
    + +
    + + {% if object.date_acquired%} + {% with date_acq=object.date_acquired|date:"Y-m-d" %} + {% render_field form.date_acquired|add_class:'form-control'|attr:'type="date"' value=date_acq %} + {% endwith %} + {% else %} + + {% endif %} +
    + +
    + + {% with date_sol=object.form.date_sold|date:"Y-m-d" %} + {% render_field form.date_sold|add_class:'form-control'|attr:'type="date"' value=date_sol %} + {% endwith %} +
    + {% else %} +
    +
    Purchased From
    +
    {{ object.purchased_from|default_if_none:'-' }}
    + +
    Purchase Price
    +
    £{{ object.purchase_price|default_if_none:'-' }}
    + +
    Salvage Value
    +
    £{{ object.salvage_value|default_if_none:'-' }}
    + +
    Date Acquired
    +
    {{ object.date_acquired|default_if_none:'-' }}
    + {% if object.date_sold %} +
    Date Sold
    +
    {{ object.date_sold|default_if_none:'-' }}
    + {% endif %} +
    + {% endif %} +
    +
    From 1811cc98ba1e7ea764b3a36ae5bbfc4d67524557 Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Tue, 8 Oct 2019 17:04:10 +0100 Subject: [PATCH 21/29] Oops. Create is an edit state... --- assets/templates/partials/asset_form.html | 2 +- assets/templates/partials/cable_form.html | 2 +- assets/templates/partials/parent_form.html | 2 +- assets/templates/partials/purchasedetails_form.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/templates/partials/asset_form.html b/assets/templates/partials/asset_form.html index c4b17ed1..0de1a72b 100644 --- a/assets/templates/partials/asset_form.html +++ b/assets/templates/partials/asset_form.html @@ -5,7 +5,7 @@ Asset Details
    - {% if edit or duplicate %} + {% if create or edit or duplicate %}
    {% if duplicate %} diff --git a/assets/templates/partials/cable_form.html b/assets/templates/partials/cable_form.html index 3d64fe42..40ccff89 100644 --- a/assets/templates/partials/cable_form.html +++ b/assets/templates/partials/cable_form.html @@ -5,7 +5,7 @@ Cable Details
    - {% if edit or duplicate %} + {% if create or edit or duplicate %}
    From dc68a1f808abf92a228ac0b05c9ef3ac51e5c620 Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Tue, 8 Oct 2019 17:51:33 +0100 Subject: [PATCH 22/29] Improve handling of really long strings Partially resolves #350, my table fix/hack doesn't work on mobile. --- assets/templates/asset_update.html | 4 ++-- assets/templates/partials/asset_form.html | 4 ++-- assets/templates/partials/asset_list_table_body.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 90e264e9..63a220a7 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -9,9 +9,9 @@ diff --git a/assets/templates/partials/asset_form.html b/assets/templates/partials/asset_form.html index 0de1a72b..86c2c7a4 100644 --- a/assets/templates/partials/asset_form.html +++ b/assets/templates/partials/asset_form.html @@ -57,7 +57,7 @@
    {{ object.asset_id }}
    Description
    -
    {{ object.description }}
    +
    {{ object.description }}
    Category
    {{ object.category }}
    @@ -69,7 +69,7 @@
    {{ object.serial_number|default:'-' }}
    Comments
    -
    {{ object.comments|default:'-'|linebreaksbr }}
    +
    {{ object.comments|default:'-'|linebreaksbr }}
    {% endif %}
    diff --git a/assets/templates/partials/asset_list_table_body.html b/assets/templates/partials/asset_list_table_body.html index 523e99e1..1eea6a73 100644 --- a/assets/templates/partials/asset_list_table_body.html +++ b/assets/templates/partials/asset_list_table_body.html @@ -12,7 +12,7 @@ {% endif %} ">
    {{ item.asset_id }}{{ item.description }}{{ item.description }} {{ item.category }} {{ item.status }} From 5c9d7466c2d8c3742f07438cb88a0fee8d7241b8 Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Tue, 8 Oct 2019 18:01:15 +0100 Subject: [PATCH 23/29] Change asset buttons - added cancel button, moved stuff --- assets/templates/partials/asset_buttons.html | 43 ++++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/assets/templates/partials/asset_buttons.html b/assets/templates/partials/asset_buttons.html index 606a9bd7..e2ead5b9 100644 --- a/assets/templates/partials/asset_buttons.html +++ b/assets/templates/partials/asset_buttons.html @@ -1,20 +1,27 @@ +{% if edit and object %} + + + Duplicate + Delete +{% elif duplicate %} + + +{% elif create %} + + +{% else %} +
    - {% if edit and object %} - - - Duplicate - Delete - {% elif duplicate %} - - - Cancel - {% elif create %} - - - {% else %} - - Edit - Duplicate - Delete - {% endif %} + Edit + Duplicate + Delete
    +{% endif %} +{% if create or edit or duplicate %} +
    + +{% endif %} From 1d6208414f75402c4ca6db5a31f089b9fff45b52 Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Tue, 8 Oct 2019 18:32:49 +0100 Subject: [PATCH 24/29] Fix old duplicate link --- assets/templates/partials/asset_buttons.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/templates/partials/asset_buttons.html b/assets/templates/partials/asset_buttons.html index e2ead5b9..14873409 100644 --- a/assets/templates/partials/asset_buttons.html +++ b/assets/templates/partials/asset_buttons.html @@ -1,7 +1,7 @@ {% if edit and object %} - Duplicate + Duplicate Delete {% elif duplicate %} From b7e14b7dc394155610bc9d422374fafd96d9c243 Mon Sep 17 00:00:00 2001 From: Matthew Smith Date: Tue, 8 Oct 2019 22:33:41 +0100 Subject: [PATCH 25/29] Added form validation * Asset IDs must be unique * Asset IDs that are legacy and unchange have no validation applied * Asset IDs must be alphanumeric * Values must be >=0 * Lengths, CSAs, Circuits and Cores of cables must all be >0 --- RIGS/migrations/0035_auto_20191008_2148.py | 18 +++++++ assets/forms.py | 57 ++++++++++++++++++-- assets/migrations/0009_auto_20191008_2148.py | 18 +++++++ assets/models.py | 2 +- assets/views.py | 1 - 5 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 RIGS/migrations/0035_auto_20191008_2148.py create mode 100644 assets/migrations/0009_auto_20191008_2148.py diff --git a/RIGS/migrations/0035_auto_20191008_2148.py b/RIGS/migrations/0035_auto_20191008_2148.py new file mode 100644 index 00000000..756ceac5 --- /dev/null +++ b/RIGS/migrations/0035_auto_20191008_2148.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.13 on 2019-10-08 20:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('RIGS', '0034_event_risk_assessment_edit_url'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='risk_assessment_edit_url', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='risk assessment'), + ), + ] diff --git a/assets/forms.py b/assets/forms.py index e33c87ec..a6c33c8e 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -1,16 +1,12 @@ from django import forms from django.core.exceptions import ValidationError +import re from assets import models class AssetForm(forms.ModelForm): - - def clean_date_sold(self): - if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]: - raise ValidationError("Cannot sell an item before it is acquired") - return self.cleaned_data["date_sold"] class Meta: model = models.Asset @@ -19,10 +15,61 @@ class AssetForm(forms.ModelForm): 'is_cable' : forms.CheckboxInput() } + def clean_date_sold(self): + if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]: + raise ValidationError("Cannot sell an item before it is acquired") + return self.cleaned_data["date_sold"] + + def clean_asset_id(self): + # If the asset ID has been changed + if self.instance.asset_id and self.instance.asset_id == self.cleaned_data["asset_id"]: #If the item was not changed + return self.cleaned_data["asset_id"] + else: + if re.search("^[a-zA-Z0-9]+$", self.cleaned_data["asset_id"]) == None: + raise ValidationError("An Asset ID can only consist of letters and numbers") + return self.cleaned_data["asset_id"] + + def clean_purchase_price(self): + purchase_price = self.cleaned_data["purchase_price"] + if purchase_price and purchase_price < 0: + raise ValidationError("A price cannot be negative") + return purchase_price + + def clean_salvage_value(self): + salvage_value = self.cleaned_data["salvage_value"] + if salvage_value and salvage_value < 0: + raise ValidationError("A price cannot be negative") + return salvage_value + + class CableForm(AssetForm): class Meta(AssetForm.Meta): model = models.Cable + def clean_length(self): + length = self.cleaned_data["length"] + if length <= 0: + raise ValidationError("The length of a cable must be more than 0") + return length + + def clean_csa(self): + csa = self.cleaned_data["csa"] + if csa <= 0: + raise ValidationError("The CSA of a cable must be more than 0") + return csa + + def clean_circuits(self): + circuits = self.cleaned_data["circuits"] + if circuits <= 0: + raise ValidationError("There must be at least one circuit") + return circuits + + def clean_cores(self): + cores = self.cleaned_data["cores"] + if cores <= 0: + raise ValidationError("There must be at least one core") + return cores + class SupplierForm(forms.Form): class Meta: model = models.Supplier diff --git a/assets/migrations/0009_auto_20191008_2148.py b/assets/migrations/0009_auto_20191008_2148.py new file mode 100644 index 00000000..cbb474cc --- /dev/null +++ b/assets/migrations/0009_auto_20191008_2148.py @@ -0,0 +1,18 @@ +# Generated by Django 2.0.13 on 2019-10-08 20:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0008_auto_20191002_1931'), + ] + + operations = [ + migrations.AlterField( + model_name='asset', + name='asset_id', + field=models.CharField(max_length=10, unique=True), + ), + ] diff --git a/assets/models.py b/assets/models.py index 37a57628..adeb2ced 100644 --- a/assets/models.py +++ b/assets/models.py @@ -40,7 +40,7 @@ class Supplier(models.Model): class Asset(PolymorphicModel): parent = models.ForeignKey(to='self', related_name='asset_parent', blank=True, null=True, on_delete=models.SET_NULL) - asset_id = models.CharField(max_length=10) + asset_id = models.CharField(max_length=10, unique=True) description = models.CharField(max_length=120) category = models.ForeignKey(to=AssetCategory, on_delete=models.CASCADE) status = models.ForeignKey(to=AssetStatus, on_delete=models.CASCADE) diff --git a/assets/views.py b/assets/views.py index 3791edbc..e05a5e5c 100644 --- a/assets/views.py +++ b/assets/views.py @@ -74,7 +74,6 @@ class AssetEdit(LoginRequiredMixin, generic.UpdateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['form'] = forms.AssetForm context['edit'] = True return context From f5fc41ce30dcdfbe4f4431b3af79a96269d68fba Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Fri, 11 Oct 2019 13:30:22 +0100 Subject: [PATCH 26/29] Initial work on asset DB perms Partial #352. --- RIGS/management/commands/generateSampleData.py | 4 ++-- assets/migrations/0010_auto_20191011_1322.py | 17 +++++++++++++++++ assets/models.py | 5 +++++ assets/templates/asset_update.html | 2 ++ .../partials/asset_list_table_body.html | 3 +++ assets/urls.py | 15 ++++++++------- 6 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 assets/migrations/0010_auto_20191011_1322.py diff --git a/RIGS/management/commands/generateSampleData.py b/RIGS/management/commands/generateSampleData.py index 5263a030..e17e2ff4 100644 --- a/RIGS/management/commands/generateSampleData.py +++ b/RIGS/management/commands/generateSampleData.py @@ -121,8 +121,8 @@ class Command(BaseCommand): self.keyholder_group = Group.objects.create(name='Keyholders') self.finance_group = Group.objects.create(name='Finance') - keyholderPerms = ["add_event", "change_event", "view_event", "add_eventitem", "change_eventitem", "delete_eventitem", "add_organisation", "change_organisation", "view_organisation", "add_person", "change_person", "view_person", "view_profile", "add_venue", "change_venue", "view_venue"] - financePerms = ["change_event", "view_event", "add_eventitem", "change_eventitem", "add_invoice", "change_invoice", "view_invoice", "add_organisation", "change_organisation", "view_organisation", "add_payment", "change_payment", "delete_payment", "add_person", "change_person", "view_person"] + keyholderPerms = ["add_event", "change_event", "view_event", "add_eventitem", "change_eventitem", "delete_eventitem", "add_organisation", "change_organisation", "view_organisation", "add_person", "change_person", "view_person", "view_profile", "add_venue", "change_venue", "view_venue", "add_asset", "change_asset", "delete_asset", "asset_finance"] + financePerms = ["change_event", "view_event", "add_eventitem", "change_eventitem", "add_invoice", "change_invoice", "view_invoice", "add_organisation", "change_organisation", "view_organisation", "add_payment", "change_payment", "delete_payment", "add_person", "change_person", "view_person", "asset_finance", "change_asset"] for permId in keyholderPerms: self.keyholder_group.permissions.add(Permission.objects.get(codename=permId)) diff --git a/assets/migrations/0010_auto_20191011_1322.py b/assets/migrations/0010_auto_20191011_1322.py new file mode 100644 index 00000000..919c8466 --- /dev/null +++ b/assets/migrations/0010_auto_20191011_1322.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.13 on 2019-10-11 12:22 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0009_auto_20191008_2148'), + ] + + operations = [ + migrations.AlterModelOptions( + name='asset', + options={'permissions': (('asset_finance', 'Can see financial data for assets'),)}, + ), + ] diff --git a/assets/models.py b/assets/models.py index adeb2ced..d809f25e 100644 --- a/assets/models.py +++ b/assets/models.py @@ -62,6 +62,11 @@ class Asset(PolymorphicModel): def __str__(self): return str(self.asset_id) + ' - ' + self.description + class Meta: + permissions = ( + ('asset_finance', 'Can see financial data for assets'), + ) + class Connector(models.Model): description = models.CharField(max_length=80) current_rating = models.DecimalField(decimal_places=2, max_digits=10, help_text='Amps') diff --git a/assets/templates/asset_update.html b/assets/templates/asset_update.html index 63a220a7..39291814 100644 --- a/assets/templates/asset_update.html +++ b/assets/templates/asset_update.html @@ -32,9 +32,11 @@
    + {% if perms.asset.asset_financial %}
    {% include 'partials/purchasedetails_form.html' %}
    + {% endif %} diff --git a/assets/templates/partials/asset_list_table_body.html b/assets/templates/partials/asset_list_table_body.html index 1eea6a73..17012240 100644 --- a/assets/templates/partials/asset_list_table_body.html +++ b/assets/templates/partials/asset_list_table_body.html @@ -16,10 +16,13 @@
    {{ item.category }} {{ item.status }} +
    View + {% if perms.assets.change_asset %} Edit Duplicate + {% endif %}