2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 04:25:42 +00:00

Merge branch 'inventree:master' into fr-2986-shipment-page-action

This commit is contained in:
Maksim Stojkovic
2022-05-20 16:02:45 +10:00
committed by GitHub
34 changed files with 902 additions and 44 deletions

View File

@ -105,6 +105,9 @@ class PurchaseOrderResource(ModelResource):
model = PurchaseOrder
skip_unchanged = True
clean_model_instances = True
exclude = [
'metadata',
]
class PurchaseOrderLineItemResource(ModelResource):
@ -147,6 +150,9 @@ class SalesOrderResource(ModelResource):
model = SalesOrder
skip_unchanged = True
clean_model_instances = True
exclude = [
'metadata',
]
class SalesOrderLineItemResource(ModelResource):

View File

@ -667,9 +667,9 @@ class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
outstanding = str2bool(outstanding)
if outstanding:
queryset = queryset.filter(status__in=models.SalesOrderStatus.OPEN)
queryset = queryset.filter(status__in=SalesOrderStatus.OPEN)
else:
queryset = queryset.exclude(status__in=models.SalesOrderStatus.OPEN)
queryset = queryset.exclude(status__in=SalesOrderStatus.OPEN)
# Filter by 'overdue' status
overdue = params.get('overdue', None)

View File

@ -12,6 +12,8 @@ from decimal import Decimal
from django.db import models, transaction
from django.db.models import Q, F, Sum
from django.db.models.functions import Coalesce
from django.db.models.signals import post_save
from django.dispatch.dispatcher import receiver
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError
@ -809,6 +811,21 @@ class SalesOrder(Order):
return self.pending_shipments().count()
@receiver(post_save, sender=SalesOrder, dispatch_uid='build_post_save_log')
def after_save_sales_order(sender, instance: SalesOrder, created: bool, **kwargs):
"""
Callback function to be executed after a SalesOrder instance is saved
"""
if created and getSetting('SALESORDER_DEFAULT_SHIPMENT'):
# A new SalesOrder has just been created
# Create default shipment
SalesOrderShipment.objects.create(
order=instance,
reference='1',
)
class PurchaseOrderAttachment(InvenTreeAttachment):
"""
Model for storing file attachments against a PurchaseOrder object

View File

@ -2,6 +2,8 @@
Tests for the Order API
"""
import io
from datetime import datetime, timedelta
from rest_framework import status
@ -323,6 +325,77 @@ class PurchaseOrderTest(OrderTest):
self.assertEqual(order.get_metadata('yam'), 'yum')
class PurchaseOrderDownloadTest(OrderTest):
"""Unit tests for downloading PurchaseOrder data via the API endpoint"""
required_cols = [
'id',
'line_items',
'description',
'issue_date',
'notes',
'reference',
'status',
'supplier_reference',
]
excluded_cols = [
'metadata',
]
def test_download_wrong_format(self):
"""Incorrect format should default raise an error"""
url = reverse('api-po-list')
with self.assertRaises(ValueError):
self.download_file(
url,
{
'export': 'xyz',
}
)
def test_download_csv(self):
"""Download PurchaseOrder data as .csv"""
with self.download_file(
reverse('api-po-list'),
{
'export': 'csv',
},
expected_code=200,
expected_fn='InvenTree_PurchaseOrders.csv',
) as fo:
data = self.process_csv(
fo,
required_cols=self.required_cols,
excluded_cols=self.excluded_cols,
required_rows=models.PurchaseOrder.objects.count()
)
for row in data:
order = models.PurchaseOrder.objects.get(pk=row['id'])
self.assertEqual(order.description, row['description'])
self.assertEqual(order.reference, row['reference'])
def test_download_line_items(self):
with self.download_file(
reverse('api-po-line-list'),
{
'export': 'xlsx',
},
decode=False,
expected_code=200,
expected_fn='InvenTree_PurchaseOrderItems.xlsx',
) as fo:
self.assertTrue(isinstance(fo, io.BytesIO))
class PurchaseOrderReceiveTest(OrderTest):
"""
Unit tests for receiving items against a PurchaseOrder
@ -908,6 +981,177 @@ class SalesOrderTest(OrderTest):
self.assertEqual(order.get_metadata('xyz'), 'abc')
class SalesOrderLineItemTest(OrderTest):
"""
Tests for the SalesOrderLineItem API
"""
def setUp(self):
super().setUp()
# List of salable parts
parts = Part.objects.filter(salable=True)
# Create a bunch of SalesOrderLineItems for each order
for idx, so in enumerate(models.SalesOrder.objects.all()):
for part in parts:
models.SalesOrderLineItem.objects.create(
order=so,
part=part,
quantity=(idx + 1) * 5,
reference=f"Order {so.reference} - line {idx}",
)
self.url = reverse('api-so-line-list')
def test_so_line_list(self):
# List *all* lines
response = self.get(
self.url,
{},
expected_code=200,
)
n = models.SalesOrderLineItem.objects.count()
# We should have received *all* lines
self.assertEqual(len(response.data), n)
# List *all* lines, but paginate
response = self.get(
self.url,
{
"limit": 5,
},
expected_code=200,
)
self.assertEqual(response.data['count'], n)
self.assertEqual(len(response.data['results']), 5)
n_orders = models.SalesOrder.objects.count()
n_parts = Part.objects.filter(salable=True).count()
# List by part
for part in Part.objects.filter(salable=True):
response = self.get(
self.url,
{
'part': part.pk,
'limit': 10,
}
)
self.assertEqual(response.data['count'], n_orders)
# List by order
for order in models.SalesOrder.objects.all():
response = self.get(
self.url,
{
'order': order.pk,
'limit': 10,
}
)
self.assertEqual(response.data['count'], n_parts)
class SalesOrderDownloadTest(OrderTest):
"""Unit tests for downloading SalesOrder data via the API endpoint"""
def test_download_fail(self):
"""Test that downloading without the 'export' option fails"""
url = reverse('api-so-list')
with self.assertRaises(ValueError):
self.download_file(url, {}, expected_code=200)
def test_download_xls(self):
url = reverse('api-so-list')
# Download .xls file
with self.download_file(
url,
{
'export': 'xls',
},
expected_code=200,
expected_fn='InvenTree_SalesOrders.xls',
decode=False,
) as fo:
self.assertTrue(isinstance(fo, io.BytesIO))
def test_download_csv(self):
url = reverse('api-so-list')
required_cols = [
'line_items',
'id',
'reference',
'customer',
'status',
'shipment_date',
'notes',
'description',
]
excluded_cols = [
'metadata'
]
# Download .xls file
with self.download_file(
url,
{
'export': 'csv',
},
expected_code=200,
expected_fn='InvenTree_SalesOrders.csv',
decode=True
) as fo:
data = self.process_csv(
fo,
required_cols=required_cols,
excluded_cols=excluded_cols,
required_rows=models.SalesOrder.objects.count()
)
for line in data:
order = models.SalesOrder.objects.get(pk=line['id'])
self.assertEqual(line['description'], order.description)
self.assertEqual(line['status'], str(order.status))
# Download only outstanding sales orders
with self.download_file(
url,
{
'export': 'tsv',
'outstanding': True,
},
expected_code=200,
expected_fn='InvenTree_SalesOrders.tsv',
decode=True,
) as fo:
self.process_csv(
fo,
required_cols=required_cols,
excluded_cols=excluded_cols,
required_rows=models.SalesOrder.objects.filter(status__in=SalesOrderStatus.OPEN).count(),
delimiter='\t',
)
class SalesOrderAllocateTest(OrderTest):
"""
Unit tests for allocating stock items against a SalesOrder

