mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add setting to allow or prohibit duplicate IPN values
This commit is contained in:
parent
eead52a5dd
commit
a6028f027a
@ -64,6 +64,13 @@ class InvenTreeSetting(models.Model):
|
|||||||
'description': _('Regular expression pattern for matching Part IPN')
|
'description': _('Regular expression pattern for matching Part IPN')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'PART_ALLOW_DUPLICATE_IPN': {
|
||||||
|
'name': _('Allow Duplicate IPN'),
|
||||||
|
'description': _('Allow multiple parts to share the same IPN'),
|
||||||
|
'default': True,
|
||||||
|
'validator': bool,
|
||||||
|
},
|
||||||
|
|
||||||
'PART_COPY_BOM': {
|
'PART_COPY_BOM': {
|
||||||
'name': _('Copy Part BOM Data'),
|
'name': _('Copy Part BOM Data'),
|
||||||
'description': _('Copy BOM data by default when duplicating a part'),
|
'description': _('Copy BOM data by default when duplicating a part'),
|
||||||
@ -306,6 +313,10 @@ class InvenTreeSetting(models.Model):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Enforce standard boolean representation
|
||||||
|
if setting.is_bool():
|
||||||
|
value = InvenTree.helpers.str2bool(value)
|
||||||
|
|
||||||
setting.value = str(value)
|
setting.value = str(value)
|
||||||
setting.save()
|
setting.save()
|
||||||
|
|
||||||
@ -317,6 +328,10 @@ class InvenTreeSetting(models.Model):
|
|||||||
def name(self):
|
def name(self):
|
||||||
return InvenTreeSetting.get_setting_name(self.key)
|
return InvenTreeSetting.get_setting_name(self.key)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_value(self):
|
||||||
|
return InvenTreeSetting.get_default_value(self.key)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
return InvenTreeSetting.get_setting_description(self.key)
|
return InvenTreeSetting.get_setting_description(self.key)
|
||||||
|
@ -70,3 +70,13 @@ class SettingsTest(TestCase):
|
|||||||
InvenTreeSetting.set_setting(key, value, self.user)
|
InvenTreeSetting.set_setting(key, value, self.user)
|
||||||
|
|
||||||
self.assertEqual(value, InvenTreeSetting.get_setting(key))
|
self.assertEqual(value, InvenTreeSetting.get_setting(key))
|
||||||
|
|
||||||
|
# Any fields marked as 'boolean' must have a default value specified
|
||||||
|
setting = InvenTreeSetting.get_setting_object(key)
|
||||||
|
|
||||||
|
if setting.is_bool():
|
||||||
|
if setting.default_value in ['', None]:
|
||||||
|
raise ValueError(f'Default value for boolean setting {key} not provided')
|
||||||
|
|
||||||
|
if setting.default_value not in [True, False]:
|
||||||
|
raise ValueError(f'Non-boolean default value specified for {key}')
|
||||||
|
@ -529,6 +529,18 @@ class Part(MPTTModel):
|
|||||||
"""
|
"""
|
||||||
super().validate_unique(exclude)
|
super().validate_unique(exclude)
|
||||||
|
|
||||||
|
# User can decide whether duplicate IPN (Internal Part Number) values are allowed
|
||||||
|
allow_duplicate_ipn = common.models.InvenTreeSetting.get_setting('PART_ALLOW_DUPLICATE_IPN')
|
||||||
|
|
||||||
|
if not allow_duplicate_ipn:
|
||||||
|
parts = Part.objects.filter(IPN__iexact=self.IPN)
|
||||||
|
parts = parts.exclude(pk=self.pk)
|
||||||
|
|
||||||
|
if parts.exists():
|
||||||
|
raise ValidationError({
|
||||||
|
'IPN': _('Duplicate IPN not allowed in part settings'),
|
||||||
|
})
|
||||||
|
|
||||||
# Part name uniqueness should be case insensitive
|
# Part name uniqueness should be case insensitive
|
||||||
try:
|
try:
|
||||||
parts = Part.objects.exclude(id=self.id).filter(
|
parts = Part.objects.exclude(id=self.id).filter(
|
||||||
|
@ -249,3 +249,28 @@ class PartSettingsTest(TestCase):
|
|||||||
self.assertEqual(part.trackable, val)
|
self.assertEqual(part.trackable, val)
|
||||||
|
|
||||||
Part.objects.filter(pk=part.pk).delete()
|
Part.objects.filter(pk=part.pk).delete()
|
||||||
|
|
||||||
|
def test_duplicate_ipn(self):
|
||||||
|
"""
|
||||||
|
Test the setting which controls duplicate IPN values
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create a part
|
||||||
|
Part.objects.create(name='Hello', description='A thing', IPN='IPN123')
|
||||||
|
|
||||||
|
# Attempt to create a duplicate item (should fail)
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
Part.objects.create(name='Hello', description='A thing', IPN='IPN123')
|
||||||
|
|
||||||
|
# Attempt to create item with duplicate IPN (should be allowed by default)
|
||||||
|
Part.objects.create(name='Hello', description='A thing', IPN='IPN123', revision='B')
|
||||||
|
|
||||||
|
# And attempt again with the same values (should fail)
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
Part.objects.create(name='Hello', description='A thing', IPN='IPN123', revision='B')
|
||||||
|
|
||||||
|
# Now update the settings so duplicate IPN values are *not* allowed
|
||||||
|
InvenTreeSetting.set_setting('PART_ALLOW_DUPLICATE_IPN', False, self.user)
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
Part.objects.create(name='Hello', description='A thing', IPN='IPN123', revision='C')
|
||||||
|
@ -1247,12 +1247,21 @@ class StockItem(MPTTModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def required_test_count(self):
|
def required_test_count(self):
|
||||||
|
"""
|
||||||
|
Return the number of 'required tests' for this StockItem
|
||||||
|
"""
|
||||||
return self.part.getRequiredTests().count()
|
return self.part.getRequiredTests().count()
|
||||||
|
|
||||||
def hasRequiredTests(self):
|
def hasRequiredTests(self):
|
||||||
|
"""
|
||||||
|
Return True if there are any 'required tests' associated with this StockItem
|
||||||
|
"""
|
||||||
return self.part.getRequiredTests().count() > 0
|
return self.part.getRequiredTests().count() > 0
|
||||||
|
|
||||||
def passedAllRequiredTests(self):
|
def passedAllRequiredTests(self):
|
||||||
|
"""
|
||||||
|
Returns True if this StockItem has passed all required tests
|
||||||
|
"""
|
||||||
|
|
||||||
status = self.requiredTestStatus()
|
status = self.requiredTestStatus()
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
<thead></thead>
|
<thead></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_IPN_REGEX" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_IPN_REGEX" %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="PART_ALLOW_DUPLICATE_IPN" %}
|
||||||
|
<tr><td colspan='4'></td></tr>
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_COMPONENT" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_COMPONENT" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_PURCHASEABLE" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_PURCHASEABLE" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_SALABLE" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SALABLE" %}
|
||||||
|
Loading…
Reference in New Issue
Block a user