mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 20:16:44 +00:00
Part responsible owner (#5774)
* Add "responsible_owner" field to part model - Will replace "responsible" field * Data migration - Adds 'responsible_owner' value for parts which have 'responsible' set - Selects correct content type - Performs reverse migratoin * Update part serializer - Point to the new field - Rename to preserve compatibility - OPTIONS metadata will take care of the rest * Remove old 'responsible' field * Bump API version * Fix typo * Fix serializer field
This commit is contained in:
parent
2dfe2d97bc
commit
39c499622d
@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 140
|
INVENTREE_API_VERSION = 141
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
||||||
|
|
||||||
|
v141 -> 2023-10-23 : https://github.com/inventree/InvenTree/pull/5774
|
||||||
|
- Changed 'part.responsible' from User to Owner
|
||||||
|
|
||||||
v140 -> 2023-10-20 : https://github.com/inventree/InvenTree/pull/5664
|
v140 -> 2023-10-20 : https://github.com/inventree/InvenTree/pull/5664
|
||||||
- Expand API token functionality
|
- Expand API token functionality
|
||||||
- Multiple API tokens can be generated per user
|
- Multiple API tokens can be generated per user
|
||||||
|
@ -96,7 +96,7 @@ for provider in providers.registry.get_list():
|
|||||||
social_auth_urlpatterns += provider_urlpatterns
|
social_auth_urlpatterns += provider_urlpatterns
|
||||||
|
|
||||||
|
|
||||||
class SocialProvierListView(ListAPIView):
|
class SocialProviderListView(ListAPIView):
|
||||||
"""List of available social providers."""
|
"""List of available social providers."""
|
||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ from web.urls import urlpatterns as platform_urls
|
|||||||
|
|
||||||
from .api import APISearchView, InfoView, NotFoundView
|
from .api import APISearchView, InfoView, NotFoundView
|
||||||
from .magic_login import GetSimpleLoginView
|
from .magic_login import GetSimpleLoginView
|
||||||
from .social_auth_urls import SocialProvierListView, social_auth_urlpatterns
|
from .social_auth_urls import SocialProviderListView, social_auth_urlpatterns
|
||||||
from .views import (AboutView, AppearanceSelectView, CustomConnectionsView,
|
from .views import (AboutView, AppearanceSelectView, CustomConnectionsView,
|
||||||
CustomEmailView, CustomLoginView,
|
CustomEmailView, CustomLoginView,
|
||||||
CustomPasswordResetFromKeyView,
|
CustomPasswordResetFromKeyView,
|
||||||
@ -83,7 +83,7 @@ apipatterns = [
|
|||||||
path('auth/', include([
|
path('auth/', include([
|
||||||
re_path(r'^registration/account-confirm-email/(?P<key>[-:\w]+)/$', ConfirmEmailView.as_view(), name='account_confirm_email'),
|
re_path(r'^registration/account-confirm-email/(?P<key>[-:\w]+)/$', ConfirmEmailView.as_view(), name='account_confirm_email'),
|
||||||
path('registration/', include('dj_rest_auth.registration.urls')),
|
path('registration/', include('dj_rest_auth.registration.urls')),
|
||||||
path('providers/', SocialProvierListView.as_view(), name='social_providers'),
|
path('providers/', SocialProviderListView.as_view(), name='social_providers'),
|
||||||
path('social/', include(social_auth_urlpatterns)),
|
path('social/', include(social_auth_urlpatterns)),
|
||||||
path('social/', SocialAccountListView.as_view(), name='social_account_list'),
|
path('social/', SocialAccountListView.as_view(), name='social_account_list'),
|
||||||
path('social/<int:pk>/disconnect/', SocialAccountDisconnectView.as_view(), name='social_account_disconnect'),
|
path('social/<int:pk>/disconnect/', SocialAccountDisconnectView.as_view(), name='social_account_disconnect'),
|
||||||
|
20
InvenTree/part/migrations/0115_part_responsible_owner.py
Normal file
20
InvenTree/part/migrations/0115_part_responsible_owner.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 3.2.22 on 2023-10-23 01:59
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0009_auto_20231020_2356'),
|
||||||
|
('part', '0114_alter_part_minimum_stock'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='part',
|
||||||
|
name='responsible_owner',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Owner responsible for this part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responsible', to='users.owner', verbose_name='Responsible'),
|
||||||
|
),
|
||||||
|
]
|
76
InvenTree/part/migrations/0116_auto_20231023_0332.py
Normal file
76
InvenTree/part/migrations/0116_auto_20231023_0332.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Generated by Django 3.2.22 on 2023-10-23 03:32
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_part_responsible_owner(apps, schema_editor):
|
||||||
|
"""Copy existing part.responsible field to part.responsible_owner"""
|
||||||
|
|
||||||
|
Owner = apps.get_model('users', 'Owner')
|
||||||
|
Part = apps.get_model('part', 'Part')
|
||||||
|
User = apps.get_model('auth', 'user')
|
||||||
|
ContentType = apps.get_model('contenttypes', 'contenttype')
|
||||||
|
|
||||||
|
user_type = ContentType.objects.get_for_model(User)
|
||||||
|
|
||||||
|
parts = Part.objects.exclude(responsible=None)
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
|
||||||
|
# Find a corresponding Owner object, or create one if it does not exist
|
||||||
|
owner, _created = Owner.objects.get_or_create(
|
||||||
|
owner_type=user_type,
|
||||||
|
owner_id=part.responsible.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
part.responsible_owner = owner
|
||||||
|
part.save()
|
||||||
|
|
||||||
|
if parts.count() > 0:
|
||||||
|
print(f"Added 'responsible_owner' for {parts.count()} parts")
|
||||||
|
|
||||||
|
|
||||||
|
def reverse_owner_migration(apps, schema_editor):
|
||||||
|
"""Reverse the owner migration:
|
||||||
|
|
||||||
|
- Set the 'responsible' field to a selected user
|
||||||
|
- Only where 'responsible_owner' is set
|
||||||
|
- Only where 'responsible_owner' is a User object
|
||||||
|
"""
|
||||||
|
|
||||||
|
Part = apps.get_model('part', 'Part')
|
||||||
|
User = apps.get_model('auth', 'user')
|
||||||
|
ContentType = apps.get_model('contenttypes', 'contenttype')
|
||||||
|
|
||||||
|
user_type = ContentType.objects.get_for_model(User)
|
||||||
|
|
||||||
|
parts = Part.objects.exclude(responsible_owner=None)
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
|
||||||
|
if part.responsible_owner.owner_type == user_type:
|
||||||
|
|
||||||
|
# Attempt to find matching user
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=part.responsible_owner.owner_id)
|
||||||
|
part.responsible = user
|
||||||
|
part.save()
|
||||||
|
except User.DoesNotExist:
|
||||||
|
print("User does not exist:", part.responsible_owner.owner_id)
|
||||||
|
|
||||||
|
if parts.count() > 0:
|
||||||
|
print(f"Added 'responsible' for {parts.count()} parts")
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0115_part_responsible_owner'),
|
||||||
|
('users', '0005_owner_model'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
migrate_part_responsible_owner,
|
||||||
|
reverse_code=reverse_owner_migration,
|
||||||
|
)
|
||||||
|
]
|
17
InvenTree/part/migrations/0117_remove_part_responsible.py
Normal file
17
InvenTree/part/migrations/0117_remove_part_responsible.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.2.22 on 2023-10-23 05:28
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('part', '0116_auto_20231023_0332'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='part',
|
||||||
|
name='responsible',
|
||||||
|
),
|
||||||
|
]
|
@ -41,6 +41,7 @@ import InvenTree.fields
|
|||||||
import InvenTree.ready
|
import InvenTree.ready
|
||||||
import InvenTree.tasks
|
import InvenTree.tasks
|
||||||
import part.settings as part_settings
|
import part.settings as part_settings
|
||||||
|
import users.models
|
||||||
from build import models as BuildModels
|
from build import models as BuildModels
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
from common.settings import currency_code_default
|
from common.settings import currency_code_default
|
||||||
@ -379,7 +380,7 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
|
|||||||
notes: Additional notes field for this part
|
notes: Additional notes field for this part
|
||||||
creation_date: Date that this part was added to the database
|
creation_date: Date that this part was added to the database
|
||||||
creation_user: User who added this part to the database
|
creation_user: User who added this part to the database
|
||||||
responsible: User who is responsible for this part (optional)
|
responsible_owner: Owner (either user or group) which is responsible for this part (optional)
|
||||||
last_stocktake: Date at which last stocktake was performed for this Part
|
last_stocktake: Date at which last stocktake was performed for this Part
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -1036,7 +1037,13 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
|
|||||||
|
|
||||||
creation_user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Creation User'), related_name='parts_created')
|
creation_user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Creation User'), related_name='parts_created')
|
||||||
|
|
||||||
responsible = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Responsible'), help_text=_('User responsible for this part'), related_name='parts_responible')
|
responsible_owner = models.ForeignKey(
|
||||||
|
users.models.Owner, on_delete=models.SET_NULL,
|
||||||
|
blank=True, null=True,
|
||||||
|
verbose_name=_('Responsible'),
|
||||||
|
help_text=_('Owner responsible for this part'),
|
||||||
|
related_name='parts_responsible'
|
||||||
|
)
|
||||||
|
|
||||||
last_stocktake = models.DateField(
|
last_stocktake = models.DateField(
|
||||||
blank=True, null=True,
|
blank=True, null=True,
|
||||||
|
@ -26,6 +26,7 @@ import part.filters
|
|||||||
import part.stocktake
|
import part.stocktake
|
||||||
import part.tasks
|
import part.tasks
|
||||||
import stock.models
|
import stock.models
|
||||||
|
import users.models
|
||||||
from InvenTree.status_codes import BuildStatusGroups
|
from InvenTree.status_codes import BuildStatusGroups
|
||||||
from InvenTree.tasks import offload_task
|
from InvenTree.tasks import offload_task
|
||||||
|
|
||||||
@ -695,6 +696,12 @@ class PartSerializer(InvenTree.serializers.RemoteImageMixin, InvenTree.serialize
|
|||||||
read_only=True,
|
read_only=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
responsible = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=users.models.Owner.objects.all(),
|
||||||
|
required=False, allow_null=True,
|
||||||
|
source='responsible_owner',
|
||||||
|
)
|
||||||
|
|
||||||
# Annotated fields
|
# Annotated fields
|
||||||
allocated_to_build_orders = serializers.FloatField(read_only=True)
|
allocated_to_build_orders = serializers.FloatField(read_only=True)
|
||||||
allocated_to_sales_orders = serializers.FloatField(read_only=True)
|
allocated_to_sales_orders = serializers.FloatField(read_only=True)
|
||||||
|
@ -384,11 +384,11 @@
|
|||||||
<td>{% include 'clip_link.html' with link=part.link new_window=True %}</td>
|
<td>{% include 'clip_link.html' with link=part.link new_window=True %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if part.responsible %}
|
{% if part.responsible_owner %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><span class='fas fa-user'></span></td>
|
<td><span class='fas fa-user'></span></td>
|
||||||
<td>{% trans "Responsible" %}</td>
|
<td>{% trans "Responsible" %}</td>
|
||||||
<td> <span class='badge badge-right rounded-pill bg-dark'>{{ part.responsible }}</span></td>
|
<td> <span class='badge badge-right rounded-pill bg-dark'>{{ part.responsible_owner }}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user