mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-17 12:35:46 +00:00
Part stocktake (#4069)
* Remove stat context variables
* Revert "Remove stat context variables"
This reverts commit 0989c308d0
.
* Allow longer timeout for image download tests
* Create PartStocktake model
Model for representing stocktake entries against any given part
* Admin interface support for new model
* Adds API endpoint for listing stocktake information
* Bump API version
* Enable filtering and ordering for API endpoint
* Add model to permission group
* Add UI hooks for displaying stocktake data for a particular part
* Fix encoded type for 'quantity' field
* Load stocktake table for part
* Add "stocktake" button
* Add "note" field for stocktake
* Add user information when performing stocktake
* First pass at UI elements for performing stocktake
* Add user information to stocktake table
* Auto-calculate quantity based on available stock items
* add stocktake data as tabular inline (admin)
* js linting
* Add indication that a stock item has not been updated recently
* Display last stocktake information on part page
* js style fix
* Test fix for ongoing CI issues
* Add configurable option for controlling default "delete_on_deplete" behaviour
* Add id values to cells
* Hide action buttons (at least for now)
* Adds refresh button to table
* Add API endpoint to delete or edit stocktake entries
* Adds unit test for API list endpoint
* javascript linting
* More unit testing
* Add Part API filter for stocktake
* Add 'last_stocktake' field to Part model
- Gets filled out automatically when a new PartStocktake instance is created
* Update part table to include last_stocktake date
* Add simple unit test
* Fix test
This commit is contained in:
@ -20,7 +20,7 @@ from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
||||
StockStatus)
|
||||
from part.models import (BomItem, BomItemSubstitute, Part, PartCategory,
|
||||
PartCategoryParameterTemplate, PartParameterTemplate,
|
||||
PartRelated)
|
||||
PartRelated, PartStocktake)
|
||||
from stock.models import StockItem, StockLocation
|
||||
|
||||
|
||||
@ -2779,3 +2779,141 @@ class PartInternalPriceBreakTest(InvenTreeAPITestCase):
|
||||
|
||||
with self.assertRaises(Part.DoesNotExist):
|
||||
p.refresh_from_db()
|
||||
|
||||
|
||||
class PartStocktakeTest(InvenTreeAPITestCase):
|
||||
"""Unit tests for the part stocktake functionality"""
|
||||
|
||||
superuser = False
|
||||
is_staff = False
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
'part',
|
||||
'location',
|
||||
]
|
||||
|
||||
def test_list_endpoint(self):
|
||||
"""Test the list endpoint for the stocktake data"""
|
||||
|
||||
url = reverse('api-part-stocktake-list')
|
||||
|
||||
self.assignRole('part.view')
|
||||
|
||||
# Initially, no stocktake entries
|
||||
response = self.get(url, expected_code=200)
|
||||
|
||||
self.assertEqual(len(response.data), 0)
|
||||
|
||||
total = 0
|
||||
|
||||
# Create some entries
|
||||
for p in Part.objects.all():
|
||||
|
||||
for n in range(p.pk):
|
||||
PartStocktake.objects.create(
|
||||
part=p,
|
||||
quantity=(n + 1) * 100,
|
||||
)
|
||||
|
||||
total += p.pk
|
||||
|
||||
response = self.get(
|
||||
url,
|
||||
{
|
||||
'part': p.pk,
|
||||
},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
# List by part ID
|
||||
self.assertEqual(len(response.data), p.pk)
|
||||
|
||||
# List all entries
|
||||
response = self.get(url, {}, expected_code=200)
|
||||
|
||||
self.assertEqual(len(response.data), total)
|
||||
|
||||
def test_create_stocktake(self):
|
||||
"""Test that stocktake entries can be created via the API"""
|
||||
|
||||
url = reverse('api-part-stocktake-list')
|
||||
|
||||
self.assignRole('part.add')
|
||||
self.assignRole('part.view')
|
||||
|
||||
for p in Part.objects.all():
|
||||
|
||||
# Initially no stocktake information available
|
||||
self.assertIsNone(p.latest_stocktake)
|
||||
|
||||
note = f"Note {p.pk}"
|
||||
quantity = p.pk + 5
|
||||
|
||||
self.post(
|
||||
url,
|
||||
{
|
||||
'part': p.pk,
|
||||
'quantity': quantity,
|
||||
'note': note,
|
||||
},
|
||||
expected_code=201,
|
||||
)
|
||||
|
||||
p.refresh_from_db()
|
||||
stocktake = p.latest_stocktake
|
||||
|
||||
self.assertIsNotNone(stocktake)
|
||||
self.assertEqual(stocktake.quantity, quantity)
|
||||
self.assertEqual(stocktake.part, p)
|
||||
self.assertEqual(stocktake.note, note)
|
||||
|
||||
def test_edit_stocktake(self):
|
||||
"""Test that a Stoctake instance can be edited and deleted via the API.
|
||||
|
||||
Note that only 'staff' users can perform these actions.
|
||||
"""
|
||||
|
||||
p = Part.objects.all().first()
|
||||
|
||||
st = PartStocktake.objects.create(part=p, quantity=10)
|
||||
|
||||
url = reverse('api-part-stocktake-detail', kwargs={'pk': st.pk})
|
||||
self.assignRole('part.view')
|
||||
|
||||
# Test we can retrieve via API
|
||||
self.get(url, expected_code=403)
|
||||
|
||||
# Assign staff permission
|
||||
self.user.is_staff = True
|
||||
self.user.save()
|
||||
|
||||
self.get(url, expected_code=200)
|
||||
|
||||
# Try to edit data
|
||||
self.patch(
|
||||
url,
|
||||
{
|
||||
'note': 'Another edit',
|
||||
},
|
||||
expected_code=403
|
||||
)
|
||||
|
||||
# Assign 'edit' role permission
|
||||
self.assignRole('part.change')
|
||||
|
||||
# Try again
|
||||
self.patch(
|
||||
url,
|
||||
{
|
||||
'note': 'Editing note field again',
|
||||
},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
# Try to delete
|
||||
self.delete(url, expected_code=403)
|
||||
|
||||
self.assignRole('part.delete')
|
||||
|
||||
self.delete(url, expected_code=204)
|
||||
|
Reference in New Issue
Block a user