mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Render sales orders to a calendar view
This commit is contained in:
parent
b4277e09e8
commit
5f6442ba6b
@ -152,6 +152,11 @@ class SalesOrderStatus(StatusCode):
|
|||||||
PENDING,
|
PENDING,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Completed orders
|
||||||
|
COMPLETE = [
|
||||||
|
SHIPPED,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockStatus(StatusCode):
|
class StockStatus(StatusCode):
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework import filters
|
from rest_framework import filters
|
||||||
|
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
@ -293,6 +295,13 @@ class SOList(generics.ListCreateAPIView):
|
|||||||
except (Part.DoesNotExist, ValueError):
|
except (Part.DoesNotExist, ValueError):
|
||||||
pass
|
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
|
return queryset
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
|
@ -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())
|
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):
|
def __str__(self):
|
||||||
|
|
||||||
prefix = getSetting('SALESORDER_REFERENCE_PREFIX')
|
prefix = getSetting('SALESORDER_REFERENCE_PREFIX')
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load inventree_extras %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
@ -12,6 +13,10 @@ InvenTree | {% trans "Sales Orders" %}
|
|||||||
<h3>{% trans "Sales Orders" %}</h3>
|
<h3>{% trans "Sales Orders" %}</h3>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
<div class='calendar-container' style="width: 80%; align-items: center; align-content: center;">
|
||||||
|
<div id='calendar'></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id='table-buttons'>
|
<div id='table-buttons'>
|
||||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||||
{% if roles.sales_order.add %}
|
{% if roles.sales_order.add %}
|
||||||
@ -29,9 +34,106 @@ InvenTree | {% trans "Sales Orders" %}
|
|||||||
|
|
||||||
{% endblock %}
|
{% 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 js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
|
|
||||||
loadSalesOrderTable("#sales-order-table", {
|
loadSalesOrderTable("#sales-order-table", {
|
||||||
url: "{% url 'api-so-list' %}",
|
url: "{% url 'api-so-list' %}",
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user