mirror of
https://github.com/inventree/InvenTree.git
synced 2025-08-09 21:30:54 +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:
23
InvenTree/company/migrations/0048_auto_20220913_0312.py
Normal file
23
InvenTree/company/migrations/0048_auto_20220913_0312.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.2.15 on 2022-09-13 03:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('company', '0047_supplierpart_pack_size'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='supplierpart',
|
||||
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='supplierpart',
|
||||
name='barcode_hash',
|
||||
field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'),
|
||||
),
|
||||
]
|
@@ -21,7 +21,7 @@ import InvenTree.helpers
|
||||
import InvenTree.validators
|
||||
from common.settings import currency_code_default
|
||||
from InvenTree.fields import InvenTreeURLField, RoundingDecimalField
|
||||
from InvenTree.models import InvenTreeAttachment
|
||||
from InvenTree.models import InvenTreeAttachment, InvenTreeBarcodeMixin
|
||||
from InvenTree.status_codes import PurchaseOrderStatus
|
||||
|
||||
|
||||
@@ -391,7 +391,7 @@ class SupplierPartManager(models.Manager):
|
||||
)
|
||||
|
||||
|
||||
class SupplierPart(models.Model):
|
||||
class SupplierPart(InvenTreeBarcodeMixin, models.Model):
|
||||
"""Represents a unique part as provided by a Supplier Each SupplierPart is identified by a SKU (Supplier Part Number) Each SupplierPart is also linked to a Part or ManufacturerPart object. A Part may be available from multiple suppliers.
|
||||
|
||||
Attributes:
|
||||
|
@@ -30,6 +30,22 @@
|
||||
{% url 'admin:company_supplierpart_change' part.pk as url %}
|
||||
{% include "admin_button.html" with url=url %}
|
||||
{% endif %}
|
||||
{% if barcodes %}
|
||||
<!-- Barcode actions menu -->
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-qrcode'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a class='dropdown-item' href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||
{% if part.barcode_hash %}
|
||||
<li><a class='dropdown-item' href='#' id='barcode-unlink'><span class='fas fa-unlink'></span> {% trans "Unlink 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 %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if roles.purchase_order.change or roles.purchase_order.add or roles.purchase_order.delete %}
|
||||
<div class='btn-group'>
|
||||
<button id='supplier-part-actions' title='{% trans "Supplier part actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
@@ -100,6 +116,13 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<td>{% decimal part.available %}<span class='badge bg-dark rounded-pill float-right'>{% render_date part.availability_updated %}</span></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>
|
||||
|
||||
{% endblock details %}
|
||||
@@ -241,6 +264,33 @@ src="{% static 'img/blank_image.png' %}"
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
|
||||
{% if barcodes %}
|
||||
|
||||
$("#show-qr-code").click(function() {
|
||||
launchModalForm("{% url 'supplier-part-qr' part.pk %}",
|
||||
{
|
||||
no_post: true,
|
||||
});
|
||||
});
|
||||
|
||||
$("#barcode-link").click(function() {
|
||||
linkBarcodeDialog(
|
||||
{
|
||||
supplierpart: {{ part.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Supplier Part" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$("#barcode-unlink").click(function() {
|
||||
unlinkBarcode({
|
||||
supplierpart: {{ part.pk }},
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
function reloadPriceBreaks() {
|
||||
$("#price-break-table").bootstrapTable("refresh");
|
||||
}
|
||||
|
@@ -25,5 +25,10 @@ manufacturer_part_urls = [
|
||||
]
|
||||
|
||||
supplier_part_urls = [
|
||||
re_path(r'^(?P<pk>\d+)/', views.SupplierPartDetail.as_view(template_name='company/supplier_part.html'), name='supplier-part-detail'),
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path('^qr_code/?', views.SupplierPartQRCode.as_view(), name='supplier-part-qr'),
|
||||
re_path('^.*$', views.SupplierPartDetail.as_view(template_name='company/supplier_part.html'), name='supplier-part-detail'),
|
||||
]))
|
||||
|
||||
|
||||
]
|
||||
|
@@ -4,7 +4,7 @@ from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView
|
||||
|
||||
from InvenTree.views import InvenTreeRoleMixin
|
||||
from InvenTree.views import InvenTreeRoleMixin, QRCodeView
|
||||
from plugin.views import InvenTreePluginViewMixin
|
||||
|
||||
from .models import Company, ManufacturerPart, SupplierPart
|
||||
@@ -112,3 +112,18 @@ class SupplierPartDetail(InvenTreePluginViewMixin, DetailView):
|
||||
context_object_name = 'part'
|
||||
queryset = SupplierPart.objects.all()
|
||||
permission_required = 'purchase_order.view'
|
||||
|
||||
|
||||
class SupplierPartQRCode(QRCodeView):
|
||||
"""View for displaying a QR code for a StockItem object."""
|
||||
|
||||
ajax_form_title = _("Stock Item QR Code")
|
||||
role_required = 'stock.view'
|
||||
|
||||
def get_qr_data(self):
|
||||
"""Generate QR code data for the StockItem."""
|
||||
try:
|
||||
part = SupplierPart.objects.get(id=self.pk)
|
||||
return part.format_barcode()
|
||||
except SupplierPart.DoesNotExist:
|
||||
return None
|
||||
|
Reference in New Issue
Block a user