2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-19 05:25:42 +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

@ -84,7 +84,7 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
RuleSetInline,
]
list_display = ('name', 'admin', 'part_category', 'part', 'stock_location',
list_display = ('name', 'admin', 'part_category', 'part', 'stocktake', 'stock_location',
'stock_item', 'build', 'purchase_order', 'sales_order')
def get_rule_set(self, obj, rule_set_type):
@ -137,6 +137,10 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
"""Return the ruleset for the Part role"""
return self.get_rule_set(obj, 'part')
def stocktake(self, obj):
"""Return the ruleset for the Stocktake role"""
return self.get_rule_set(obj, 'stocktake')
def stock_location(self, obj):
"""Return the ruleset for the StockLocation role"""
return self.get_rule_set(obj, 'stock_location')

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-02-16 22:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0005_owner_model'),
]
operations = [
migrations.AlterField(
model_name='ruleset',
name='name',
field=models.CharField(choices=[('admin', 'Admin'), ('part_category', 'Part Categories'), ('part', 'Parts'), ('stocktake', 'Stocktake'), ('stock_location', 'Stock Locations'), ('stock', 'Stock Items'), ('build', 'Build Orders'), ('purchase_order', 'Purchase Orders'), ('sales_order', 'Sales Orders')], help_text='Permission set', max_length=50),
),
]

View File

@ -21,7 +21,7 @@ logger = logging.getLogger("inventree")
class RuleSet(models.Model):
"""A RuleSet is somewhat like a superset of the django permission class, in that in encapsulates a bunch of permissions.
"""A RuleSet is somewhat like a superset of the django permission class, in that in encapsulates a bunch of permissions.
There are *many* apps models used within InvenTree,
so it makes sense to group them into "roles".
@ -36,6 +36,7 @@ class RuleSet(models.Model):
('admin', _('Admin')),
('part_category', _('Part Categories')),
('part', _('Parts')),
('stocktake', _('Stocktake')),
('stock_location', _('Stock Locations')),
('stock', _('Stock Items')),
('build', _('Build Orders')),
@ -97,13 +98,16 @@ class RuleSet(models.Model):
'part_partrelated',
'part_partstar',
'part_partcategorystar',
'part_partstocktake',
'company_supplierpart',
'company_manufacturerpart',
'company_manufacturerpartparameter',
'company_manufacturerpartattachment',
'label_partlabel',
],
'stocktake': [
'part_partstocktake',
'part_partstocktakereport',
],
'stock_location': [
'stock_stocklocation',
'label_stocklocationlabel',
@ -467,13 +471,13 @@ def update_group_roles(group, debug=False):
# Enable all action permissions for certain children models
# if parent model has 'change' permission
for (parent, child) in RuleSet.RULESET_CHANGE_INHERIT:
parent_change_perm = f'{parent}.change_{parent}'
parent_child_string = f'{parent}_{child}'
# Check if parent change permission exists
if parent_change_perm in group_permissions:
# Add child model permissions
for action in ['add', 'change', 'delete']:
# Check each type of permission
for action in ['view', 'change', 'add', 'delete']:
parent_perm = f'{parent}.{action}_{parent}'
if parent_perm in group_permissions:
child_perm = f'{parent}.{action}_{child}'
# Check if child permission not already in group

View File

@ -126,6 +126,7 @@ class RuleSetModelTest(TestCase):
# Add some more rules
for rule in rulesets:
rule.can_view = True
rule.can_add = True
rule.can_change = True