2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-17 12:35:46 +00:00

Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters
2022-07-06 10:19:34 +10:00
9 changed files with 136 additions and 11 deletions

View File

@ -7,6 +7,9 @@ INVENTREE_API_VERSION = 61
""" """
Increment this API version number whenever there is a significant change to the API that any clients need to know about Increment this API version number whenever there is a significant change to the API that any clients need to know about
v62 -> 2022-07-05 : https://github.com/inventree/InvenTree/pull/3296
- Allows search on BOM List API endpoint
v61 -> 2022-06-12 : https://github.com/inventree/InvenTree/pull/3183 v61 -> 2022-06-12 : https://github.com/inventree/InvenTree/pull/3183
- Migrate the "Convert Stock Item" form class to use the API - Migrate the "Convert Stock Item" form class to use the API
- There is now an API endpoint for converting a stock item to a valid variant - There is now an API endpoint for converting a stock item to a valid variant

View File

@ -741,7 +741,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
"""Initialization routine for the serializer""" """Initialization routine for the serializer"""
order_detail = kwargs.pop('order_detail', False) order_detail = kwargs.pop('order_detail', False)
part_detail = kwargs.pop('part_detail', True) part_detail = kwargs.pop('part_detail', True)
item_detail = kwargs.pop('item_detail', False) item_detail = kwargs.pop('item_detail', True)
location_detail = kwargs.pop('location_detail', False) location_detail = kwargs.pop('location_detail', False)
customer_detail = kwargs.pop('customer_detail', False) customer_detail = kwargs.pop('customer_detail', False)

View File

@ -24,6 +24,7 @@ from common.models import InvenTreeSetting
from company.models import Company, ManufacturerPart, SupplierPart from company.models import Company, ManufacturerPart, SupplierPart
from InvenTree.api import (APIDownloadMixin, AttachmentMixin, from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
ListCreateDestroyAPIView) ListCreateDestroyAPIView)
from InvenTree.filters import InvenTreeOrderingFilter
from InvenTree.helpers import DownloadFile, increment, isNull, str2bool from InvenTree.helpers import DownloadFile, increment, isNull, str2bool
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI, from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI,
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI,
@ -1756,12 +1757,32 @@ class BomList(ListCreateDestroyAPIView):
filter_backends = [ filter_backends = [
DjangoFilterBackend, DjangoFilterBackend,
filters.SearchFilter, filters.SearchFilter,
filters.OrderingFilter, InvenTreeOrderingFilter,
] ]
filterset_fields = [ filterset_fields = [
] ]
search_fields = [
'reference',
'sub_part__name',
'sub_part__description',
'sub_part__IPN',
'sub_part__revision',
'sub_part__keywords',
'sub_part__category__name',
]
ordering_fields = [
'quantity',
'sub_part',
'available_stock',
]
ordering_field_aliases = {
'sub_part': 'sub_part__name',
}
class BomImportUpload(CreateAPI): class BomImportUpload(CreateAPI):
"""API endpoint for uploading a complete Bill of Materials. """API endpoint for uploading a complete Bill of Materials.

View File

@ -24,6 +24,7 @@
part: 100 part: 100
sub_part: 5 sub_part: 5
quantity: 25 quantity: 25
reference: ABCDE
# 3 x Orphan # 3 x Orphan
- model: part.bomitem - model: part.bomitem
@ -32,6 +33,7 @@
part: 100 part: 100
sub_part: 50 sub_part: 50
quantity: 3 quantity: 3
reference: VWXYZ
- model: part.bomitem - model: part.bomitem
pk: 5 pk: 5
@ -39,6 +41,7 @@
part: 1 part: 1
sub_part: 5 sub_part: 5
quantity: 3 quantity: 3
reference: LMNOP
# Make "Assembly" from "Bob" # Make "Assembly" from "Bob"
- model: part.bomitem - model: part.bomitem

View File

