2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-02 11:40:58 +00:00

[Feature] Stocktake reports (#4345)

* Add settings to control upcoming stocktake features

* Adds migration for "cost range" when performing stocktake

* Add cost data to PartStocktakeSerializer

Implement a new custom serializer for currency data type

* Refactor existing currency serializers

* Update stocktake table and forms

* Prevent trailing zeroes in forms

* Calculate cost range when adding manual stocktake entry

* Display interactive chart for part stocktake history

* Ensure chart data are converted to common currency

* Adds new model for building stocktake reports

* Add admin integration for new model

* Adds API endpoint to expose list of stocktake reports available for download

- No ability to edit or delete via API

* Add setting to control automated deletion of old stocktake reports

* Updates for settings page

- Load part stocktake report table
- Refactor function to render a downloadable media file
- Fix bug with forcing files to be downloaded
- Split js code into separate templates
- Make use of onPanelLoad functionalitty

* Fix conflicting migration files

* Adds API endpoint for manual generation of stocktake report

* Offload task to generate new stocktake report

* Adds python function to perform stocktake on a single part instance

* Small bug fixes

* Various tweaks

- Prevent new stocktake models from triggering plugin events when created
- Construct a simple csv dataset

* Generate new report

* Updates for report generation

- Prefetch related data
- Add extra columns
- Keep track of stocktake instances (for saving to database later on)

* Updates:

- Add confirmation message
- Serializer validation checks

* Ensure that background worker is running before manually scheduling a new stocktake report

* Add extra fields to stocktake models

Also move code from part/models.py to part/tasks.py

* Add 'part_count' to PartStocktakeReport table

* Updates for stocktake generation

- remove old performStocktake javascript code
- Now handled by automated server-side calculation
- Generate report for a single part

* Add a new "role" for stocktake

- Allows fine-grained control on viewing / creating / deleting stocktake data
- More in-line with existing permission controls
- Remove STOCKTAKE_OWNER setting

* Add serializer field to limit stocktake report to particular locations

* Use location restriction when generating a stocktake report

* Add UI buttons to perform stocktake for a whole category tree

* Add button to perform stocktake report for a location tree

* Adds a background tasks to handle periodic generation of stocktake reports

- Reports are generated at fixed intervals
- Deletes old reports after certain number of days

* Implement notifications for new stocktake reports

- If manually requested by a user, notify that user
- Cleanup notification table
- Amend PartStocktakeModel for better notification rendering

* Hide buttons on location and category page if stocktake is not enabled

* Cleanup log messages during server start

* Extend functionality of RoleRequired permission mixin

- Allow 'role_required' attribute to be added to an API view
- Useful when using a serializer class that does not have a model defined

* Add boolean option to toggle whether a report will be generated

* Update generateStocktake function

* Improve location filtering

- Don't limit the actual stock items
- Instead, select only parts which exist within a given location tree

* Update API version

* String tweaks

* Fix permissions for PartStocktake API

* More unit testing for stocktake functionality

* QoL fix

* Fix for assigning inherited permissions
This commit is contained in:
Oliver
2023-02-17 11:42:48 +11:00
committed by GitHub
parent e6c9db2ff3
commit 0f445ea6e4
45 changed files with 1700 additions and 713 deletions

View File

@ -1568,6 +1568,35 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'validator': bool,
'requires_restart': True,
},
'STOCKTAKE_ENABLE': {
'name': _('Stocktake Functionality'),
'description': _('Enable stocktake functionality for recording stock levels and calculating stock value'),
'validator': bool,
'default': False,
},
'STOCKTAKE_AUTO_DAYS': {
'name': _('Automatic Stocktake Period'),
'description': _('Number of days between automatic stocktake recording (set to zero to disable)'),
'validator': [
int,
MinValueValidator(0),
],
'default': 0,
},
'STOCKTAKE_DELETE_REPORT_DAYS': {
'name': _('Delete Old Reports'),
'description': _('Stocktake reports will be deleted after specified number of days'),
'default': 30,
'units': 'days',
'validator': [
int,
MinValueValidator(7),
]
},
}
typ = 'inventree'
@ -1900,7 +1929,7 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
'DISPLAY_STOCKTAKE_TAB': {
'name': _('Part Stocktake'),
'description': _('Display part stocktake information'),
'description': _('Display part stocktake information (if stocktake functionality is enabled)'),
'default': True,
'validator': bool,
},

View File

@ -180,7 +180,7 @@ class MethodStorageClass:
Args:
selected_classes (class, optional): References to the classes that should be registered. Defaults to None.
"""
logger.info('collecting notification methods')
logger.debug('Collecting notification methods')
current_method = InvenTree.helpers.inheritors(NotificationMethod) - IGNORED_NOTIFICATION_CLS
# for testing selective loading is made available
@ -196,7 +196,7 @@ class MethodStorageClass:
filtered_list[ref] = item
storage.liste = list(filtered_list.values())
logger.info(f'found {len(storage.liste)} notification methods')
logger.info(f'Found {len(storage.liste)} notification methods')
def get_usersettings(self, user) -> list:
"""Returns all user settings for a specific user.

View File

@ -141,27 +141,13 @@ class NotificationMessageSerializer(InvenTreeModelSerializer):
"""Serializer for the InvenTreeUserSetting model."""
target = serializers.SerializerMethodField(read_only=True)
source = serializers.SerializerMethodField(read_only=True)
user = serializers.PrimaryKeyRelatedField(read_only=True)
category = serializers.CharField(read_only=True)
name = serializers.CharField(read_only=True)
message = serializers.CharField(read_only=True)
creation = serializers.CharField(read_only=True)
age = serializers.IntegerField(read_only=True)
age_human = serializers.CharField(read_only=True)
read = serializers.BooleanField()
def get_target(self, obj):
"""Function to resolve generic object reference to target."""
target = get_objectreference(obj, 'target_content_type', 'target_object_id')
if target and 'link' not in target:
@ -202,6 +188,15 @@ class NotificationMessageSerializer(InvenTreeModelSerializer):
'read',
]
read_only_fields = [
'category',
'name',
'message',
'creation',
'age',
'age_human',
]
class NewsFeedEntrySerializer(InvenTreeModelSerializer):
"""Serializer for the NewsFeedEntry model."""