mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Merge branch 'inventree:master' into matmair/issue2279
This commit is contained in:
		| @@ -315,7 +315,7 @@ def WrapWithQuotes(text, quote='"'): | ||||
|     return text | ||||
|  | ||||
|  | ||||
| def MakeBarcode(object_name, object_pk, object_data={}, **kwargs): | ||||
| def MakeBarcode(object_name, object_pk, object_data=None, **kwargs): | ||||
|     """ Generate a string for a barcode. Adds some global InvenTree parameters. | ||||
|  | ||||
|     Args: | ||||
| @@ -327,6 +327,8 @@ def MakeBarcode(object_name, object_pk, object_data={}, **kwargs): | ||||
|     Returns: | ||||
|         json string of the supplied data plus some other data | ||||
|     """ | ||||
|     if object_data is None: | ||||
|         object_data = {} | ||||
|  | ||||
|     url = kwargs.get('url', False) | ||||
|     brief = kwargs.get('brief', True) | ||||
|   | ||||
| @@ -109,14 +109,14 @@ class BarcodeScan(APIView): | ||||
|         # No plugin is found! | ||||
|         # However, the hash of the barcode may still be associated with a StockItem! | ||||
|         else: | ||||
|             hash = hash_barcode(barcode_data) | ||||
|             result_hash = hash_barcode(barcode_data) | ||||
|  | ||||
|             response['hash'] = hash | ||||
|             response['hash'] = result_hash | ||||
|             response['plugin'] = None | ||||
|  | ||||
|             # Try to look for a matching StockItem | ||||
|             try: | ||||
|                 item = StockItem.objects.get(uid=hash) | ||||
|                 item = StockItem.objects.get(uid=result_hash) | ||||
|                 serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True) | ||||
|                 response['stockitem'] = serializer.data | ||||
|                 response['url'] = reverse('stock-item-detail', kwargs={'pk': item.id}) | ||||
| @@ -182,8 +182,8 @@ class BarcodeAssign(APIView): | ||||
|         # Matching plugin was found | ||||
|         if plugin is not None: | ||||
|  | ||||
|             hash = plugin.hash() | ||||
|             response['hash'] = hash | ||||
|             result_hash = plugin.hash() | ||||
|             response['hash'] = result_hash | ||||
|             response['plugin'] = plugin.name | ||||
|  | ||||
|             # Ensure that the barcode does not already match a database entry | ||||
| @@ -208,14 +208,14 @@ class BarcodeAssign(APIView): | ||||
|                     match_found = True | ||||
|  | ||||
|         else: | ||||
|             hash = hash_barcode(barcode_data) | ||||
|             result_hash = hash_barcode(barcode_data) | ||||
|  | ||||
|             response['hash'] = hash | ||||
|             response['hash'] = result_hash | ||||
|             response['plugin'] = None | ||||
|  | ||||
|             # Lookup stock item by hash | ||||
|             try: | ||||
|                 item = StockItem.objects.get(uid=hash) | ||||
|                 item = StockItem.objects.get(uid=result_hash) | ||||
|                 response['error'] = _('Barcode hash already matches Stock Item') | ||||
|                 match_found = True | ||||
|             except StockItem.DoesNotExist: | ||||
|   | ||||
| @@ -124,12 +124,12 @@ class BarcodeAPITest(APITestCase): | ||||
|  | ||||
|         self.assertIn('success', data) | ||||
|  | ||||
|         hash = data['hash'] | ||||
|         result_hash = data['hash'] | ||||
|  | ||||
|         # Read the item out from the database again | ||||
|         item = StockItem.objects.get(pk=522) | ||||
|  | ||||
|         self.assertEqual(hash, item.uid) | ||||
|         self.assertEqual(result_hash, item.uid) | ||||
|  | ||||
|         # Ensure that the same UID cannot be assigned to a different stock item! | ||||
|         response = self.client.post( | ||||
|   | ||||
| @@ -193,7 +193,7 @@ class BuildOutputCompleteTest(BuildAPITest): | ||||
|         self.assertTrue('accept_unallocated' in response.data) | ||||
|  | ||||
|         # Accept unallocated stock | ||||
|         response = self.post( | ||||
|         self.post( | ||||
|             finish_url, | ||||
|             { | ||||
|                 'accept_unallocated': True, | ||||
|   | ||||
| @@ -67,7 +67,6 @@ class WebhookView(CsrfExemptMixin, APIView): | ||||
|                 message, | ||||
|             ) | ||||
|  | ||||
|         # return results | ||||
|         data = self.webhook.get_return(payload, headers, request) | ||||
|         return HttpResponse(data) | ||||
|  | ||||
|   | ||||
| @@ -9,8 +9,6 @@ import os | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
| from django.core.exceptions import ValidationError | ||||
|  | ||||
| # from company.models import ManufacturerPart, SupplierPart | ||||
|  | ||||
|  | ||||
| class FileManager: | ||||
|     """ Class for managing an uploaded file """ | ||||
|   | ||||
| @@ -1480,11 +1480,9 @@ class WebhookEndpoint(models.Model): | ||||
|  | ||||
|     def process_webhook(self): | ||||
|         if self.token: | ||||
|             self.token = self.token | ||||
|             self.verify = VerificationMethod.TOKEN | ||||
|             # TODO make a object-setting | ||||
|         if self.secret: | ||||
|             self.secret = self.secret | ||||
|             self.verify = VerificationMethod.HMAC | ||||
|             # TODO make a object-setting | ||||
|         return True | ||||
| @@ -1494,6 +1492,7 @@ class WebhookEndpoint(models.Model): | ||||
|  | ||||
|         # no token | ||||
|         if self.verify == VerificationMethod.NONE: | ||||
|             # do nothing as no method was chosen | ||||
|             pass | ||||
|  | ||||
|         # static token | ||||
|   | ||||
| @@ -10,6 +10,8 @@ from django.contrib.auth import get_user_model | ||||
| from .models import InvenTreeSetting, WebhookEndpoint, WebhookMessage, NotificationEntry | ||||
| from .api import WebhookView | ||||
|  | ||||
| CONTENT_TYPE_JSON = 'application/json' | ||||
|  | ||||
|  | ||||
| class SettingsTest(TestCase): | ||||
|     """ | ||||
| @@ -105,7 +107,7 @@ class WebhookMessageTests(TestCase): | ||||
|     def test_missing_token(self): | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|         ) | ||||
|  | ||||
|         assert response.status_code == HTTPStatus.FORBIDDEN | ||||
| @@ -116,7 +118,7 @@ class WebhookMessageTests(TestCase): | ||||
|     def test_bad_token(self): | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|             **{'HTTP_TOKEN': '1234567fghj'}, | ||||
|         ) | ||||
|  | ||||
| @@ -126,7 +128,7 @@ class WebhookMessageTests(TestCase): | ||||
|     def test_bad_url(self): | ||||
|         response = self.client.post( | ||||
|             '/api/webhook/1234/', | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|         ) | ||||
|  | ||||
|         assert response.status_code == HTTPStatus.NOT_FOUND | ||||
| @@ -135,7 +137,7 @@ class WebhookMessageTests(TestCase): | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             data="{'this': 123}", | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|             **{'HTTP_TOKEN': str(self.endpoint_def.token)}, | ||||
|         ) | ||||
|  | ||||
| @@ -152,7 +154,7 @@ class WebhookMessageTests(TestCase): | ||||
|         # check | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|         ) | ||||
|  | ||||
|         assert response.status_code == HTTPStatus.OK | ||||
| @@ -167,7 +169,7 @@ class WebhookMessageTests(TestCase): | ||||
|         # check | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|         ) | ||||
|  | ||||
|         assert response.status_code == HTTPStatus.FORBIDDEN | ||||
| @@ -182,7 +184,7 @@ class WebhookMessageTests(TestCase): | ||||
|         # check | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|             **{'HTTP_TOKEN': str('68MXtc/OiXdA5e2Nq9hATEVrZFpLb3Zb0oau7n8s31I=')}, | ||||
|         ) | ||||
|  | ||||
| @@ -193,7 +195,7 @@ class WebhookMessageTests(TestCase): | ||||
|         response = self.client.post( | ||||
|             self.url, | ||||
|             data={"this": "is a message"}, | ||||
|             content_type='application/json', | ||||
|             content_type=CONTENT_TYPE_JSON, | ||||
|             **{'HTTP_TOKEN': str(self.endpoint_def.token)}, | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -92,5 +92,4 @@ class OrderMatchItemForm(MatchItemForm): | ||||
|                 default_amount=clean_decimal(row.get('purchase_price', '')), | ||||
|             ) | ||||
|  | ||||
|         # return default | ||||
|         return super().get_special_field(col_guess, row, file_manager) | ||||
|   | ||||
| @@ -446,10 +446,10 @@ class PartSerialNumberDetail(generics.RetrieveAPIView): | ||||
|         } | ||||
|  | ||||
|         if latest is not None: | ||||
|             next = increment(latest) | ||||
|             next_serial = increment(latest) | ||||
|  | ||||
|             if next != increment: | ||||
|                 data['next'] = next | ||||
|             if next_serial != increment: | ||||
|                 data['next'] = next_serial | ||||
|  | ||||
|         return Response(data) | ||||
|  | ||||
|   | ||||
| @@ -75,7 +75,6 @@ class BomMatchItemForm(MatchItemForm): | ||||
|                 }) | ||||
|             ) | ||||
|  | ||||
|         # return default | ||||
|         return super().get_special_field(col_guess, row, file_manager) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1530,15 +1530,15 @@ class Part(MPTTModel): | ||||
|         returns a string representation of a hash object which can be compared with a stored value | ||||
|         """ | ||||
|  | ||||
|         hash = hashlib.md5(str(self.id).encode()) | ||||
|         result_hash = hashlib.md5(str(self.id).encode()) | ||||
|  | ||||
|         # List *all* BOM items (including inherited ones!) | ||||
|         bom_items = self.get_bom_items().all().prefetch_related('sub_part') | ||||
|  | ||||
|         for item in bom_items: | ||||
|             hash.update(str(item.get_item_hash()).encode()) | ||||
|             result_hash.update(str(item.get_item_hash()).encode()) | ||||
|  | ||||
|         return str(hash.digest()) | ||||
|         return str(result_hash.digest()) | ||||
|  | ||||
|     def is_bom_valid(self): | ||||
|         """ Check if the BOM is 'valid' - if the calculated checksum matches the stored value | ||||
| @@ -2188,9 +2188,7 @@ def after_save_part(sender, instance: Part, created, **kwargs): | ||||
|     Function to be executed after a Part is saved | ||||
|     """ | ||||
|  | ||||
|     if created: | ||||
|         pass | ||||
|     else: | ||||
|     if not created: | ||||
|         # Check part stock only if we are *updating* the part (not creating it) | ||||
|  | ||||
|         # Run this check in the background | ||||
| @@ -2678,18 +2676,18 @@ class BomItem(models.Model): | ||||
|         """ | ||||
|  | ||||
|         # Seed the hash with the ID of this BOM item | ||||
|         hash = hashlib.md5(str(self.id).encode()) | ||||
|         result_hash = hashlib.md5(str(self.id).encode()) | ||||
|  | ||||
|         # Update the hash based on line information | ||||
|         hash.update(str(self.sub_part.id).encode()) | ||||
|         hash.update(str(self.sub_part.full_name).encode()) | ||||
|         hash.update(str(self.quantity).encode()) | ||||
|         hash.update(str(self.note).encode()) | ||||
|         hash.update(str(self.reference).encode()) | ||||
|         hash.update(str(self.optional).encode()) | ||||
|         hash.update(str(self.inherited).encode()) | ||||
|         result_hash.update(str(self.sub_part.id).encode()) | ||||
|         result_hash.update(str(self.sub_part.full_name).encode()) | ||||
|         result_hash.update(str(self.quantity).encode()) | ||||
|         result_hash.update(str(self.note).encode()) | ||||
|         result_hash.update(str(self.reference).encode()) | ||||
|         result_hash.update(str(self.optional).encode()) | ||||
|         result_hash.update(str(self.inherited).encode()) | ||||
|  | ||||
|         return str(hash.digest()) | ||||
|         return str(result_hash.digest()) | ||||
|  | ||||
|     def validate_hash(self, valid=True): | ||||
|         """ Mark this item as 'valid' (store the checksum hash). | ||||
|   | ||||
| @@ -293,7 +293,7 @@ def progress_bar(val, max, *args, **kwargs): | ||||
|     Render a progress bar element | ||||
|     """ | ||||
|  | ||||
|     id = kwargs.get('id', 'progress-bar') | ||||
|     item_id = kwargs.get('id', 'progress-bar') | ||||
|  | ||||
|     if val > max: | ||||
|         style = 'progress-bar-over' | ||||
| @@ -317,7 +317,7 @@ def progress_bar(val, max, *args, **kwargs): | ||||
|         style_tags.append(f'max-width: {max_width};') | ||||
|  | ||||
|     html = f""" | ||||
|     <div id='{id}' class='progress' style='{" ".join(style_tags)}'> | ||||
|     <div id='{item_id}' class='progress' style='{" ".join(style_tags)}'> | ||||
|         <div class='progress-bar {style}' role='progressbar' aria-valuemin='0' aria-valuemax='100' style='width:{percent}%'></div> | ||||
|         <div class='progress-value'>{val} / {max}</div> | ||||
|     </div> | ||||
|   | ||||
| @@ -31,8 +31,8 @@ class TemplateTagTest(TestCase): | ||||
|         self.assertEqual(type(inventree_extras.inventree_version()), str) | ||||
|  | ||||
|     def test_hash(self): | ||||
|         hash = inventree_extras.inventree_commit_hash() | ||||
|         self.assertGreater(len(hash), 5) | ||||
|         result_hash = inventree_extras.inventree_commit_hash() | ||||
|         self.assertGreater(len(result_hash), 5) | ||||
|  | ||||
|     def test_date(self): | ||||
|         d = inventree_extras.inventree_commit_date() | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| from django.test import TestCase | ||||
|  | ||||
|  | ||||
| class SupplierPartTest(TestCase): | ||||
|  | ||||
|     def setUp(self): | ||||
|         pass | ||||
| @@ -25,8 +25,8 @@ def hash_barcode(barcode_data): | ||||
|  | ||||
|     barcode_data = ''.join(list(printable_chars)) | ||||
|  | ||||
|     hash = hashlib.md5(str(barcode_data).encode()) | ||||
|     return str(hash.hexdigest()) | ||||
|     result_hash = hashlib.md5(str(barcode_data).encode()) | ||||
|     return str(result_hash.hexdigest()) | ||||
|  | ||||
|  | ||||
| class BarcodeMixin: | ||||
|   | ||||
| @@ -173,8 +173,8 @@ class IntegrationPluginBase(MixinBase, plugin_base.InvenTreePluginBase): | ||||
|         """ | ||||
|         License of plugin | ||||
|         """ | ||||
|         license = getattr(self, 'LICENSE', None) | ||||
|         return license | ||||
|         lic = getattr(self, 'LICENSE', None) | ||||
|         return lic | ||||
|     # endregion | ||||
|  | ||||
|     @property | ||||
|   | ||||
| @@ -94,10 +94,8 @@ class PluginConfig(models.Model): | ||||
|         ret = super().save(force_insert, force_update, *args, **kwargs) | ||||
|  | ||||
|         if not reload: | ||||
|             if self.active is False and self.__org_active is True: | ||||
|                 registry.reload_plugins() | ||||
|  | ||||
|             elif self.active is True and self.__org_active is False: | ||||
|             if (self.active is False and self.__org_active is True) or \ | ||||
|                (self.active is True and self.__org_active is False): | ||||
|                 registry.reload_plugins() | ||||
|  | ||||
|         return ret | ||||
|   | ||||
| @@ -390,6 +390,10 @@ class PluginsRegistry: | ||||
|             logger.warning("activate_integration_schedule failed, database not ready") | ||||
|  | ||||
|     def deactivate_integration_schedule(self): | ||||
|         """ | ||||
|         Deactivate ScheduleMixin | ||||
|         currently nothing is done | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def activate_integration_app(self, plugins, force_reload=False): | ||||
|   | ||||
| @@ -1022,7 +1022,7 @@ class StockItem(MPTTModel): | ||||
|     def has_tracking_info(self): | ||||
|         return self.tracking_info_count > 0 | ||||
|  | ||||
|     def add_tracking_entry(self, entry_type, user, deltas={}, notes='', **kwargs): | ||||
|     def add_tracking_entry(self, entry_type, user, deltas=None, notes='', **kwargs): | ||||
|         """ | ||||
|         Add a history tracking entry for this StockItem | ||||
|  | ||||
| @@ -1033,6 +1033,8 @@ class StockItem(MPTTModel): | ||||
|             notes - User notes associated with this tracking entry | ||||
|             url - Optional URL associated with this tracking entry | ||||
|         """ | ||||
|         if deltas is None: | ||||
|             deltas = {} | ||||
|  | ||||
|         # Has a location been specified? | ||||
|         location = kwargs.get('location', None) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user