mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Allow shipment numbers to be non-unique for different sales orders
- must be unique for a given sales order
This commit is contained in:
parent
f3f3030b37
commit
3f9b280e17
@ -22,7 +22,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('shipment_date', models.DateField(blank=True, help_text='Date of shipment', null=True, verbose_name='Shipment Date')),
|
||||
('reference', models.CharField(default=order.models.get_next_shipment_number, unique=True, help_text='Shipment reference', max_length=100, verbose_name='Reference')),
|
||||
('reference', models.CharField(default='1', help_text='Shipment reference', max_length=100, verbose_name='Reference')),
|
||||
('notes', markdownx.models.MarkdownxField(blank=True, help_text='Shipment notes', verbose_name='Notes')),
|
||||
('checked_by', models.ForeignKey(blank=True, help_text='User who checked this shipment', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Checked By')),
|
||||
('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='shipments', to='order.salesorder', verbose_name='Order')),
|
||||
|
22
InvenTree/order/migrations/0060_auto_20211129_1339.py
Normal file
22
InvenTree/order/migrations/0060_auto_20211129_1339.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Generated by Django 3.2.5 on 2021-11-29 13:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('order', '0059_salesordershipment_tracking_number'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='salesordershipment',
|
||||
name='reference',
|
||||
field=models.CharField(default='1', help_text='Shipment number', max_length=100, verbose_name='Shipment'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='salesordershipment',
|
||||
unique_together={('order', 'reference')},
|
||||
),
|
||||
]
|
@ -900,35 +900,6 @@ class SalesOrderLineItem(OrderLineItem):
|
||||
return self.shipped >= self.quantity
|
||||
|
||||
|
||||
def get_next_shipment_number():
|
||||
"""
|
||||
Returns the next available SalesOrderShipment reference number"
|
||||
"""
|
||||
|
||||
if SalesOrderShipment.objects.count() == 0:
|
||||
return "001"
|
||||
|
||||
shipment = SalesOrderShipment.objects.exclude(reference=None).last()
|
||||
|
||||
attempts = set([shipment.reference])
|
||||
|
||||
reference = shipment.reference
|
||||
|
||||
while 1:
|
||||
reference = increment(reference)
|
||||
|
||||
if reference in attempts:
|
||||
# Escape infinite recursion
|
||||
return reference
|
||||
|
||||
if SalesOrderShipment.objects.filter(reference=reference).exists():
|
||||
attempts.add(reference)
|
||||
else:
|
||||
break
|
||||
|
||||
return reference
|
||||
|
||||
|
||||
class SalesOrderShipment(models.Model):
|
||||
"""
|
||||
The SalesOrderShipment model represents a physical shipment made against a SalesOrder.
|
||||
@ -945,6 +916,12 @@ class SalesOrderShipment(models.Model):
|
||||
notes: Custom notes field for this shipment
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
# Shipment reference must be unique for a given sales order
|
||||
unique_together = [
|
||||
'order', 'reference',
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
return reverse('api-so-shipment-list')
|
||||
@ -976,10 +953,9 @@ class SalesOrderShipment(models.Model):
|
||||
reference = models.CharField(
|
||||
max_length=100,
|
||||
blank=False,
|
||||
unique=True,
|
||||
verbose_name=('Reference'),
|
||||
help_text=_('Shipment reference'),
|
||||
default=get_next_shipment_number,
|
||||
verbose_name=('Shipment'),
|
||||
help_text=_('Shipment number'),
|
||||
default='1',
|
||||
)
|
||||
|
||||
notes = MarkdownxField(
|
||||
|
@ -155,6 +155,7 @@
|
||||
$('#new-shipment').click(function() {
|
||||
createSalesOrderShipment({
|
||||
order: {{ order.pk }},
|
||||
reference: '{{ order.reference }}',
|
||||
onSuccess: function(data) {
|
||||
$('#pending-shipments-table').bootstrapTable('refresh');
|
||||
}
|
||||
|
@ -7,11 +7,15 @@ from django.core.exceptions import ValidationError
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from company.models import Company
|
||||
from stock.models import StockItem
|
||||
from order.models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation
|
||||
from part.models import Part
|
||||
|
||||
from InvenTree import status_codes as status
|
||||
|
||||
from order.models import SalesOrder, SalesOrderLineItem, SalesOrderShipment, SalesOrderAllocation
|
||||
|
||||
from part.models import Part
|
||||
|
||||
from stock.models import StockItem
|
||||
|
||||
|
||||
class SalesOrderTest(TestCase):
|
||||
"""
|
||||
|
@ -75,16 +75,56 @@ function completeShipment(shipment_id) {
|
||||
|
||||
// Open a dialog to create a new sales order shipment
|
||||
function createSalesOrderShipment(options={}) {
|
||||
constructForm('{% url "api-so-shipment-list" %}', {
|
||||
method: 'POST',
|
||||
fields: salesOrderShipmentFields(options),
|
||||
title: '{% trans "Create New Shipment" %}',
|
||||
onSuccess: function(data) {
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess(data);
|
||||
|
||||
// Work out the next shipment number for the given order
|
||||
inventreeGet(
|
||||
'{% url "api-so-shipment-list" %}',
|
||||
{
|
||||
order: options.order,
|
||||
},
|
||||
{
|
||||
success: function(results) {
|
||||
// "predict" the next reference number
|
||||
var ref = results.length + 1;
|
||||
|
||||
var found = false;
|
||||
|
||||
while (!found) {
|
||||
|
||||
var no_match = true;
|
||||
|
||||
for (var ii = 0; ii < results.length; ii++) {
|
||||
if (ref.toString() == results[ii].reference.toString()) {
|
||||
no_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (no_match) {
|
||||
break;
|
||||
} else {
|
||||
ref++;
|
||||
}
|
||||
}
|
||||
|
||||
var fields = salesOrderShipmentFields(options);
|
||||
|
||||
fields.reference.value = ref;
|
||||
fields.reference.prefix = global_settings.SALESORDER_REFERENCE_PREFIX + options.reference;
|
||||
|
||||
constructForm('{% url "api-so-shipment-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create New Shipment" %}',
|
||||
onSuccess: function(data) {
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1271,15 +1311,20 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
title: '{% trans "Shipment" %}',
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
},
|
||||
{
|
||||
field: 'shipment_date',
|
||||
title: '{% trans "Shipment Date" %}',
|
||||
visible: options.shipped,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return value;
|
||||
} else {
|
||||
return '{% trans "Not shipped" %}';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'tracking_number',
|
||||
title: '{% trans "Tracking" %}',
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
@ -1711,7 +1756,7 @@ function loadSalesOrderAllocationTable(table, options={}) {
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
sortable: true,
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user