Render sales orders to a calendar view

This commit is contained in:
Oliver Walters 2021-01-07 18:47:29 +11:00
parent b4277e09e8
commit 5f6442ba6b
4 changed files with 154 additions and 0 deletions

View File

@ -152,6 +152,11 @@ class SalesOrderStatus(StatusCode):
PENDING,
]
# Completed orders
COMPLETE = [
SHIPPED,
]
class StockStatus(StatusCode):

View File

@ -9,6 +9,8 @@ from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
from rest_framework import filters
from django.db.models import Q
from django.conf.urls import url, include
from InvenTree.helpers import str2bool
@ -293,6 +295,13 @@ class SOList(generics.ListCreateAPIView):
except (Part.DoesNotExist, ValueError):
pass
# Filter by 'date range'
min_date = params.get('min_date', None)
max_date = params.get('max_date', None)
if min_date is not None and max_date is not None:
queryset = SalesOrder.filter_interesting_orders(queryset, min_date, max_date)
return queryset
filter_backends = [

View File

@ -301,6 +301,44 @@ class SalesOrder(Order):
OVERDUE_FILTER = Q(status__in=SalesOrderStatus.OPEN) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
@staticmethod
def filter_interesting_orders(queryset, min_date, max_date):
"""
Filter by "minimum and maximum date range"
- Specified as min_date, max_date
- Both must be specified for filter to be applied
- Determine which "interesting" orders exist between these dates
To be "interesting":
- A "completed" order where the completion date lies within the date range
- A "pending" order where the target date lies within the date range
- TODO: An "overdue" order where the target date is in the past
"""
DATE_FMT = '%Y-%m-%d' # ISO format date string
# Ensure that both dates are valid
try:
min_date = datetime.strptime(str(min_date), DATE_FMT).date()
max_date = datetime.strptime(str(max_date), DATE_FMT).date()
except (ValueError, TypeError):
# Date processing error, return queryset unchanged
return queryset
# Construct a queryset for "completed" orders within the range
COMPLETED = Q(status__in=SalesOrderStatus.COMPLETE) & Q(shipment_date__gte=min_date) & Q(shipment_date__lte=max_date)
# Construct a queryset for "pending" orders within the range
PENDING = Q(status__in=SalesOrderStatus.OPEN) & ~Q(target_date=None) & Q(target_date__gte=min_date) & Q(target_date__lte=max_date)
# Construct a queryset for "overdue" orders within the range
FILTER = COMPLETED | PENDING
queryset = queryset.filter(FILTER)
return queryset
def __str__(self):
prefix = getSetting('SALESORDER_REFERENCE_PREFIX')

View File

@ -1,5 +1,6 @@
{% extends "base.html" %}
{% load inventree_extras %}
{% load static %}
{% load i18n %}
@ -12,6 +13,10 @@ InvenTree | {% trans "Sales Orders" %}
<h3>{% trans "Sales Orders" %}</h3>
<hr>
<div class='calendar-container' style="width: 80%; align-items: center; align-content: center;">
<div id='calendar'></div>
</div>
<div id='table-buttons'>
<div class='button-toolbar container-fluid' style='float: right;'>
{% if roles.sales_order.add %}
@ -29,9 +34,106 @@ InvenTree | {% trans "Sales Orders" %}
{% endblock %}
{% block js_load %}
{{ block.super }}
<script type='text/javascript'>
function startDate(calendar) {
return calendar.currentData.dateProfile.activeRange.start.toISOString().split("T")[0];
}
function endDate(calendar) {
return calendar.currentData.dateProfile.activeRange.end.toISOString().split("T")[0];
}
function loadOrderEvents(calendar) {
var start = startDate(calendar);
var end = endDate(calendar);
// Clear existing orders from the calendar
var events = calendar.getEvents();
events.forEach(function(event) {
event.remove();
});
// Request orders from the server within specified date range
inventreeGet(
'{% url "api-so-list" %}',
{
customer_detail: true,
min_date: start,
max_date: end,
},
{
success: function(response) {
var prefix = '{% settings_value "SALESORDER_REFERENCE_PREFIX" %}';
for (var idx = 0; idx < response.length; idx++) {
var order = response[idx];
var date = order.creation_date;
if (order.shipment_date) {
date = order.shipment_date;
} else if (order.target_date) {
date = order.target_date;
}
var title = `${prefix}${order.reference} - ${order.customer_detail.name}`;
// Default color is blue
var color = '#4c68f5';
// Overdue orders are red
if (order.overdue) {
color = '#c22525';
} else if (order.status == {{ SalesOrderStatus.SHIPPED }}) {
color = '#25c235';
}
var event = {
title: title,
start: date,
end: date,
url: `/order/sales-order/${order.pk}/`,
backgroundColor: color,
};
calendar.addEvent(event);
}
}
}
);
}
var calendar = null;
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
nowIndicator: true,
aspectRatio: 2,
width: '80%',
datesSet: function() {
loadOrderEvents(calendar);
},
});
calendar.render();
});
</script>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadSalesOrderTable("#sales-order-table", {
url: "{% url 'api-so-list' %}",
});