mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Calendar view for purchase orders
This commit is contained in:
parent
38b6367453
commit
76c86e7b2f
@ -93,6 +93,7 @@ dynamic_javascript_urls = [
|
|||||||
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
|
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
|
||||||
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),
|
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),
|
||||||
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.js'), name='build.js'),
|
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.js'), name='build.js'),
|
||||||
|
url(r'^calendar.js', DynamicJsView.as_view(template_name='js/calendar.js'), name='calendar.js'),
|
||||||
url(r'^company.js', DynamicJsView.as_view(template_name='js/company.js'), name='company.js'),
|
url(r'^company.js', DynamicJsView.as_view(template_name='js/company.js'), name='company.js'),
|
||||||
url(r'^order.js', DynamicJsView.as_view(template_name='js/order.js'), name='order.js'),
|
url(r'^order.js', DynamicJsView.as_view(template_name='js/order.js'), name='order.js'),
|
||||||
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.js'), name='part.js'),
|
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.js'), name='part.js'),
|
||||||
|
@ -107,6 +107,13 @@ class POList(generics.ListCreateAPIView):
|
|||||||
except (ValueError, SupplierPart.DoesNotExist):
|
except (ValueError, SupplierPart.DoesNotExist):
|
||||||
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 = PurchaseOrder.filterByDate(queryset, min_date, max_date)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
@ -298,7 +305,7 @@ class SOList(generics.ListCreateAPIView):
|
|||||||
max_date = params.get('max_date', None)
|
max_date = params.get('max_date', None)
|
||||||
|
|
||||||
if min_date is not None and max_date is not None:
|
if min_date is not None and max_date is not None:
|
||||||
queryset = SalesOrder.filter_interesting_orders(queryset, min_date, max_date)
|
queryset = SalesOrder.filterByDate(queryset, min_date, max_date)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
@ -121,6 +121,44 @@ class PurchaseOrder(Order):
|
|||||||
received_by: User that received the goods
|
received_by: User that received the goods
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def filterByDate(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 bewteen these dates
|
||||||
|
|
||||||
|
To be "interesting":
|
||||||
|
- A "received" order where the received date lies within the date range
|
||||||
|
- TODO: 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 "received" orders within the range
|
||||||
|
received = Q(status=PurchaseOrderStatus.COMPLETE) & Q(complete_date__gte=min_date) & Q(complete_date__lte=max_date)
|
||||||
|
|
||||||
|
# TODO - Construct a queryset for "pending" orders within the range
|
||||||
|
|
||||||
|
# TODO - Construct a queryset for "overdue" orders within the range
|
||||||
|
|
||||||
|
flt = received
|
||||||
|
|
||||||
|
queryset = queryset.filter(flt)
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
||||||
prefix = getSetting('PURCHASEORDER_REFERENCE_PREFIX')
|
prefix = getSetting('PURCHASEORDER_REFERENCE_PREFIX')
|
||||||
@ -302,7 +340,7 @@ 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
|
@staticmethod
|
||||||
def filter_interesting_orders(queryset, min_date, max_date):
|
def filterByDate(queryset, min_date, max_date):
|
||||||
"""
|
"""
|
||||||
Filter by "minimum and maximum date range"
|
Filter by "minimum and maximum date range"
|
||||||
|
|
||||||
@ -332,7 +370,8 @@ class SalesOrder(Order):
|
|||||||
# Construct a queryset for "pending" orders within the range
|
# 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)
|
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
|
# TODO: Construct a queryset for "overdue" orders within the range
|
||||||
|
|
||||||
flt = completed | pending
|
flt = completed | pending
|
||||||
|
|
||||||
queryset = queryset.filter(flt)
|
queryset = queryset.filter(flt)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load inventree_extras %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
@ -18,6 +19,12 @@ InvenTree | {% trans "Purchase Orders" %}
|
|||||||
<button class='btn btn-primary' type='button' id='po-create' title='{% trans "Create new purchase order" %}'>
|
<button class='btn btn-primary' type='button' id='po-create' title='{% trans "Create new purchase order" %}'>
|
||||||
<span class='fas fa-plus-circle'></span> {% trans "New Purchase Order" %}</button>
|
<span class='fas fa-plus-circle'></span> {% trans "New Purchase Order" %}</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<button class='btn btn-default' type='button' id='view-calendar' title='{% trans "Display calendar view" %}'>
|
||||||
|
<span class='fas fa-calendar-alt'></span>
|
||||||
|
</button>
|
||||||
|
<button class='btn btn-default' type='button' id='view-list' title='{% trans "Display list view" %}'>
|
||||||
|
<span class='fas fa-th-list'></span>
|
||||||
|
</button>
|
||||||
<div class='filter-list' id='filter-list-purchaseorder'>
|
<div class='filter-list' id='filter-list-purchaseorder'>
|
||||||
<!-- An empty div in which the filter list will be constructed -->
|
<!-- An empty div in which the filter list will be constructed -->
|
||||||
</div>
|
</div>
|
||||||
@ -27,11 +34,116 @@ InvenTree | {% trans "Purchase Orders" %}
|
|||||||
<table class='table table-striped table-condensed po-table' data-toolbar='#table-buttons' id='purchase-order-table'>
|
<table class='table table-striped table-condensed po-table' data-toolbar='#table-buttons' id='purchase-order-table'>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div id='purchase-order-calendar'></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js_load %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<script type='text/javascript'>
|
||||||
|
|
||||||
|
function loadOrderEvents(calendar) {
|
||||||
|
|
||||||
|
var start = startDate(calendar);
|
||||||
|
var end = endDate(calendar);
|
||||||
|
|
||||||
|
clearEvents(calendar);
|
||||||
|
|
||||||
|
// Request purchase orders from the server within specified date range
|
||||||
|
inventreeGet(
|
||||||
|
'{% url "api-po-list" %}',
|
||||||
|
{
|
||||||
|
supplier_detail: true,
|
||||||
|
min_date: start,
|
||||||
|
max_date: end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
success: function(response) {
|
||||||
|
var prefix = '{% settings_value "PURCHASEORDER_REFERENCE_PREFIX" %}';
|
||||||
|
|
||||||
|
for (var idx = 0; idx < response.length; idx++) {
|
||||||
|
|
||||||
|
var order = response[idx];
|
||||||
|
|
||||||
|
var date = order.creation_date;
|
||||||
|
|
||||||
|
if (order.complete_date) {
|
||||||
|
date = order.complete_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
var title = `${prefix}${order.reference} - ${order.supplier_detail.name}`;
|
||||||
|
|
||||||
|
var color = '#25c235';
|
||||||
|
|
||||||
|
var event = {
|
||||||
|
title: title,
|
||||||
|
start: date,
|
||||||
|
end: date,
|
||||||
|
url: `/order/purchase-order/${order.pk}/`,
|
||||||
|
backgroundColor: color,
|
||||||
|
};
|
||||||
|
|
||||||
|
calendar.addEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var calendar = null;
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var el = document.getElementById('purchase-order-calendar');
|
||||||
|
|
||||||
|
calendar = new FullCalendar.Calendar(el, {
|
||||||
|
initialView: 'dayGridMonth',
|
||||||
|
nowIndicator: true,
|
||||||
|
aspectRatio: 2.5,
|
||||||
|
datesSet: function() {
|
||||||
|
loadOrderEvents(calendar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
calendar.render();
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
|
$('#purchase-order-calendar').hide();
|
||||||
|
$('#view-list').hide();
|
||||||
|
|
||||||
|
$('#view-calendar').click(function() {
|
||||||
|
// Hide the list view, show the calendar view
|
||||||
|
$("#purchase-order-table").hide();
|
||||||
|
$("#view-calendar").hide();
|
||||||
|
$(".fixed-table-pagination").hide();
|
||||||
|
$(".columns-right").hide();
|
||||||
|
$(".search").hide();
|
||||||
|
$('#filter-list-salesorder').hide();
|
||||||
|
|
||||||
|
$("#purchase-order-calendar").show();
|
||||||
|
$("#view-list").show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#view-list").click(function() {
|
||||||
|
// Hide the calendar view, show the list view
|
||||||
|
$("#purchase-order-calendar").hide();
|
||||||
|
$("#view-list").hide();
|
||||||
|
|
||||||
|
$(".fixed-table-pagination").show();
|
||||||
|
$(".columns-right").show();
|
||||||
|
$(".search").show();
|
||||||
|
$("#purchase-order-table").show();
|
||||||
|
$('#filter-list-salesorder').show();
|
||||||
|
$("#view-calendar").show();
|
||||||
|
});
|
||||||
|
|
||||||
$("#po-create").click(function() {
|
$("#po-create").click(function() {
|
||||||
launchModalForm("{% url 'po-create' %}",
|
launchModalForm("{% url 'po-create' %}",
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ InvenTree | {% trans "Sales Orders" %}
|
|||||||
<h3>{% trans "Sales Orders" %}</h3>
|
<h3>{% trans "Sales Orders" %}</h3>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|
||||||
<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 %}
|
||||||
@ -34,10 +33,9 @@ InvenTree | {% trans "Sales Orders" %}
|
|||||||
|
|
||||||
<table class='table table-striped table-condensed po-table' data-toolbar='#table-buttons' id='sales-order-table'>
|
<table class='table table-striped table-condensed po-table' data-toolbar='#table-buttons' id='sales-order-table'>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id='sales-order-calendar'></div>
|
<div id='sales-order-calendar'></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_load %}
|
{% block js_load %}
|
||||||
@ -45,25 +43,12 @@ InvenTree | {% trans "Sales Orders" %}
|
|||||||
|
|
||||||
<script type='text/javascript'>
|
<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) {
|
function loadOrderEvents(calendar) {
|
||||||
|
|
||||||
var start = startDate(calendar);
|
var start = startDate(calendar);
|
||||||
var end = endDate(calendar);
|
var end = endDate(calendar);
|
||||||
|
|
||||||
// Clear existing orders from the calendar
|
clearEvents(calendar);
|
||||||
var events = calendar.getEvents();
|
|
||||||
|
|
||||||
events.forEach(function(event) {
|
|
||||||
event.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Request orders from the server within specified date range
|
// Request orders from the server within specified date range
|
||||||
inventreeGet(
|
inventreeGet(
|
||||||
@ -124,7 +109,6 @@ InvenTree | {% trans "Sales Orders" %}
|
|||||||
initialView: 'dayGridMonth',
|
initialView: 'dayGridMonth',
|
||||||
nowIndicator: true,
|
nowIndicator: true,
|
||||||
aspectRatio: 2.5,
|
aspectRatio: 2.5,
|
||||||
width: '80%',
|
|
||||||
datesSet: function() {
|
datesSet: function() {
|
||||||
loadOrderEvents(calendar);
|
loadOrderEvents(calendar);
|
||||||
},
|
},
|
||||||
@ -149,6 +133,7 @@ $('#view-calendar').click(function() {
|
|||||||
$(".fixed-table-pagination").hide();
|
$(".fixed-table-pagination").hide();
|
||||||
$(".columns-right").hide();
|
$(".columns-right").hide();
|
||||||
$(".search").hide();
|
$(".search").hide();
|
||||||
|
$('#filter-list-salesorder').hide();
|
||||||
|
|
||||||
$("#sales-order-calendar").show();
|
$("#sales-order-calendar").show();
|
||||||
$("#view-list").show();
|
$("#view-list").show();
|
||||||
@ -163,6 +148,7 @@ $("#view-list").click(function() {
|
|||||||
$(".columns-right").show();
|
$(".columns-right").show();
|
||||||
$(".search").show();
|
$(".search").show();
|
||||||
$("#sales-order-table").show();
|
$("#sales-order-table").show();
|
||||||
|
$('#filter-list-salesorder').show();
|
||||||
$("#view-calendar").show();
|
$("#view-calendar").show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -123,6 +123,7 @@ InvenTree
|
|||||||
<script type='text/javascript' src="{% url 'stock.js' %}"></script>
|
<script type='text/javascript' src="{% url 'stock.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'build.js' %}"></script>
|
<script type='text/javascript' src="{% url 'build.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'order.js' %}"></script>
|
<script type='text/javascript' src="{% url 'order.js' %}"></script>
|
||||||
|
<script type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
||||||
<script type='text/javascript' src="{% url 'table_filters.js' %}"></script>
|
<script type='text/javascript' src="{% url 'table_filters.js' %}"></script>
|
||||||
|
|
||||||
<script type='text/javascript' src="{% static 'fontawesome/js/solid.js' %}"></script>
|
<script type='text/javascript' src="{% static 'fontawesome/js/solid.js' %}"></script>
|
||||||
|
25
InvenTree/templates/js/calendar.js
Normal file
25
InvenTree/templates/js/calendar.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper functions for calendar display
|
||||||
|
*/
|
||||||
|
|
||||||
|
function startDate(calendar) {
|
||||||
|
// Extract the first displayed date on the calendar
|
||||||
|
return calendar.currentData.dateProfile.activeRange.start.toISOString().split("T")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function endDate(calendar) {
|
||||||
|
// Extract the last display date on the calendar
|
||||||
|
return calendar.currentData.dateProfile.activeRange.end.toISOString().split("T")[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearEvents(calendar) {
|
||||||
|
// Remove all events from the calendar
|
||||||
|
|
||||||
|
var events = calendar.getEvents();
|
||||||
|
|
||||||
|
events.forEach(function(event) {
|
||||||
|
event.remove();
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user