mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1232 from SchrodingersGat/purchase-order-target-date
Purchase order target date
This commit is contained in:
commit
449b462bf2
@ -5,16 +5,19 @@
|
||||
fields:
|
||||
name: ACME
|
||||
description: A Cool Military Enterprise
|
||||
|
||||
- model: company.company
|
||||
pk: 2
|
||||
fields:
|
||||
name: Appel Computers
|
||||
description: Think more differenter
|
||||
|
||||
- model: company.company
|
||||
pk: 3
|
||||
fields:
|
||||
name: Zerg Corp
|
||||
description: We eat the competition
|
||||
|
||||
- model: company.company
|
||||
pk: 4
|
||||
fields:
|
||||
@ -22,3 +25,9 @@
|
||||
description: A company that we sell things to!
|
||||
is_customer: True
|
||||
|
||||
- model: company.company
|
||||
pk: 5
|
||||
fields:
|
||||
name: Another customer!
|
||||
description: Yet another company
|
||||
is_customer: True
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -80,6 +80,17 @@ class POList(generics.ListCreateAPIView):
|
||||
else:
|
||||
queryset = queryset.exclude(status__in=PurchaseOrderStatus.OPEN)
|
||||
|
||||
# Filter by 'overdue' status
|
||||
overdue = params.get('overdue', None)
|
||||
|
||||
if overdue is not None:
|
||||
overdue = str2bool(overdue)
|
||||
|
||||
if overdue:
|
||||
queryset = queryset.filter(PurchaseOrder.OVERDUE_FILTER)
|
||||
else:
|
||||
queryset = queryset.exclude(PurchaseOrder.OVERDUE_FILTER)
|
||||
|
||||
# Special filtering for 'status' field
|
||||
status = params.get('status', None)
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
reference: '0001'
|
||||
description: "Ordering some screws"
|
||||
supplier: 1
|
||||
status: 10 # Pending
|
||||
|
||||
# Ordering some screws from Zerg Corp
|
||||
- model: order.purchaseorder
|
||||
@ -15,6 +16,39 @@
|
||||
reference: '0002'
|
||||
description: "Ordering some more screws"
|
||||
supplier: 3
|
||||
status: 10 # Pending
|
||||
|
||||
- model: order.purchaseorder
|
||||
pk: 3
|
||||
fields:
|
||||
reference: '0003'
|
||||
description: 'Another PO'
|
||||
supplier: 3
|
||||
status: 20 # Placed
|
||||
|
||||
- model: order.purchaseorder
|
||||
pk: 4
|
||||
fields:
|
||||
reference: '0004'
|
||||
description: 'Another PO'
|
||||
supplier: 3
|
||||
status: 20 # Placed
|
||||
|
||||
- model: order.purchaseorder
|
||||
pk: 5
|
||||
fields:
|
||||
reference: '0005'
|
||||
description: 'Another PO'
|
||||
supplier: 3
|
||||
status: 30 # Complete
|
||||
|
||||
- model: order.purchaseorder
|
||||
pk: 6
|
||||
fields:
|
||||
reference: '0006'
|
||||
description: 'Another PO'
|
||||
supplier: 3
|
||||
status: 40 # Cancelled
|
||||
|
||||
# Add some line items against PO 0001
|
||||
|
||||
|
39
InvenTree/order/fixtures/sales_order.yaml
Normal file
39
InvenTree/order/fixtures/sales_order.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
- model: order.salesorder
|
||||
pk: 1
|
||||
fields:
|
||||
reference: 'ABC123'
|
||||
description: "One sales order, please"
|
||||
customer: 4
|
||||
status: 10 # Pending
|
||||
|
||||
- model: order.salesorder
|
||||
pk: 2
|
||||
fields:
|
||||
reference: 'ABC124'
|
||||
description: "One sales order, please"
|
||||
customer: 4
|
||||
status: 10 # Pending
|
||||
|
||||
- model: order.salesorder
|
||||
pk: 3
|
||||
fields:
|
||||
reference: 'ABC125'
|
||||
description: "One sales order, please"
|
||||
customer: 4
|
||||
status: 10 # Pending
|
||||
|
||||
- model: order.salesorder
|
||||
pk: 4
|
||||
fields:
|
||||
reference: 'ABC126'
|
||||
description: "One sales order, please"
|
||||
customer: 5
|
||||
status: 20 # Shipped
|
||||
|
||||
- model: order.salesorder
|
||||
pk: 5
|
||||
fields:
|
||||
reference: 'ABC127'
|
||||
description: "One sales order, please"
|
||||
customer: 5
|
||||
status: 60 # Returned
|
@ -94,6 +94,7 @@ class EditPurchaseOrderForm(HelperForm):
|
||||
self.field_prefix = {
|
||||
'reference': 'PO',
|
||||
'link': 'fa-link',
|
||||
'target_date': 'fa-calendar-alt',
|
||||
}
|
||||
|
||||
self.field_placeholder = {
|
||||
@ -102,6 +103,10 @@ class EditPurchaseOrderForm(HelperForm):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
target_date = DatePickerFormField(
|
||||
help_text=_('Target date for order delivery. Order will be overdue after this date.'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = PurchaseOrder
|
||||
fields = [
|
||||
@ -109,6 +114,7 @@ class EditPurchaseOrderForm(HelperForm):
|
||||
'supplier',
|
||||
'supplier_reference',
|
||||
'description',
|
||||
'target_date',
|
||||
'link',
|
||||
]
|
||||
|
||||
|
28
InvenTree/order/migrations/0041_auto_20210114_1728.py
Normal file
28
InvenTree/order/migrations/0041_auto_20210114_1728.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 3.0.7 on 2021-01-14 06:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('order', '0040_salesorder_target_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='purchaseorder',
|
||||
name='target_date',
|
||||
field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Delivery Date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='purchaseorder',
|
||||
name='complete_date',
|
||||
field=models.DateField(blank=True, help_text='Date order was completed', null=True, verbose_name='Completion Date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='purchaseorder',
|
||||
name='issue_date',
|
||||
field=models.DateField(blank=True, help_text='Date order was issued', null=True, verbose_name='Issue Date'),
|
||||
),
|
||||
]
|
@ -119,8 +119,11 @@ class PurchaseOrder(Order):
|
||||
supplier: Reference to the company supplying the goods in the order
|
||||
supplier_reference: Optional field for supplier order reference code
|
||||
received_by: User that received the goods
|
||||
target_date: Expected delivery target date for PurchaseOrder completion (optional)
|
||||
"""
|
||||
|
||||
OVERDUE_FILTER = Q(status__in=PurchaseOrderStatus.OPEN) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
|
||||
|
||||
@staticmethod
|
||||
def filterByDate(queryset, min_date, max_date):
|
||||
"""
|
||||
@ -132,7 +135,7 @@ class PurchaseOrder(Order):
|
||||
|
||||
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
|
||||
- A "pending" order where the target date lies within the date range
|
||||
- TODO: An "overdue" order where the target date is in the past
|
||||
"""
|
||||
|
||||
@ -149,13 +152,12 @@ class PurchaseOrder(Order):
|
||||
# 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
|
||||
# Construct a queryset for "pending" orders within the range
|
||||
pending = Q(status__in=PurchaseOrderStatus.OPEN) & ~Q(target_date=None) & Q(target_date__gte=min_date) & Q(target_date__lte=max_date)
|
||||
|
||||
# TODO - Construct a queryset for "overdue" orders within the range
|
||||
|
||||
flt = received
|
||||
|
||||
queryset = queryset.filter(flt)
|
||||
queryset = queryset.filter(received | pending)
|
||||
|
||||
return queryset
|
||||
|
||||
@ -186,9 +188,23 @@ class PurchaseOrder(Order):
|
||||
related_name='+'
|
||||
)
|
||||
|
||||
issue_date = models.DateField(blank=True, null=True, help_text=_('Date order was issued'))
|
||||
issue_date = models.DateField(
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Issue Date'),
|
||||
help_text=_('Date order was issued')
|
||||
)
|
||||
|
||||
complete_date = models.DateField(blank=True, null=True, help_text=_('Date order was completed'))
|
||||
target_date = models.DateField(
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Target Delivery Date'),
|
||||
help_text=_('Expected date for order delivery. Order will be overdue after this date.'),
|
||||
)
|
||||
|
||||
complete_date = models.DateField(
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Completion Date'),
|
||||
help_text=_('Date order was completed')
|
||||
)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('po-detail', kwargs={'pk': self.id})
|
||||
@ -256,6 +272,18 @@ class PurchaseOrder(Order):
|
||||
self.complete_date = datetime.now().date()
|
||||
self.save()
|
||||
|
||||
def is_overdue(self):
|
||||
"""
|
||||
Returns True if this PurchaseOrder is "overdue"
|
||||
|
||||
Makes use of the OVERDUE_FILTER to avoid code duplication.
|
||||
"""
|
||||
|
||||
query = PurchaseOrder.objects.filter(pk=self.pk)
|
||||
query = query.filter(PurchaseOrder.OVERDUE_FILTER)
|
||||
|
||||
return query.exists()
|
||||
|
||||
def can_cancel(self):
|
||||
"""
|
||||
A PurchaseOrder can only be cancelled under the following circumstances:
|
||||
@ -423,17 +451,13 @@ class SalesOrder(Order):
|
||||
"""
|
||||
Returns true if this SalesOrder is "overdue":
|
||||
|
||||
- Not completed
|
||||
- Target date is "in the past"
|
||||
Makes use of the OVERDUE_FILTER to avoid code duplication.
|
||||
"""
|
||||
|
||||
# Order cannot be deemed overdue if target_date is not set
|
||||
if self.target_date is None:
|
||||
return False
|
||||
query = SalesOrder.objects.filter(pk=self.pk)
|
||||
query = query.filer(SalesOrder.OVERDUE_FILTER)
|
||||
|
||||
today = datetime.now().date()
|
||||
|
||||
return self.is_pending and self.target_date < today
|
||||
return query.exists()
|
||||
|
||||
@property
|
||||
def is_pending(self):
|
||||
|
@ -40,12 +40,24 @@ class POSerializer(InvenTreeModelSerializer):
|
||||
def annotate_queryset(queryset):
|
||||
"""
|
||||
Add extra information to the queryset
|
||||
|
||||
- Number of liens in the PurchaseOrder
|
||||
- Overdue status of the PurchaseOrder
|
||||
"""
|
||||
|
||||
queryset = queryset.annotate(
|
||||
line_items=SubqueryCount('lines')
|
||||
)
|
||||
|
||||
queryset = queryset.annotate(
|
||||
overdue=Case(
|
||||
When(
|
||||
PurchaseOrder.OVERDUE_FILTER, then=Value(True, output_field=BooleanField()),
|
||||
),
|
||||
default=Value(False, output_field=BooleanField())
|
||||
)
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True)
|
||||
@ -54,6 +66,8 @@ class POSerializer(InvenTreeModelSerializer):
|
||||
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
||||
overdue = serializers.BooleanField(required=False, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = PurchaseOrder
|
||||
|
||||
@ -65,12 +79,14 @@ class POSerializer(InvenTreeModelSerializer):
|
||||
'description',
|
||||
'line_items',
|
||||
'link',
|
||||
'overdue',
|
||||
'reference',
|
||||
'supplier',
|
||||
'supplier_detail',
|
||||
'supplier_reference',
|
||||
'status',
|
||||
'status_text',
|
||||
'target_date',
|
||||
'notes',
|
||||
]
|
||||
|
||||
|
@ -26,7 +26,12 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<a href="{% url 'admin:order_purchaseorder_change' order.pk %}"><span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<h3>{% purchase_order_status_label order.status large=True %}</h3>
|
||||
<h3>
|
||||
{% purchase_order_status_label order.status large=True %}
|
||||
{% if order.is_overdue %}
|
||||
<span class='label label-large label-large-red'>{% trans "Overdue" %}</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<hr>
|
||||
<p>{{ order.description }}</p>
|
||||
<div class='btn-row'>
|
||||
@ -72,7 +77,12 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<tr>
|
||||
<td><span class='fas fa-info'></span></td>
|
||||
<td>{% trans "Order Status" %}</td>
|
||||
<td>{% purchase_order_status_label order.status %}</td>
|
||||
<td>
|
||||
{% purchase_order_status_label order.status %}
|
||||
{% if order.is_overdue %}
|
||||
<span class='label label-red'>{% trans "Overdue" %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><span class='fas fa-building'></span></td>
|
||||
@ -105,6 +115,13 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<td>{{ order.issue_date }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% 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.status == PurchaseOrderStatus.COMPLETE %}
|
||||
<tr>
|
||||
<td><span class='fas fa-calendar-alt'></span></td>
|
||||
|
@ -70,6 +70,8 @@ InvenTree | {% trans "Purchase Orders" %}
|
||||
|
||||
if (order.complete_date) {
|
||||
date = order.complete_date;
|
||||
} else if (order.target_date) {
|
||||
date = order.target_date;
|
||||
}
|
||||
|
||||
var title = `${prefix}${order.reference} - ${order.supplier_detail.name}`;
|
||||
|
@ -2,12 +2,16 @@
|
||||
Tests for the Order API
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework import status
|
||||
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from .models import PurchaseOrder, SalesOrder
|
||||
|
||||
|
||||
class OrderTest(APITestCase):
|
||||
|
||||
@ -18,6 +22,8 @@ class OrderTest(APITestCase):
|
||||
'location',
|
||||
'supplier_part',
|
||||
'stock',
|
||||
'order',
|
||||
'sales_order',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
@ -26,21 +32,80 @@ class OrderTest(APITestCase):
|
||||
get_user_model().objects.create_user('testuser', 'test@testing.com', 'password')
|
||||
self.client.login(username='testuser', password='password')
|
||||
|
||||
def doGet(self, url, options=''):
|
||||
def doGet(self, url, data={}):
|
||||
|
||||
return self.client.get(url + "?" + options, format='json')
|
||||
return self.client.get(url, data=data, format='json')
|
||||
|
||||
def doPost(self, url, data={}):
|
||||
return self.client.post(url, data=data, format='json')
|
||||
|
||||
def filter(self, filters, count):
|
||||
"""
|
||||
Test API filters
|
||||
"""
|
||||
|
||||
response = self.doGet(
|
||||
self.LIST_URL,
|
||||
filters
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), count)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class PurchaseOrderTest(OrderTest):
|
||||
"""
|
||||
Tests for the PurchaseOrder API
|
||||
"""
|
||||
|
||||
LIST_URL = reverse('api-po-list')
|
||||
|
||||
def test_po_list(self):
|
||||
|
||||
url = reverse('api-po-list')
|
||||
# List *ALL* PO items
|
||||
self.filter({}, 6)
|
||||
|
||||
# Filter by supplier
|
||||
self.filter({'supplier': 1}, 1)
|
||||
self.filter({'supplier': 3}, 5)
|
||||
|
||||
# Filter by "outstanding"
|
||||
self.filter({'outstanding': True}, 4)
|
||||
self.filter({'outstanding': False}, 2)
|
||||
|
||||
# Filter by "status"
|
||||
self.filter({'status': 10}, 2)
|
||||
self.filter({'status': 40}, 1)
|
||||
|
||||
def test_overdue(self):
|
||||
"""
|
||||
Test "overdue" status
|
||||
"""
|
||||
|
||||
self.filter({'overdue': True}, 0)
|
||||
self.filter({'overdue': False}, 6)
|
||||
|
||||
order = PurchaseOrder.objects.get(pk=1)
|
||||
order.target_date = datetime.now().date() - timedelta(days=10)
|
||||
order.save()
|
||||
|
||||
self.filter({'overdue': True}, 1)
|
||||
self.filter({'overdue': False}, 5)
|
||||
|
||||
def test_po_detail(self):
|
||||
|
||||
url = '/api/order/po/1/'
|
||||
|
||||
# List all order items
|
||||
response = self.doGet(url)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Filter by stuff
|
||||
response = self.doGet(url, 'status=10&part=1&supplier_part=1')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
data = response.data
|
||||
|
||||
self.assertEqual(data['pk'], 1)
|
||||
self.assertEqual(data['description'], 'Ordering some screws')
|
||||
|
||||
def test_po_attachments(self):
|
||||
|
||||
@ -50,6 +115,60 @@ class OrderTest(APITestCase):
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
class SalesOrderTest(OrderTest):
|
||||
"""
|
||||
Tests for the SalesOrder API
|
||||
"""
|
||||
|
||||
LIST_URL = reverse('api-so-list')
|
||||
|
||||
def test_so_list(self):
|
||||
|
||||
# All orders
|
||||
self.filter({}, 5)
|
||||
|
||||
# Filter by customer
|
||||
self.filter({'customer': 4}, 3)
|
||||
self.filter({'customer': 5}, 2)
|
||||
|
||||
# Filter by outstanding
|
||||
self.filter({'outstanding': True}, 3)
|
||||
self.filter({'outstanding': False}, 2)
|
||||
|
||||
# Filter by status
|
||||
self.filter({'status': 10}, 3) # PENDING
|
||||
self.filter({'status': 20}, 1) # SHIPPED
|
||||
self.filter({'status': 99}, 0) # Invalid
|
||||
|
||||
def test_overdue(self):
|
||||
"""
|
||||
Test "overdue" status
|
||||
"""
|
||||
|
||||
self.filter({'overdue': True}, 0)
|
||||
self.filter({'overdue': False}, 5)
|
||||
|
||||
for pk in [1, 2]:
|
||||
order = SalesOrder.objects.get(pk=pk)
|
||||
order.target_date = datetime.now().date() - timedelta(days=10)
|
||||
order.save()
|
||||
|
||||
self.filter({'overdue': True}, 2)
|
||||
self.filter({'overdue': False}, 3)
|
||||
|
||||
def test_so_detail(self):
|
||||
|
||||
url = '/api/order/so/1/'
|
||||
|
||||
response = self.doGet(url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
data = response.data
|
||||
|
||||
self.assertEqual(data['pk'], 1)
|
||||
|
||||
def test_so_attachments(self):
|
||||
|
||||
url = reverse('api-so-attachment-list')
|
||||
|
@ -41,7 +41,7 @@ class OrderTest(TestCase):
|
||||
|
||||
next_ref = PurchaseOrder.getNextOrderNumber()
|
||||
|
||||
self.assertEqual(next_ref, '0003')
|
||||
self.assertEqual(next_ref, '0007')
|
||||
|
||||
def test_on_order(self):
|
||||
""" There should be 3 separate items on order for the M2x4 LPHS part """
|
||||
|
@ -35,6 +35,7 @@ InvenTree | {% trans "Index" %}
|
||||
{% if roles.purchase_order.view %}
|
||||
{% include "InvenTree/po_outstanding.html" with collapse_id="po_outstanding" %}
|
||||
{% endif %}
|
||||
{% include "InvenTree/po_overdue.html" with collapse_id="po_overdue" %}
|
||||
{% if roles.sales_order.view %}
|
||||
{% include "InvenTree/so_outstanding.html" with collapse_id="so_outstanding" %}
|
||||
{% include "InvenTree/so_overdue.html" with collapse_id="so_overdue" %}
|
||||
@ -130,6 +131,14 @@ loadPurchaseOrderTable("#po-outstanding-table", {
|
||||
}
|
||||
});
|
||||
|
||||
loadPurchaseOrderTable("#po-overdue-table", {
|
||||
url: "{% url 'api-po-list' %}",
|
||||
params: {
|
||||
supplier_detail: true,
|
||||
overdue: true,
|
||||
}
|
||||
});
|
||||
|
||||
loadSalesOrderTable("#so-outstanding-table", {
|
||||
url: "{% url 'api-so-list' %}",
|
||||
params: {
|
||||
@ -158,6 +167,7 @@ loadSalesOrderTable("#so-overdue-table", {
|
||||
{% include "InvenTree/index/on_load.html" with label="stock-to-build" %}
|
||||
|
||||
{% include "InvenTree/index/on_load.html" with label="po-outstanding" %}
|
||||
{% include "InvenTree/index/on_load.html" with label="po-overdue" %}
|
||||
{% include "InvenTree/index/on_load.html" with label="so-outstanding" %}
|
||||
{% include "InvenTree/index/on_load.html" with label="so-overdue" %}
|
||||
|
||||
|
15
InvenTree/templates/InvenTree/po_overdue.html
Normal file
15
InvenTree/templates/InvenTree/po_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 Purchase Orders" %}<span class='badge' id='po-overdue-count'><span class='fas fa-spin fa-spinner'></span></span>
|
||||
{% endblock %}
|
||||
|
||||
{% block collapse_content %}
|
||||
|
||||
<table class='table table-striped table-condensed' id='po-overdue-table'>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
@ -141,9 +141,9 @@ function loadPurchaseOrderTable(table, options) {
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Purchase Order" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
@ -153,13 +153,19 @@ function loadPurchaseOrderTable(table, options) {
|
||||
value = `${prefix}${value}`;
|
||||
}
|
||||
|
||||
return renderLink(value, `/order/purchase-order/${row.pk}/`);
|
||||
var html = renderLink(value, `/order/purchase-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'supplier_detail',
|
||||
title: '{% trans "Supplier" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`);
|
||||
}
|
||||
@ -170,27 +176,32 @@ function loadPurchaseOrderTable(table, options) {
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
return purchaseOrderStatusDisplay(row.status, row.status_text);
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Date" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}'
|
||||
title: '{% trans "Items" %}',
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -214,6 +214,10 @@ function getAvailableTableFilters(tableKey) {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user