mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-16 17:28:11 +00:00
Add definition for Parameter model
This commit is contained in:
@@ -1,109 +0,0 @@
|
|||||||
# Generated by Django 4.2.25 on 2025-10-28 11:11
|
|
||||||
|
|
||||||
import common.validators
|
|
||||||
import InvenTree.models
|
|
||||||
import InvenTree.validators
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("common", "0039_emailthread_emailmessage"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name="ParameterTemplate",
|
|
||||||
fields=[
|
|
||||||
(
|
|
||||||
"id",
|
|
||||||
models.AutoField(
|
|
||||||
auto_created=True,
|
|
||||||
primary_key=True,
|
|
||||||
serialize=False,
|
|
||||||
verbose_name="ID",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"metadata",
|
|
||||||
models.JSONField(
|
|
||||||
blank=True,
|
|
||||||
help_text="JSON metadata field, for use by external plugins",
|
|
||||||
null=True,
|
|
||||||
verbose_name="Plugin Metadata",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"name",
|
|
||||||
models.CharField(
|
|
||||||
help_text="Parameter Name",
|
|
||||||
max_length=100,
|
|
||||||
unique=True,
|
|
||||||
verbose_name="Name",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"units",
|
|
||||||
models.CharField(
|
|
||||||
blank=True,
|
|
||||||
help_text="Physical units for this parameter",
|
|
||||||
max_length=25,
|
|
||||||
validators=[InvenTree.validators.validate_physical_units],
|
|
||||||
verbose_name="Units",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"description",
|
|
||||||
models.CharField(
|
|
||||||
blank=True,
|
|
||||||
help_text="Parameter description",
|
|
||||||
max_length=250,
|
|
||||||
verbose_name="Description",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"model_type",
|
|
||||||
models.CharField(
|
|
||||||
blank=True,
|
|
||||||
default="",
|
|
||||||
help_text="Target model type for this parameter",
|
|
||||||
max_length=100,
|
|
||||||
validators=[common.validators.validate_parameter_template_model_type],
|
|
||||||
verbose_name="Model type",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"checkbox",
|
|
||||||
models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text="Is this parameter a checkbox?",
|
|
||||||
verbose_name="Checkbox",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"choices",
|
|
||||||
models.CharField(
|
|
||||||
blank=True,
|
|
||||||
help_text="Valid choices for this parameter (comma-separated)",
|
|
||||||
max_length=5000,
|
|
||||||
verbose_name="Choices",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"selectionlist",
|
|
||||||
models.ForeignKey(
|
|
||||||
blank=True,
|
|
||||||
help_text="Selection list for this parameter",
|
|
||||||
null=True,
|
|
||||||
on_delete=django.db.models.deletion.SET_NULL,
|
|
||||||
related_name="templates",
|
|
||||||
to="common.selectionlist",
|
|
||||||
verbose_name="Selection List",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
bases=(InvenTree.models.PluginValidationMixin, models.Model),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
# Generated by Django 4.2.25 on 2025-11-10 10:51
|
||||||
|
|
||||||
|
import InvenTree.models
|
||||||
|
import InvenTree.validators
|
||||||
|
import common.validators
|
||||||
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("common", "0039_emailthread_emailmessage"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="ParameterTemplate",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"metadata",
|
||||||
|
models.JSONField(
|
||||||
|
blank=True,
|
||||||
|
help_text="JSON metadata field, for use by external plugins",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Plugin Metadata",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"model_type",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Target model type for this parameter template",
|
||||||
|
max_length=100,
|
||||||
|
validators=[
|
||||||
|
common.validators.validate_parameter_template_model_type
|
||||||
|
],
|
||||||
|
verbose_name="Model type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
help_text="Parameter Name",
|
||||||
|
max_length=100,
|
||||||
|
unique=True,
|
||||||
|
verbose_name="Name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"units",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Physical units for this parameter",
|
||||||
|
max_length=25,
|
||||||
|
validators=[InvenTree.validators.validate_physical_units],
|
||||||
|
verbose_name="Units",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Parameter description",
|
||||||
|
max_length=250,
|
||||||
|
verbose_name="Description",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"checkbox",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Is this parameter a checkbox?",
|
||||||
|
verbose_name="Checkbox",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"choices",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Valid choices for this parameter (comma-separated)",
|
||||||
|
max_length=5000,
|
||||||
|
verbose_name="Choices",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"selectionlist",
|
||||||
|
models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Selection list for this parameter",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="templates",
|
||||||
|
to="common.selectionlist",
|
||||||
|
verbose_name="Selection List",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
bases=(InvenTree.models.PluginValidationMixin, models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Parameter",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"metadata",
|
||||||
|
models.JSONField(
|
||||||
|
blank=True,
|
||||||
|
help_text="JSON metadata field, for use by external plugins",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Plugin Metadata",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated",
|
||||||
|
models.DateTimeField(
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
help_text="Timestamp of last update",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Updated",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"model_type",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Target model type for this parameter",
|
||||||
|
max_length=100,
|
||||||
|
validators=[common.validators.validate_parameter_model_type],
|
||||||
|
verbose_name="Model type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"model_id",
|
||||||
|
models.PositiveIntegerField(
|
||||||
|
help_text="ID of the target model for this parameter",
|
||||||
|
verbose_name="Model ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"data",
|
||||||
|
models.CharField(
|
||||||
|
help_text="Parameter Value",
|
||||||
|
max_length=500,
|
||||||
|
validators=[django.core.validators.MinLengthValidator(1)],
|
||||||
|
verbose_name="Data",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"data_numeric",
|
||||||
|
models.FloatField(blank=True, default=None, null=True),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"note",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Optional note field",
|
||||||
|
max_length=500,
|
||||||
|
verbose_name="Note",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"template",
|
||||||
|
models.ForeignKey(
|
||||||
|
help_text="Parameter template",
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="parameters",
|
||||||
|
to="common.parametertemplate",
|
||||||
|
verbose_name="Template",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated_by",
|
||||||
|
models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="User who last updated this object",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="%(class)s_updated",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="Update By",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "Parameter",
|
||||||
|
"verbose_name_plural": "Parameters",
|
||||||
|
"unique_together": {("model_type", "model_id", "template")},
|
||||||
|
},
|
||||||
|
bases=(InvenTree.models.PluginValidationMixin, models.Model),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -3,60 +3,70 @@
|
|||||||
from django.db import migrations
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
def copy_templates(old_model, new_model):
|
def copy_part_parameters(apps, schema_editor):
|
||||||
"""Copy from one model type to another."""
|
"""Forward migration: copy from PartParameterTemplate to ParameterTemplate."""
|
||||||
|
PartParameterTemplate = apps.get_model("part", "PartParameterTemplate")
|
||||||
|
ParameterTemplate = apps.get_model("common", "ParameterTemplate")
|
||||||
|
|
||||||
|
PartParameter = apps.get_model("part", "PartParameter")
|
||||||
|
Parameter = apps.get_model("common", "Parameter")
|
||||||
|
|
||||||
|
# First, create a ParameterTemplate instance for each PartParameterTemplate
|
||||||
templates = []
|
templates = []
|
||||||
|
|
||||||
# Clear out all existing instances
|
for template in PartParameterTemplate.objects.all():
|
||||||
new_model.objects.all().delete()
|
templates.append(ParameterTemplate(
|
||||||
|
|
||||||
assert new_model.objects.count() == 0
|
|
||||||
|
|
||||||
for template in old_model.objects.all():
|
|
||||||
templates.append(new_model(
|
|
||||||
name=template.name,
|
name=template.name,
|
||||||
description=template.description,
|
description=template.description,
|
||||||
units=template.units,
|
units=template.units,
|
||||||
checkbox=template.checkbox,
|
checkbox=template.checkbox,
|
||||||
choices=template.choices,
|
choices=template.choices,
|
||||||
selectionlist=template.selectionlist,
|
selectionlist=template.selectionlist,
|
||||||
|
model_type='part'
|
||||||
|
))
|
||||||
|
|
||||||
|
if len(templates) > 0:
|
||||||
|
ParameterTemplate.objects.bulk_create(templates)
|
||||||
|
print(f"\nMigrated {len(templates)} PartParameterTemplate instances.")
|
||||||
|
|
||||||
|
assert ParameterTemplate.objects.filter(model_type='part').count() == len(templates)
|
||||||
|
|
||||||
|
# Next, copy PartParameter instances to Parameter instances
|
||||||
|
parameters = []
|
||||||
|
|
||||||
|
for parameter in PartParameter.objects.all():
|
||||||
|
# Find the corresponding ParameterTemplate
|
||||||
|
template = ParameterTemplate.objects.get(name=parameter.template.name, model_type='part')
|
||||||
|
|
||||||
|
parameters.append(Parameter(
|
||||||
|
template=template,
|
||||||
|
model_type='part',
|
||||||
|
model_id=parameter.part.id,
|
||||||
|
data=parameter.data,
|
||||||
|
data_numeric=parameter.data_numeric,
|
||||||
|
note=parameter.note,
|
||||||
|
updated=parameter.updated,
|
||||||
|
updated_by=parameter.updated_by
|
||||||
))
|
))
|
||||||
|
|
||||||
|
if len(parameters) > 0:
|
||||||
|
Parameter.objects.bulk_create(parameters)
|
||||||
|
print(f"\nMigrated {len(parameters)} PartParameter instances.")
|
||||||
|
|
||||||
if len(templates) > 0:
|
assert Parameter.objects.filter(model_type='part').count() == len(parameters)
|
||||||
new_model.objects.bulk_create(templates)
|
|
||||||
print(f"Migrated {len(templates)} ParameterTemplate instances.")
|
|
||||||
|
|
||||||
assert new_model.objects.count() == len(templates)
|
|
||||||
|
|
||||||
|
|
||||||
def forward_copy_templates(apps, schema_editor):
|
|
||||||
"""Forward migration: copy from PartParameterTemplate to ParameterTemplate."""
|
|
||||||
PartParameterTemplate = apps.get_model("part", "PartParameterTemplate")
|
|
||||||
ParameterTemplate = apps.get_model("common", "ParameterTemplate")
|
|
||||||
|
|
||||||
copy_templates(PartParameterTemplate, ParameterTemplate)
|
|
||||||
|
|
||||||
|
|
||||||
def reverse_copy_templates(apps, schema_editor):
|
|
||||||
"""Reverse migration: copy from ParameterTemplate to PartParameterTemplate."""
|
|
||||||
ParameterTemplate = apps.get_model("common", "ParameterTemplate")
|
|
||||||
PartParameterTemplate = apps.get_model("part", "PartParameterTemplate")
|
|
||||||
|
|
||||||
copy_templates(ParameterTemplate, PartParameterTemplate)
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("part", "0132_partparametertemplate_selectionlist"),
|
("part", "0132_partparametertemplate_selectionlist"),
|
||||||
("common", "0040_parametertemplate"),
|
("common", "0040_parametertemplate_parameter"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunPython(
|
migrations.RunPython(
|
||||||
forward_copy_templates,
|
copy_part_parameters,
|
||||||
reverse_code=reverse_copy_templates
|
reverse_code=migrations.RunPython.noop
|
||||||
)
|
),
|
||||||
|
# TODO: Data migration for ManufacturerPartParameter
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import base64
|
|||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import timedelta, timezone
|
from datetime import timedelta, timezone
|
||||||
@@ -28,7 +29,7 @@ from django.core.files.base import ContentFile
|
|||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.core.mail import EmailMultiAlternatives, get_connection
|
from django.core.mail import EmailMultiAlternatives, get_connection
|
||||||
from django.core.mail.utils import DNS_NAME
|
from django.core.mail.utils import DNS_NAME
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinLengthValidator, MinValueValidator
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import enums
|
from django.db.models import enums
|
||||||
from django.db.models.signals import post_delete, post_save
|
from django.db.models.signals import post_delete, post_save
|
||||||
@@ -48,6 +49,7 @@ from rest_framework.exceptions import PermissionDenied
|
|||||||
from taggit.managers import TaggableManager
|
from taggit.managers import TaggableManager
|
||||||
|
|
||||||
import common.validators
|
import common.validators
|
||||||
|
import InvenTree.conversion
|
||||||
import InvenTree.fields
|
import InvenTree.fields
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
@@ -2376,6 +2378,7 @@ class ParameterTemplate(
|
|||||||
Attributes:
|
Attributes:
|
||||||
name: The name (key) of the template
|
name: The name (key) of the template
|
||||||
description: A description of the template
|
description: A description of the template
|
||||||
|
model_type: The type of model to which this template applies (e.g. 'part')
|
||||||
units: The units associated with the template (if applicable)
|
units: The units associated with the template (if applicable)
|
||||||
checkbox: Is this template a checkbox (boolean) type?
|
checkbox: Is this template a checkbox (boolean) type?
|
||||||
choices: Comma-separated list of choices (if applicable)
|
choices: Comma-separated list of choices (if applicable)
|
||||||
@@ -2478,7 +2481,7 @@ class ParameterTemplate(
|
|||||||
blank=True,
|
blank=True,
|
||||||
validators=[common.validators.validate_parameter_template_model_type],
|
validators=[common.validators.validate_parameter_template_model_type],
|
||||||
verbose_name=_('Model type'),
|
verbose_name=_('Model type'),
|
||||||
help_text=_('Target model type for this parameter'),
|
help_text=_('Target model type for this parameter template'),
|
||||||
)
|
)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
@@ -2546,9 +2549,6 @@ class Parameter(
|
|||||||
class Meta:
|
class Meta:
|
||||||
"""Meta options for Parameter model."""
|
"""Meta options for Parameter model."""
|
||||||
|
|
||||||
# TODO: Make this non-abstract, actually implement...
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
verbose_name = _('Parameter')
|
verbose_name = _('Parameter')
|
||||||
verbose_name_plural = _('Parameters')
|
verbose_name_plural = _('Parameters')
|
||||||
unique_together = [['model_type', 'model_id', 'template']]
|
unique_together = [['model_type', 'model_id', 'template']]
|
||||||
@@ -2574,6 +2574,11 @@ class Parameter(
|
|||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""Perform custom delete checks before deleting a Parameter instance."""
|
||||||
|
# TODO: Custom delete checks against the model type this is linked to...
|
||||||
|
super().delete()
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""Validate the Parameter before saving to the database."""
|
"""Validate the Parameter before saving to the database."""
|
||||||
super().clean()
|
super().clean()
|
||||||
@@ -2589,6 +2594,86 @@ class Parameter(
|
|||||||
|
|
||||||
# TODO: Validate against plugins
|
# TODO: Validate against plugins
|
||||||
|
|
||||||
|
def calculate_numeric_value(self):
|
||||||
|
"""Calculate a numeric value for the parameter data.
|
||||||
|
|
||||||
|
- If a 'units' field is provided, then the data will be converted to the base SI unit.
|
||||||
|
- Otherwise, we'll try to do a simple float cast
|
||||||
|
"""
|
||||||
|
if self.template.units:
|
||||||
|
try:
|
||||||
|
self.data_numeric = InvenTree.conversion.convert_physical_value(
|
||||||
|
self.data, self.template.units
|
||||||
|
)
|
||||||
|
except (ValidationError, ValueError):
|
||||||
|
self.data_numeric = None
|
||||||
|
|
||||||
|
# No units provided, so try to cast to a float
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.data_numeric = float(self.data)
|
||||||
|
except ValueError:
|
||||||
|
self.data_numeric = None
|
||||||
|
|
||||||
|
if self.data_numeric is not None and type(self.data_numeric) is float:
|
||||||
|
# Prevent out of range numbers, etc
|
||||||
|
# Ref: https://github.com/inventree/InvenTree/issues/7593
|
||||||
|
if math.isnan(self.data_numeric) or math.isinf(self.data_numeric):
|
||||||
|
self.data_numeric = None
|
||||||
|
|
||||||
|
model_type = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
default='',
|
||||||
|
blank=True,
|
||||||
|
validators=[common.validators.validate_parameter_model_type],
|
||||||
|
verbose_name=_('Model type'),
|
||||||
|
help_text=_('Target model type for this parameter'),
|
||||||
|
)
|
||||||
|
|
||||||
|
model_id = models.PositiveIntegerField(
|
||||||
|
verbose_name=_('Model ID'),
|
||||||
|
help_text=_('ID of the target model for this parameter'),
|
||||||
|
)
|
||||||
|
|
||||||
|
template = models.ForeignKey(
|
||||||
|
ParameterTemplate,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='parameters',
|
||||||
|
verbose_name=_('Template'),
|
||||||
|
help_text=_('Parameter template'),
|
||||||
|
)
|
||||||
|
|
||||||
|
data = models.CharField(
|
||||||
|
max_length=500,
|
||||||
|
verbose_name=_('Data'),
|
||||||
|
help_text=_('Parameter Value'),
|
||||||
|
validators=[MinLengthValidator(1)],
|
||||||
|
)
|
||||||
|
|
||||||
|
data_numeric = models.FloatField(default=None, null=True, blank=True)
|
||||||
|
|
||||||
|
note = models.CharField(
|
||||||
|
max_length=500,
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_('Note'),
|
||||||
|
help_text=_('Optional note field'),
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def units(self):
|
||||||
|
"""Return the units associated with the template."""
|
||||||
|
return self.template.units
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the template."""
|
||||||
|
return self.template.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
"""Return the description of the template."""
|
||||||
|
return self.template.description
|
||||||
|
|
||||||
|
|
||||||
class BarcodeScanResult(InvenTree.models.InvenTreeModel):
|
class BarcodeScanResult(InvenTree.models.InvenTreeModel):
|
||||||
"""Model for storing barcode scans results."""
|
"""Model for storing barcode scans results."""
|
||||||
|
|||||||
Reference in New Issue
Block a user