From 77471cb89c87808e3f49bf6149d1259ee0382efd Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 24 Apr 2020 10:20:56 +1000 Subject: [PATCH] Form for shipping a SalesOrder - Returns "False" for now --- InvenTree/build/views.py | 4 +- InvenTree/order/forms.py | 11 ++++ InvenTree/order/models.py | 15 +++++ .../templates/order/sales_order_base.html | 6 ++ .../templates/order/sales_order_cancel.html | 5 +- .../templates/order/sales_order_detail.html | 10 +-- .../templates/order/sales_order_ship.html | 28 ++++++++ .../templates/order/so_allocation_delete.html | 14 ++++ .../templates/order/so_lineitem_delete.html | 3 +- InvenTree/order/urls.py | 1 + InvenTree/order/views.py | 64 ++++++++++++++++++- InvenTree/stock/views.py | 2 +- InvenTree/templates/modal_form.html | 6 +- 13 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 InvenTree/order/templates/order/sales_order_ship.html create mode 100644 InvenTree/order/templates/order/so_allocation_delete.html diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index feda6ee9eb..b785bcb914 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -125,7 +125,7 @@ class BuildAutoAllocate(AjaxUpdateView): if confirm is False: form.errors['confirm'] = [_('Confirm stock allocation')] - form.non_field_errors = _('Check the confirmation box at the bottom of the list') + form.non_field_errors = [_('Check the confirmation box at the bottom of the list')] else: build.autoAllocate() valid = True @@ -159,7 +159,7 @@ class BuildUnallocate(AjaxUpdateView): if confirm is False: form.errors['confirm'] = [_('Confirm unallocation of build stock')] - form.non_field_errors = _('Check the confirmation box') + form.non_field_errors = [_('Check the confirmation box')] else: build.unallocateStock() valid = True diff --git a/InvenTree/order/forms.py b/InvenTree/order/forms.py index cc7535bac9..9991fe6670 100644 --- a/InvenTree/order/forms.py +++ b/InvenTree/order/forms.py @@ -63,6 +63,17 @@ class CancelSalesOrderForm(HelperForm): ] +class ShipSalesOrderForm(HelperForm): + + confirm = forms.BooleanField(required=False, help_text=_('Ship order')) + + class Meta: + model = SalesOrder + fields = [ + 'confirm', + ] + + class ReceivePurchaseOrderForm(HelperForm): location = TreeNodeChoiceField(queryset=StockLocation.objects.all(), required=True, help_text=_('Receive parts to this location')) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 85eabfa3d0..d3d0314f38 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -302,10 +302,21 @@ class SalesOrder(Order): return True + def is_over_allocated(self): + """ Return true if any lines in the order are over-allocated """ + + for line in self.lines.all(): + if line.is_over_allocated(): + return True + + return False + @transaction.atomic def ship_order(self, user): """ Mark this order as 'shipped' """ + return False + if not self.status == SalesOrderStatus.PENDING: return False @@ -447,6 +458,10 @@ class SalesOrderLineItem(OrderLineItem): return query['allocated'] def is_fully_allocated(self): + print("Line:", self.pk) + print("Allocated:", self.allocated_quantity()) + print("Quantity:", self.quantity) + return self.allocated_quantity() >= self.quantity def is_over_allocated(self): diff --git a/InvenTree/order/templates/order/sales_order_base.html b/InvenTree/order/templates/order/sales_order_base.html index 9aeaa02e73..9a8a061a79 100644 --- a/InvenTree/order/templates/order/sales_order_base.html +++ b/InvenTree/order/templates/order/sales_order_base.html @@ -124,4 +124,10 @@ $("#cancel-order").click(function() { }); }); +$("#ship-order").click(function() { + launchModalForm("{% url 'so-ship' order.id %}", { + reload: true, + }); +}); + {% endblock %} \ No newline at end of file diff --git a/InvenTree/order/templates/order/sales_order_cancel.html b/InvenTree/order/templates/order/sales_order_cancel.html index 91707ae737..2f0fe3beb1 100644 --- a/InvenTree/order/templates/order/sales_order_cancel.html +++ b/InvenTree/order/templates/order/sales_order_cancel.html @@ -4,6 +4,9 @@ {% block pre_form_content %} -{% trans "Cancelling this order means that the order will no longer be editable." %} +
+

{% trans "Warning" %}