View File

@ -10,6 +10,8 @@ from company.models import Company
from InvenTree import status_codes as status
from common.models import InvenTreeSetting
from order.models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation, SalesOrderShipment
from part.models import Part
@ -200,3 +202,37 @@ class SalesOrderTest(TestCase):
self.assertTrue(self.line.is_fully_allocated())
self.assertEqual(self.line.fulfilled_quantity(), 50)
self.assertEqual(self.line.allocated_quantity(), 50)
def test_default_shipment(self):
# Test sales order default shipment creation
# Default setting value should be False
self.assertEqual(False, InvenTreeSetting.get_setting('SALESORDER_DEFAULT_SHIPMENT'))
# Create an order
order_1 = SalesOrder.objects.create(
customer=self.customer,
reference='1235',
customer_reference='ABC 55556'
)
# Order should have no shipments when setting is False
self.assertEqual(0, order_1.shipment_count)
# Update setting to True
InvenTreeSetting.set_setting('SALESORDER_DEFAULT_SHIPMENT', True, None)
self.assertEqual(True, InvenTreeSetting.get_setting('SALESORDER_DEFAULT_SHIPMENT'))
# Create a second order
order_2 = SalesOrder.objects.create(
customer=self.customer,
reference='1236',
customer_reference='ABC 55557'
)
# Order should have one shipment
self.assertEqual(1, order_2.shipment_count)
self.assertEqual(1, order_2.pending_shipments().count())
# Shipment should have default reference of '1'
self.assertEqual('1', order_2.pending_shipments()[0].reference)