mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #2138 from rocheparadox/master
Custom name format for parts - FR InvenTree#2085
This commit is contained in:
commit
05791a8efd
@ -5,6 +5,7 @@ Custom field validators for InvenTree
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.core.exceptions import FieldDoesNotExist
|
||||||
|
|
||||||
from moneyed import CURRENCIES
|
from moneyed import CURRENCIES
|
||||||
|
|
||||||
@ -156,3 +157,33 @@ def validate_overage(value):
|
|||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Overage must be an integer value or a percentage")
|
_("Overage must be an integer value or a percentage")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_part_name_format(self):
|
||||||
|
"""
|
||||||
|
Validate part name format.
|
||||||
|
Make sure that each template container has a field of Part Model
|
||||||
|
"""
|
||||||
|
|
||||||
|
jinja_template_regex = re.compile('{{.*?}}')
|
||||||
|
field_name_regex = re.compile('(?<=part\\.)[A-z]+')
|
||||||
|
for jinja_template in jinja_template_regex.findall(str(self)):
|
||||||
|
# make sure at least one and only one field is present inside the parser
|
||||||
|
field_names = field_name_regex.findall(jinja_template)
|
||||||
|
if len(field_names) < 1:
|
||||||
|
raise ValidationError({
|
||||||
|
'value': 'At least one field must be present inside a jinja template container i.e {{}}'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Make sure that the field_name exists in Part model
|
||||||
|
from part.models import Part
|
||||||
|
|
||||||
|
for field_name in field_names:
|
||||||
|
try:
|
||||||
|
Part._meta.get_field(field_name)
|
||||||
|
except FieldDoesNotExist:
|
||||||
|
raise ValidationError({
|
||||||
|
'value': f'{field_name} does not exist in Part Model'
|
||||||
|
})
|
||||||
|
|
||||||
|
return True
|
||||||
|
@ -25,6 +25,7 @@ from django.core.exceptions import ValidationError
|
|||||||
|
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
import InvenTree.fields
|
import InvenTree.fields
|
||||||
|
import InvenTree.validators
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -702,6 +703,14 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
|||||||
'validator': bool
|
'validator': bool
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'PART_NAME_FORMAT': {
|
||||||
|
'name': _('Part Name Display Format'),
|
||||||
|
'description': _('Format to display the part name'),
|
||||||
|
'default': "{{ part.IPN if part.IPN }}{{ ' | ' if part.IPN }}{{ part.name }}{{ ' | ' if part.revision }}"
|
||||||
|
"{{ part.revision if part.revision }}",
|
||||||
|
'validator': InvenTree.validators.validate_part_name_format
|
||||||
|
},
|
||||||
|
|
||||||
'REPORT_DEBUG_MODE': {
|
'REPORT_DEBUG_MODE': {
|
||||||
'name': _('Debug Mode'),
|
'name': _('Debug Mode'),
|
||||||
'description': _('Generate reports in debug mode (HTML output)'),
|
'description': _('Generate reports in debug mode (HTML output)'),
|
||||||
|
@ -136,3 +136,24 @@ class SettingsViewTest(TestCase):
|
|||||||
for value in [False, 'False']:
|
for value in [False, 'False']:
|
||||||
self.post(url, {'value': value}, valid=True)
|
self.post(url, {'value': value}, valid=True)
|
||||||
self.assertFalse(InvenTreeSetting.get_setting('PART_COMPONENT'))
|
self.assertFalse(InvenTreeSetting.get_setting('PART_COMPONENT'))
|
||||||
|
|
||||||
|
def test_part_name_format(self):
|
||||||
|
"""
|
||||||
|
Try posting some valid and invalid name formats for PART_NAME_FORMAT
|
||||||
|
"""
|
||||||
|
setting = InvenTreeSetting.get_setting_object('PART_NAME_FORMAT')
|
||||||
|
|
||||||
|
# test default value
|
||||||
|
self.assertEqual(setting.value, "{{ part.IPN if part.IPN }}{{ ' | ' if part.IPN }}{{ part.name }}"
|
||||||
|
"{{ ' | ' if part.revision }}{{ part.revision if part.revision }}")
|
||||||
|
|
||||||
|
url = self.get_url(setting.pk)
|
||||||
|
|
||||||
|
# Try posting an invalid part name format
|
||||||
|
invalid_values = ['{{asset.IPN}}', '{{part}}', '{{"|"}}', '{{part.falcon}}']
|
||||||
|
for invalid_value in invalid_values:
|
||||||
|
self.post(url, {'value': invalid_value}, valid=False)
|
||||||
|
|
||||||
|
# try posting valid value
|
||||||
|
new_format = "{{ part.name if part.name }} {{ ' with revision ' if part.revision }} {{ part.revision }}"
|
||||||
|
self.post(url, {'value': new_format}, valid=True)
|
||||||
|
@ -23,6 +23,8 @@ from django.contrib.auth.models import User
|
|||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
from markdownx.models import MarkdownxField
|
from markdownx.models import MarkdownxField
|
||||||
|
|
||||||
from django_cleanup import cleanup
|
from django_cleanup import cleanup
|
||||||
@ -38,6 +40,7 @@ from datetime import datetime
|
|||||||
import hashlib
|
import hashlib
|
||||||
from djmoney.contrib.exchange.models import convert_money
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
from common.settings import currency_code_default
|
from common.settings import currency_code_default
|
||||||
|
from common.models import InvenTreeSetting
|
||||||
|
|
||||||
from InvenTree import helpers
|
from InvenTree import helpers
|
||||||
from InvenTree import validators
|
from InvenTree import validators
|
||||||
@ -555,7 +558,9 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self):
|
def full_name(self):
|
||||||
""" Format a 'full name' for this Part.
|
""" Format a 'full name' for this Part based on the format PART_NAME_FORMAT defined in Inventree settings
|
||||||
|
|
||||||
|
As a failsafe option, the following is done
|
||||||
|
|
||||||
- IPN (if not null)
|
- IPN (if not null)
|
||||||
- Part name
|
- Part name
|
||||||
@ -564,6 +569,20 @@ class Part(MPTTModel):
|
|||||||
Elements are joined by the | character
|
Elements are joined by the | character
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
full_name_pattern = InvenTreeSetting.get_setting('PART_NAME_FORMAT')
|
||||||
|
|
||||||
|
try:
|
||||||
|
context = {'part': self}
|
||||||
|
template_string = Template(full_name_pattern)
|
||||||
|
full_name = template_string.render(context)
|
||||||
|
|
||||||
|
return full_name
|
||||||
|
|
||||||
|
except AttributeError as attr_err:
|
||||||
|
|
||||||
|
logger.warning(f"exception while trying to create full name for part {self.name}", attr_err)
|
||||||
|
|
||||||
|
# Fallback to default format
|
||||||
elements = []
|
elements = []
|
||||||
|
|
||||||
if self.IPN:
|
if self.IPN:
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
{% 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" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_ALLOW_DUPLICATE_IPN" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_ALLOW_EDIT_IPN" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_ALLOW_EDIT_IPN" %}
|
||||||
|
{% include "InvenTree/settings/setting.html" with key="PART_NAME_FORMAT" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_PRICE_IN_FORMS" icon="fa-dollar-sign" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_PRICE_IN_FORMS" icon="fa-dollar-sign" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_PRICE_IN_BOM" icon="fa-dollar-sign" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_PRICE_IN_BOM" icon="fa-dollar-sign" %}
|
||||||
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_RELATED" icon="fa-random" %}
|
{% include "InvenTree/settings/setting.html" with key="PART_SHOW_RELATED" icon="fa-random" %}
|
||||||
|
@ -876,23 +876,7 @@ function loadPartTable(table, url, options={}) {
|
|||||||
switchable: false,
|
switchable: false,
|
||||||
formatter: function(value, row) {
|
formatter: function(value, row) {
|
||||||
|
|
||||||
var name = '';
|
var name = row.full_name;
|
||||||
|
|
||||||
if (row.IPN) {
|
|
||||||
name += row.IPN;
|
|
||||||
name += ' | ';
|
|
||||||
}
|
|
||||||
|
|
||||||
name += value;
|
|
||||||
|
|
||||||
if (row.revision) {
|
|
||||||
name += ' | ';
|
|
||||||
name += row.revision;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.is_template) {
|
|
||||||
name = '<i>' + name + '</i>';
|
|
||||||
}
|
|
||||||
|
|
||||||
var display = imageHoverIcon(row.thumbnail) + renderLink(name, '/part/' + row.pk + '/');
|
var display = imageHoverIcon(row.thumbnail) + renderLink(name, '/part/' + row.pk + '/');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user