Merge pull request #804 from SchrodingersGat/order-auto-increment

Order auto increment
This commit is contained in:
Oliver 2020-05-14 15:36:05 +10:00 committed by GitHub
commit cf9891398a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 150 additions and 22 deletions

View File

@ -120,6 +120,59 @@ def normalize(d):
return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize() return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()
def increment(n):
"""
Attempt to increment an integer (or a string that looks like an integer!)
e.g.
001 -> 002
2 -> 3
AB01 -> AB02
QQQ -> QQQ
"""
value = str(n).strip()
# Ignore empty strings
if not value:
return value
pattern = r"(.*?)(\d+)?$"
result = re.search(pattern, value)
# No match!
if result is None:
return value
groups = result.groups()
# If we cannot match the regex, then simply return the provided value
if not len(groups) == 2:
return value
prefix, number = groups
# No number extracted? Simply return the prefix (without incrementing!)
if not number:
return prefix
# Record the width of the number
width = len(number)
try:
number = int(number) + 1
number = str(number)
except ValueError:
pass
number = number.zfill(width)
return prefix + number
def decimal2string(d): def decimal2string(d):
""" """
Format a Decimal number as a string, Format a Decimal number as a string,

View File

@ -108,6 +108,29 @@ class TestQuoteWrap(TestCase):
self.assertEqual(helpers.WrapWithQuotes('hello"'), '"hello"') self.assertEqual(helpers.WrapWithQuotes('hello"'), '"hello"')
class TestIncrement(TestCase):
def tests(self):
""" Test 'intelligent' incrementing function """
tests = [
("", ""),
(1, "2"),
("001", "002"),
("1001", "1002"),
("ABC123", "ABC124"),
("XYZ0", "XYZ1"),
("123Q", "123Q"),
("QQQ", "QQQ"),
]
for test in tests:
a, b = test
result = helpers.increment(a)
self.assertEqual(result, b)
class TestMakeBarcode(TestCase): class TestMakeBarcode(TestCase):
""" Tests for barcode string creation """ """ Tests for barcode string creation """

View File

@ -85,10 +85,10 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
$('#company-edit').click(function() { $('#company-edit').click(function() {
launchModalForm( launchModalForm(
"{% url 'company-edit' company.id %}", "{% url 'company-edit' company.id %}",
{ {
reload: true reload: true
}); });
}); });
$("#company-order-2").click(function() { $("#company-order-2").click(function() {
@ -104,10 +104,10 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
$('#company-delete').click(function() { $('#company-delete').click(function() {
launchModalForm( launchModalForm(
"{% url 'company-delete' company.id %}", "{% url 'company-delete' company.id %}",
{ {
redirect: "{% url 'company-index' %}" redirect: "{% url 'company-index' %}"
}); });
}); });
enableDragAndDrop( enableDragAndDrop(
@ -123,11 +123,11 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
$("#company-thumb").click(function() { $("#company-thumb").click(function() {
launchModalForm( launchModalForm(
"{% url 'company-image' company.id %}", "{% url 'company-image' company.id %}",
{ {
reload: true reload: true
} }
); );
}); });
{% endblock %} {% endblock %}

View File

@ -33,9 +33,16 @@
} }
}); });
$("#new-sales-order").click(function() { $("#new-sales-order").click(function() {
// TODO - Create a new sales order launchModalForm(
"{% url 'so-create' %}",
{
data: {
customer: {{ company.id }},
},
follow: true,
},
);
}); });
{% endblock %} {% endblock %}

View File

@ -4,7 +4,7 @@
- model: order.purchaseorder - model: order.purchaseorder
pk: 1 pk: 1
fields: fields:
reference: 0001 reference: '0001'
description: "Ordering some screws" description: "Ordering some screws"
supplier: 1 supplier: 1
@ -12,7 +12,7 @@
- model: order.purchaseorder - model: order.purchaseorder
pk: 2 pk: 2
fields: fields:
reference: 0002 reference: '0002'
description: "Ordering some more screws" description: "Ordering some more screws"
supplier: 3 supplier: 3

View File

@ -24,7 +24,7 @@ from stock import models as stock_models
from company.models import Company, SupplierPart from company.models import Company, SupplierPart
from InvenTree.fields import RoundingDecimalField from InvenTree.fields import RoundingDecimalField
from InvenTree.helpers import decimal2string from InvenTree.helpers import decimal2string, increment
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus
from InvenTree.models import InvenTreeAttachment from InvenTree.models import InvenTreeAttachment
@ -49,6 +49,43 @@ class Order(models.Model):
ORDER_PREFIX = "" ORDER_PREFIX = ""
@classmethod
def getNextOrderNumber(cls):
"""
Try to predict the next order-number
"""
if cls.objects.count() == 0:
return None
# We will assume that the latest pk has the highest PO number
order = cls.objects.last()
ref = order.reference
if not ref:
return None
tries = set()
tries.add(ref)
while 1:
new_ref = increment(ref)
if new_ref in tries:
# We are in a looping situation - simply return the original one
return ref
# Check that the new ref does not exist in the database
if cls.objects.filter(reference=new_ref).exists():
tries.add(new_ref)
new_ref = increment(new_ref)
else:
break
return new_ref
def __str__(self): def __str__(self):
el = [] el = []

View File

@ -31,11 +31,17 @@ class OrderTest(TestCase):
self.assertEqual(order.get_absolute_url(), '/order/purchase-order/1/') self.assertEqual(order.get_absolute_url(), '/order/purchase-order/1/')
self.assertEqual(str(order), 'PO 1 - ACME') self.assertEqual(str(order), 'PO 0001 - ACME')
line = PurchaseOrderLineItem.objects.get(pk=1) line = PurchaseOrderLineItem.objects.get(pk=1)
self.assertEqual(str(line), "100 x ACME0001 from ACME (for PO 1 - ACME)") self.assertEqual(str(line), "100 x ACME0001 from ACME (for PO 0001 - ACME)")
def test_increment(self):
next_ref = PurchaseOrder.getNextOrderNumber()
self.assertEqual(next_ref, '0003')
def test_on_order(self): def test_on_order(self):
""" There should be 3 separate items on order for the M2x4 LPHS part """ """ There should be 3 separate items on order for the M2x4 LPHS part """

View File

@ -29,7 +29,7 @@ from . import forms as order_forms
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.helpers import DownloadFile, str2bool from InvenTree.helpers import DownloadFile, str2bool
from InvenTree.status_codes import PurchaseOrderStatus, StockStatus from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -290,6 +290,7 @@ class PurchaseOrderCreate(AjaxCreateView):
def get_initial(self): def get_initial(self):
initials = super().get_initial().copy() initials = super().get_initial().copy()
initials['reference'] = PurchaseOrder.getNextOrderNumber()
initials['status'] = PurchaseOrderStatus.PENDING initials['status'] = PurchaseOrderStatus.PENDING
supplier_id = self.request.GET.get('supplier', None) supplier_id = self.request.GET.get('supplier', None)
@ -320,7 +321,8 @@ class SalesOrderCreate(AjaxCreateView):
def get_initial(self): def get_initial(self):
initials = super().get_initial().copy() initials = super().get_initial().copy()
initials['status'] = PurchaseOrderStatus.PENDING initials['reference'] = SalesOrder.getNextOrderNumber()
initials['status'] = SalesOrderStatus.PENDING
customer_id = self.request.GET.get('customer', None) customer_id = self.request.GET.get('customer', None)