2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 13:05:42 +00:00

Barcode Refactor (#3640)

* define a simple model mixin class for barcode

* Adds generic function for assigning a barcode to a model instance

* StockItem model now implements the BarcodeMixin class

* Implement simple unit tests for new code

* Fix unit tests

* Data migration for uid field

* Remove references to old 'uid' field

* Migration for removing old uid field from StockItem model

* Bump API version

* Change lookup_barcode to be a classmethod

* Change barcode_model_type to be a class method

* Cleanup for generic barcode scan and assign API:

- Raise ValidationError as appropriate
- Improved unit testing
- Groundwork for future generic implementation

* Further unit tests for barcode scanning

* Adjust error messages for compatibility

* Unit test fix

* Fix hash_barcode function

- Add unit tests to ensure it produces the same results as before the refactor

* Add BarcodeMixin to Part model

* Remove old format_barcode function from Part model

* Further fixes for unit tests

* Add support for assigning arbitrary barcode to Part instance

- Simplify barcode API
- Add more unit tests

* More unit test fixes

* Update unit test

* Adds generic endpoint for unassigning barcode data

* Update web dialog for unlinking a barcode

* Template cleanup

* Add Barcode mixin to StockLocation class

* Add some simple unit tests for new model mixin

* Support assigning / unassigning barcodes for StockLocation

* remove failing outdated test

* Update template to integrate new barcode support for StockLocation

* Add BarcodeMixin to SupplierPart model

* Adds QR code view for SupplierPart

* Major simplification of barcode API endpoints

- Separate existing barcode plugin into two separate classes
- Simplify and consolidate the response from barcode scanning
- Update unit testing

* Yet more unit test fixes

* Yet yet more unit test fixes
This commit is contained in:
Oliver
2022-09-15 14:14:51 +10:00
committed by GitHub
parent 7645492cc2
commit 187707c892
34 changed files with 1115 additions and 495 deletions

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.15 on 2022-09-12 00:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0085_partparametertemplate_description'),
]
operations = [
migrations.AddField(
model_name='part',
name='barcode_data',
field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'),
),
migrations.AddField(
model_name='part',
name='barcode_hash',
field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'),
),
]

View File

@ -43,7 +43,7 @@ from InvenTree import helpers, validators
from InvenTree.fields import InvenTreeNotesField, InvenTreeURLField
from InvenTree.helpers import decimal2money, decimal2string, normalize
from InvenTree.models import (DataImportMixin, InvenTreeAttachment,
InvenTreeTree)
InvenTreeBarcodeMixin, InvenTreeTree)
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
SalesOrderStatus)
from order import models as OrderModels
@ -300,7 +300,7 @@ class PartManager(TreeManager):
@cleanup.ignore
class Part(MetadataMixin, MPTTModel):
class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
"""The Part object represents an abstract part, the 'concept' of an actual entity.
An actual physical instance of a Part is a StockItem which is treated separately.
@ -941,18 +941,6 @@ class Part(MetadataMixin, MPTTModel):
responsible = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Responsible'), related_name='parts_responible')
def format_barcode(self, **kwargs):
"""Return a JSON string for formatting a barcode for this Part object."""
return helpers.MakeBarcode(
"part",
self.id,
{
"name": self.full_name,
"url": reverse('api-part-detail', kwargs={'pk': self.id}),
},
**kwargs
)
@property
def category_path(self):
"""Return the category path of this Part instance"""

View File

@ -45,6 +45,11 @@
{% if barcodes %}
<li><a class='dropdown-item' href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
{% endif %}
{% if part.barcode_hash %}
<li><a class='dropdown-item' href='#' id='barcode-unlink'><span class='fas fa-unlink'></span> {% trans "Unink Barcode" %}</a></li>
{% else %}
<li><a class='dropdown-item' href='#' id='barcode-link'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
{% endif %}
{% if labels_enabled %}
<li><a class='dropdown-item' href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
{% endif %}
@ -167,6 +172,7 @@
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
</table>
<!-- Part info messages -->
@ -295,6 +301,13 @@
<td>{{ part.keywords }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.barcode_hash %}
<tr>
<td><span class='fas fa-barcode'></span></td>
<td>{% trans "Barcode Identifier" %}</td>
<td {% if part.barcode_data %}title='{{ part.barcode_data }}'{% endif %}>{{ part.barcode_hash }}</td>
</tr>
{% endif %}
</table>
</div>
<div class='col-sm-6'>
@ -391,6 +404,7 @@
}
);
{% if barcodes %}
$("#show-qr-code").click(function() {
launchModalForm(
"{% url 'part-qr' part.id %}",
@ -400,6 +414,24 @@
);
});
$('#barcode-unlink').click(function() {
unlinkBarcode({
part: {{ part.pk }},
});
});
$('#barcode-link').click(function() {
linkBarcodeDialog(
{
part: {{ part.pk }},
},
{
title: '{% trans "Link Barcode to Part" %}',
}
);
});
{% endif %}
{% if labels_enabled %}
$('#print-label').click(function() {
printPartLabels([{{ part.pk }}]);

View File

@ -144,6 +144,15 @@ class PartTest(TestCase):
Part.objects.rebuild()
def test_barcode_mixin(self):
"""Test the barcode mixin functionality"""
self.assertEqual(Part.barcode_model_type(), 'part')
p = Part.objects.get(pk=1)
barcode = p.format_barcode(brief=True)
self.assertEqual(barcode, '{"part": 1}')
def test_tree(self):
"""Test that the part variant tree is working properly"""
chair = Part.objects.get(pk=10000)
@ -243,7 +252,7 @@ class PartTest(TestCase):
"""Test barcode format functionality"""
barcode = self.r1.format_barcode(brief=False)
self.assertIn('InvenTree', barcode)
self.assertIn(self.r1.name, barcode)
self.assertIn('"part": {"id": 3}', barcode)
def test_copy(self):
"""Test that we can 'deep copy' a Part instance"""