Adds detail page for ReturnOrder

This commit is contained in:
Oliver Walters 2023-03-14 23:05:05 +11:00
parent e6e76c7a2c
commit 9ae6160353
9 changed files with 313 additions and 29 deletions

View File

@ -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:

View File

@ -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<pk>\d+)/', include([
path(r'<int:pk>/', 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<pk>\d+)/', include([
path(r'<int:pk>/', 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<pk>\d+)/', include([
path(r'<int:pk>/', 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('<int:pk>/', 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('<int:pk>/', ReturnOrderDetail.as_view(), name='api-return-order-detail'),

View File

@ -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 %}
<li class='breadcrumb-item'><a href='{% url "return-order-index" %}'>{% trans "Return Orders" %}</a></li>
<li class="breadcrumb-item active" aria-current="page"><a href='{% url "return-order-detail" order.id %}'>{{ order }}</a></li>
{% endblock breadcrumbs %}
{% block thumbnail %}
<img class='part-thumb'
{% if order.customer and order.customer.image %}
src="{{ order.customer.image.url }}"
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}
/>
{% 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 %}
<!-- TODO: Printing actions -->
<!-- TODO: Order actions-->
{% endblock actions %}
{% block details %}
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Order Reference" %}</td>
<td>{{ order.reference }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Order Description" %}</td>
<td>{{ order.description }}{% include "clip.html" %}</td>
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
<td>
{% return_order_status_label order.status %}
</td>
</tr>
</table>
{% endblock details %}
{% block details_right %}
<table class='table table-striped table-condensed'>
<col width='25'>
{% if order.customer %}
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Customer" %}</td>
<td><a href="{% url 'company-detail' order.customer.id %}">{{ order.customer.name }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if order.customer_reference %}
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Customer Reference" %}</td>
<td>{{ order.customer_reference }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if order.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>External Link</td>
<td><a href="{{ order.link }}">{{ order.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Created" %}</td>
<td>{% render_date order.creation_date %}<span class='badge badge-right rounded-pill bg-dark'>{{ order.created_by }}</span></td>
</tr>
{% if order.responsible %}
<tr>
<td><span class='fas fa-users'></span></td>
<td>{% trans "Responsible" %}</td>
<td>{{ order.responsible }}</td>
</tr>
{% endif %}
</table>
{% endblock details_right %}
{% block js_ready %}
{{ block.super }}
<!-- TODO: Javascript callbacks -->
{% if report_enabled %}
<!-- TODO: Report callbacks -->
{% endif %}
<!-- TODO: Export order callback -->
{% endblock js_ready %}

View File

@ -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 %}
<div class='panel panel-hidden' id='panel-order-details'>
<div class='panel-heading'>
<h4>{% trans "Order Details" %}</h4>
{% include "spacer.html" %}
</div>
<div class='panel-content'>
<!-- TODO: Order details here -->
</div>
</div>
<div class='panel panel-hidden' id='panel-order-attachments'>
<div class='panel-heading'>
<div class='d-flex flex-wrap'>
<h4>{% trans "Attachments" %}</h4>
{% include "spacer.html" %}
<div class='btn-group' role='group'>
{% include "attachment_button.html" %}
</div>
</div>
</div>
<div class='panel-content'>
{% include "attachment_table.html" %}
</div>
</div>
<div class='panel panel-hidden' id='panel-order-notes'>
<div class='panel-heading'>
<div class='d-flex flex-wrap'>
<h4>{% trans "Order Notes" %}</h4>
{% include "spacer.html" %}
<div class='btn-group' role='group'>
{% include "notes_buttons.html" %}
</div>
</div>
</div>
<div class='panel-content'>
<textarea id='order-notes'></textarea>
</div>
</div>
{% 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 %}

View File

@ -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" %}

View File

@ -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"), {

View File

@ -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<pk>\d+)/', include(purchase_order_detail_urls)),
path(r'<int:pk>/', 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<pk>\d+)/', include(sales_order_detail_urls)),
path(r'<int:pk>/', 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'<int:pk>/', 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'),

View File

@ -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)"""

View File

@ -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."""