mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +00:00
Merge pull request #2895 from exp/issue-2885
Issue 2885 - Support more serial number formats and fix a couple of bugs
This commit is contained in:
commit
cd0de941ac
@ -427,8 +427,9 @@ def extract_serial_numbers(serials, expected_quantity, next_number: int):
|
|||||||
serials = serials.strip()
|
serials = serials.strip()
|
||||||
|
|
||||||
# fill in the next serial number into the serial
|
# fill in the next serial number into the serial
|
||||||
if '~' in serials:
|
while '~' in serials:
|
||||||
serials = serials.replace('~', str(next_number))
|
serials = serials.replace('~', str(next_number), 1)
|
||||||
|
next_number += 1
|
||||||
|
|
||||||
# Split input string by whitespace or comma (,) characters
|
# Split input string by whitespace or comma (,) characters
|
||||||
groups = re.split("[\s,]+", serials)
|
groups = re.split("[\s,]+", serials)
|
||||||
@ -438,6 +439,12 @@ def extract_serial_numbers(serials, expected_quantity, next_number: int):
|
|||||||
|
|
||||||
# Helper function to check for duplicated numbers
|
# Helper function to check for duplicated numbers
|
||||||
def add_sn(sn):
|
def add_sn(sn):
|
||||||
|
# Attempt integer conversion first, so numerical strings are never stored
|
||||||
|
try:
|
||||||
|
sn = int(sn)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
if sn in numbers:
|
if sn in numbers:
|
||||||
errors.append(_('Duplicate serial: {sn}').format(sn=sn))
|
errors.append(_('Duplicate serial: {sn}').format(sn=sn))
|
||||||
else:
|
else:
|
||||||
@ -451,15 +458,25 @@ def extract_serial_numbers(serials, expected_quantity, next_number: int):
|
|||||||
if len(serials) == 0:
|
if len(serials) == 0:
|
||||||
raise ValidationError([_("Empty serial number string")])
|
raise ValidationError([_("Empty serial number string")])
|
||||||
|
|
||||||
for group in groups:
|
# If the user has supplied the correct number of serials, don't process them for groups
|
||||||
|
# just add them so any duplicates (or future validations) are checked
|
||||||
|
if len(groups) == expected_quantity:
|
||||||
|
for group in groups:
|
||||||
|
add_sn(group)
|
||||||
|
|
||||||
|
if len(errors) > 0:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
|
return numbers
|
||||||
|
|
||||||
|
for group in groups:
|
||||||
group = group.strip()
|
group = group.strip()
|
||||||
|
|
||||||
# Hyphen indicates a range of numbers
|
# Hyphen indicates a range of numbers
|
||||||
if '-' in group:
|
if '-' in group:
|
||||||
items = group.split('-')
|
items = group.split('-')
|
||||||
|
|
||||||
if len(items) == 2:
|
if len(items) == 2 and all([i.isnumeric() for i in items]):
|
||||||
a = items[0].strip()
|
a = items[0].strip()
|
||||||
b = items[1].strip()
|
b = items[1].strip()
|
||||||
|
|
||||||
@ -471,13 +488,14 @@ def extract_serial_numbers(serials, expected_quantity, next_number: int):
|
|||||||
for n in range(a, b + 1):
|
for n in range(a, b + 1):
|
||||||
add_sn(n)
|
add_sn(n)
|
||||||
else:
|
else:
|
||||||
errors.append(_("Invalid group: {g}").format(g=group))
|
errors.append(_("Invalid group range: {g}").format(g=group))
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
errors.append(_("Invalid group: {g}").format(g=group))
|
errors.append(_("Invalid group: {g}").format(g=group))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
errors.append(_("Invalid group: {g}").format(g=group))
|
# More than 2 hyphens or non-numeric group so add without interpolating
|
||||||
|
add_sn(group)
|
||||||
|
|
||||||
# plus signals either
|
# plus signals either
|
||||||
# 1: 'start+': expected number of serials, starting at start
|
# 1: 'start+': expected number of serials, starting at start
|
||||||
@ -495,23 +513,17 @@ def extract_serial_numbers(serials, expected_quantity, next_number: int):
|
|||||||
|
|
||||||
# case 1
|
# case 1
|
||||||
else:
|
else:
|
||||||
end = start + expected_quantity
|
end = start + (expected_quantity - len(numbers))
|
||||||
|
|
||||||
for n in range(start, end):
|
for n in range(start, end):
|
||||||
add_sn(n)
|
add_sn(n)
|
||||||
# no case
|
# no case
|
||||||
else:
|
else:
|
||||||
errors.append(_("Invalid group: {g}").format(g=group))
|
errors.append(_("Invalid group sequence: {g}").format(g=group))
|
||||||
|
|
||||||
# At this point, we assume that the "group" is just a single serial value
|
# At this point, we assume that the "group" is just a single serial value
|
||||||
elif group:
|
elif group:
|
||||||
|
add_sn(group)
|
||||||
try:
|
|
||||||
# First attempt to add as an integer value
|
|
||||||
add_sn(int(group))
|
|
||||||
except (ValueError):
|
|
||||||
# As a backup, add as a string value
|
|
||||||
add_sn(group)
|
|
||||||
|
|
||||||
# No valid input group detected
|
# No valid input group detected
|
||||||
else:
|
else:
|
||||||
|
@ -252,6 +252,31 @@ class TestSerialNumberExtraction(TestCase):
|
|||||||
sn = e("1, 2, 3, 4, 5", 5, 1)
|
sn = e("1, 2, 3, 4, 5", 5, 1)
|
||||||
self.assertEqual(len(sn), 5)
|
self.assertEqual(len(sn), 5)
|
||||||
|
|
||||||
|
# Test partially specifying serials
|
||||||
|
sn = e("1, 2, 4+", 5, 1)
|
||||||
|
self.assertEqual(len(sn), 5)
|
||||||
|
self.assertEqual(sn, [1, 2, 4, 5, 6])
|
||||||
|
|
||||||
|
# Test groups are not interpolated if enough serials are supplied
|
||||||
|
sn = e("1, 2, 3, AF5-69H, 5", 5, 1)
|
||||||
|
self.assertEqual(len(sn), 5)
|
||||||
|
self.assertEqual(sn, [1, 2, 3, "AF5-69H", 5])
|
||||||
|
|
||||||
|
# Test groups are not interpolated with more than one hyphen in a word
|
||||||
|
sn = e("1, 2, TG-4SR-92, 4+", 5, 1)
|
||||||
|
self.assertEqual(len(sn), 5)
|
||||||
|
self.assertEqual(sn, [1, 2, "TG-4SR-92", 4, 5])
|
||||||
|
|
||||||
|
# Test groups are not interpolated with alpha characters
|
||||||
|
sn = e("1, A-2, 3+", 5, 1)
|
||||||
|
self.assertEqual(len(sn), 5)
|
||||||
|
self.assertEqual(sn, [1, "A-2", 3, 4, 5])
|
||||||
|
|
||||||
|
# Test multiple placeholders
|
||||||
|
sn = e("1 2 ~ ~ ~", 5, 3)
|
||||||
|
self.assertEqual(len(sn), 5)
|
||||||
|
self.assertEqual(sn, [1, 2, 3, 4, 5])
|
||||||
|
|
||||||
sn = e("1-5, 10-15", 11, 1)
|
sn = e("1-5, 10-15", 11, 1)
|
||||||
self.assertIn(3, sn)
|
self.assertIn(3, sn)
|
||||||
self.assertIn(13, sn)
|
self.assertIn(13, sn)
|
||||||
@ -307,6 +332,10 @@ class TestSerialNumberExtraction(TestCase):
|
|||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
e("10, a, 7-70j", 4, 1)
|
e("10, a, 7-70j", 4, 1)
|
||||||
|
|
||||||
|
# Test groups are not interpolated with word characters
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
e("1, 2, 3, E-5", 5, 1)
|
||||||
|
|
||||||
def test_combinations(self):
|
def test_combinations(self):
|
||||||
e = helpers.extract_serial_numbers
|
e = helpers.extract_serial_numbers
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user