+ {% trans "Cancelling this order means that the order will no longer be editable." %} +
{% endblock %} \ No newline at end of file diff --git a/InvenTree/order/templates/order/sales_order_detail.html b/InvenTree/order/templates/order/sales_order_detail.html index e4200f73f8..fb6feeab78 100644 --- a/InvenTree/order/templates/order/sales_order_detail.html +++ b/InvenTree/order/templates/order/sales_order_detail.html @@ -23,9 +23,13 @@ {% block js_ready %} {{ block.super }} +function reloadTable() { + $("#so-lines-table").bootstrapTable("refresh"); +} + $("#new-so-line").click(function() { launchModalForm("{% url 'so-line-item-create' %}", { - reload: true, + success: reloadTable, data: { order: {{ order.id }}, }, @@ -200,10 +204,6 @@ $("#so-lines-table").inventreeTable({ ], }); -function reloadTable() { - $("#so-lines-table").bootstrapTable("refresh"); -} - function setupCallbacks(table) { var table = $("#so-lines-table"); diff --git a/InvenTree/order/templates/order/sales_order_ship.html b/InvenTree/order/templates/order/sales_order_ship.html new file mode 100644 index 0000000000..cb4a01f2f0 --- /dev/null +++ b/InvenTree/order/templates/order/sales_order_ship.html @@ -0,0 +1,28 @@ +{% extends "modal_form.html" %} + +{% load i18n %} + +{% block pre_form_content %} + +{% if not order.is_fully_allocated %} +
+

{% trans "Warning" %}

+ {% trans "This order has not been fully allocated. If the order is marked as shipped, it can no longer be adjusted." %} +
+ {% trans "Ensure that the order allocation is correct before shipping the order." %} +
+{% endif %} + +{% if order.is_over_allocated %} +
+ {% trans "Some line items in this order have been over-allocated" %} +
+ {% trans "Ensure that this is correct before shipping the order." %} +
+{% endif %} + +
+ {% trans "Shipping this order means that the order will no longer be editable." %} +
+ +{% endblock %} \ No newline at end of file diff --git a/InvenTree/order/templates/order/so_allocation_delete.html b/InvenTree/order/templates/order/so_allocation_delete.html new file mode 100644 index 0000000000..e4cbe0b602 --- /dev/null +++ b/InvenTree/order/templates/order/so_allocation_delete.html @@ -0,0 +1,14 @@ +{% extends "modal_delete_form.html" %} +{% load i18n %} +{% load inventree_extras %} + +{% block pre_form_content %} +
+ {% trans "This action will unallocate the following stock from the Sales Order" %}: +
+ + {% decimal allocation.get_allocated %} x {{ allocation.line.part.full_name }} + {% if allocation.item.location %} ({{ allocation.get_location }}){% endif %} + +
+{% endblock %} \ No newline at end of file diff --git a/InvenTree/order/templates/order/so_lineitem_delete.html b/InvenTree/order/templates/order/so_lineitem_delete.html index 3264fea625..1d9f80d137 100644 --- a/InvenTree/order/templates/order/so_lineitem_delete.html +++ b/InvenTree/order/templates/order/so_lineitem_delete.html @@ -1,5 +1,6 @@ {% extends "modal_delete_form.html" %} +{% load i18n %} {% block pre_form_content %} -Are you sure you wish to delete this line item? +{% trans "Are you sure you wish to delete this line item?" %} {% endblock %} \ No newline at end of file diff --git a/InvenTree/order/urls.py b/InvenTree/order/urls.py index b6b57e9518..703899d60f 100644 --- a/InvenTree/order/urls.py +++ b/InvenTree/order/urls.py @@ -56,6 +56,7 @@ sales_order_detail_urls = [ url(r'^edit/', views.SalesOrderEdit.as_view(), name='so-edit'), url(r'^cancel/', views.SalesOrderCancel.as_view(), name='so-cancel'), + url(r'^ship/', views.SalesOrderShip.as_view(), name='so-ship'), url(r'^attachments/', views.SalesOrderDetail.as_view(template_name='order/so_attachments.html'), name='so-attachments'), url(r'^notes/', views.SalesOrderNotes.as_view(), name='so-notes'), diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index 8242b98daf..15da7e57fd 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -416,13 +416,16 @@ class SalesOrderCancel(AjaxUpdateView): else: valid = True + + if valid: + if not order.cancel_order(): + form.non_field_errors = [_('Could not cancel order')] + valid = False + data = { 'form_valid': valid, } - if valid: - order.cancel_order() - return self.renderJsonResponse(request, form, data) @@ -495,6 +498,47 @@ class PurchaseOrderComplete(AjaxUpdateView): return self.renderJsonResponse(request, form, data) + +class SalesOrderShip(AjaxUpdateView): + """ View for 'shipping' a SalesOrder """ + form_class = order_forms.ShipSalesOrderForm + model = SalesOrder + context_object_name = 'order' + ajax_template_name = 'order/sales_order_ship.html' + ajax_form_title = _('Ship Order') + + def context_data(self): + ctx = super().get_context_data() + ctx['order'] = self.get_object() + + return ctx + + def post(self, request, *args, **kwargs): + + order = self.get_object() + form = self.get_form() + + confirm = str2bool(request.POST.get('confirm', False)) + + valid = False + + if not confirm: + form.errors['confirm'] = [_('Confirm order shipment')] + else: + valid = True + + if valid: + if not order.ship_order(request.user): + form.non_field_errors = [_('Could not ship order')] + valid = False + + data = { + 'form_valid': valid, + } + + return self.renderJsonResponse(request, form, data) + + class PurchaseOrderExport(AjaxView): """ File download for a purchase order @@ -1111,6 +1155,18 @@ class SOLineItemCreate(AjaxCreateView): form_class = order_forms.EditSalesOrderLineItemForm ajax_form_title = _('Add Line Item') + def get_form(self, *args, **kwargs): + + form = super().get_form(*args, **kwargs) + + # If the order is specified, hide the widget + order_id = form['order'].value() + + if SalesOrder.objects.filter(id=order_id).exists(): + form.fields['order'].widget = HiddenInput() + + return form + def get_initial(self): """ Extract initial data for this line item: @@ -1293,3 +1349,5 @@ class SalesOrderAllocationDelete(AjaxDeleteView): model = SalesOrderAllocation ajax_form_title = _("Remove allocation") + context_object_name = 'allocation' + ajax_template_name = "order/so_allocation_delete.html" diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 24c61fd89b..638f15569d 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -731,7 +731,7 @@ class StockItemSerialize(AjaxUpdateView): if k in ['quantity', 'destination', 'serial_numbers']: form.errors[k] = messages[k] else: - form.non_field_errors = messages[k] + form.non_field_errors = [messages[k]] valid = False diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html index 98f1c49693..0a5ae59916 100644 --- a/InvenTree/templates/modal_form.html +++ b/InvenTree/templates/modal_form.html @@ -13,7 +13,11 @@ {% if form.non_field_errors %} {% endif %} {% endblock %}