From 9b77340782fc858259ca8fbc745dce766365057b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 08:55:29 +1000 Subject: [PATCH 01/10] Change field ordering for SupplierPart form - Import fields first! --- InvenTree/part/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 040444093d..1157012196 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -101,13 +101,13 @@ class EditSupplierPartForm(HelperForm): class Meta: model = SupplierPart fields = [ + 'part', 'supplier', 'SKU', - 'part', 'description', - 'URL', 'manufacturer', 'MPN', + 'URL', 'note', 'single_price', 'base_cost', From e5fc43a00fae733574a0e201b3a3aae79251e5f9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 09:00:54 +1000 Subject: [PATCH 02/10] Fix improper references to template_name for ajax forms - Should be ajax_template_name --- InvenTree/part/views.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index db9f66994c..6db1edfc8e 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -158,7 +158,6 @@ class PartEdit(AjaxUpdateView): """ View for editing Part object """ model = Part - template_name = 'part/edit.html' form_class = EditPartForm ajax_template_name = 'modal_form.html' ajax_form_title = 'Edit Part Properties' @@ -246,7 +245,6 @@ class PartDelete(AjaxDeleteView): """ View to delete a Part object """ model = Part - template_name = 'part/delete.html' ajax_template_name = 'part/partial_delete.html' ajax_form_title = 'Confirm Part Deletion' context_object_name = 'part' @@ -270,7 +268,6 @@ class CategoryDetail(DetailView): class CategoryEdit(AjaxUpdateView): """ Update view to edit a PartCategory """ model = PartCategory - template_name = 'part/category_edit.html' form_class = EditCategoryForm ajax_template_name = 'modal_form.html' ajax_form_title = 'Edit Part Category' @@ -286,7 +283,7 @@ class CategoryEdit(AjaxUpdateView): class CategoryDelete(AjaxDeleteView): """ Delete view to delete a PartCategory """ model = PartCategory - template_name = 'part/category_delete.html' + ajax_template_name = 'part/category_delete.html' context_object_name = 'category' success_url = '/part/' @@ -302,7 +299,6 @@ class CategoryCreate(AjaxCreateView): ajax_form_action = reverse_lazy('category-create') ajax_form_title = 'Create new part category' ajax_template_name = 'modal_form.html' - template_name = 'part/category_new.html' form_class = EditCategoryForm def get_context_data(self, **kwargs): @@ -345,7 +341,6 @@ class BomItemCreate(AjaxCreateView): """ Create view for making a new BomItem object """ model = BomItem form_class = EditBomItemForm - template_name = 'part/bom-create.html' ajax_template_name = 'modal_form.html' ajax_form_title = 'Create BOM item' @@ -372,7 +367,6 @@ class BomItemEdit(AjaxUpdateView): model = BomItem form_class = EditBomItemForm - template_name = 'part/bom-edit.html' ajax_template_name = 'modal_form.html' ajax_form_title = 'Edit BOM item' @@ -380,7 +374,7 @@ class BomItemEdit(AjaxUpdateView): class BomItemDelete(AjaxDeleteView): """ Delete view for removing BomItem """ model = BomItem - template_name = 'part/bom-delete.html' + ajax_template_name = 'part/bom-delete.html' context_object_name = 'item' ajax_form_title = 'Confim BOM item deletion' @@ -397,7 +391,6 @@ class SupplierPartEdit(AjaxUpdateView): """ Update view for editing SupplierPart """ model = SupplierPart - template_name = 'company/partedit.html' context_object_name = 'part' form_class = EditSupplierPartForm ajax_template_name = 'modal_form.html' @@ -440,4 +433,4 @@ class SupplierPartDelete(AjaxDeleteView): """ Delete view for removing a SupplierPart """ model = SupplierPart success_url = '/supplier/' - template_name = 'company/partdelete.html' + ajax_template_name = 'company/partdelete.html' From cd438f05699656a01d7ffe3f405fb6e88e56114a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 09:53:42 +1000 Subject: [PATCH 03/10] Add generic method for retriving GET or POST params --- InvenTree/InvenTree/views.py | 16 ++++++++++++++++ InvenTree/part/forms.py | 2 ++ InvenTree/part/views.py | 22 +++++++++++++--------- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 551f624cb9..ffd757ebdc 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -69,6 +69,22 @@ class AjaxMixin(object): ajax_form_action = '' ajax_form_title = '' + def get_param(self, name, method='GET'): + """ Get a request query parameter value from URL e.g. ?part=3 + + Args: + name: Variable name e.g. 'part' + method: Request type ('GET' or 'POST') + + Returns: + Value of the supplier parameter or None if parameter is not available + """ + + if method == 'POST': + return self.request.POST.get(name, None) + else: + return self.request.GET.get(name, None) + def get_data(self): """ Get extra context data (default implementation is empty dict) diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 1157012196..bba8c3e973 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -92,6 +92,8 @@ class EditBomItemForm(HelperForm): 'quantity', 'note' ] + + # Prevent editing of the part associated with this BomItem widgets = {'part': forms.HiddenInput()} diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 6db1edfc8e..7667b3908a 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -406,6 +406,7 @@ class SupplierPartCreate(AjaxCreateView): ajax_form_title = 'Create new Supplier Part' context_object_name = 'part' + def get_initial(self): """ Provide initial data for new SupplierPart: @@ -414,18 +415,21 @@ class SupplierPartCreate(AjaxCreateView): """ initials = super(SupplierPartCreate, self).get_initial().copy() - supplier_id = self.request.GET.get('supplier', None) - part_id = self.request.GET.get('part', None) + supplier_id = self.get_param('supplier') + part_id = self.get_param('part') if supplier_id: - initials['supplier'] = get_object_or_404(Company, pk=supplier_id) - # TODO - # self.fields['supplier'].disabled = True + try: + initials['supplier'] = Company.objects.get(pk=supplier_id) + except Company.DoesNotExist: + initials['supplier'] = None + if part_id: - initials['part'] = get_object_or_404(Part, pk=part_id) - # TODO - # self.fields['part'].disabled = True - + try: + initials['part'] = Part.objects.get(pk=part_id) + except Part.DoesNotExist: + initials['part'] = None + return initials From 96a15453c4e113e65e203d3250617a414892f594 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 10:35:17 +1000 Subject: [PATCH 04/10] Simplify AjaxCreateView - Screams in reading the documentation --- InvenTree/InvenTree/views.py | 44 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index ffd757ebdc..ee4a7bbacb 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -150,39 +150,37 @@ class AjaxCreateView(AjaxMixin, CreateView): """ def get(self, request, *args, **kwargs): + """ Creates form with initial data, and renders JSON response """ - response = super(CreateView, self).get(request, *args, **kwargs) + super(CreateView, self).get(request, *args, **kwargs) - if request.is_ajax(): - # Initialize a a new form - form = self.form_class(initial=self.get_initial()) - - return self.renderJsonResponse(request, form) - - else: - return response + form = self.get_form() + return self.renderJsonResponse(request, form) def post(self, request, *args, **kwargs): - form = self.form_class(data=request.POST, files=request.FILES) + """ Responds to form POST. Validates POST data and returns status info. - if request.is_ajax(): + Steps: + 1. Validate POST form data + 2. If valid, save form + 3. Return status info (success / failure) + """ + form = self.get_form() - data = { - 'form_valid': form.is_valid(), - } + # Extra JSON data sent alongside form + data = { + 'form_valid': form.is_valid(), + } - if form.is_valid(): - obj = form.save() + if form.is_valid(): + obj = form.save() - # Return the PK of the newly-created object - data['pk'] = obj.pk + # Return the PK of the newly-created object + data['pk'] = obj.pk - data['url'] = obj.get_absolute_url() + data['url'] = obj.get_absolute_url() - return self.renderJsonResponse(request, form, data) - - else: - return super(CreateView, self).post(request, *args, **kwargs) + return self.renderJsonResponse(request, form, data) class AjaxUpdateView(AjaxMixin, UpdateView): From 72ff25e0c138ff3c84cfce6b23e18d1c9790f757 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 10:39:46 +1000 Subject: [PATCH 05/10] Simplify AjaxUpdateView - Using get_form() is MUCH simpler --- InvenTree/InvenTree/views.py | 37 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index ee4a7bbacb..0aa0e38e63 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -184,7 +184,6 @@ class AjaxCreateView(AjaxMixin, CreateView): class AjaxUpdateView(AjaxMixin, UpdateView): - """ An 'AJAXified' UpdateView for updating an object in the db - Returns form in JSON format (for delivery to a modal window) - Handles repeated form validation (via AJAX) until the form is valid @@ -192,35 +191,31 @@ class AjaxUpdateView(AjaxMixin, UpdateView): def get(self, request, *args, **kwargs): - html_response = super(UpdateView, self).get(request, *args, **kwargs) + super(UpdateView, self).get(request, *args, **kwargs) - if request.is_ajax(): - form = self.form_class(instance=self.get_object()) + form = self.get_form() + # form = self.form_class(instance=self.get_object()) - return self.renderJsonResponse(request, form) - - else: - return html_response + return self.renderJsonResponse(request, form) def post(self, request, *args, **kwargs): - form = self.form_class(instance=self.get_object(), data=request.POST, files=request.FILES) + super(UpdateView, self).post(request, *args, **kwargs) - if request.is_ajax(): + form = self.get_form() - data = {'form_valid': form.is_valid()} + data = { + 'form_valid': form.is_valid() + } - if form.is_valid(): - obj = form.save() + if form.is_valid(): + obj = form.save() + + # Include context data about the updated object + data['pk'] = obj.id + data['url'] = obj.get_absolute_url() - data['pk'] = obj.id - data['url'] = obj.get_absolute_url() - - response = self.renderJsonResponse(request, form, data) - return response - - else: - return super(UpdateView, self).post(request, *args, **kwargs) + return self.renderJsonResponse(request, form, data) class AjaxDeleteView(AjaxMixin, DeleteView): From ff068de16d597e7c56cdee6bbde35e6cedf3abcb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 10:41:54 +1000 Subject: [PATCH 06/10] Simplify AjaxDeleteView --- InvenTree/InvenTree/views.py | 46 ++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 0aa0e38e63..e7dad657ba 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -227,39 +227,33 @@ class AjaxDeleteView(AjaxMixin, DeleteView): def get(self, request, *args, **kwargs): - html_response = super(DeleteView, self).get(request, *args, **kwargs) + super(DeleteView, self).get(request, *args, **kwargs) - if request.is_ajax(): + data = { + 'id': self.get_object().id, + 'delete': False, + 'title': self.ajax_form_title, + 'html_data': render_to_string( + self.ajax_template_name, + self.get_context_data(), + request=request) + } - data = {'id': self.get_object().id, - 'delete': False, - 'title': self.ajax_form_title, - 'html_data': render_to_string(self.ajax_template_name, - self.get_context_data(), - request=request) - } - - return JsonResponse(data) - - else: - return html_response + return JsonResponse(data) def post(self, request, *args, **kwargs): - if request.is_ajax(): + obj = self.get_object() + pk = obj.id + obj.delete() - obj = self.get_object() - pk = obj.id - obj.delete() - - data = {'id': pk, - 'delete': True} - - return self.renderJsonResponse(request, data=data) - - else: - return super(DeleteView, self).post(request, *args, **kwargs) + data = { + 'id': pk, + 'delete': True + } + return self.renderJsonResponse(request, data=data) + class IndexView(TemplateView): """ View for InvenTree index page """ From 101ce53cd30106956860d8e67805276121f41074 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 10:46:07 +1000 Subject: [PATCH 07/10] Update docstrings for InvenTree/views.py --- InvenTree/InvenTree/views.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index e7dad657ba..21adcd0021 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -160,10 +160,9 @@ class AjaxCreateView(AjaxMixin, CreateView): def post(self, request, *args, **kwargs): """ Responds to form POST. Validates POST data and returns status info. - Steps: - 1. Validate POST form data - 2. If valid, save form - 3. Return status info (success / failure) + - Validate POST form data + - If valid, save form + - Return status info (success / failure) """ form = self.get_form() @@ -190,6 +189,11 @@ class AjaxUpdateView(AjaxMixin, UpdateView): """ def get(self, request, *args, **kwargs): + """ Respond to GET request. + + - Populates form with object data + - Renders form to JSON and returns to client + """ super(UpdateView, self).get(request, *args, **kwargs) @@ -199,6 +203,13 @@ class AjaxUpdateView(AjaxMixin, UpdateView): return self.renderJsonResponse(request, form) def post(self, request, *args, **kwargs): + """ Respond to POST request. + + - Updates model with POST field data + - Performs form and object validation + - If errors exist, re-render the form + - Otherwise, return sucess status + """ super(UpdateView, self).post(request, *args, **kwargs) @@ -226,6 +237,11 @@ class AjaxDeleteView(AjaxMixin, DeleteView): """ def get(self, request, *args, **kwargs): + """ Respond to GET request + + - Render a DELETE confirmation form to JSON + - Return rendered form to client + """ super(DeleteView, self).get(request, *args, **kwargs) @@ -242,6 +258,11 @@ class AjaxDeleteView(AjaxMixin, DeleteView): return JsonResponse(data) def post(self, request, *args, **kwargs): + """ Respond to POST request + + - DELETE the object + - Render success message to JSON and return to client + """ obj = self.get_object() pk = obj.id @@ -253,7 +274,7 @@ class AjaxDeleteView(AjaxMixin, DeleteView): } return self.renderJsonResponse(request, data=data) - + class IndexView(TemplateView): """ View for InvenTree index page """ From 624c5094c5851bfbef63569ab5ce393f25238010 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 10:57:32 +1000 Subject: [PATCH 08/10] Prevent editing of SupplierPart fields if initial values are provided --- InvenTree/part/views.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 7667b3908a..b8762f556d 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -406,6 +406,23 @@ class SupplierPartCreate(AjaxCreateView): ajax_form_title = 'Create new Supplier Part' context_object_name = 'part' + def get_form(self): + form = super(AjaxCreateView, self).get_form() + + # Was the form supplied with initial data? + + print("Initial:", form.initial) + print(dir(form)) + + if form.initial.get('supplier', None): + # Hide the supplier field + form.fields['supplier'].widget.attrs['disabled'] = True + + if form.initial.get('part', None): + # Hide the part field + form.fields['part'].widget.attrs['disabled'] = True + + return form def get_initial(self): """ Provide initial data for new SupplierPart: From a9fbbc3a37cf44c85db14d0a4a521cc9bebb8ca7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 11:09:19 +1000 Subject: [PATCH 09/10] Remove references to get_object_or_404 - Properly handle DoesNotExist errors --- InvenTree/InvenTree/views.py | 7 +++---- InvenTree/build/views.py | 5 ++++- InvenTree/part/api.py | 27 +++++++++++++++------------ InvenTree/part/views.py | 30 ++++++++++++++++++++++++------ InvenTree/stock/api.py | 27 +++++++++++++++------------ InvenTree/stock/views.py | 24 +++++++++++++++++------- 6 files changed, 78 insertions(+), 42 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 21adcd0021..26303a6549 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -150,7 +150,7 @@ class AjaxCreateView(AjaxMixin, CreateView): """ def get(self, request, *args, **kwargs): - """ Creates form with initial data, and renders JSON response """ + """ Creates form with initial data, and renders JSON response """ super(CreateView, self).get(request, *args, **kwargs) @@ -161,7 +161,7 @@ class AjaxCreateView(AjaxMixin, CreateView): """ Responds to form POST. Validates POST data and returns status info. - Validate POST form data - - If valid, save form + - If valid, save form - Return status info (success / failure) """ form = self.get_form() @@ -198,8 +198,7 @@ class AjaxUpdateView(AjaxMixin, UpdateView): super(UpdateView, self).get(request, *args, **kwargs) form = self.get_form() - # form = self.form_class(instance=self.get_object()) - + return self.renderJsonResponse(request, form) def post(self, request, *args, **kwargs): diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index c8d40e80e6..dc7b3141c3 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -101,7 +101,10 @@ class BuildCreate(AjaxCreateView): part_id = self.request.GET.get('part', None) if part_id: - initials['part'] = get_object_or_404(Part, pk=part_id) + try: + initials['part'] = Part.objects.get(pk=part_id) + except Part.DoesNotExist: + pass return initials diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 8f93cede7c..46a15cfc62 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -11,7 +11,6 @@ from rest_framework import generics, permissions from django.db.models import Q from django.conf.urls import url, include -from django.shortcuts import get_object_or_404 from .models import Part, PartCategory, BomItem from .models import SupplierPart, SupplierPriceBreak @@ -99,20 +98,24 @@ class PartList(generics.ListCreateAPIView): parts_list = Part.objects.all() if cat_id: - category = get_object_or_404(PartCategory, pk=cat_id) + try: + category = PartCategory.objects.get(pk=cat_id) + + # Filter by the supplied category + flt = Q(category=cat_id) - # Filter by the supplied category - flt = Q(category=cat_id) + if self.request.query_params.get('include_child_categories', None): + childs = category.getUniqueChildren() + for child in childs: + # Ignore the top-level category (already filtered) + if str(child) == str(cat_id): + continue + flt |= Q(category=child) - if self.request.query_params.get('include_child_categories', None): - childs = category.getUniqueChildren() - for child in childs: - # Ignore the top-level category (already filtered) - if str(child) == str(cat_id): - continue - flt |= Q(category=child) + parts_list = parts_list.filter(flt) - parts_list = parts_list.filter(flt) + except PartCategory.DoesNotExist: + pass return parts_list diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index b8762f556d..eb7d9ea0f3 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -84,7 +84,10 @@ class PartCreate(AjaxCreateView): cat_id = self.get_category_id() if cat_id: - context['category'] = get_object_or_404(PartCategory, pk=cat_id) + try: + context['category'] = PartCategory.objects.get(pk=cat_id) + except PartCategory.DoesNotExist: + pass return context @@ -111,7 +114,10 @@ class PartCreate(AjaxCreateView): initials = super(PartCreate, self).get_initial() if self.get_category_id(): - initials['category'] = get_object_or_404(PartCategory, pk=self.get_category_id()) + try: + initials['category'] = PartCategory.objects.get(pk=self.get_category_id()) + except PartCategory.DoesNotExist: + pass return initials @@ -275,7 +281,10 @@ class CategoryEdit(AjaxUpdateView): def get_context_data(self, **kwargs): context = super(CategoryEdit, self).get_context_data(**kwargs).copy() - context['category'] = get_object_or_404(PartCategory, pk=self.kwargs['pk']) + try: + context['category'] = PartCategory.objects.get(pk=self.kwargs['pk']) + except: + pass return context @@ -311,7 +320,10 @@ class CategoryCreate(AjaxCreateView): parent_id = self.request.GET.get('category', None) if parent_id: - context['category'] = get_object_or_404(PartCategory, pk=parent_id) + try: + context['category'] = PartCategory.objects.get(pk=parent_id) + except PartCategory.DoesNotExist: + pass return context @@ -325,7 +337,10 @@ class CategoryCreate(AjaxCreateView): parent_id = self.request.GET.get('category', None) if parent_id: - initials['parent'] = get_object_or_404(PartCategory, pk=parent_id) + try: + initials['parent'] = PartCategory.objects.get(pk=parent_id) + except PartCategory.DoesNotExist: + pass return initials @@ -357,7 +372,10 @@ class BomItemCreate(AjaxCreateView): parent_id = self.request.GET.get('parent', None) if parent_id: - initials['part'] = get_object_or_404(Part, pk=parent_id) + try: + initials['part'] = Part.objects.get(pk=parent_id) + except Part.DoesNotExist: + pass return initials diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index c352605ad2..8604f54c8f 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -7,7 +7,6 @@ from django_filters import NumberFilter from django.conf.urls import url, include from django.db.models import Q -from django.shortcuts import get_object_or_404 from .models import StockLocation, StockItem from .models import StockItemTracking @@ -238,20 +237,24 @@ class StockList(generics.ListCreateAPIView): stock_list = StockItem.objects.all() if loc_id: - location = get_object_or_404(StockLocation, pk=loc_id) + try: + location = StockLocation.objects.get(pk=loc_id) - # Filter by the supplied category - flt = Q(location=loc_id) + # Filter by the supplied category + flt = Q(location=loc_id) - if self.request.query_params.get('include_child_locations', None): - childs = location.getUniqueChildren() - for child in childs: - # Ignore the top-level category (already filtered!) - if str(child) == str(loc_id): - continue - flt |= Q(location=child) + if self.request.query_params.get('include_child_locations', None): + childs = location.getUniqueChildren() + for child in childs: + # Ignore the top-level category (already filtered!) + if str(child) == str(loc_id): + continue + flt |= Q(location=child) - stock_list = stock_list.filter(flt) + stock_list = stock_list.filter(flt) + + except StockLocation.DoesNotExist: + pass return stock_list diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 70d3f7ded3..b9c258f79d 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -5,8 +5,6 @@ Django views for interacting with Stock app # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.shortcuts import get_object_or_404 - from django.views.generic import DetailView, ListView from django.forms.models import model_to_dict @@ -106,7 +104,10 @@ class StockLocationCreate(AjaxCreateView): loc_id = self.request.GET.get('location', None) if loc_id: - initials['parent'] = get_object_or_404(StockLocation, pk=loc_id) + try: + initials['parent'] = StockLocation.objects.get(pk=loc_id) + except StockLocation.DoesNotExist: + pass return initials @@ -126,6 +127,8 @@ class StockItemCreate(AjaxCreateView): ajax_form_title = 'Create new Stock Item' def get_initial(self): + """ Provide initial data to create a new StockItem object + """ # Is the client attempting to copy an existing stock item? item_to_copy = self.request.GET.get('copy', None) @@ -144,15 +147,22 @@ class StockItemCreate(AjaxCreateView): part_id = self.request.GET.get('part', None) loc_id = self.request.GET.get('location', None) + # Part field has been specified if part_id: - part = get_object_or_404(Part, pk=part_id) - if part: - initials['part'] = get_object_or_404(Part, pk=part_id) + try: + part = Part.objects.get(pk=part_id) + initials['part'] = part initials['location'] = part.default_location initials['supplier_part'] = part.default_supplier + except Part.DoesNotExist: + pass + # Location has been specified if loc_id: - initials['location'] = get_object_or_404(StockLocation, pk=loc_id) + try: + initials['location'] = StockLocation.objects.get(pk=loc_id) + except StockLocation.DoesNotExist: + pass return initials From 1171953e65de65e567f85e54911bb3e7342900dd Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 11:24:26 +1000 Subject: [PATCH 10/10] Limit SupplierPart options in CreateBomItem form - If Part is selected, limit the options in supplier_part field - Only allow supplier_parts which map back to the same part --- InvenTree/part/views.py | 5 ----- InvenTree/stock/views.py | 25 +++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index eb7d9ea0f3..d0f3a1ac01 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -426,11 +426,6 @@ class SupplierPartCreate(AjaxCreateView): def get_form(self): form = super(AjaxCreateView, self).get_form() - - # Was the form supplied with initial data? - - print("Initial:", form.initial) - print(dir(form)) if form.initial.get('supplier', None): # Hide the supplier field diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index b9c258f79d..a1f0d2335c 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -126,6 +126,27 @@ class StockItemCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' ajax_form_title = 'Create new Stock Item' + def get_form(self): + """ Get form for StockItem creation. + Overrides the default get_form() method to intelligently limit + ForeignKey choices based on other selections + """ + + form = super(AjaxCreateView, self).get_form() + + # If the user has selected a Part, limit choices for SupplierPart + if form['part'].value() is not None: + part = form['part'].value() + parts = form.fields['supplier_part'].queryset + parts = parts.filter(part=part) + form.fields['supplier_part'].queryset = parts + + # Otherwise if the user has selected a SupplierPart, we know what Part they meant! + elif form['supplier_part'].value() is not None: + pass + + return form + def get_initial(self): """ Provide initial data to create a new StockItem object """ @@ -188,7 +209,7 @@ class StockItemDelete(AjaxDeleteView): model = StockItem success_url = '/stock/' - template_name = 'stock/item_delete.html' + ajax_template_name = 'stock/item_delete.html' context_object_name = 'item' ajax_form_title = 'Delete Stock Item' @@ -200,7 +221,7 @@ class StockItemMove(AjaxUpdateView): """ model = StockItem - template_name = 'modal_form.html' + ajax_template_name = 'modal_form.html' context_object_name = 'item' ajax_form_title = 'Move Stock Item' form_class = MoveStockItemForm