Added data migration for existing SalesOrder instances

- If a SalesOrder is "PENDING" or there are allocations available, a shipment is created
This commit is contained in:
Oliver 2021-10-25 22:35:27 +11:00
parent 2f7e0974b7
commit ce5b47460a
5 changed files with 137 additions and 8 deletions

View File

@ -18,7 +18,6 @@ class Migration(migrations.Migration):
name='SalesOrderShipment', name='SalesOrderShipment',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Shipped'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Shipment status', verbose_name='Status')),
('shipment_date', models.DateField(blank=True, help_text='Date of shipment', null=True, verbose_name='Shipment Date')), ('shipment_date', models.DateField(blank=True, help_text='Date of shipment', null=True, verbose_name='Shipment Date')),
('reference', models.CharField(blank=True, help_text='Shipment reference', max_length=100, verbose_name='Reference')), ('reference', models.CharField(blank=True, help_text='Shipment reference', max_length=100, verbose_name='Reference')),
('notes', markdownx.models.MarkdownxField(blank=True, help_text='Shipment notes', verbose_name='Notes')), ('notes', markdownx.models.MarkdownxField(blank=True, help_text='Shipment notes', verbose_name='Notes')),

View File

@ -0,0 +1,89 @@
# Generated by Django 3.2.5 on 2021-10-25 06:45
from django.db import migrations
from InvenTree.status_codes import SalesOrderStatus
def add_shipment(apps, schema_editor):
"""
Create a SalesOrderShipment for each existing SalesOrder instance.
Any "allocations" are marked against that shipment.
For each existing SalesOrder instance, we create a default SalesOrderShipment,
and associate each SalesOrderAllocation with this shipment
"""
Allocation = apps.get_model('order', 'salesorderallocation')
SalesOrder = apps.get_model('order', 'salesorder')
Shipment = apps.get_model('order', 'salesordershipment')
n = 0
for order in SalesOrder.objects.all():
"""
We only create an automatic shipment for "PENDING" orders,
as SalesOrderAllocations were historically deleted for "SHIPPED" or "CANCELLED" orders
"""
allocations = Allocation.objects.filter(
line__order=order
)
if allocations.count() == 0 and order.status != SalesOrderStatus.PENDING:
continue
# Create a new Shipment instance against this order
shipment = Shipment.objects.create(
order=order,
)
shipment.save()
# Iterate through each allocation associated with this order
for allocation in allocations:
allocation.shipment = shipment
allocation.save()
n += 1
if n > 0:
print(f"\nCreated SalesOrderShipment for {n} SalesOrder instances")
def reverse_add_shipment(apps, schema_editor):
"""
Reverse the migration, delete and SalesOrderShipment instances
"""
Allocation = apps.get_model('order', 'salesorderallocation')
# First, ensure that all SalesOrderAllocation objects point to a null shipment
for allocation in Allocation.objects.exclude(shipment=None):
allocation.shipment = None
allocation.save()
SOS = apps.get_model('order', 'salesordershipment')
n = SOS.objects.count()
print(f"Deleting {n} SalesOrderShipment instances")
SOS.objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('order', '0054_salesorderallocation_shipment'),
]
operations = [
migrations.RunPython(
add_shipment,
reverse_code=reverse_add_shipment,
)
]

View File

@ -929,13 +929,6 @@ class SalesOrderShipment(models.Model):
help_text=_('Sales Order'), help_text=_('Sales Order'),
) )
status = models.PositiveIntegerField(
default=SalesOrderStatus.PENDING,
choices=SalesOrderStatus.items(),
verbose_name=_('Status'),
help_text=_('Shipment status'),
)
shipment_date = models.DateField( shipment_date = models.DateField(
null=True, blank=True, null=True, blank=True,
verbose_name=_('Shipment Date'), verbose_name=_('Shipment Date'),

View File

@ -5,6 +5,7 @@ Unit tests for the 'order' model data migrations
from django_test_migrations.contrib.unittest_case import MigratorTestCase from django_test_migrations.contrib.unittest_case import MigratorTestCase
from InvenTree import helpers from InvenTree import helpers
from InvenTree.status_codes import SalesOrderStatus
class TestForwardMigrations(MigratorTestCase): class TestForwardMigrations(MigratorTestCase):
@ -57,3 +58,49 @@ class TestForwardMigrations(MigratorTestCase):
# The integer reference field must have been correctly updated # The integer reference field must have been correctly updated
self.assertEqual(order.reference_int, ii) self.assertEqual(order.reference_int, ii)
class TestShipmentMigration(MigratorTestCase):
"""
Test data migration for the "SalesOrderShipment" model
"""
migrate_from = ('order', '0051_auto_20211014_0623')
migrate_to = ('order', '0055_auto_20211025_0645')
def prepare(self):
"""
Create an initial SalesOrder
"""
Company = self.old_state.apps.get_model('company', 'company')
customer = Company.objects.create(
name='My customer',
description='A customer we sell stuff too',
is_customer=True
)
SalesOrder = self.old_state.apps.get_model('order', 'salesorder')
for ii in range(5):
order = SalesOrder.objects.create(
reference=f'SO{ii}',
customer=customer,
description='A sales order for stuffs',
status=SalesOrderStatus.PENDING,
)
order.save()
def test_shipment_creation(self):
"""
Check that a SalesOrderShipment has been created
"""
SalesOrder = self.new_state.apps.get_model('order', 'salesorder')
Shipment = self.new_state.apps.get_model('order', 'salesordershipment')
# Check that the correct number of Shipments have been created
self.assertEqual(SalesOrder.objects.count(), 5)
self.assertEqual(Shipment.objects.count(), 5)

View File

@ -1681,6 +1681,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
location_detail: true, location_detail: true,
in_stock: true, in_stock: true,
part: line_item.part, part: line_item.part,
include_variants: false,
exclude_so_allocation: options.order, exclude_so_allocation: options.order,
}, },
auto_fill: true, auto_fill: true,