diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 8e324ef7c5..ac530a1386 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -1122,7 +1122,10 @@ def render_currency(money, decimal_places=None, currency=None, include_symbol=Tr include_symbol: Render with the appropriate currency symbol """ - if money is None or money.amount is None: + if money in [None, '']: + return '-' + + if type(money) is not Money: return '-' if currency is not None: diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index 67033b31ee..fbe117cab9 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -1313,6 +1313,28 @@ class ReturnOrderDetail(RetrieveUpdateDestroyAPI): return self.serializer_class(*args, **kwargs) +class ReturnOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): + """API endpoint for listing (and creating) a ReturnOrderAttachment (file upload)""" + + queryset = models.ReturnOrderAttachment.objects.all() + serializer_class = serializers.ReturnOrderAttachmentSerializer + + filter_backends = [ + rest_filters.DjangoFilterBackend, + ] + + filterset_fields = [ + 'order', + ] + + +class ReturnOrderAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI): + """Detail endpoint for the ReturnOrderAttachment model""" + + queryset = models.ReturnOrderAttachment.objects.all() + serializer_class = serializers.ReturnOrderAttachmentSerializer + + class OrderCalendarExport(ICalFeed): """Calendar export for Purchase/Sales Orders @@ -1474,7 +1496,7 @@ order_api_urls = [ ])), # Individual purchase order detail URLs - re_path(r'^(?P\d+)/', include([ + path(r'/', include([ re_path(r'^cancel/', PurchaseOrderCancel.as_view(), name='api-po-cancel'), re_path(r'^complete/', PurchaseOrderComplete.as_view(), name='api-po-complete'), re_path(r'^issue/', PurchaseOrderIssue.as_view(), name='api-po-issue'), @@ -1509,7 +1531,7 @@ order_api_urls = [ ])), re_path(r'^shipment/', include([ - re_path(r'^(?P\d+)/', include([ + path(r'/', include([ path('ship/', SalesOrderShipmentComplete.as_view(), name='api-so-shipment-ship'), re_path(r'^.*$', SalesOrderShipmentDetail.as_view(), name='api-so-shipment-detail'), ])), @@ -1517,7 +1539,7 @@ order_api_urls = [ ])), # Sales order detail view - re_path(r'^(?P\d+)/', include([ + path(r'/', include([ re_path(r'^allocate/', SalesOrderAllocate.as_view(), name='api-so-allocate'), re_path(r'^allocate-serials/', SalesOrderAllocateSerials.as_view(), name='api-so-allocate-serials'), re_path(r'^cancel/', SalesOrderCancel.as_view(), name='api-so-cancel'), @@ -1553,6 +1575,11 @@ order_api_urls = [ # API endpoints for return orders re_path(r'^return/', include([ + re_path(r'^attachment/', include([ + path('/', ReturnOrderAttachmentDetail.as_view(), name='api-return-order-attachment-detail'), + re_path(r'^.*$', ReturnOrderAttachmentList.as_view(), name='api-return-order-attachment-list'), + ])), + # Return Order detail path('/', ReturnOrderDetail.as_view(), name='api-return-order-detail'), diff --git a/InvenTree/order/templates/order/return_order_base.html b/InvenTree/order/templates/order/return_order_base.html new file mode 100644 index 0000000000..8d57c6c61c --- /dev/null +++ b/InvenTree/order/templates/order/return_order_base.html @@ -0,0 +1,115 @@ +{% extends "page_base.html" %} + +{% load i18n %} +{% load static %} +{% load inventree_extras %} +{% load status_codes %} + +{% block page_title %} +{% inventree_title %} | {% trans "Return Order" %} +{% endblock page_title %} + +{% block breadcrumbs %} + + +{% endblock breadcrumbs %} + +{% block thumbnail %} + +{% endblock thumbnail%} + +{% block heading %} +{% trans "Return Order" %} {{ order.reference }} +{% endblock heading %} + +{% block actions %} +{% if user.is_staff and roles.return_order.change %} +{% url 'admin:order_returnorder_change' order.pk as url %} +{% include "admin_button.html" with url=url %} +{% endif %} + + +{% endblock actions %} + +{% block details %} + + + + + + + + + + + + + + + + + + +
{% trans "Order Reference" %}{{ order.reference }}{% include "clip.html"%}
{% trans "Order Description" %}{{ order.description }}{% include "clip.html" %}
{% trans "Order Status" %} + {% return_order_status_label order.status %} +
+ +{% endblock details %} + +{% block details_right %} + + + {% if order.customer %} + + + + + + {% endif %} + {% if order.customer_reference %} + + + + + + {% endif %} + {% if order.link %} + + + + + + {% endif %} + + + + + + {% if order.responsible %} + + + + + + {% endif %} +
{% trans "Customer" %}{{ order.customer.name }}{% include "clip.html"%}
{% trans "Customer Reference" %}{{ order.customer_reference }}{% include "clip.html"%}
External Link{{ order.link }}{% include "clip.html"%}
{% trans "Created" %}{% render_date order.creation_date %}{{ order.created_by }}
{% trans "Responsible" %}{{ order.responsible }}
+{% endblock details_right %} + +{% block js_ready %} +{{ block.super }} + + + +{% if report_enabled %} + +{% endif %} + + + +{% endblock js_ready %} diff --git a/InvenTree/order/templates/order/return_order_detail.html b/InvenTree/order/templates/order/return_order_detail.html new file mode 100644 index 0000000000..8cff9bbf9a --- /dev/null +++ b/InvenTree/order/templates/order/return_order_detail.html @@ -0,0 +1,111 @@ +{% extends "order/return_order_base.html" %} + +{% load inventree_extras %} +{% load status_codes %} +{% load i18n %} +{% load static %} + +{% block sidebar %} +{% include "order/return_order_sidebar.html" %} +{% endblock %} + +{% block page_content %} + +
+
+

