Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
164 commits
Select commit Hold shift + click to select a range
b8c20e5
feat: display linked barcode on stock detail page
mahekp05 Jun 21, 2026
f57f73f
Merge remote-tracking branch 'upstream/master' into fix-11745-linked-…
mahekp05 Jul 10, 2026
08a915f
test: cover linked barcode display on stock detail (#11745)
mahekp05 Jul 10, 2026
cb5216a
chore: bump API version to 520 for barcode_data field (#11745)
mahekp05 Jul 10, 2026
f0f389c
feat: expose read-only barcode_data on all barcode-supporting seriali…
mahekp05 Jul 11, 2026
63e6a58
feat: show linked barcode on all detail pages via shared helper (#11745)
mahekp05 Jul 11, 2026
a49b0d8
Merge remote-tracking branch 'upstream/master' into fix-11745-linked-…
mahekp05 Jul 11, 2026
162b314
feat: validate BarcodeSerializerMixin configuration on init (#11745)
mahekp05 Jul 14, 2026
5fc8c93
Remove built-in dashboard history widgets (#12114)
SchrodingersGat Jun 8, 2026
34496e8
Calendar tweaks (#12115)
SchrodingersGat Jun 8, 2026
00e4113
chore(deps): bump the dependencies group with 3 updates (#12125)
dependabot[bot] Jun 9, 2026
9440ef2
feat(frontend): add hotkey registration function and hotkey helper mo…
matmair Jun 9, 2026
1e11433
Remove reference to lead_time field (#12133)
SchrodingersGat Jun 9, 2026
8ec2415
bump container deps (#12136)
matmair Jun 9, 2026
df5df37
[UI] User management spotlight (#12144)
SchrodingersGat Jun 10, 2026
bb74f45
[UI] Update parametric data table hover (#12145)
SchrodingersGat Jun 10, 2026
5b11c67
[UI] User Filters (#12143)
SchrodingersGat Jun 10, 2026
88ad3a7
[report] Printing fixes (#12142)
SchrodingersGat Jun 10, 2026
f4cbbe4
Make plugin registry hash independent of plugin discovery order (#12151)
Nasawa Jun 11, 2026
1ffa0df
New Crowdin translations by GitHub Action (#12069)
github-actions[bot] Jun 11, 2026
8a605d5
Improvements for template tables (#12155)
SchrodingersGat Jun 11, 2026
9a89dfe
Machine fixes (#12154)
SchrodingersGat Jun 11, 2026
355b70f
feat(ci): add flakeiness (#12148)
matmair Jun 12, 2026
9b63600
chore(deps): bump the dependencies group across 2 directories with 16…
dependabot[bot] Jun 13, 2026
f9ef40c
Remember last used label template and printer plugin per model type (…
Sanidhyavijay24 Jun 13, 2026
d3207c1
switch from n to nvm as that is an offical channel (#12140)
matmair Jun 13, 2026
c490acd
[UI] Calendar month colors (#12157)
SchrodingersGat Jun 13, 2026
8c527af
feat(frontend): add more general hotkeys (#12138)
matmair Jun 13, 2026
3d23f13
[UI] Calendar Updates (#12161)
SchrodingersGat Jun 14, 2026
c628b52
Improve security posture of PDF reporting (#12160)
SchrodingersGat Jun 14, 2026
efdb2c2
SelectionList Updates (#12139)
SchrodingersGat Jun 14, 2026
02fefc5
New Crowdin translations by GitHub Action (#12156)
github-actions[bot] Jun 14, 2026
f395ae8
fix: support non-integer PKs in NotificationMessage (fixes UUID overf…
kaizeenn Jun 14, 2026
ae29430
Suppress dulwich warnings (#12163)
SchrodingersGat Jun 14, 2026
e07f003
Part category parameters (#12165)
SchrodingersGat Jun 15, 2026
92c54c0
[UI] Edit cat param (#12166)
SchrodingersGat Jun 15, 2026
214b873
Data import permissions (#12169)
SchrodingersGat Jun 15, 2026
737c7f9
Permissions fix (#12168)
SchrodingersGat Jun 15, 2026
f2caf72
bump frontend (#12137)
matmair Jun 15, 2026
a0fc6c2
chore(deps): bump the dependencies group with 4 updates (#12172)
dependabot[bot] Jun 15, 2026
e4f5c41
bump docker base (#12178)
matmair Jun 16, 2026
23b9f54
Stocktake exporter updates (#12179)
SchrodingersGat Jun 16, 2026
418e346
chore(deps): bump cryptography from 48.0.0 to 48.0.1 in /src/backend …
dependabot[bot] Jun 16, 2026
cb0403d
fix(frontend): adress broken UI tests (#12183)
matmair Jun 16, 2026
8474b1f
[import] specify fk lookup field (#12180)
SchrodingersGat Jun 17, 2026
47e55bc
Mysql filter fix (#12185)
SchrodingersGat Jun 17, 2026
b5050d1
[UI] Additional table filters (#12186)
SchrodingersGat Jun 17, 2026
8a648a3
Change order custom status via api (#11982)
johnluetke Jun 17, 2026
47ef929
fix(ci): disable running on dependabot and backport branches (#12189)
matmair Jun 17, 2026
544eaee
fix(backend): import session metadata (#12184)
matmair Jun 17, 2026
21fbabe
fix(ci): improve release pipe stability (#12191)
matmair Jun 17, 2026
5ec5533
chore(deps): bump dompurify from 3.4.8 to 3.4.9 in /src/frontend (#12…
dependabot[bot] Jun 17, 2026
e7b05a3
New Crowdin translations by GitHub Action (#12167)
github-actions[bot] Jun 18, 2026
d4a5640
System Health Checks (#12193)
SchrodingersGat Jun 18, 2026
ef092f2
Report Generation Updates (#12187)
SchrodingersGat Jun 18, 2026
8de95de
chore(deps): bump form-data from 4.0.5 to 4.0.6 in /src/frontend (#12…
dependabot[bot] Jun 18, 2026
8d0b63c
Fix order event name (#12196)
SchrodingersGat Jun 18, 2026
e04a3cb
[bug] Fix double save (#12194)
SchrodingersGat Jun 18, 2026
b62ad77
[API] Adjust permissions for machine restart (#12197)
SchrodingersGat Jun 18, 2026
233000a
chore(deps): bump dompurify from 3.4.9 to 3.4.11 in /src/frontend (#1…
dependabot[bot] Jun 18, 2026
d77965b
[UI] Fix status renderer functions (#12202)
SchrodingersGat Jun 19, 2026
ebfda4a
chore(deps): bump pypdf from 6.13.0 to 6.13.3 in /src/backend (#12201)
dependabot[bot] Jun 19, 2026
d8c23a9
[UI] Tree improvements (#12204)
SchrodingersGat Jun 19, 2026
4d18c79
[UI] Fix NewsWidget (#12205)
SchrodingersGat Jun 19, 2026
36fc472
chore(deps): bump the dependencies group across 1 directory with 12 u…
dependabot[bot] Jun 19, 2026
a80f7f8
Report locale updates (#12208)
SchrodingersGat Jun 20, 2026
61d1a52
chore(deps): bump undici from 8.4.1 to 8.5.0 in /src/frontend (#12200)
dependabot[bot] Jun 20, 2026
4254b5e
Fix signup warning (#12213)
SchrodingersGat Jun 20, 2026
9132df6
chore(deps): bump undici from 6.26.0 to 8.5.0 in /src/frontend (#12214)
dependabot[bot] Jun 20, 2026
d8cae35
Agents (#12212)
SchrodingersGat Jun 20, 2026
be793dc
build(docker): compile backend translations during image build (#12203)
geekcj12 Jun 20, 2026
8939620
Add meaningful message on CSRF failure (#12216)
SchrodingersGat Jun 20, 2026
70ad93b
devops: fix chromium test uploads to flakiness.io (#12219)
aslushnikov Jun 21, 2026
e1e3aa9
Bump ty (#12211)
matmair Jun 21, 2026
fbd07d4
color scheme: Use the users prefered mode by default (#12227)
reid-p Jun 23, 2026
0dd33e4
chore(deps): bump the dependencies group with 3 updates (#12220)
dependabot[bot] Jun 23, 2026
84a99a2
extend barcode scans API (#12233)
matmair Jun 24, 2026
ece1e56
New Crowdin translations by GitHub Action (#12192)
github-actions[bot] Jun 24, 2026
a2bcdb8
[UI] Tweak today color (#12235)
SchrodingersGat Jun 24, 2026
b4cf921
[UI] Tweak "order parts" wizard (#12236)
SchrodingersGat Jun 24, 2026
18ecd52
[bug] Allocated query fix (#12234)
SchrodingersGat Jun 24, 2026
0eaf8ac
1.5.0 (#12239)
SchrodingersGat Jun 24, 2026
e54d987
[report] Tweak datamatrix helper (#12240)
SchrodingersGat Jun 24, 2026
5a99437
!feat(backend): bump minimum python version (#12223)
matmair Jun 24, 2026
b918052
Update devcontainer Dockerfile (#12244)
SchrodingersGat Jun 24, 2026
cad1954
Allow stock transfer to merge into existing stock (optional) #6951 (#…
neilbeukes Jun 24, 2026
6025478
New Crowdin translations by GitHub Action (#12238)
github-actions[bot] Jun 25, 2026
475a75e
Revert devcontainer to python 3.12 (#12249)
SchrodingersGat Jun 25, 2026
af34ec6
Project code active (#12250)
SchrodingersGat Jun 25, 2026
0dab69d
fix js-yaml, esbuild vulns (#12190)
matmair Jun 25, 2026
026ea6e
Skip "many" fields in DataExport (#12255)
SchrodingersGat Jun 25, 2026
a0250bd
[UI] Improve revision selector (#12254)
SchrodingersGat Jun 25, 2026
185a5d6
Bug fix for exception handler (#12257)
SchrodingersGat Jun 25, 2026
a5e4682
chore(backend): typing fixes (#12247)
matmair Jun 25, 2026
ede97da
chore(backend): harden gh setup (#12262)
matmair Jun 25, 2026
500f5c7
chore(backend): dep bump (#12258)
matmair Jun 25, 2026
2c54ec4
[UI] Stock column fix (#12268)
SchrodingersGat Jun 27, 2026
63ebf66
chore(backend): Remove devops (#12265)
matmair Jun 27, 2026
941cac7
update sponsored ressources (#12264)
matmair Jun 27, 2026
a47f9c4
[UI] Table tweaks (#12271)
SchrodingersGat Jun 29, 2026
71f7176
[UI] Fix stock dates (#12272)
SchrodingersGat Jun 29, 2026
9d3f217
fix: preserve supplier import stock workflow (#12215)
suzunn Jun 29, 2026
09fac76
Fix dashboard issue (#12275)
SchrodingersGat Jun 29, 2026
4944a13
chore(deps): bump the dependencies group with 5 updates (#12278)
dependabot[bot] Jun 29, 2026
09e0a3b
[UI] Fix 'available' display (#12279)
SchrodingersGat Jun 29, 2026
9a965e0
Fix stocktake bug for counting serialized items (#12280)
SchrodingersGat Jun 30, 2026
3327db9
[UI] Table field refactor (#12274)
SchrodingersGat Jun 30, 2026
c6771fa
Fix for STOCK_COUNT event (#12281)
SchrodingersGat Jun 30, 2026
7d1bc92
Allow stock adjustment from "category" detail view (#12256)
SchrodingersGat Jun 30, 2026
3a318c5
New Crowdin translations by GitHub Action (#12251)
github-actions[bot] Jun 30, 2026
98a2f43
[UI] Tree select in form fields (#12217)
SchrodingersGat Jun 30, 2026
2c08872
Check build complete (#12289)
SchrodingersGat Jul 1, 2026
cc4134c
Display login errors to user (#12288)
SchrodingersGat Jul 1, 2026
b6a2abf
Add playwright test for part image upload (#11597)
martonmiklos Jul 1, 2026
9154ad8
fix pin syntax (#12287)
matmair Jul 1, 2026
6602825
[UI] Fix imports (#12292)
SchrodingersGat Jul 2, 2026
5ad5e45
[UI] Tweak unique keys (#12293)
SchrodingersGat Jul 2, 2026
a39e1f7
chore(deps): bump the dependencies group across 1 directory with 27 u…
dependabot[bot] Jul 2, 2026
5d9e28a
fix test assumption (#12296)
matmair Jul 3, 2026
dcb3eec
[API] Refactor API for duplicating objects (#12294)
SchrodingersGat Jul 3, 2026
3a651c7
[UI] Snippet editor (#12299)
SchrodingersGat Jul 3, 2026
073491c
[UI] Add "IPN" column to installed items table (#12297)
SchrodingersGat Jul 3, 2026
a48745f
[docs] Report helper docs (#12300)
SchrodingersGat Jul 3, 2026
de0a3df
[db] Fix for UUID fields (#12301)
SchrodingersGat Jul 3, 2026
7cf171a
Fix OpenAPI multipart file fields to use binary format (#12298)
martonmiklos Jul 3, 2026
4d42a4f
reduce duplication (#12302)
matmair Jul 5, 2026
9eab08b
[UI] Pricing override (#12306)
SchrodingersGat Jul 6, 2026
20ebf6c
[feature] Consumable part (#12295)
SchrodingersGat Jul 6, 2026
bd5bd9b
Order click-through on pricing panel (#12307)
SchrodingersGat Jul 6, 2026
4369e54
Pricing bugs (#12305)
SchrodingersGat Jul 6, 2026
4dbc24b
Add --keep option to setup-test task (#12308)
SchrodingersGat Jul 6, 2026
e2effc7
Table refactor (#12309)
SchrodingersGat Jul 6, 2026
496975c
chore(deps): bump python in /contrib/container (#12276)
dependabot[bot] Jul 6, 2026
c2d3b60
chore(deps): bump the dependencies group across 1 directory with 31 u…
dependabot[bot] Jul 6, 2026
a65264c
New Crowdin translations by GitHub Action (#12284)
github-actions[bot] Jul 7, 2026
c83ad8c
chore(deps): bump the dependencies group with 4 updates (#12312)
dependabot[bot] Jul 7, 2026
a46ba31
[bug] Barcode bug fix (#12319)
SchrodingersGat Jul 7, 2026
5443bf0
Fix for stocktake exporter (#12324)
SchrodingersGat Jul 7, 2026
d434267
Robustify playwright tests (#12331)
SchrodingersGat Jul 8, 2026
e300bad
[UI] Add form submission shortcut (#12328)
SchrodingersGat Jul 8, 2026
0bcb837
Fix currency choices for PartPricing model (#12336)
SchrodingersGat Jul 8, 2026
1473f30
Fix notes on part creation (#12332)
ledvinap Jul 8, 2026
87adad3
[bug] Plugin load fix (#12329)
SchrodingersGat Jul 9, 2026
513ea2b
Adjust default behavior of "migrate" command (#12320)
SchrodingersGat Jul 9, 2026
7e20e22
Import internal price (#12341)
SchrodingersGat Jul 9, 2026
f84ae4a
[UI] Docs links (#12342)
SchrodingersGat Jul 9, 2026
5e6e112
[docs] Admin docs (#12345)
SchrodingersGat Jul 9, 2026
ac1bd0f
Currency options fix (#12343)
SchrodingersGat Jul 9, 2026
4ccc872
Add IPN column to BOM pricing panel (#12344)
SchrodingersGat Jul 9, 2026
7bfc802
[UI] Form fix (#12348)
SchrodingersGat Jul 10, 2026
c6fd0cb
[UI] Page Load Improvements (#12334)
SchrodingersGat Jul 10, 2026
2883348
Bug fixes for PO receive (#12349)
SchrodingersGat Jul 10, 2026
ae9dc0c
Improved error context (#12350)
SchrodingersGat Jul 10, 2026
70bf894
test: cover linked barcode display on stock detail (#11745)
mahekp05 Jul 10, 2026
0487670
chore: bump API version to 520 for barcode_data field (#11745)
mahekp05 Jul 10, 2026
391199b
feat: expose read-only barcode_data on all barcode-supporting seriali…
mahekp05 Jul 11, 2026
31e1ca1
feat: show linked barcode on all detail pages via shared helper (#11745)
mahekp05 Jul 11, 2026
470a005
Fix data import bug (#12355)
SchrodingersGat Jul 11, 2026
5cd6fcb
Remove dead pricing code on Part model (#12357)
SchrodingersGat Jul 11, 2026
abb39e1
[refactor] Refactor StockItem methods (#12358)
SchrodingersGat Jul 11, 2026
409b635
feat: validate BarcodeSerializerMixin configuration on init (#11745)
mahekp05 Jul 14, 2026
bc09c97
Merge branch 'fix-11745-linked-barcode-ui' of https://github.com/mahe…
mahekp05 Aug 4, 2026
b351b8f
Merge remote-tracking branch 'upstream/master' into fix-11745-linked-…
mahekp05 Aug 4, 2026
0e1ca83
Merge branch 'master' into fix-11745-linked-barcode-ui
mahekp05 Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/backend/InvenTree/InvenTree/api_version.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""InvenTree API version information."""

# InvenTree API version
INVENTREE_API_VERSION = 530
INVENTREE_API_VERSION = 531
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""

INVENTREE_API_TEXT = """

v531 -> 2026-08-03 : https://github.com/inventree/InvenTree/pull/12354
- Adds read-only "barcode_data" field to the API endpoints for all models supporting custom barcodes (Part, StockItem, StockLocation, Build, SupplierPart, ManufacturerPart, PurchaseOrder, SalesOrder, ReturnOrder, TransferOrder, SalesOrderShipment)

v530 -> 2026-07-28 : https://github.com/inventree/InvenTree/pull/12487
- Adds searching against ReportSnippet API endpoint

Expand Down
60 changes: 60 additions & 0 deletions src/backend/InvenTree/InvenTree/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.core.exceptions import ValidationError as DjangoValidationError
from django.core.files.storage import default_storage
from django.db import models
Expand Down Expand Up @@ -1016,6 +1017,65 @@ def __init__(self, *args, **kwargs):
self.fields.pop('notes', None)


class BarcodeSerializerMixin(serializers.Serializer):
"""Serializer mixin for models which support custom (third-party) barcode assignment.

Exposes the read-only 'barcode_data' field (the linked barcode string) so that
it can be displayed in the user interface. Applies to any model inheriting from
InvenTree.models.InvenTreeBarcodeMixin (e.g. Part, StockItem, StockLocation).

Note: The serializer's Meta.fields must include 'barcode_data' for it to appear.
The field is read-only here; barcodes are assigned via the barcode link endpoints.
Comment thread
matmair marked this conversation as resolved.

Correct configuration is validated on initialization (see __init__), which raises
ImproperlyConfigured if the mixin is applied incorrectly.
"""

barcode_data = serializers.CharField(
read_only=True, label=_('Barcode Data'), help_text=_('Third party barcode data')
)

def __init__(self, *args, **kwargs):
"""Validate that the serializer is correctly configured for barcode data.

For concrete model serializers this checks that:
- the underlying model supports custom barcodes (InvenTreeBarcodeMixin), and
- 'barcode_data' is included in Meta.fields.

This guards against the mixin being added to an incompatible serializer, or
being added without exposing the field.
"""
super().__init__(*args, **kwargs)

meta = getattr(self, 'Meta', None)
model = getattr(meta, 'model', None)

# Abstract / non-model serializers (e.g. AbstractOrderSerializer) are skipped
if model is None:
return

# Import here to avoid a circular import at module load time
from InvenTree.models import InvenTreeBarcodeMixin

if not issubclass(model, InvenTreeBarcodeMixin):
raise ImproperlyConfigured(
f'{self.__class__.__name__} uses BarcodeSerializerMixin, but its model '
f"'{model.__name__}' does not inherit InvenTreeBarcodeMixin."
)

meta_fields = getattr(meta, 'fields', None)

if (
meta_fields is not None
and meta_fields != '__all__'
and 'barcode_data' not in meta_fields
):
raise ImproperlyConfigured(
f'{self.__class__.__name__} uses BarcodeSerializerMixin, but '
f"'barcode_data' is missing from Meta.fields."
)


class ContentTypeField(serializers.ChoiceField):
"""Serializer field which represents a ContentType as 'app_label.model_name'.

Expand Down
81 changes: 81 additions & 0 deletions src/backend/InvenTree/InvenTree/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django.contrib import admin
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.urls import path, reverse

from rest_framework.serializers import SerializerMethodField
Expand Down Expand Up @@ -130,6 +131,86 @@ def test_basic_setup(self):
self.assertContains(response, 'field_d')
self.assertNotContains(response, 'field_e')


class BarcodeSerializerMixinTest(InvenTreeAPITestCase):
"""Tests for the shared BarcodeSerializerMixin.

Ref #11745: every model which supports custom (third-party) barcodes must
expose the linked barcode string ('barcode_data') via its API serializer,
as a read-only field, so that it can be displayed in the user interface.
"""

def test_barcode_data_field_exposed(self):
"""'barcode_data' must be present and read-only on every barcode serializer."""
from build.serializers import BuildSerializer
from company.serializers import (
ManufacturerPartSerializer,
SupplierPartSerializer,
)
from order.serializers import (
PurchaseOrderSerializer,
ReturnOrderSerializer,
SalesOrderSerializer,
SalesOrderShipmentSerializer,
TransferOrderSerializer,
)
from part.serializers import PartSerializer
from stock.serializers import LocationSerializer, StockItemSerializer

serializers = [
PartSerializer,
StockItemSerializer,
LocationSerializer,
BuildSerializer,
ManufacturerPartSerializer,
SupplierPartSerializer,
PurchaseOrderSerializer,
SalesOrderSerializer,
ReturnOrderSerializer,
TransferOrderSerializer,
SalesOrderShipmentSerializer,
]

for serializer in serializers:
fields = serializer().fields
self.assertIn(
'barcode_data',
fields,
f"'barcode_data' missing from {serializer.__name__}",
)
self.assertTrue(
fields['barcode_data'].read_only,
f"'barcode_data' must be read-only in {serializer.__name__}",
)

def test_misconfiguration_raises(self):
"""The mixin must reject serializers which are configured incorrectly."""
from part.models import Part

# Case 1: the model does not support custom barcodes
class NoBarcodeModelSerializer(
InvenTree.serializers.BarcodeSerializerMixin,
InvenTree.serializers.InvenTreeModelSerializer,
):
class Meta:
model = User
fields = ['id', 'barcode_data']

with self.assertRaises(ImproperlyConfigured):
NoBarcodeModelSerializer()

# Case 2: 'barcode_data' is omitted from Meta.fields
class MissingFieldSerializer(
InvenTree.serializers.BarcodeSerializerMixin,
InvenTree.serializers.InvenTreeModelSerializer,
):
class Meta:
model = Part
fields = ['pk']

with self.assertRaises(ImproperlyConfigured):
MissingFieldSerializer()

def test_permission_gating(self):
"""An OptionalField which embeds a model should respect the model's permissions.

Expand Down
3 changes: 3 additions & 0 deletions src/backend/InvenTree/build/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from InvenTree.fields import PrefetchedPrimaryKeyRelatedField
from InvenTree.mixins import DataImportExportSerializerMixin
from InvenTree.serializers import (
BarcodeSerializerMixin,
BulkPrefetchSerializerMixin,
CustomStatusSerializerMixin,
DuplicateOptionsSerializer,
Expand Down Expand Up @@ -61,6 +62,7 @@


class BuildSerializer(
BarcodeSerializerMixin,
CustomStatusSerializerMixin,
FilterableSerializerMixin,
NotesFieldMixin,
Expand All @@ -81,6 +83,7 @@ class Meta:
'pk',
'title',
'barcode_hash',
'barcode_data',
'batch',
'creation_date',
'completed',
Expand Down
5 changes: 5 additions & 0 deletions src/backend/InvenTree/company/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from InvenTree.mixins import DataImportExportSerializerMixin
from InvenTree.ready import isGeneratingSchema
from InvenTree.serializers import (
BarcodeSerializerMixin,
DuplicateOptionsSerializer,
FilterableSerializerMixin,
InvenTreeCurrencySerializer,
Expand Down Expand Up @@ -231,6 +232,7 @@ class Meta:

@register_importer()
class ManufacturerPartSerializer(
BarcodeSerializerMixin,
FilterableSerializerMixin,
DataImportExportSerializerMixin,
InvenTreeTaggitSerializer,
Expand All @@ -257,6 +259,7 @@ class Meta:
'MPN',
'link',
'barcode_hash',
'barcode_data',
'notes',
'tags',
'parameters',
Expand Down Expand Up @@ -355,6 +358,7 @@ class Meta:

@register_importer()
class SupplierPartSerializer(
BarcodeSerializerMixin,
FilterableSerializerMixin,
DataImportExportSerializerMixin,
InvenTreeTaggitSerializer,
Expand Down Expand Up @@ -398,6 +402,7 @@ class Meta:
'note',
'pk',
'barcode_hash',
'barcode_data',
'packaging',
'pack_quantity',
'pack_quantity_native',
Expand Down
5 changes: 5 additions & 0 deletions src/backend/InvenTree/order/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from InvenTree.helpers import extract_serial_numbers, hash_barcode, normalize, str2bool
from InvenTree.mixins import DataImportExportSerializerMixin
from InvenTree.serializers import (
BarcodeSerializerMixin,
CustomStatusSerializerMixin,
DuplicateOptionsSerializer,
FilterableSerializerMixin,
Expand Down Expand Up @@ -73,6 +74,7 @@ class TotalPriceMixin(serializers.Serializer):


class AbstractOrderSerializer(
BarcodeSerializerMixin,
CustomStatusSerializerMixin,
DataImportExportSerializerMixin,
InvenTreeTaggitSerializer,
Expand Down Expand Up @@ -231,6 +233,7 @@ def order_fields(extra_fields):
'status_custom_key',
'notes',
'barcode_hash',
'barcode_data',
'overdue',
'duplicate',
# Extra detail fields
Expand Down Expand Up @@ -1424,6 +1427,7 @@ def annotate_queryset(queryset):

@register_importer()
class SalesOrderShipmentSerializer(
BarcodeSerializerMixin,
DataImportExportSerializerMixin,
FilterableSerializerMixin,
InvenTreeTaggitSerializer,
Expand Down Expand Up @@ -1451,6 +1455,7 @@ class Meta:
'tracking_number',
'invoice_number',
'barcode_hash',
'barcode_data',
'link',
'notes',
# Extra detail fields
Expand Down
2 changes: 2 additions & 0 deletions src/backend/InvenTree/part/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ class Meta:

@register_importer()
class PartSerializer(
InvenTree.serializers.BarcodeSerializerMixin,
InvenTree.serializers.FilterableSerializerMixin,
DataImportExportSerializerMixin,
InvenTree.serializers.NotesFieldMixin,
Expand All @@ -555,6 +556,7 @@ class Meta:
'active',
'assembly',
'barcode_hash',
'barcode_data',
'category',
'category_detail',
'category_path',
Expand Down
4 changes: 4 additions & 0 deletions src/backend/InvenTree/stock/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ def validate(self, data):

@register_importer()
class StockItemSerializer(
InvenTree.serializers.BarcodeSerializerMixin,
CustomStatusSerializerMixin,
InvenTree.serializers.FilterableSerializerMixin,
DataImportExportSerializerMixin,
Expand Down Expand Up @@ -378,6 +379,7 @@ class Meta:
'SKU',
'MPN',
'barcode_hash',
'barcode_data',
'creation_date',
'stocktake_date',
'updated',
Expand Down Expand Up @@ -1417,6 +1419,7 @@ class Meta:

@register_importer()
class LocationSerializer(
InvenTree.serializers.BarcodeSerializerMixin,
InvenTree.serializers.FilterableSerializerMixin,
DataImportExportSerializerMixin,
InvenTree.serializers.InvenTreeTaggitSerializer,
Expand All @@ -1433,6 +1436,7 @@ class Meta:
fields = [
'pk',
'barcode_hash',
'barcode_data',
'name',
'level',
'description',
Expand Down
28 changes: 28 additions & 0 deletions src/backend/InvenTree/stock/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,34 @@ def test_creation_date_set_on_serialize(self):
for new_item in new_items:
self.assertIsNotNone(new_item.creation_date)

def test_barcode_data_in_detail_api(self):
"""Test that 'barcode_data' (the linked barcode string) is exposed via the API.

Regression test for #11745: the linked barcode string must be present in
the StockItem serializer output so it can be displayed in the user interface.
It must be read-only (assigned/cleared only via the barcode link endpoints).
"""
item = StockItem.objects.get(pk=1)
url = reverse('api-stock-detail', kwargs={'pk': item.pk})

# With no external barcode assigned, the field is present but empty
response = self.get(url, expected_code=200)
self.assertIn('barcode_data', response.data)
self.assertEqual(response.data['barcode_data'], '')

# Assign a third-party barcode to the stock item
item.assign_barcode(barcode_data='TEST-123')
item.refresh_from_db()

# The linked barcode string must now be returned by the detail endpoint
response = self.get(url, expected_code=200)
self.assertEqual(response.data['barcode_data'], 'TEST-123')

# The field must be read-only: a PATCH must not overwrite it
self.patch(url, data={'barcode_data': 'HACKED-VALUE'}, expected_code=200)
item.refresh_from_db()
self.assertEqual(item.barcode_data, 'TEST-123')

def test_bulk_serialize_benchmark(self):
"""Benchmark: measure the number of DB queries required to serialize 100 stock items at once."""
InvenTreeSetting.set_setting('ENABLE_PLUGINS_EVENTS', True, change_user=None)
Expand Down
17 changes: 17 additions & 0 deletions src/frontend/src/components/details/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ export type DetailsField = {
type BadgeType = 'owner' | 'user' | 'group';
type ValueFormatterReturn = string | number | null | React.ReactNode;

/**
* Construct a reusable "Linked Barcode" detail field for any model which
* supports custom (third-party) barcode assignment (i.e. exposes 'barcode_data').
*
* The field is copyable and only rendered when a barcode is linked to the instance.
*/
export function barcodeDataField(instance: any): DetailsField {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SchrodingersGat do we need to do a memo trick here?

return {
type: 'text',
name: 'barcode_data',
icon: 'barcode',
label: t`Linked Barcode`,
copy: true,
hidden: !instance?.barcode_data
};
}

type StringDetailField =
| {
type: 'string' | 'text';
Expand Down
Loading
Loading