@ -1647,6 +1647,102 @@ class BomItemTest(InvenTreeAPITestCase):
for key in ['available_stock', 'available_substitute_stock']: for key in ['available_stock', 'available_substitute_stock']:
self.assertTrue(key in el) self.assertTrue(key in el)
def test_bom_list_search(self):
"""Test that we can search the BOM list API endpoint"""
url = reverse('api-bom-list')
response = self.get(url, expected_code=200)
self.assertEqual(len(response.data), 6)
# Limit the results with a search term
response = self.get(
url,
{
'search': '0805',
},
expected_code=200,
)
self.assertEqual(len(response.data), 3)
# Search by 'reference' field
for q in ['ABCDE', 'LMNOP', 'VWXYZ']:
response = self.get(
url,
{
'search': q,
},
expected_code=200
)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['reference'], q)
# Search by nonsense data
response = self.get(
url,
{
'search': 'xxxxxxxxxxxxxxxxx',
},
expected_code=200
)
self.assertEqual(len(response.data), 0)
def test_bom_list_ordering(self):
"""Test that the BOM list results can be ordered"""
url = reverse('api-bom-list')
# Order by increasing quantity
response = self.get(
f"{url}?ordering=+quantity",
expected_code=200
)
self.assertEqual(len(response.data), 6)
q1 = response.data[0]['quantity']
q2 = response.data[-1]['quantity']
self.assertTrue(q1 < q2)
# Order by decreasing quantity
response = self.get(
f"{url}?ordering=-quantity",
expected_code=200,
)
self.assertEqual(q1, response.data[-1]['quantity'])
self.assertEqual(q2, response.data[0]['quantity'])
# Now test ordering by 'sub_part' (which is actually 'sub_part__name')
response = self.get(
url,
{
'ordering': 'sub_part',
'sub_part_detail': True,
},
expected_code=200,
)
n1 = response.data[0]['sub_part_detail']['name']
n2 = response.data[-1]['sub_part_detail']['name']
response = self.get(
url,
{
'ordering': '-sub_part',
'sub_part_detail': True,
},
expected_code=200,
)
self.assertEqual(n1, response.data[-1]['sub_part_detail']['name'])
self.assertEqual(n2, response.data[0]['sub_part_detail']['name'])
def test_get_bom_detail(self): def test_get_bom_detail(self):
"""Get the detail view for a single BomItem object.""" """Get the detail view for a single BomItem object."""
url = reverse('api-bom-item-detail', kwargs={'pk': 3}) url = reverse('api-bom-item-detail', kwargs={'pk': 3})

View File

@ -3236,10 +3236,12 @@ function showAllocationSubTable(index, row, element, options) {
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var text = ''; var text = '';
if (row.serial != null && row.quantity == 1) { var item = row.item_detail;
text = `{% trans "Serial Number" %}: ${row.serial}`;
} else { var text = `{% trans "Quantity" %}: ${row.quantity}`;
text = `{% trans "Quantity" %}: ${row.quantity}`;
if (item && item.serial != null && row.quantity == 1) {
text = `{% trans "Serial Number" %}: ${item.serial}`;
} }
return renderLink(text, `/stock/item/${row.item}/`); return renderLink(text, `/stock/item/${row.item}/`);

View File

@ -94,9 +94,9 @@ distlib==0.3.4 \
--hash=sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b \ --hash=sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b \
--hash=sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579 --hash=sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579
# via virtualenv # via virtualenv
django==3.2.13 \ django==3.2.14 \
--hash=sha256:6d93497a0a9bf6ba0e0b1a29cccdc40efbfc76297255b1309b3a884a688ec4b6 \ --hash=sha256:677182ba8b5b285a4e072f3ac17ceee6aff1b5ce77fd173cc5b6a2d3dc022fcf \
--hash=sha256:b896ca61edc079eb6bbaa15cf6071eb69d6aac08cce5211583cfb41515644fdf --hash=sha256:a8681e098fa60f7c33a4b628d6fcd3fe983a0939ff1301ecacac21d0b38bad56
# via # via
# -c requirements.txt # -c requirements.txt
# django-debug-toolbar # django-debug-toolbar

View File

@ -1,5 +1,5 @@
# Please keep this list sorted - if you pin a version provide a reason # Please keep this list sorted - if you pin a version provide a reason
Django<4 # Django package Django>=3.2.14,<4 # Django package
coreapi # API documentation for djangorestframework coreapi # API documentation for djangorestframework
cryptography==3.4.8 # Core cryptographic functionality cryptography==3.4.8 # Core cryptographic functionality
django-allauth # SSO for external providers via OpenID django-allauth # SSO for external providers via OpenID

View File

@ -42,7 +42,7 @@ defusedxml==0.7.1
# python3-openid # python3-openid
diff-match-patch==20200713 diff-match-patch==20200713
# via django-import-export # via django-import-export
django==3.2.13 django==3.2.14
# via # via
# -r requirements.in # -r requirements.in
# django-allauth # django-allauth