diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 3012cdc61f..2817664908 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -900,7 +900,7 @@ PLUGINS_ENABLED = _is_true(get_setting( PLUGIN_FILE = get_plugin_file() # Plugin Directories (local plugins will be loaded from these directories) -PLUGIN_DIRS = ['plugin.builtin', 'barcodes.plugins', ] +PLUGIN_DIRS = ['plugin.builtin', ] if not TESTING: # load local deploy directory in prod diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 2b31d7c3b5..d408bd7346 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -18,7 +18,6 @@ from build.urls import build_urls from order.urls import order_urls from plugin.urls import get_plugin_urls -from barcodes.api import barcode_api_urls from common.api import common_api_urls, settings_api_urls from part.api import part_api_urls, bom_api_urls from company.api import company_api_urls @@ -28,6 +27,7 @@ from order.api import order_api_urls from label.api import label_api_urls from report.api import report_api_urls from plugin.api import plugin_api_urls +from plugin.barcode import barcode_api_urls from django.conf import settings from django.conf.urls.static import static @@ -59,7 +59,6 @@ if settings.PLUGINS_ENABLED: ) apipatterns += [ - re_path(r'^barcode/', include(barcode_api_urls)), re_path(r'^settings/', include(settings_api_urls)), re_path(r'^part/', include(part_api_urls)), re_path(r'^bom/', include(bom_api_urls)), @@ -75,6 +74,7 @@ apipatterns += [ # Plugin endpoints re_path(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'), + re_path(r'^barcode/', include(barcode_api_urls)), # Webhook enpoint path('', include(common_api_urls)), diff --git a/InvenTree/barcodes/barcode.py b/InvenTree/barcodes/barcode.py deleted file mode 100644 index 030552c866..0000000000 --- a/InvenTree/barcodes/barcode.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -import warnings - -import plugin.builtin.barcode.mixins as mixin -import plugin.integration - - -hash_barcode = mixin.hash_barcode - - -class BarcodePlugin(mixin.BarcodeMixin, plugin.integration.IntegrationPluginBase): - """ - Legacy barcode plugin definition - will be replaced - Please use the new Integration Plugin API and the BarcodeMixin - """ - # TODO @matmair remove this with InvenTree 0.7.0 - def __init__(self, barcode_data=None): - warnings.warn("using the BarcodePlugin is depreceated", DeprecationWarning) - super().__init__() - self.init(barcode_data) diff --git a/InvenTree/barcodes/plugins/digikey_barcode.py b/InvenTree/barcodes/plugins/digikey_barcode.py deleted file mode 100644 index 656cdfa6f4..0000000000 --- a/InvenTree/barcodes/plugins/digikey_barcode.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -DigiKey barcode decoding -""" - -from barcodes.barcode import BarcodePlugin - - -class DigikeyBarcodePlugin(BarcodePlugin): - - PLUGIN_NAME = "DigikeyBarcode" - - def validate(self): - """ - TODO: Validation of Digikey barcodes. - """ - - return False diff --git a/InvenTree/label/apps.py b/InvenTree/label/apps.py index 318f4af984..633907f330 100644 --- a/InvenTree/label/apps.py +++ b/InvenTree/label/apps.py @@ -265,6 +265,13 @@ class LabelConfig(AppConfig): 'width': 70, 'height': 24, }, + { + 'file': 'part_label_code128.html', + 'name': 'Barcode Part Label', + 'description': 'Simple part label with Code128 barcode', + 'width': 70, + 'height': 24, + }, ] for label in labels: diff --git a/InvenTree/label/templates/label/part/part_label_code128.html b/InvenTree/label/templates/label/part/part_label_code128.html new file mode 100644 index 0000000000..7f8ef144ec --- /dev/null +++ b/InvenTree/label/templates/label/part/part_label_code128.html @@ -0,0 +1,33 @@ +{% extends "label/label_base.html" %} + +{% load barcode %} + +{% block style %} + +.qr { + position: fixed; + left: 0mm; + top: 0mm; + height: {{ height }}mm; + width: {{ height }}mm; +} + +.part { + font-family: Arial, Helvetica, sans-serif; + display: inline; + position: absolute; + left: {{ height }}mm; + top: 2mm; +} + +{% endblock %} + +{% block content %} + + + +
+ {{ part.full_name }} +
+ +{% endblock %} \ No newline at end of file diff --git a/InvenTree/label/tests.py b/InvenTree/label/tests.py index 1e7e7994ae..53724a36fc 100644 --- a/InvenTree/label/tests.py +++ b/InvenTree/label/tests.py @@ -5,20 +5,29 @@ from __future__ import unicode_literals import os -from django.test import TestCase from django.conf import settings from django.apps import apps +from django.urls import reverse from django.core.exceptions import ValidationError from InvenTree.helpers import validateFilterString +from InvenTree.api_tester import InvenTreeAPITestCase -from .models import StockItemLabel, StockLocationLabel +from .models import StockItemLabel, StockLocationLabel, PartLabel from stock.models import StockItem -class LabelTest(TestCase): +class LabelTest(InvenTreeAPITestCase): + + fixtures = [ + 'category', + 'part', + 'location', + 'stock' + ] def setUp(self) -> None: + super().setUp() # ensure the labels were created apps.get_app_config('label').create_labels() @@ -77,3 +86,13 @@ class LabelTest(TestCase): with self.assertRaises(ValidationError): validateFilterString(bad_filter_string, model=StockItem) + + def test_label_rendering(self): + """Test label rendering""" + + labels = PartLabel.objects.all() + part = PartLabel.objects.first() + + for label in labels: + url = reverse('api-part-label-print', kwargs={'pk': label.pk}) + self.get(f'{url}?parts={part.pk}', expected_code=200) diff --git a/InvenTree/barcodes/api.py b/InvenTree/plugin/barcode.py similarity index 97% rename from InvenTree/barcodes/api.py rename to InvenTree/plugin/barcode.py index f36e4b1703..98b111f073 100644 --- a/InvenTree/barcodes/api.py +++ b/InvenTree/plugin/barcode.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- - -from django.urls import reverse -from django.urls import path, re_path +from django.urls import reverse, path, re_path from django.utils.translation import gettext_lazy as _ from rest_framework.exceptions import ValidationError @@ -12,8 +10,8 @@ from rest_framework.views import APIView from stock.models import StockItem from stock.serializers import StockItemSerializer -from barcodes.plugins.inventree_barcode import InvenTreeBarcodePlugin -from barcodes.barcode import hash_barcode +from plugin.builtin.barcodes.inventree_barcode import InvenTreeBarcodePlugin +from plugin.builtin.barcodes.mixins import hash_barcode from plugin import registry diff --git a/InvenTree/plugin/builtin/barcode/__init__.py b/InvenTree/plugin/builtin/barcode/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/InvenTree/barcodes/__init__.py b/InvenTree/plugin/builtin/barcodes/__init__.py similarity index 100% rename from InvenTree/barcodes/__init__.py rename to InvenTree/plugin/builtin/barcodes/__init__.py diff --git a/InvenTree/barcodes/plugins/inventree_barcode.py b/InvenTree/plugin/builtin/barcodes/inventree_barcode.py similarity index 96% rename from InvenTree/barcodes/plugins/inventree_barcode.py rename to InvenTree/plugin/builtin/barcodes/inventree_barcode.py index 604936c216..939bf12c08 100644 --- a/InvenTree/barcodes/plugins/inventree_barcode.py +++ b/InvenTree/plugin/builtin/barcodes/inventree_barcode.py @@ -13,7 +13,8 @@ references model objects actually exist in the database. import json -from barcodes.barcode import BarcodePlugin +from plugin import IntegrationPluginBase +from plugin.mixins import BarcodeMixin from stock.models import StockItem, StockLocation from part.models import Part @@ -21,7 +22,7 @@ from part.models import Part from rest_framework.exceptions import ValidationError -class InvenTreeBarcodePlugin(BarcodePlugin): +class InvenTreeBarcodePlugin(BarcodeMixin, IntegrationPluginBase): PLUGIN_NAME = "InvenTreeBarcode" diff --git a/InvenTree/plugin/builtin/barcode/mixins.py b/InvenTree/plugin/builtin/barcodes/mixins.py similarity index 100% rename from InvenTree/plugin/builtin/barcode/mixins.py rename to InvenTree/plugin/builtin/barcodes/mixins.py diff --git a/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py b/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py new file mode 100644 index 0000000000..1424d89ff2 --- /dev/null +++ b/InvenTree/plugin/builtin/barcodes/test_inventree_barcode.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +"""Unit tests for InvenTreeBarcodePlugin""" + +from django.contrib.auth import get_user_model +from django.urls import reverse + +from rest_framework.test import APITestCase +from rest_framework import status + + +class TestInvenTreeBarcode(APITestCase): + + fixtures = [ + 'category', + 'part', + 'location', + 'stock' + ] + + def setUp(self): + # Create a user for auth + user = get_user_model() + user.objects.create_user('testuser', 'test@testing.com', 'password') + + self.client.login(username='testuser', password='password') + + def test_errors(self): + """ + Test all possible error cases for assigment action + """ + + def test_assert_error(barcode_data): + response = self.client.post( + reverse('api-barcode-link'), format='json', + data={ + 'barcode': barcode_data, + 'stockitem': 521 + } + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('error', response.data) + + # test with already existing stock + test_assert_error('{"stockitem": 521}') + + # test with already existing stock location + test_assert_error('{"stocklocation": 7}') + + # test with already existing part location + test_assert_error('{"part": 10004}') + + # test with hash + test_assert_error('{"blbla": 10004}') + + def test_scan(self): + """ + Test that a barcode can be scanned + """ + + response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004'}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIn('success', response.data) diff --git a/InvenTree/plugin/mixins/__init__.py b/InvenTree/plugin/mixins/__init__.py index fdbe863e19..97d27b48fc 100644 --- a/InvenTree/plugin/mixins/__init__.py +++ b/InvenTree/plugin/mixins/__init__.py @@ -7,7 +7,7 @@ from ..builtin.integration.mixins import APICallMixin, AppMixin, LabelPrintingMi from common.notifications import SingleNotificationMethod, BulkNotificationMethod from ..builtin.action.mixins import ActionMixin -from ..builtin.barcode.mixins import BarcodeMixin +from ..builtin.barcodes.mixins import BarcodeMixin __all__ = [ 'APICallMixin', diff --git a/InvenTree/barcodes/tests.py b/InvenTree/plugin/test_barcode.py similarity index 82% rename from InvenTree/barcodes/tests.py rename to InvenTree/plugin/test_barcode.py index b092e7eb32..43a713a57b 100644 --- a/InvenTree/barcodes/tests.py +++ b/InvenTree/plugin/test_barcode.py @@ -264,57 +264,3 @@ class BarcodeAPITest(APITestCase): self.assertIn('error', data) self.assertNotIn('success', data) - - -class TestInvenTreeBarcode(APITestCase): - - fixtures = [ - 'category', - 'part', - 'location', - 'stock' - ] - - def setUp(self): - # Create a user for auth - user = get_user_model() - user.objects.create_user('testuser', 'test@testing.com', 'password') - - self.client.login(username='testuser', password='password') - - def test_errors(self): - """ - Test all possible error cases for assigment action - """ - - def test_assert_error(barcode_data): - response = self.client.post( - reverse('api-barcode-link'), format='json', - data={ - 'barcode': barcode_data, - 'stockitem': 521 - } - ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertIn('error', response.data) - - # test with already existing stock - test_assert_error('{"stockitem": 521}') - - # test with already existing stock location - test_assert_error('{"stocklocation": 7}') - - # test with already existing part location - test_assert_error('{"part": 10004}') - - # test with hash - test_assert_error('{"blbla": 10004}') - - def test_scan(self): - """ - Test that a barcode can be scanned - """ - - response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004'}) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertIn('success', response.data)