mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
initial implementation of barcode generation using plugins
This commit is contained in:
parent
0db280ad74
commit
24bd5d0962
@ -396,38 +396,6 @@ def WrapWithQuotes(text, quote='"'):
|
||||
return text
|
||||
|
||||
|
||||
def MakeBarcode(cls_name, object_pk: int, object_data=None, **kwargs):
|
||||
"""Generate a string for a barcode. Adds some global InvenTree parameters.
|
||||
|
||||
Args:
|
||||
cls_name: string describing the object type e.g. 'StockItem'
|
||||
object_pk (int): ID (Primary Key) of the object in the database
|
||||
object_data: Python dict object containing extra data which will be rendered to string (must only contain stringable values)
|
||||
|
||||
Returns:
|
||||
json string of the supplied data plus some other data
|
||||
"""
|
||||
if object_data is None:
|
||||
object_data = {}
|
||||
|
||||
brief = kwargs.get('brief', True)
|
||||
|
||||
data = {}
|
||||
|
||||
if brief:
|
||||
data[cls_name] = object_pk
|
||||
else:
|
||||
data['tool'] = 'InvenTree'
|
||||
data['version'] = InvenTree.version.inventreeVersion()
|
||||
data['instance'] = InvenTree.version.inventreeInstanceName()
|
||||
|
||||
# Ensure PK is included
|
||||
object_data['id'] = object_pk
|
||||
data[cls_name] = object_data
|
||||
|
||||
return str(json.dumps(data, sort_keys=True))
|
||||
|
||||
|
||||
def GetExportFormats():
|
||||
"""Return a list of allowable file formats for importing or exporting tabular data."""
|
||||
return ['csv', 'xlsx', 'tsv', 'json']
|
||||
|
@ -15,9 +15,6 @@ from djmoney.contrib.exchange.models import convert_money
|
||||
from djmoney.money import Money
|
||||
from PIL import Image
|
||||
|
||||
import InvenTree
|
||||
import InvenTree.helpers_model
|
||||
import InvenTree.version
|
||||
from common.notifications import (
|
||||
InvenTreeNotificationBodies,
|
||||
NotificationBody,
|
||||
@ -331,9 +328,7 @@ def notify_users(
|
||||
'instance': instance,
|
||||
'name': content.name.format(**content_context),
|
||||
'message': content.message.format(**content_context),
|
||||
'link': InvenTree.helpers_model.construct_absolute_url(
|
||||
instance.get_absolute_url()
|
||||
),
|
||||
'link': construct_absolute_url(instance.get_absolute_url()),
|
||||
'template': {'subject': content.name.format(**content_context)},
|
||||
}
|
||||
|
||||
|
@ -3,9 +3,7 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
@ -934,6 +932,8 @@ class InvenTreeBarcodeMixin(models.Model):
|
||||
|
||||
- barcode_data : Raw data associated with an assigned barcode
|
||||
- barcode_hash : A 'hash' of the assigned barcode data used to improve matching
|
||||
|
||||
The barcode_model_type_code() classmethod must be implemented in the model class.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
@ -964,11 +964,25 @@ class InvenTreeBarcodeMixin(models.Model):
|
||||
# By default, use the name of the class
|
||||
return cls.__name__.lower()
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
r"""Return a 'short' code for the model type.
|
||||
|
||||
This is used to generate a efficient QR code for the model type.
|
||||
It is expected to match this pattern: [0-9A-Z $%*+-.\/:]{2}
|
||||
|
||||
Note: Due to the shape constrains (45**2=2025 different allowed codes)
|
||||
this needs to be explicitly implemented in the model class to avoid collisions.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
'barcode_model_type_code() must be implemented in the model class'
|
||||
)
|
||||
|
||||
def format_barcode(self, **kwargs):
|
||||
"""Return a JSON string for formatting a QR code for this model instance."""
|
||||
return InvenTree.helpers.MakeBarcode(
|
||||
self.__class__.barcode_model_type(), self.pk, **kwargs
|
||||
)
|
||||
from plugin.base.barcodes.helper import generate_barcode
|
||||
|
||||
return generate_barcode(self)
|
||||
|
||||
def format_matched_response(self):
|
||||
"""Format a standard response for a matched barcode."""
|
||||
@ -986,7 +1000,7 @@ class InvenTreeBarcodeMixin(models.Model):
|
||||
@property
|
||||
def barcode(self):
|
||||
"""Format a minimal barcode string (e.g. for label printing)."""
|
||||
return self.format_barcode(brief=True)
|
||||
return self.format_barcode()
|
||||
|
||||
@classmethod
|
||||
def lookup_barcode(cls, barcode_hash):
|
||||
|
@ -115,6 +115,11 @@ class Build(
|
||||
|
||||
return defaults
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return "BO"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Custom save method for the BuildOrder model"""
|
||||
self.validate_reference_field(self.reference)
|
||||
|
@ -9,12 +9,13 @@ import hmac
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from datetime import timedelta, timezone
|
||||
from enum import Enum
|
||||
from io import BytesIO
|
||||
from secrets import compare_digest
|
||||
from typing import Any, Callable, Collection, TypedDict, Union
|
||||
from typing import Any, Callable, TypedDict, Union
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings as django_settings
|
||||
@ -49,6 +50,7 @@ import InvenTree.ready
|
||||
import InvenTree.tasks
|
||||
import InvenTree.validators
|
||||
import order.validators
|
||||
import plugin.base.barcodes.helper
|
||||
import report.helpers
|
||||
import users.models
|
||||
from InvenTree.sanitizer import sanitize_svg
|
||||
@ -56,6 +58,13 @@ from plugin import registry
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import NotRequired
|
||||
else:
|
||||
|
||||
class NotRequired:
|
||||
"""NotRequired type helper is only supported with Python 3.11+."""
|
||||
|
||||
|
||||
class MetaMixin(models.Model):
|
||||
"""A base class for InvenTree models to include shared meta fields.
|
||||
@ -1167,7 +1176,7 @@ class InvenTreeSettingsKeyType(SettingsKeyType):
|
||||
requires_restart: If True, a server restart is required after changing the setting
|
||||
"""
|
||||
|
||||
requires_restart: bool
|
||||
requires_restart: NotRequired[bool]
|
||||
|
||||
|
||||
class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
@ -1402,6 +1411,12 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'BARCODE_GENERATION_PLUGIN': {
|
||||
'name': _('Barcode Generation Plugin'),
|
||||
'description': _('Plugin to use for internal barcode data generation'),
|
||||
'choices': plugin.base.barcodes.helper.barcode_plugins,
|
||||
'default': 'inventreebarcode',
|
||||
},
|
||||
'PART_ENABLE_REVISION': {
|
||||
'name': _('Part Revisions'),
|
||||
'description': _('Enable revision field for Part'),
|
||||
|
@ -474,6 +474,11 @@ class ManufacturerPart(
|
||||
"""Return the API URL associated with the ManufacturerPart instance."""
|
||||
return reverse('api-manufacturer-part-list')
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'MP'
|
||||
|
||||
part = models.ForeignKey(
|
||||
'part.Part',
|
||||
on_delete=models.CASCADE,
|
||||
@ -676,6 +681,11 @@ class SupplierPart(
|
||||
"""Return custom API filters for this particular instance."""
|
||||
return {'manufacturer_part': {'part': self.part.pk}}
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'SP'
|
||||
|
||||
def clean(self):
|
||||
"""Custom clean action for the SupplierPart model.
|
||||
|
||||
|
@ -408,6 +408,11 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
|
||||
return defaults
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'PO'
|
||||
|
||||
@staticmethod
|
||||
def filterByDate(queryset, min_date, max_date):
|
||||
"""Filter by 'minimum and maximum date range'.
|
||||
@ -871,6 +876,11 @@ class SalesOrder(TotalPriceMixin, Order):
|
||||
|
||||
return defaults
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'SO'
|
||||
|
||||
@staticmethod
|
||||
def filterByDate(queryset, min_date, max_date):
|
||||
"""Filter by "minimum and maximum date range".
|
||||
@ -2035,6 +2045,11 @@ class ReturnOrder(TotalPriceMixin, Order):
|
||||
|
||||
return defaults
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'RO'
|
||||
|
||||
def __str__(self):
|
||||
"""Render a string representation of this ReturnOrder."""
|
||||
return f"{self.reference} - {self.customer.name if self.customer else _('no customer')}"
|
||||
|
@ -416,6 +416,11 @@ class Part(
|
||||
"""Return API query filters for limiting field results against this instance."""
|
||||
return {'variant_of': {'exclude_tree': self.pk}}
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'PA'
|
||||
|
||||
def report_context(self):
|
||||
"""Return custom report context information."""
|
||||
return {
|
||||
@ -426,11 +431,11 @@ class Part(
|
||||
'name': self.name,
|
||||
'parameters': self.parameters_map(),
|
||||
'part': self,
|
||||
'qr_data': self.format_barcode(brief=True),
|
||||
'qr_data': self.barcode,
|
||||
'qr_url': self.get_absolute_url(),
|
||||
'revision': self.revision,
|
||||
'test_template_list': self.getTestTemplates(),
|
||||
'test_templates': self.getTestTemplates(),
|
||||
'test_templates': self.getTestTemplateMap(),
|
||||
}
|
||||
|
||||
def get_context_data(self, request, **kwargs):
|
||||
|
@ -6,16 +6,17 @@ from django.db.models import F
|
||||
from django.urls import path
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from rest_framework import permissions
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework.exceptions import PermissionDenied, ValidationError
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
import order.models
|
||||
import plugin.base.barcodes.helper
|
||||
import stock.models
|
||||
from InvenTree.helpers import hash_barcode
|
||||
from plugin import registry
|
||||
from plugin.builtin.barcodes.inventree_barcode import InvenTreeInternalBarcodePlugin
|
||||
from users.models import RuleSet
|
||||
|
||||
from . import serializers as barcode_serializers
|
||||
@ -129,6 +130,45 @@ class BarcodeScan(BarcodeView):
|
||||
return Response(result)
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
post=extend_schema(responses={200: barcode_serializers.BarcodeSerializer})
|
||||
)
|
||||
class BarcodeGenerate(CreateAPIView):
|
||||
"""Endpoint for generating a barcode for a database object.
|
||||
|
||||
The barcode is generated by the selected barcode plugin.
|
||||
"""
|
||||
|
||||
serializer_class = barcode_serializers.BarcodeGenerateSerializer
|
||||
|
||||
def queryset(self):
|
||||
"""This API view does not have a queryset."""
|
||||
return None
|
||||
|
||||
# Default permission classes (can be overridden)
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""Perform the barcode generation action."""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
model = serializer.validated_data.get('model')
|
||||
pk = serializer.validated_data.get('pk')
|
||||
model_cls = plugin.base.barcodes.helper.get_supported_barcode_models_map().get(
|
||||
model, None
|
||||
)
|
||||
|
||||
if model_cls is None:
|
||||
raise ValidationError({'error': _('Model is not supported')})
|
||||
|
||||
model_instance = model_cls.objects.get(pk=pk)
|
||||
|
||||
barcode_data = plugin.base.barcodes.helper.generate_barcode(model_instance)
|
||||
|
||||
return Response({'barcode': barcode_data}, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class BarcodeAssign(BarcodeView):
|
||||
"""Endpoint for assigning a barcode to a stock item.
|
||||
|
||||
@ -161,7 +201,7 @@ class BarcodeAssign(BarcodeView):
|
||||
|
||||
valid_labels = []
|
||||
|
||||
for model in InvenTreeInternalBarcodePlugin.get_supported_barcode_models():
|
||||
for model in plugin.base.barcodes.helper.get_supported_barcode_models():
|
||||
label = model.barcode_model_type()
|
||||
valid_labels.append(label)
|
||||
|
||||
@ -203,7 +243,7 @@ class BarcodeUnassign(BarcodeView):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
data = serializer.validated_data
|
||||
|
||||
supported_models = InvenTreeInternalBarcodePlugin.get_supported_barcode_models()
|
||||
supported_models = plugin.base.barcodes.helper.get_supported_barcode_models()
|
||||
|
||||
supported_labels = [model.barcode_model_type() for model in supported_models]
|
||||
model_names = ', '.join(supported_labels)
|
||||
@ -567,6 +607,8 @@ class BarcodeSOAllocate(BarcodeView):
|
||||
|
||||
|
||||
barcode_api_urls = [
|
||||
# Generate a barcode for a database object
|
||||
path('generate/', BarcodeGenerate.as_view(), name='api-barcode-generate'),
|
||||
# Link a third-party barcode to an item (e.g. Part / StockItem / etc)
|
||||
path('link/', BarcodeAssign.as_view(), name='api-barcode-link'),
|
||||
# Unlink a third-party barcode from an item
|
||||
|
48
src/backend/InvenTree/plugin/base/barcodes/helper.py
Normal file
48
src/backend/InvenTree/plugin/base/barcodes/helper.py
Normal file
@ -0,0 +1,48 @@
|
||||
"""Helper functions for barcode generation."""
|
||||
|
||||
import logging
|
||||
from typing import Type
|
||||
|
||||
import InvenTree.helpers_model
|
||||
from InvenTree.models import InvenTreeBarcodeMixin
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
def barcode_plugins() -> list:
|
||||
"""Return a list of plugin choices which can be used for barcode generation."""
|
||||
try:
|
||||
from plugin import registry
|
||||
|
||||
plugins = registry.with_mixin('barcode', active=True)
|
||||
except Exception:
|
||||
plugins = []
|
||||
|
||||
return [
|
||||
(plug.slug, plug.human_name) for plug in plugins if plug.has_barcode_generation
|
||||
]
|
||||
|
||||
|
||||
def generate_barcode(model_instance: InvenTreeBarcodeMixin):
|
||||
"""Generate a barcode for a given model instance."""
|
||||
from common.settings import get_global_setting
|
||||
from plugin import registry
|
||||
|
||||
# Find the selected barcode generation plugin
|
||||
slug = get_global_setting('BARCODE_GENERATION_PLUGIN', create=False)
|
||||
|
||||
plugin = registry.get_plugin(slug)
|
||||
|
||||
return plugin.generate(model_instance)
|
||||
|
||||
|
||||
def get_supported_barcode_models() -> list[Type[InvenTreeBarcodeMixin]]:
|
||||
"""Returns a list of database models which support barcode functionality."""
|
||||
return InvenTree.helpers_model.getModelsWithMixin(InvenTreeBarcodeMixin)
|
||||
|
||||
|
||||
def get_supported_barcode_models_map():
|
||||
"""Return a mapping of barcode model types to the model class."""
|
||||
return {
|
||||
model.barcode_model_type(): model for model in get_supported_barcode_models()
|
||||
}
|
@ -10,6 +10,7 @@ from django.db.models import F, Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from company.models import Company, SupplierPart
|
||||
from InvenTree.models import InvenTreeBarcodeMixin
|
||||
from order.models import PurchaseOrder, PurchaseOrderStatus
|
||||
from plugin.base.integration.SettingsMixin import SettingsMixin
|
||||
from stock.models import StockLocation
|
||||
@ -53,6 +54,30 @@ class BarcodeMixin:
|
||||
"""
|
||||
return None
|
||||
|
||||
@property
|
||||
def has_barcode_generation(self):
|
||||
"""Does this plugin support barcode generation."""
|
||||
try:
|
||||
# Attempt to call the generate method
|
||||
self.generate(None) # type: ignore
|
||||
except NotImplementedError:
|
||||
# If a NotImplementedError is raised, then barcode generation is not supported
|
||||
return False
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
def generate(self, model_instance: InvenTreeBarcodeMixin):
|
||||
"""Generate barcode data for the given model instance.
|
||||
|
||||
Arguments:
|
||||
model_instance: The model instance to generate barcode data for. It is extending the InvenTreeBarcodeMixin.
|
||||
|
||||
Returns: The generated barcode data.
|
||||
"""
|
||||
raise NotImplementedError('Generate must be implemented by a plugin')
|
||||
|
||||
|
||||
class SupplierBarcodeMixin(BarcodeMixin):
|
||||
"""Mixin that provides default implementations for scan functions for supplier barcodes.
|
||||
|
@ -6,9 +6,9 @@ from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
import order.models
|
||||
import plugin.base.barcodes.helper
|
||||
import stock.models
|
||||
from order.status_codes import PurchaseOrderStatus, SalesOrderStatus
|
||||
from plugin.builtin.barcodes.inventree_barcode import InvenTreeInternalBarcodePlugin
|
||||
|
||||
|
||||
class BarcodeSerializer(serializers.Serializer):
|
||||
@ -23,6 +23,30 @@ class BarcodeSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
|
||||
class BarcodeGenerateSerializer(serializers.Serializer):
|
||||
"""Serializer for generating a barcode."""
|
||||
|
||||
model = serializers.CharField(
|
||||
required=True, help_text=_('Model name to generate barcode for')
|
||||
)
|
||||
|
||||
pk = serializers.IntegerField(
|
||||
required=True,
|
||||
help_text=_('Primary key of model object to generate barcode for'),
|
||||
)
|
||||
|
||||
def validate_model(self, model: str):
|
||||
"""Validate the provided model."""
|
||||
supported_models = (
|
||||
plugin.base.barcodes.helper.get_supported_barcode_models_map()
|
||||
)
|
||||
|
||||
if model not in supported_models.keys():
|
||||
raise ValidationError(_('Model is not supported'))
|
||||
|
||||
return model
|
||||
|
||||
|
||||
class BarcodeAssignMixin(serializers.Serializer):
|
||||
"""Serializer for linking and unlinking barcode to an internal class."""
|
||||
|
||||
@ -30,7 +54,7 @@ class BarcodeAssignMixin(serializers.Serializer):
|
||||
"""Generate serializer fields for each supported model type."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
for model in InvenTreeInternalBarcodePlugin.get_supported_barcode_models():
|
||||
for model in plugin.base.barcodes.helper.get_supported_barcode_models():
|
||||
self.fields[model.barcode_model_type()] = (
|
||||
serializers.PrimaryKeyRelatedField(
|
||||
queryset=model.objects.all(),
|
||||
@ -45,7 +69,7 @@ class BarcodeAssignMixin(serializers.Serializer):
|
||||
"""Return a list of model fields."""
|
||||
fields = [
|
||||
model.barcode_model_type()
|
||||
for model in InvenTreeInternalBarcodePlugin.get_supported_barcode_models()
|
||||
for model in plugin.base.barcodes.helper.get_supported_barcode_models()
|
||||
]
|
||||
|
||||
return fields
|
||||
|
@ -9,28 +9,44 @@ references model objects actually exist in the database.
|
||||
|
||||
import json
|
||||
|
||||
from django.core.validators import MaxLengthValidator, MinLengthValidator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import plugin.base.barcodes.helper
|
||||
from InvenTree.helpers import hash_barcode
|
||||
from InvenTree.helpers_model import getModelsWithMixin
|
||||
from InvenTree.models import InvenTreeBarcodeMixin
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import BarcodeMixin
|
||||
from plugin.mixins import BarcodeMixin, SettingsMixin
|
||||
|
||||
|
||||
class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||
class InvenTreeInternalBarcodePlugin(SettingsMixin, BarcodeMixin, InvenTreePlugin):
|
||||
"""Builtin BarcodePlugin for matching and generating internal barcodes."""
|
||||
|
||||
NAME = 'InvenTreeBarcode'
|
||||
TITLE = _('InvenTree Barcodes')
|
||||
DESCRIPTION = _('Provides native support for barcodes')
|
||||
VERSION = '2.0.0'
|
||||
VERSION = '2.1.0'
|
||||
AUTHOR = _('InvenTree contributors')
|
||||
|
||||
@staticmethod
|
||||
def get_supported_barcode_models():
|
||||
"""Returns a list of database models which support barcode functionality."""
|
||||
return getModelsWithMixin(InvenTreeBarcodeMixin)
|
||||
SETTINGS = {
|
||||
'INTERNAL_BARCODE_FORMAT': {
|
||||
'name': _('Internal Barcode Format'),
|
||||
'description': _('Select an internal barcode format'),
|
||||
'choices': [
|
||||
('json', _('JSON barcodes (require more space)')),
|
||||
('short', _('Short barcodes (made for optimized space)')),
|
||||
],
|
||||
'default': 'json',
|
||||
},
|
||||
'SHORT_BARCODE_PREFIX': {
|
||||
'name': _('Short Barcode Prefix'),
|
||||
'description': _(
|
||||
'Customize the prefix used for short barcodes, may be useful for environments with multiple InvenTree instances'
|
||||
),
|
||||
'validator': [str, MinLengthValidator(4), MaxLengthValidator(4)],
|
||||
'default': 'INV-',
|
||||
},
|
||||
}
|
||||
|
||||
def format_matched_response(self, label, model, instance):
|
||||
"""Format a response for the scanned data."""
|
||||
@ -53,7 +69,7 @@ class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
supported_models = self.get_supported_barcode_models()
|
||||
supported_models = plugin.base.barcodes.helper.get_supported_barcode_models()
|
||||
|
||||
if barcode_dict is not None and type(barcode_dict) is dict:
|
||||
# Look for various matches. First good match will be returned
|
||||
@ -79,3 +95,18 @@ class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||
|
||||
if instance is not None:
|
||||
return self.format_matched_response(label, model, instance)
|
||||
|
||||
def generate(self, model_instance: InvenTreeBarcodeMixin):
|
||||
"""Generate a barcode for a given model instance."""
|
||||
barcode_format = self.get_setting('INTERNAL_BARCODE_FORMAT')
|
||||
|
||||
if barcode_format == 'json':
|
||||
return json.dumps({model_instance.barcode_model_type(): model_instance.pk})
|
||||
|
||||
if barcode_format == 'short':
|
||||
prefix = self.get_setting('SHORT_BARCODE_PREFIX')
|
||||
model_type_code = model_instance.barcode_model_type_code()
|
||||
|
||||
return f'{prefix}{model_type_code}{model_instance.pk}'
|
||||
|
||||
return None
|
||||
|
@ -142,11 +142,16 @@ class StockLocation(
|
||||
"""Return API url."""
|
||||
return reverse('api-location-list')
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'SL'
|
||||
|
||||
def report_context(self):
|
||||
"""Return report context data for this StockLocation."""
|
||||
return {
|
||||
'location': self,
|
||||
'qr_data': self.format_barcode(brief=True),
|
||||
'qr_data': self.barcode,
|
||||
'parent': self.parent,
|
||||
'stock_location': self,
|
||||
'stock_items': self.get_stock_items(),
|
||||
@ -367,6 +372,11 @@ class StockItem(
|
||||
"""Custom API instance filters."""
|
||||
return {'parent': {'exclude_tree': self.pk}}
|
||||
|
||||
@classmethod
|
||||
def barcode_model_type_code(cls):
|
||||
"""Return the associated barcode model type code for this model."""
|
||||
return 'SI'
|
||||
|
||||
def get_test_keys(self, include_installed=True):
|
||||
"""Construct a flattened list of test 'keys' for this StockItem."""
|
||||
keys = []
|
||||
@ -397,7 +407,7 @@ class StockItem(
|
||||
'item': self,
|
||||
'name': self.part.full_name,
|
||||
'part': self.part,
|
||||
'qr_data': self.format_barcode(brief=True),
|
||||
'qr_data': self.barcode,
|
||||
'qr_url': self.get_absolute_url(),
|
||||
'parameters': self.part.parameters_map(),
|
||||
'quantity': InvenTree.helpers.normalize(self.quantity),
|
||||
|
@ -16,6 +16,7 @@
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_INPUT_DELAY" icon="fa-hourglass-half" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_WEBCAM_SUPPORT" icon="fa-video" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_SHOW_TEXT" icon="fa-closed-captioning" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_GENERATION_PLUGIN" icon="fa-qrcode" %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -98,7 +98,8 @@ export default function SystemSettings() {
|
||||
'BARCODE_ENABLE',
|
||||
'BARCODE_INPUT_DELAY',
|
||||
'BARCODE_WEBCAM_SUPPORT',
|
||||
'BARCODE_SHOW_TEXT'
|
||||
'BARCODE_SHOW_TEXT',
|
||||
'BARCODE_GENERATION_PLUGIN'
|
||||
]}
|
||||
/>
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user