{% trans "Order Details" %}

+ {% include "spacer.html" %} +
+
+ +
+
+ +
+
+
+

{% trans "Attachments" %}

+ {% include "spacer.html" %} +
+ {% include "attachment_button.html" %} +
+
+
+
+ {% include "attachment_table.html" %} +
+
+ +
+
+
+

{% trans "Order Notes" %}

+ {% include "spacer.html" %} +
+ {% include "notes_buttons.html" %} +
+
+
+
+ +
+
+ + +{% endblock page_content %} + +{% block js_ready %} +{{ block.super }} + +// Callback function when the 'details' panel is loaded +onPanelLoad('order-details', function() { + // TODO +}); + +// Callback function when the 'notes' panel is loaded +onPanelLoad('order-notes', function() { + setupNotesField( + 'order-notes', + '{% url "api-return-order-detail" order.pk %}', + { + {% if roles.purchase_order.change %} + editable: true, + {% else %} + editable: false, + {% endif %} + } + ); +}); + +// Callback function when the 'attachments' panel is loaded +onPanelLoad('order-attachments', function() { + enableDragAndDrop( + '#attachment-dropzone', + '{% url "api-return-order-attachment-list" %}', + { + data: { + order: {{ order.id }}, + }, + label: 'attachment', + success: function(data, status, xhr) { + reloadAttachmentTable(); + } + } + ); + + loadAttachmentTable('{% url "api-return-order-attachment-list" %}', { + filters: { + order: {{ order.pk }}, + }, + fields: { + order: { + value: {{ order.pk }}, + hidden: true, + }, + } + }); +}); + +enableSidebar('returnorder'); + +{% endblock js_ready %} diff --git a/InvenTree/order/templates/order/return_order_sidebar.html b/InvenTree/order/templates/order/return_order_sidebar.html new file mode 100644 index 0000000000..a9a0519885 --- /dev/null +++ b/InvenTree/order/templates/order/return_order_sidebar.html @@ -0,0 +1,10 @@ +{% load i18n %} +{% load static %} +{% load inventree_extras %} + +{% trans "Order Details" as text %} +{% include "sidebar_item.html" with label='order-details' text=text icon="fa-info-circle" %} +{% trans "Attachments" as text %} +{% include "sidebar_item.html" with label='order-attachments' text=text icon="fa-paperclip" %} +{% trans "Notes" as text %} +{% include "sidebar_item.html" with label='order-notes' text=text icon="fa-clipboard" %} diff --git a/InvenTree/order/templates/order/sales_order_detail.html b/InvenTree/order/templates/order/sales_order_detail.html index 5b28b88f1a..269a1f1397 100644 --- a/InvenTree/order/templates/order/sales_order_detail.html +++ b/InvenTree/order/templates/order/sales_order_detail.html @@ -209,30 +209,32 @@ ); }); - enableDragAndDrop( - '#attachment-dropzone', - '{% url "api-so-attachment-list" %}', - { - data: { - order: {{ order.id }}, - }, - label: 'attachment', - success: function(data, status, xhr) { - reloadAttachmentTable(); + onPanelLoad('order-attachments', function() { + enableDragAndDrop( + '#attachment-dropzone', + '{% url "api-so-attachment-list" %}', + { + data: { + order: {{ order.id }}, + }, + label: 'attachment', + success: function(data, status, xhr) { + reloadAttachmentTable(); + } } - } - ); + ); - loadAttachmentTable('{% url "api-so-attachment-list" %}', { - filters: { - order: {{ order.pk }}, - }, - fields: { - order: { - value: {{ order.pk }}, - hidden: true, + loadAttachmentTable('{% url "api-so-attachment-list" %}', { + filters: { + order: {{ order.pk }}, }, - } + fields: { + order: { + value: {{ order.pk }}, + hidden: true, + }, + } + }); }); loadBuildTable($("#builds-table"), { diff --git a/InvenTree/order/urls.py b/InvenTree/order/urls.py index aca666702d..7305bf8543 100644 --- a/InvenTree/order/urls.py +++ b/InvenTree/order/urls.py @@ -4,7 +4,7 @@ - Detail view of Purchase Orders """ -from django.urls import include, re_path +from django.urls import include, path, re_path from . import views @@ -21,7 +21,7 @@ purchase_order_urls = [ re_path(r'^pricing/', views.LineItemPricing.as_view(), name='line-pricing'), # Display detail view for a single purchase order - re_path(r'^(?P\d+)/', include(purchase_order_detail_urls)), + path(r'/', include(purchase_order_detail_urls)), # Display complete list of purchase orders re_path(r'^.*$', views.PurchaseOrderIndex.as_view(), name='purchase-order-index'), @@ -35,7 +35,7 @@ sales_order_detail_urls = [ sales_order_urls = [ # Display detail view for a single SalesOrder - re_path(r'^(?P\d+)/', include(sales_order_detail_urls)), + path(r'/', include(sales_order_detail_urls)), # Display list of all sales orders re_path(r'^.*$', views.SalesOrderIndex.as_view(), name='sales-order-index'), @@ -43,6 +43,7 @@ sales_order_urls = [ return_order_urls = [ + path(r'/', views.ReturnOrderDetail.as_view(), name='return-order-detail'), # Display list of all return orders re_path(r'^.*$', views.ReturnOrderIndex.as_view(), name='return-order-index'), diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index 7027f0a0f3..3d319a8d28 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -75,6 +75,14 @@ class SalesOrderDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView) template_name = 'order/sales_order_detail.html' +class ReturnOrderDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView): + """Detail view for a ReturnOrder object""" + + context_object_name = 'order' + queryset = ReturnOrder.objects.all() + template_name = 'order/return_order_detail.html' + + class PurchaseOrderUpload(FileManagementFormView): """PurchaseOrder: Upload file, match to fields and parts (using multi-Step form)""" diff --git a/InvenTree/part/templatetags/status_codes.py b/InvenTree/part/templatetags/status_codes.py index d3811c38f5..9eef869678 100644 --- a/InvenTree/part/templatetags/status_codes.py +++ b/InvenTree/part/templatetags/status_codes.py @@ -4,7 +4,8 @@ from django import template from django.utils.safestring import mark_safe from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus, - SalesOrderStatus, StockStatus) + ReturnOrderStatus, SalesOrderStatus, + StockStatus) register = template.Library() @@ -21,6 +22,12 @@ def sales_order_status_label(key, *args, **kwargs): return mark_safe(SalesOrderStatus.render(key, large=kwargs.get('large', False))) +@register.simple_tag +def return_order_status_label(key, *args, **kwargs): + """Render a ReturnOrder status label""" + return mark_safe(ReturnOrderStatus.render(key, large=kwargs.get('large', False))) + + @register.simple_tag def stock_status_label(key, *args, **kwargs): """Render a StockItem status label."""