Fix: Allow allocation of variants to sales order lines (#5656)

* Add tests for allocating a variant to a sales order line

* Add check for variants to match template allocation

* Tweak fixtures for inventree-python, improve test checks

* Optimize check for item variants
This commit is contained in:
Joe Rogers 2023-10-04 01:39:22 +02:00 committed by GitHub
parent 06eb948528
commit c18bc4e1d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 8 deletions

View File

@ -1602,7 +1602,9 @@ class SalesOrderAllocation(models.Model):
try:
if self.line.part != self.item.part:
errors['item'] = _('Cannot allocate stock item to a line with a different part')
variants = self.line.part.get_descendants(include_self=True)
if self.line.part not in variants:
errors['item'] = _('Cannot allocate stock item to a line with a different part')
except PartModels.Part.DoesNotExist:
errors['line'] = _('Cannot allocate stock to a line without a part')

View File

@ -1716,7 +1716,7 @@ class SalesOrderAllocateTest(OrderTest):
self.assertIn('Shipment is not associated with this order', str(response.data['shipment']))
def test_allocate(self):
"""Test the the allocation endpoint acts as expected, when provided with valid data!"""
"""Test that the allocation endpoint acts as expected, when provided with valid data!"""
# First, check that there are no line items allocated against this SalesOrder
self.assertEqual(self.order.stock_allocations.count(), 0)
@ -1745,6 +1745,43 @@ class SalesOrderAllocateTest(OrderTest):
for line in self.order.lines.all():
self.assertEqual(line.allocations.count(), 1)
def test_allocate_variant(self):
"""Test that the allocation endpoint acts as expected, when provided with variant"""
# First, check that there are no line items allocated against this SalesOrder
self.assertEqual(self.order.stock_allocations.count(), 0)
data = {
"items": [],
"shipment": self.shipment.pk,
}
def check_template(line_item):
return line_item.part.is_template
for line in filter(check_template, self.order.lines.all()):
stock_item = None
# Allocate a matching variant
parts = Part.objects.filter(salable=True).filter(variant_of=line.part.pk)
for part in parts:
stock_item = part.stock_items.last()
break
# Fully-allocate each line
data['items'].append({
"line_item": line.pk,
"stock_item": stock_item.pk,
"quantity": 5
})
self.post(self.url, data, expected_code=201)
# At least one item should be allocated, and all should be variants
self.assertGreater(self.order.stock_allocations.count(), 0)
for allocation in self.order.stock_allocations.all():
self.assertNotEquals(allocation.item.part.pk, allocation.line.part.pk)
def test_shipment_complete(self):
"""Test that we can complete a shipment via the API."""
url = reverse('api-so-shipment-ship', kwargs={'pk': self.shipment.pk})

View File

@ -33,11 +33,23 @@ class SalesOrderTest(TestCase):
cls.customer = Company.objects.create(name="ABC Co", description="My customer", is_customer=True)
# Create a Part to ship
cls.part = Part.objects.create(name='Spanner', salable=True, description='A spanner that I sell')
cls.part = Part.objects.create(
name='Spanner',
salable=True,
description='A spanner that I sell',
is_template=True,
)
cls.variant = Part.objects.create(
name='Blue Spanner',
salable=True,
description='A blue spanner that I sell',
variant_of=cls.part,
)
# Create some stock!
cls.Sa = StockItem.objects.create(part=cls.part, quantity=100)
cls.Sb = StockItem.objects.create(part=cls.part, quantity=200)
cls.Sc = StockItem.objects.create(part=cls.variant, quantity=100)
# Create a SalesOrder to ship against
cls.order = SalesOrder.objects.create(
@ -145,6 +157,16 @@ class SalesOrderTest(TestCase):
self.assertTrue(self.line.is_fully_allocated())
self.assertEqual(self.line.allocated_quantity(), 50)
def test_allocate_variant(self):
"""Allocate a variant of the designated item"""
SalesOrderAllocation.objects.create(
line=self.line,
shipment=self.shipment,
item=StockItem.objects.get(pk=self.Sc.pk),
quantity=50
)
self.assertEqual(self.line.allocated_quantity(), 50)
def test_order_cancel(self):
"""Allocate line items then cancel the order"""
self.allocate_stock(True)
@ -166,8 +188,8 @@ class SalesOrderTest(TestCase):
def test_complete_order(self):
"""Allocate line items, then ship the order"""
# Assert some stuff before we run the test
# Initially there are two stock items
self.assertEqual(StockItem.objects.count(), 2)
# Initially there are three stock items
self.assertEqual(StockItem.objects.count(), 3)
# Take 25 units from each StockItem
self.allocate_stock(True)
@ -194,15 +216,17 @@ class SalesOrderTest(TestCase):
self.assertEqual(self.order.status, status.SalesOrderStatus.SHIPPED)
self.assertIsNotNone(self.order.shipment_date)
# There should now be 4 stock items
self.assertEqual(StockItem.objects.count(), 4)
# There should now be 5 stock items
self.assertEqual(StockItem.objects.count(), 5)
sa = StockItem.objects.get(pk=self.Sa.pk)
sb = StockItem.objects.get(pk=self.Sb.pk)
sc = StockItem.objects.get(pk=self.Sc.pk)
# 25 units subtracted from each of the original items
# 25 units subtracted from each of the original non-variant items
self.assertEqual(sa.quantity, 75)
self.assertEqual(sb.quantity, 175)
self.assertEqual(sc.quantity, 100)
# And 2 items created which are associated with the order
outputs = StockItem.objects.filter(sales_order=self.order)

View File

@ -156,6 +156,7 @@
variant_of: 10000
IPN: "R.CH"
trackable: true
salable: true
category: 7
tree_id: 1
level: 0