Order barcodes (#4575)

* Add barcode support to external orders

- ReturnOrder
- PurchaseOrder
- SalesOrder

* Support scanning for new model types

* Integrate UI elements for ReturnOrder

* Update PurchaseOrder page

* SalesOrder implementation
This commit is contained in:
Oliver 2023-04-04 11:30:49 +10:00 committed by GitHub
parent d6715d94c1
commit 7af2bb4e8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 221 additions and 38 deletions

View File

@ -116,13 +116,7 @@ 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 %}
{% include "barcode_data.html" with instance=part %}
</table>
{% endblock details %}

View File

@ -0,0 +1,43 @@
# Generated by Django 3.2.18 on 2023-04-04 00:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('order', '0088_auto_20230403_1402'),
]
operations = [
migrations.AddField(
model_name='purchaseorder',
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='purchaseorder',
name='barcode_hash',
field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'),
),
migrations.AddField(
model_name='returnorder',
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='returnorder',
name='barcode_hash',
field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'),
),
migrations.AddField(
model_name='salesorder',
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='salesorder',
name='barcode_hash',
field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'),
),
]

View File

@ -35,7 +35,8 @@ from InvenTree.exceptions import log_error
from InvenTree.fields import (InvenTreeModelMoneyField, InvenTreeNotesField,
InvenTreeURLField, RoundingDecimalField)
from InvenTree.helpers import decimal2string, getSetting, notify_responsible
from InvenTree.models import InvenTreeAttachment, ReferenceIndexingMixin
from InvenTree.models import (InvenTreeAttachment, InvenTreeBarcodeMixin,
ReferenceIndexingMixin)
from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus,
ReturnOrderStatus, SalesOrderStatus,
StockHistoryCode, StockStatus)
@ -130,7 +131,7 @@ class TotalPriceMixin(models.Model):
return total
class Order(MetadataMixin, ReferenceIndexingMixin):
class Order(InvenTreeBarcodeMixin, MetadataMixin, ReferenceIndexingMixin):
"""Abstract model for an order.
Instances of this class:

View File

@ -66,6 +66,8 @@ class AbstractOrderSerializer(serializers.Serializer):
# Boolean field indicating if this order is overdue (Note: must be annotated)
overdue = serializers.BooleanField(required=False, read_only=True)
barcode_hash = serializers.CharField(read_only=True)
def validate_reference(self, reference):
"""Custom validation for the reference field"""
@ -101,6 +103,7 @@ class AbstractOrderSerializer(serializers.Serializer):
'status',
'status_text',
'notes',
'barcode_hash',
'overdue',
] + extra_fields

View File

@ -23,6 +23,24 @@
{% url 'admin:order_purchaseorder_change' order.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 roles.purchase_order.change %}
{% if order.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 %}
{% endif %}
</ul>
</div>
{% endif %}
<!-- Printing options -->
<div class='btn-group' role='group'>
<button id='print-options' title='{% trans "Print actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
@ -97,6 +115,7 @@ src="{% static 'img/blank_image.png' %}"
<td>{% trans "Order Description" %}</td>
<td>{{ order.description }}{% include "clip.html" %}</td>
</tr>
{% include "barcode_data.html" with instance=order %}
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
@ -304,5 +323,33 @@ $("#export-order").click(function() {
exportOrder('{% url "po-export" order.id %}');
});
{% if barcodes %}
<!-- Barcode functionality callbacks -->
$('#show-qr-code').click(function() {
showQRDialog(
'{% trans "Purchase Order QR Code" %}',
'{"purchaseorder": {{ order.pk }}}'
);
});
{% if roles.purchase_order.change %}
$("#barcode-link").click(function() {
linkBarcodeDialog(
{
purchaseorder: {{ order.pk }},
},
{
title: '{% trans "Link Barcode to Purchase Order" %}',
}
);
});
$("#barcode-unlink").click(function() {
unlinkBarcode({
purchaseorder: {{ order.pk }},
});
});
{% endif %}
{% endif %}
{% endblock js_ready %}

View File

@ -33,6 +33,24 @@ src="{% static 'img/blank_image.png' %}"
{% url 'admin:order_returnorder_change' order.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 roles.return_order.change %}
{% if order.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 %}
{% endif %}
</ul>
</div>
{% endif %}
<!-- Printing actions -->
<div class='btn-group' role='group'>
<button id='print-options' title='{% trans "Print actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
@ -89,6 +107,9 @@ src="{% static 'img/blank_image.png' %}"
<td>{% trans "Order Description" %}</td>
<td>{{ order.description }}{% include "clip.html" %}</td>
</tr>
{% include "barcode_data.html" with instance=order %}
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
@ -230,6 +251,35 @@ $('#print-order-report').click(function() {
});
{% endif %}
{% if barcodes %}
<!-- Barcode functionality callbacks -->
$('#show-qr-code').click(function() {
showQRDialog(
'{% trans "Return Order QR Code" %}',
'{"returnorder": {{ order.pk }}}'
);
});
{% if roles.return_order.change %}
$("#barcode-link").click(function() {
linkBarcodeDialog(
{
returnorder: {{ order.pk }},
},
{
title: '{% trans "Link Barcode to Return Order" %}',
}
);
});
$("#barcode-unlink").click(function() {
unlinkBarcode({
returnorder: {{ order.pk }},
});
});
{% endif %}
{% endif %}
<!-- TODO: Export order callback -->
{% endblock js_ready %}

View File

@ -33,6 +33,24 @@ src="{% static 'img/blank_image.png' %}"
{% url 'admin:order_salesorder_change' order.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 roles.sales_order.change %}
{% if order.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 %}
{% endif %}
</ul>
</div>
{% endif %}
<!-- Printing actions -->
<div class='btn-group' role='group'>
<button id='print-options' title='{% trans "Print actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
@ -94,6 +112,7 @@ src="{% static 'img/blank_image.png' %}"
<td>{% trans "Order Description" %}</td>
<td>{{ order.description }}{% include "clip.html" %}</td>
</tr>
{% include "barcode_data.html" with instance=order %}
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
@ -279,6 +298,35 @@ $('#print-order-report').click(function() {
});
{% endif %}
{% if barcodes %}
<!-- Barcode functionality callbacks -->
$('#show-qr-code').click(function() {
showQRDialog(
'{% trans "Sales Order QR Code" %}',
'{"salesorder": {{ order.pk }}}'
);
});
{% if roles.sales_order.change %}
$("#barcode-link").click(function() {
linkBarcodeDialog(
{
salesorder: {{ order.pk }},
},
{
title: '{% trans "Link Barcode to Sales Order" %}',
}
);
});
$("#barcode-unlink").click(function() {
unlinkBarcode({
salesorder: {{ order.pk }},
});
});
{% endif %}
{% endif %}
$('#export-order').click(function() {
exportOrder('{% url "so-export" order.id %}');
});

View File

@ -302,13 +302,7 @@
<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 %}
{% include "barcode_data.html" with instance=part %}
</table>
</div>
<div class='col-sm-6'>

View File

@ -11,12 +11,13 @@ import json
from django.utils.translation import gettext_lazy as _
from company.models import SupplierPart
import company.models
import order.models
import part.models
import stock.models
from InvenTree.helpers import hash_barcode
from part.models import Part
from plugin import InvenTreePlugin
from plugin.mixins import BarcodeMixin
from stock.models import StockItem, StockLocation
class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin):
@ -33,10 +34,13 @@ class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin):
"""Returns a list of database models which support barcode functionality"""
return [
Part,
StockItem,
StockLocation,
SupplierPart,
company.models.SupplierPart,
order.models.PurchaseOrder,
order.models.ReturnOrder,
order.models.SalesOrder,
part.models.Part,
stock.models.StockItem,
stock.models.StockLocation,
]
def format_matched_response(self, label, model, instance):

View File

@ -155,13 +155,8 @@
</td>
</tr>
{% if item.barcode_hash %}
<tr>
<td><span class='fas fa-barcode'></span></td>
<td>{% trans "Barcode Identifier" %}</td>
<td {% if item.barcode_data %}title='{{ item.barcode_data }}'{% endif %}>{{ item.barcode_hash }}</td>
</tr>
{% endif %}
{% include "barcode_data.html" with instance=item %}
{% if item.batch %}
<tr>
<td><span class='fas fa-layer-group'></span></td>
@ -537,6 +532,7 @@ $('#stock-edit-status').click(function () {
{% endif %}
{% if barcodes %}
$("#show-qr-code").click(function() {
showQRDialog(
'{% trans "Stock Item QR Code" %}',
@ -544,7 +540,6 @@ $("#show-qr-code").click(function() {
);
});
{% if barcodes %}
$("#barcode-link").click(function() {
linkBarcodeDialog(
{

View File

@ -150,13 +150,7 @@
</td>
</tr>
{% endif %}
{% if location and location.barcode_hash %}
<tr>
<td><span class='fas fa-barcode'></span></td>
<td>{% trans "Barcode Identifier" %}</td>
<td {% if location.barcode_data %}title='{{ location.barcode_data }}'{% endif %}>{{ location.barcode_hash }}</td>
</tr>
{% endif %}
{% include "barcode_data.html" with instance=location %}
</table>
{% endblock details_left %}

View File

@ -0,0 +1,10 @@
{% load i18n %}
{% if instance and instance.barcode_hash %}
<tr>
<td><span class='fas fa-barcode'></span></td>
<td>{% trans "Barcode Identifier" %}</td>
<td {% if instance.barcode_data %}title='{{ instance.barcode_data }}'{% endif %}>
{{ instance.barcode_hash}}
</td>
</tr>
{% endif %}