mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Merge pull request #1707 from SchrodingersGat/order-api-fixes
Fixes for order API interface
This commit is contained in:
		| @@ -73,22 +73,50 @@ class InvenTreeAPITestCase(APITestCase): | |||||||
|                 ruleset.save() |                 ruleset.save() | ||||||
|                 break |                 break | ||||||
|  |  | ||||||
|     def get(self, url, data={}, code=200): |     def get(self, url, data={}, expected_code=200): | ||||||
|         """ |         """ | ||||||
|         Issue a GET request |         Issue a GET request | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         response = self.client.get(url, data, format='json') |         response = self.client.get(url, data, format='json') | ||||||
|  |  | ||||||
|         self.assertEqual(response.status_code, code) |         if expected_code is not None: | ||||||
|  |             self.assertEqual(response.status_code, expected_code) | ||||||
|  |  | ||||||
|         return response |         return response | ||||||
|  |  | ||||||
|     def post(self, url, data): |     def post(self, url, data, expected_code=None): | ||||||
|         """ |         """ | ||||||
|         Issue a POST request |         Issue a POST request | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         response = self.client.post(url, data=data, format='json') |         response = self.client.post(url, data=data, format='json') | ||||||
|  |  | ||||||
|  |         if expected_code is not None: | ||||||
|  |             self.assertEqual(response.status_code, expected_code) | ||||||
|  |  | ||||||
|  |         return response | ||||||
|  |  | ||||||
|  |     def delete(self, url, expected_code=None): | ||||||
|  |         """ | ||||||
|  |         Issue a DELETE request | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         response = self.client.delete(url) | ||||||
|  |  | ||||||
|  |         if expected_code is not None: | ||||||
|  |             self.assertEqual(response.status_code, expected_code) | ||||||
|  |  | ||||||
|  |         return response | ||||||
|  |  | ||||||
|  |     def patch(self, url, data, expected_code=None): | ||||||
|  |         """ | ||||||
|  |         Issue a PATCH request | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         response = self.client.patch(url, data=data, format='json') | ||||||
|  |  | ||||||
|  |         if expected_code is not None: | ||||||
|  |             self.assertEqual(response.status_code, expected_code) | ||||||
|  |  | ||||||
|         return response |         return response | ||||||
|   | |||||||
| @@ -157,7 +157,7 @@ class POList(generics.ListCreateAPIView): | |||||||
|     ordering = '-creation_date' |     ordering = '-creation_date' | ||||||
|  |  | ||||||
|  |  | ||||||
| class PODetail(generics.RetrieveUpdateAPIView): | class PODetail(generics.RetrieveUpdateDestroyAPIView): | ||||||
|     """ API endpoint for detail view of a PurchaseOrder object """ |     """ API endpoint for detail view of a PurchaseOrder object """ | ||||||
|  |  | ||||||
|     queryset = PurchaseOrder.objects.all() |     queryset = PurchaseOrder.objects.all() | ||||||
| @@ -382,7 +382,7 @@ class SOList(generics.ListCreateAPIView): | |||||||
|     ordering = '-creation_date' |     ordering = '-creation_date' | ||||||
|  |  | ||||||
|  |  | ||||||
| class SODetail(generics.RetrieveUpdateAPIView): | class SODetail(generics.RetrieveUpdateDestroyAPIView): | ||||||
|     """ |     """ | ||||||
|     API endpoint for detail view of a SalesOrder object. |     API endpoint for detail view of a SalesOrder object. | ||||||
|     """ |     """ | ||||||
|   | |||||||
| @@ -93,8 +93,10 @@ class POSerializer(InvenTreeModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|         read_only_fields = [ |         read_only_fields = [ | ||||||
|             'reference', |  | ||||||
|             'status' |             'status' | ||||||
|  |             'issue_date', | ||||||
|  |             'complete_date', | ||||||
|  |             'creation_date', | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -110,8 +112,9 @@ class POLineItemSerializer(InvenTreeModelSerializer): | |||||||
|             self.fields.pop('part_detail') |             self.fields.pop('part_detail') | ||||||
|             self.fields.pop('supplier_part_detail') |             self.fields.pop('supplier_part_detail') | ||||||
|  |  | ||||||
|     quantity = serializers.FloatField() |     # TODO: Once https://github.com/inventree/InvenTree/issues/1687 is fixed, remove default values | ||||||
|     received = serializers.FloatField() |     quantity = serializers.FloatField(default=1) | ||||||
|  |     received = serializers.FloatField(default=0) | ||||||
|  |  | ||||||
|     part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True) |     part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True) | ||||||
|     supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True) |     supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True) | ||||||
| @@ -226,8 +229,9 @@ class SalesOrderSerializer(InvenTreeModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|         read_only_fields = [ |         read_only_fields = [ | ||||||
|             'reference', |             'status', | ||||||
|             'status' |             'creation_date', | ||||||
|  |             'shipment_date', | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -313,7 +317,9 @@ class SOLineItemSerializer(InvenTreeModelSerializer): | |||||||
|     part_detail = PartBriefSerializer(source='part', many=False, read_only=True) |     part_detail = PartBriefSerializer(source='part', many=False, read_only=True) | ||||||
|     allocations = SalesOrderAllocationSerializer(many=True, read_only=True) |     allocations = SalesOrderAllocationSerializer(many=True, read_only=True) | ||||||
|  |  | ||||||
|     quantity = serializers.FloatField() |     # TODO: Once https://github.com/inventree/InvenTree/issues/1687 is fixed, remove default values | ||||||
|  |     quantity = serializers.FloatField(default=1) | ||||||
|  |  | ||||||
|     allocated = serializers.FloatField(source='allocated_quantity', read_only=True) |     allocated = serializers.FloatField(source='allocated_quantity', read_only=True) | ||||||
|     fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True) |     fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True) | ||||||
|     sale_price_string = serializers.CharField(source='sale_price', read_only=True) |     sale_price_string = serializers.CharField(source='sale_price', read_only=True) | ||||||
|   | |||||||
| @@ -110,6 +110,96 @@ class PurchaseOrderTest(OrderTest): | |||||||
|  |  | ||||||
|         self.assertEqual(response.status_code, status.HTTP_200_OK) |         self.assertEqual(response.status_code, status.HTTP_200_OK) | ||||||
|  |  | ||||||
|  |     def test_po_operations(self): | ||||||
|  |         """ | ||||||
|  |         Test that we can create / edit and delete a PurchaseOrder via the API | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         n = PurchaseOrder.objects.count() | ||||||
|  |  | ||||||
|  |         url = reverse('api-po-list') | ||||||
|  |  | ||||||
|  |         # Initially we do not have "add" permission for the PurchaseOrder model, | ||||||
|  |         # so this POST request should return 403 | ||||||
|  |         response = self.post( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'supplier': 1, | ||||||
|  |                 'reference': '123456789-xyz', | ||||||
|  |                 'description': 'PO created via the API', | ||||||
|  |             }, | ||||||
|  |             expected_code=403 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         # And no new PurchaseOrder objects should have been created | ||||||
|  |         self.assertEqual(PurchaseOrder.objects.count(), n) | ||||||
|  |  | ||||||
|  |         # Ok, now let's give this user the correct permission | ||||||
|  |         self.assignRole('purchase_order.add') | ||||||
|  |  | ||||||
|  |         # Initially we do not have "add" permission for the PurchaseOrder model, | ||||||
|  |         # so this POST request should return 403 | ||||||
|  |         response = self.post( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'supplier': 1, | ||||||
|  |                 'reference': '123456789-xyz', | ||||||
|  |                 'description': 'PO created via the API', | ||||||
|  |             }, | ||||||
|  |             expected_code=201 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.assertEqual(PurchaseOrder.objects.count(), n + 1) | ||||||
|  |  | ||||||
|  |         pk = response.data['pk'] | ||||||
|  |  | ||||||
|  |         # Try to create a PO with identical reference (should fail!) | ||||||
|  |         response = self.post( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'supplier': 1, | ||||||
|  |                 'reference': '123456789-xyz', | ||||||
|  |                 'description': 'A different description', | ||||||
|  |             }, | ||||||
|  |             expected_code=400 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.assertEqual(PurchaseOrder.objects.count(), n + 1) | ||||||
|  |  | ||||||
|  |         url = reverse('api-po-detail', kwargs={'pk': pk}) | ||||||
|  |  | ||||||
|  |         # Get detail info! | ||||||
|  |         response = self.get(url) | ||||||
|  |         self.assertEqual(response.data['pk'], pk) | ||||||
|  |         self.assertEqual(response.data['reference'], '123456789-xyz') | ||||||
|  |  | ||||||
|  |         # Try to alter (edit) the PurchaseOrder | ||||||
|  |         response = self.patch( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'reference': '12345-abc', | ||||||
|  |             }, | ||||||
|  |             expected_code=200 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         # Reference should have changed | ||||||
|  |         self.assertEqual(response.data['reference'], '12345-abc') | ||||||
|  |  | ||||||
|  |         # Now, let's try to delete it! | ||||||
|  |         # Initially, we do *not* have the required permission! | ||||||
|  |         response = self.delete(url, expected_code=403) | ||||||
|  |  | ||||||
|  |         # Now, add the "delete" permission! | ||||||
|  |         self.assignRole("purchase_order.delete") | ||||||
|  |  | ||||||
|  |         response = self.delete(url, expected_code=204) | ||||||
|  |  | ||||||
|  |         # Number of PurchaseOrder objects should have decreased | ||||||
|  |         self.assertEqual(PurchaseOrder.objects.count(), n) | ||||||
|  |  | ||||||
|  |         # And if we try to access the detail view again, it has gone | ||||||
|  |         response = self.get(url, expected_code=404) | ||||||
|  |  | ||||||
|  |  | ||||||
| class SalesOrderTest(OrderTest): | class SalesOrderTest(OrderTest): | ||||||
|     """ |     """ | ||||||
| @@ -158,8 +248,6 @@ class SalesOrderTest(OrderTest): | |||||||
|  |  | ||||||
|         response = self.get(url) |         response = self.get(url) | ||||||
|  |  | ||||||
|         self.assertEqual(response.status_code, 200) |  | ||||||
|  |  | ||||||
|         data = response.data |         data = response.data | ||||||
|  |  | ||||||
|         self.assertEqual(data['pk'], 1) |         self.assertEqual(data['pk'], 1) | ||||||
| @@ -168,6 +256,87 @@ class SalesOrderTest(OrderTest): | |||||||
|  |  | ||||||
|         url = reverse('api-so-attachment-list') |         url = reverse('api-so-attachment-list') | ||||||
|  |  | ||||||
|         response = self.get(url) |         self.get(url) | ||||||
|  |  | ||||||
|         self.assertEqual(response.status_code, status.HTTP_200_OK) |     def test_so_operations(self): | ||||||
|  |         """ | ||||||
|  |         Test that we can create / edit and delete a SalesOrder via the API | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         n = SalesOrder.objects.count() | ||||||
|  |  | ||||||
|  |         url = reverse('api-so-list') | ||||||
|  |  | ||||||
|  |         # Initially we do not have "add" permission for the SalesOrder model, | ||||||
|  |         # so this POST request should return 403 (denied) | ||||||
|  |         response = self.post( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'customer': 4, | ||||||
|  |                 'reference': '12345', | ||||||
|  |                 'description': 'Sales order', | ||||||
|  |             }, | ||||||
|  |             expected_code=403, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.assignRole('sales_order.add') | ||||||
|  |  | ||||||
|  |         # Now we should be able to create a SalesOrder via the API | ||||||
|  |         response = self.post( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'customer': 4, | ||||||
|  |                 'reference': '12345', | ||||||
|  |                 'description': 'Sales order', | ||||||
|  |             }, | ||||||
|  |             expected_code=201 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         # Check that the new order has been created | ||||||
|  |         self.assertEqual(SalesOrder.objects.count(), n + 1) | ||||||
|  |  | ||||||
|  |         # Grab the PK for the newly created SalesOrder | ||||||
|  |         pk = response.data['pk'] | ||||||
|  |  | ||||||
|  |         # Try to create a SO with identical reference (should fail) | ||||||
|  |         response = self.post( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'customer': 4, | ||||||
|  |                 'reference': '12345', | ||||||
|  |                 'description': 'Another sales order', | ||||||
|  |             }, | ||||||
|  |             expected_code=400 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         url = reverse('api-so-detail', kwargs={'pk': pk}) | ||||||
|  |  | ||||||
|  |         # Extract detail info for the SalesOrder | ||||||
|  |         response = self.get(url) | ||||||
|  |         self.assertEqual(response.data['reference'], '12345') | ||||||
|  |  | ||||||
|  |         # Try to alter (edit) the SalesOrder | ||||||
|  |         response = self.patch( | ||||||
|  |             url, | ||||||
|  |             { | ||||||
|  |                 'reference': '12345-a', | ||||||
|  |             }, | ||||||
|  |             expected_code=200 | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         # Reference should have changed | ||||||
|  |         self.assertEqual(response.data['reference'], '12345-a') | ||||||
|  |  | ||||||
|  |         # Now, let's try to delete this SalesOrder | ||||||
|  |         # Initially, we do not have the required permission | ||||||
|  |         response = self.delete(url, expected_code=403) | ||||||
|  |  | ||||||
|  |         self.assignRole('sales_order.delete') | ||||||
|  |  | ||||||
|  |         response = self.delete(url, expected_code=204) | ||||||
|  |  | ||||||
|  |         # Check that the number of sales orders has decreased | ||||||
|  |         self.assertEqual(SalesOrder.objects.count(), n) | ||||||
|  |  | ||||||
|  |         # And the resource should no longer be available | ||||||
|  |         response = self.get(url, expected_code=404) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user