mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
7050b3a410
@ -153,11 +153,13 @@ class InvenTreeSetting(models.Model):
|
|||||||
'SALESORDER_REFERENCE_PREFIX': {
|
'SALESORDER_REFERENCE_PREFIX': {
|
||||||
'name': _('Sales Order Reference Prefix'),
|
'name': _('Sales Order Reference Prefix'),
|
||||||
'description': _('Prefix value for sales order reference'),
|
'description': _('Prefix value for sales order reference'),
|
||||||
|
'default': 'SO',
|
||||||
},
|
},
|
||||||
|
|
||||||
'PURCHASEORDER_REFERENCE_PREFIX': {
|
'PURCHASEORDER_REFERENCE_PREFIX': {
|
||||||
'name': _('Purchase Order Reference Prefix'),
|
'name': _('Purchase Order Reference Prefix'),
|
||||||
'description': _('Prefix value for purchase order reference'),
|
'description': _('Prefix value for purchase order reference'),
|
||||||
|
'default': 'PO',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,6 +266,17 @@ class SOList(generics.ListCreateAPIView):
|
|||||||
else:
|
else:
|
||||||
queryset = queryset.exclude(status__in=SalesOrderStatus.OPEN)
|
queryset = queryset.exclude(status__in=SalesOrderStatus.OPEN)
|
||||||
|
|
||||||
|
# Filter by 'overdue' status
|
||||||
|
overdue = params.get('overdue', None)
|
||||||
|
|
||||||
|
if overdue is not None:
|
||||||
|
overdue = str2bool(overdue)
|
||||||
|
|
||||||
|
if overdue:
|
||||||
|
queryset = queryset.filter(SalesOrder.OVERDUE_FILTER)
|
||||||
|
else:
|
||||||
|
queryset = queryset.exclude(SalesOrder.OVERDUE_FILTER)
|
||||||
|
|
||||||
status = params.get('status', None)
|
status = params.get('status', None)
|
||||||
|
|
||||||
if status is not None:
|
if status is not None:
|
||||||
|
@ -128,6 +128,13 @@ class EditSalesOrderForm(HelperForm):
|
|||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# TODO: Improve this using a better date picker
|
||||||
|
target_date = forms.DateField(
|
||||||
|
widget=forms.DateInput(
|
||||||
|
attrs={'type': 'date'},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
fields = [
|
fields = [
|
||||||
@ -135,6 +142,7 @@ class EditSalesOrderForm(HelperForm):
|
|||||||
'customer',
|
'customer',
|
||||||
'customer_reference',
|
'customer_reference',
|
||||||
'description',
|
'description',
|
||||||
|
'target_date',
|
||||||
'link'
|
'link'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
18
InvenTree/order/migrations/0040_salesorder_target_date.py
Normal file
18
InvenTree/order/migrations/0040_salesorder_target_date.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2020-12-18 01:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('order', '0039_auto_20201112_2203'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='target_date',
|
||||||
|
field=models.DateField(blank=True, help_text='Target date for order completion. Order will be overdue after this date.', null=True, verbose_name='Target completion date'),
|
||||||
|
),
|
||||||
|
]
|
@ -9,7 +9,7 @@ from datetime import datetime
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import F, Sum
|
from django.db.models import Q, F, Sum
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
@ -26,7 +26,7 @@ from stock import models as stock_models
|
|||||||
from company.models import Company, SupplierPart
|
from company.models import Company, SupplierPart
|
||||||
|
|
||||||
from InvenTree.fields import RoundingDecimalField
|
from InvenTree.fields import RoundingDecimalField
|
||||||
from InvenTree.helpers import decimal2string, increment
|
from InvenTree.helpers import decimal2string, increment, getSetting
|
||||||
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus
|
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus
|
||||||
from InvenTree.models import InvenTreeAttachment
|
from InvenTree.models import InvenTreeAttachment
|
||||||
|
|
||||||
@ -49,8 +49,6 @@ class Order(models.Model):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ORDER_PREFIX = ""
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getNextOrderNumber(cls):
|
def getNextOrderNumber(cls):
|
||||||
"""
|
"""
|
||||||
@ -88,16 +86,6 @@ class Order(models.Model):
|
|||||||
|
|
||||||
return new_ref
|
return new_ref
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
el = []
|
|
||||||
|
|
||||||
if self.ORDER_PREFIX:
|
|
||||||
el.append(self.ORDER_PREFIX)
|
|
||||||
|
|
||||||
el.append(self.reference)
|
|
||||||
|
|
||||||
return " ".join(el)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.creation_date:
|
if not self.creation_date:
|
||||||
self.creation_date = datetime.now().date()
|
self.creation_date = datetime.now().date()
|
||||||
@ -133,10 +121,11 @@ class PurchaseOrder(Order):
|
|||||||
received_by: User that received the goods
|
received_by: User that received the goods
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ORDER_PREFIX = "PO"
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "PO {ref} - {company}".format(ref=self.reference, company=self.supplier.name)
|
|
||||||
|
prefix = getSetting('PURCHASEORDER_REFERENCE_PREFIX')
|
||||||
|
|
||||||
|
return f"{prefix}{self.reference} - {self.supplier.name}"
|
||||||
|
|
||||||
status = models.PositiveIntegerField(default=PurchaseOrderStatus.PENDING, choices=PurchaseOrderStatus.items(),
|
status = models.PositiveIntegerField(default=PurchaseOrderStatus.PENDING, choices=PurchaseOrderStatus.items(),
|
||||||
help_text=_('Purchase order status'))
|
help_text=_('Purchase order status'))
|
||||||
@ -307,10 +296,16 @@ class SalesOrder(Order):
|
|||||||
Attributes:
|
Attributes:
|
||||||
customer: Reference to the company receiving the goods in the order
|
customer: Reference to the company receiving the goods in the order
|
||||||
customer_reference: Optional field for customer order reference code
|
customer_reference: Optional field for customer order reference code
|
||||||
|
target_date: Target date for SalesOrder completion (optional)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
OVERDUE_FILTER = Q(status__in=SalesOrderStatus.OPEN) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SO {ref} - {company}".format(ref=self.reference, company=self.customer.name)
|
|
||||||
|
prefix = getSetting('SALESORDER_REFERENCE_PREFIX')
|
||||||
|
|
||||||
|
return f"{prefix}{self.reference} - {self.customer.name}"
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('so-detail', kwargs={'pk': self.id})
|
return reverse('so-detail', kwargs={'pk': self.id})
|
||||||
@ -329,6 +324,12 @@ class SalesOrder(Order):
|
|||||||
|
|
||||||
customer_reference = models.CharField(max_length=64, blank=True, help_text=_("Customer order reference code"))
|
customer_reference = models.CharField(max_length=64, blank=True, help_text=_("Customer order reference code"))
|
||||||
|
|
||||||
|
target_date = models.DateField(
|
||||||
|
null=True, blank=True,
|
||||||
|
verbose_name=_('Target completion date'),
|
||||||
|
help_text=_('Target date for order completion. Order will be overdue after this date.')
|
||||||
|
)
|
||||||
|
|
||||||
shipment_date = models.DateField(blank=True, null=True)
|
shipment_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
shipped_by = models.ForeignKey(
|
shipped_by = models.ForeignKey(
|
||||||
@ -338,6 +339,23 @@ class SalesOrder(Order):
|
|||||||
related_name='+'
|
related_name='+'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_overdue(self):
|
||||||
|
"""
|
||||||
|
Returns true if this SalesOrder is "overdue":
|
||||||
|
|
||||||
|
- Not completed
|
||||||
|
- Target date is "in the past"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Order cannot be deemed overdue if target_date is not set
|
||||||
|
if self.target_date is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
today = datetime.now().date()
|
||||||
|
|
||||||
|
return self.is_pending and self.target_date < today
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_pending(self):
|
def is_pending(self):
|
||||||
return self.status == SalesOrderStatus.PENDING
|
return self.status == SalesOrderStatus.PENDING
|
||||||
|
@ -9,6 +9,9 @@ from rest_framework import serializers
|
|||||||
|
|
||||||
from sql_util.utils import SubqueryCount
|
from sql_util.utils import SubqueryCount
|
||||||
|
|
||||||
|
from django.db.models import Case, When, Value
|
||||||
|
from django.db.models import BooleanField
|
||||||
|
|
||||||
from InvenTree.serializers import InvenTreeModelSerializer
|
from InvenTree.serializers import InvenTreeModelSerializer
|
||||||
from InvenTree.serializers import InvenTreeAttachmentSerializerField
|
from InvenTree.serializers import InvenTreeAttachmentSerializerField
|
||||||
|
|
||||||
@ -152,12 +155,24 @@ class SalesOrderSerializer(InvenTreeModelSerializer):
|
|||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""
|
"""
|
||||||
Add extra information to the queryset
|
Add extra information to the queryset
|
||||||
|
|
||||||
|
- Number of line items in the SalesOrder
|
||||||
|
- Overdue status of the SalesOrder
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = queryset.annotate(
|
queryset = queryset.annotate(
|
||||||
line_items=SubqueryCount('lines')
|
line_items=SubqueryCount('lines')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
overdue=Case(
|
||||||
|
When(
|
||||||
|
SalesOrder.OVERDUE_FILTER, then=Value(True, output_field=BooleanField()),
|
||||||
|
),
|
||||||
|
default=Value(False, output_field=BooleanField())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True)
|
customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True)
|
||||||
@ -166,24 +181,27 @@ class SalesOrderSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||||
|
|
||||||
|
overdue = serializers.BooleanField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
|
|
||||||
fields = [
|
fields = [
|
||||||
'pk',
|
'pk',
|
||||||
'shipment_date',
|
|
||||||
'creation_date',
|
'creation_date',
|
||||||
'description',
|
|
||||||
'line_items',
|
|
||||||
'link',
|
|
||||||
'reference',
|
|
||||||
'customer',
|
'customer',
|
||||||
'customer_detail',
|
'customer_detail',
|
||||||
'customer_reference',
|
'customer_reference',
|
||||||
|
'description',
|
||||||
|
'line_items',
|
||||||
|
'link',
|
||||||
|
'notes',
|
||||||
|
'overdue',
|
||||||
|
'reference',
|
||||||
'status',
|
'status',
|
||||||
'status_text',
|
'status_text',
|
||||||
'shipment_date',
|
'shipment_date',
|
||||||
'notes',
|
'target_date',
|
||||||
]
|
]
|
||||||
|
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
|
@ -37,6 +37,9 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
</h3>
|
</h3>
|
||||||
<h3>
|
<h3>
|
||||||
{% sales_order_status_label order.status large=True %}
|
{% sales_order_status_label order.status large=True %}
|
||||||
|
{% if order.is_overdue %}
|
||||||
|
<span class='label label-large label-large-red'>{% trans "Overdue" %}</span>
|
||||||
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
<hr>
|
<hr>
|
||||||
<p>{{ order.description }}</p>
|
<p>{{ order.description }}</p>
|
||||||
@ -74,7 +77,12 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-info'></span></td>
|
<td><span class='fas fa-info'></span></td>
|
||||||
<td>{% trans "Order Status" %}</td>
|
<td>{% trans "Order Status" %}</td>
|
||||||
<td>{% sales_order_status_label order.status %}</td>
|
<td>
|
||||||
|
{% sales_order_status_label order.status %}
|
||||||
|
{% if order.is_overdue %}
|
||||||
|
<span class='label label-red'>{% trans "Overdue" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-building'></span></td>
|
<td><span class='fas fa-building'></span></td>
|
||||||
@ -100,6 +108,13 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<td>{% trans "Created" %}</td>
|
<td>{% trans "Created" %}</td>
|
||||||
<td>{{ order.creation_date }}<span class='badge'>{{ order.created_by }}</span></td>
|
<td>{{ order.creation_date }}<span class='badge'>{{ order.created_by }}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if order.target_date %}
|
||||||
|
<tr>
|
||||||
|
<td><span class='fas fa-calendar-alt'></span></td>
|
||||||
|
<td>{% trans "Target Date" %}</td>
|
||||||
|
<td>{{ order.target_date }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% if order.shipment_date %}
|
{% if order.shipment_date %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-truck'></span></td>
|
<td><span class='fas fa-truck'></span></td>
|
||||||
|
@ -31,11 +31,11 @@ class OrderTest(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(order.get_absolute_url(), '/order/purchase-order/1/')
|
self.assertEqual(order.get_absolute_url(), '/order/purchase-order/1/')
|
||||||
|
|
||||||
self.assertEqual(str(order), 'PO 0001 - ACME')
|
self.assertEqual(str(order), 'PO0001 - ACME')
|
||||||
|
|
||||||
line = PurchaseOrderLineItem.objects.get(pk=1)
|
line = PurchaseOrderLineItem.objects.get(pk=1)
|
||||||
|
|
||||||
self.assertEqual(str(line), "100 x ACME0001 from ACME (for PO 0001 - ACME)")
|
self.assertEqual(str(line), "100 x ACME0001 from ACME (for PO0001 - ACME)")
|
||||||
|
|
||||||
def test_increment(self):
|
def test_increment(self):
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ class CategoryParameters(generics.ListAPIView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cat_id = int(self.request.query_params.get('category', None))
|
cat_id = int(self.kwargs.get('pk', None))
|
||||||
except TypeError:
|
except TypeError:
|
||||||
cat_id = None
|
cat_id = None
|
||||||
fetch_parent = str2bool(self.request.query_params.get('fetch_parent', 'true'))
|
fetch_parent = str2bool(self.request.query_params.get('fetch_parent', 'true'))
|
||||||
@ -910,8 +910,8 @@ part_api_urls = [
|
|||||||
|
|
||||||
# Base URL for PartCategory API endpoints
|
# Base URL for PartCategory API endpoints
|
||||||
url(r'^category/', include([
|
url(r'^category/', include([
|
||||||
|
url(r'^(?P<pk>\d+)/parameters/?', CategoryParameters.as_view(), name='api-part-category-parameters'),
|
||||||
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
|
url(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
|
||||||
url(r'^parameters/?', CategoryParameters.as_view(), name='api-part-category-parameters'),
|
|
||||||
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
url(r'^$', CategoryList.as_view(), name='api-part-category-list'),
|
||||||
])),
|
])),
|
||||||
|
|
||||||
|
@ -594,7 +594,7 @@ class Part(MPTTModel):
|
|||||||
# User can decide whether duplicate IPN (Internal Part Number) values are allowed
|
# User can decide whether duplicate IPN (Internal Part Number) values are allowed
|
||||||
allow_duplicate_ipn = common.models.InvenTreeSetting.get_setting('PART_ALLOW_DUPLICATE_IPN')
|
allow_duplicate_ipn = common.models.InvenTreeSetting.get_setting('PART_ALLOW_DUPLICATE_IPN')
|
||||||
|
|
||||||
if not allow_duplicate_ipn:
|
if self.IPN is not None and not allow_duplicate_ipn:
|
||||||
parts = Part.objects.filter(IPN__iexact=self.IPN)
|
parts = Part.objects.filter(IPN__iexact=self.IPN)
|
||||||
parts = parts.exclude(pk=self.pk)
|
parts = parts.exclude(pk=self.pk)
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
<!-- Stock adjustment menu -->
|
<!-- Stock adjustment menu -->
|
||||||
{% if roles.stock.change and not item.is_building %}
|
{% if roles.stock.change and not item.is_building %}
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
<button id='stock-options' title='{% trans "Stock adjustment actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
<button id='stock-actions' title='{% trans "Stock adjustment actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button>
|
||||||
<ul class='dropdown-menu' role='menu'>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
{% if item.in_stock %}
|
{% if item.in_stock %}
|
||||||
{% if not item.serialized %}
|
{% if not item.serialized %}
|
||||||
|
@ -29,6 +29,7 @@ InvenTree | {% trans "Index" %}
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if roles.sales_order.view %}
|
{% if roles.sales_order.view %}
|
||||||
{% include "InvenTree/so_outstanding.html" with collapse_id="so_outstanding" %}
|
{% include "InvenTree/so_outstanding.html" with collapse_id="so_outstanding" %}
|
||||||
|
{% include "InvenTree/so_overdue.html" with collapse_id="so_overdue" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -112,6 +113,14 @@ loadSalesOrderTable("#so-outstanding-table", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loadSalesOrderTable("#so-overdue-table", {
|
||||||
|
url: "{% url 'api-so-list' %}",
|
||||||
|
params: {
|
||||||
|
overdue: true,
|
||||||
|
customer_detail: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#latest-parts-table").on('load-success.bs.table', function() {
|
$("#latest-parts-table").on('load-success.bs.table', function() {
|
||||||
var count = $("#latest-parts-table").bootstrapTable('getData').length;
|
var count = $("#latest-parts-table").bootstrapTable('getData').length;
|
||||||
|
|
||||||
@ -166,4 +175,10 @@ $("#so-outstanding-table").on('load-success.bs.table', function() {
|
|||||||
$("#so-outstanding-count").html(count);
|
$("#so-outstanding-count").html(count);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#so-overdue-table").on('load-success.bs.table', function() {
|
||||||
|
var count = $("#so-overdue-table").bootstrapTable('getData').length;
|
||||||
|
|
||||||
|
$("#so-overdue-count").html(count);
|
||||||
|
});
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -45,7 +45,7 @@
|
|||||||
|
|
||||||
{% if category %}
|
{% if category %}
|
||||||
$("#param-table").inventreeTable({
|
$("#param-table").inventreeTable({
|
||||||
url: "{% url 'api-part-category-parameters' category.pk %}",
|
url: "{% url 'api-part-category-parameters' pk=category.pk %}",
|
||||||
queryParams: {
|
queryParams: {
|
||||||
ordering: 'name',
|
ordering: 'name',
|
||||||
},
|
},
|
||||||
@ -58,7 +58,7 @@
|
|||||||
switchable: false,
|
switchable: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'parameter_template_detail.name',
|
field: 'parameter_template.name',
|
||||||
title: '{% trans "Parameter Template" %}',
|
title: '{% trans "Parameter Template" %}',
|
||||||
sortable: 'true',
|
sortable: 'true',
|
||||||
},
|
},
|
||||||
|
15
InvenTree/templates/InvenTree/so_overdue.html
Normal file
15
InvenTree/templates/InvenTree/so_overdue.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{% extends "collapse_index.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block collapse_title %}
|
||||||
|
<span class='fas fa-calendar-times icon-header'></span>
|
||||||
|
{% trans "Overdue Sales Orders" %}<span class='badge' id='so-overdue-count'><span class='fas fa-spin fa-spinner'></span></span>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block collapse_content %}
|
||||||
|
|
||||||
|
<table class='table table-striped table-condensed' id='so-overdue-table'>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -235,7 +235,13 @@ function loadSalesOrderTable(table, options) {
|
|||||||
value = `${prefix}${value}`;
|
value = `${prefix}${value}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderLink(value, `/order/sales-order/${row.pk}/`);
|
var html = renderLink(value, `/order/sales-order/${row.pk}/`);
|
||||||
|
|
||||||
|
if (row.overdue) {
|
||||||
|
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -269,6 +275,11 @@ function loadSalesOrderTable(table, options) {
|
|||||||
field: 'creation_date',
|
field: 'creation_date',
|
||||||
title: '{% trans "Creation Date" %}',
|
title: '{% trans "Creation Date" %}',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
sortable: true,
|
||||||
|
field: 'target_date',
|
||||||
|
title: '{% trans "Target Date" %}',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
sortable: true,
|
sortable: true,
|
||||||
field: 'shipment_date',
|
field: 'shipment_date',
|
||||||
|
@ -217,6 +217,10 @@ function getAvailableTableFilters(tableKey) {
|
|||||||
type: 'bool',
|
type: 'bool',
|
||||||
title: '{% trans "Outstanding" %}',
|
title: '{% trans "Outstanding" %}',
|
||||||
},
|
},
|
||||||
|
overdue: {
|
||||||
|
type: 'bool',
|
||||||
|
title: '{% trans "Overdue" %}',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user