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:
@ -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,
|
||||
},
|
||||
|
@ -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.
|
||||
|
@ -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."""
|
||||
|
Reference in New Issue
Block a user