From d99ec3e1a16d46061c0f56bdc2f63ee0b459a121 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 15 Jul 2025 22:34:07 +1000 Subject: [PATCH] PartParameter updates (#10023) * Add mixin for storing user who last updated an instance * Add mixin to "PartParameter" model * Fix typo * Fix strings * Refactor mixin class * Update part parameter table: - Add "user" filter - Add "updated_by" column - Add "update" column - Add "note" column * Fix for updating date * Add user information when saving parameter * small refactors * Bump API version * Add unit test for "updated" and "updated_by" fields * Check for 'note' field * Update docs image --- .../images/part/create_part_parameter.png | Bin 56863 -> 33329 bytes .../InvenTree/InvenTree/api_version.py | 5 +- src/backend/InvenTree/common/models.py | 36 ++++++++++++ src/backend/InvenTree/importer/serializers.py | 4 +- src/backend/InvenTree/part/admin.py | 2 + src/backend/InvenTree/part/api.py | 13 ++++- ...ter_note_partparameter_updated_and_more.py | 50 +++++++++++++++++ src/backend/InvenTree/part/models.py | 15 ++++- src/backend/InvenTree/part/serializers.py | 27 +++++++-- src/backend/InvenTree/part/test_param.py | 15 ++++- src/frontend/src/forms/PartForms.tsx | 3 +- .../src/tables/part/PartParameterTable.tsx | 53 +++++++++++++++--- src/frontend/tests/pages/pui_part.spec.ts | 2 + 13 files changed, 203 insertions(+), 22 deletions(-) create mode 100644 src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py diff --git a/docs/docs/assets/images/part/create_part_parameter.png b/docs/docs/assets/images/part/create_part_parameter.png index 4ed585fbfa6c013d459ebe1e49cdf5d4a5cb0141..c36d0a4b0f806f855738d79278899aedfba7f2bc 100644 GIT binary patch literal 33329 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 }bAN=6tjqU6ew6M*GO2 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 literal 56863 zcmd432T+sU*Dva8SDFp!qJkhGMS2lXs(|zoI?^HZ-oZwbrWBDby(fm=0t(WlCJ;Iz z(h>p)B!Ps$eXzX$b7sz+Z_c^jy>l~@QJ(DVz4mH*t>0QZ(OMeHR2La9o;Y!WO69SF z&WRIeY)+gwi93H5c=GTPb`tpKA1@tcxf75<<~87t)AoOQ1NgJOliF!Tqt3 z*NGDhQbYWXUz>9E0k_eJ36aGno&=E=8XK0JF$k*sr(ca|Wkf)FOPY6$l4lkayHl76HWkdKG7}`5se${Q9*@ZSEc?u{>1R?qS2ob^tT5mt5<@>eC_U~P8U8DxFp52 zdAYuy_TRBVPxTDmJ&2Cp4ur#Ez6-iGlkuUE_YA#ASmPRpx0xFD>$m?*7w~zaC*yWf)fKjZdQ~Fq`(e z^Pe#fnRk{4VzS26W*&|UtM8LTxvl^C{n68BGp_a520^4zo;S6Og!(IgzVV^{>QXuU z#Ca}BSoCZno{v8=cUMlxE_3y}1<^PO)_3-+iQ4!~#|0S66^1sv4-#%US zXMljhd7Hm9MP2GS`TOywOSk@)a`>+&rnacJfOA+l{F&$kWoAkxzR$EHsbf(^3(9q0 z_|IY|&JOk&1ld9sqHq0`^3(s0_5WYnSo~9gl2(pCT>JE?)2;%R)Un`R0=@8;lkPAE zCn%TiAN@V?KgsUDX59Zkc&yY$Lq}|;WqQ#QAz=)v?a=2(-Ys37WdzM(v$>gB5ha`V zlZab|MdG`a(Ad%UCqe7n@Rn*sc@S$!OnR!wXzpeh8z_xouw>s}$_=DNIH-{e!uvgr zVAoxM;#t@0B5p0&aXK8%T&pyESEO<^@rQp-7v8N6QNcRrS5Vw+)A!~U0u7DYP*e8- zf8Vpo@*8~2YnEJEgSIr9>P02+EEqvj>?xHdIF4Op@;MF4&^O1HG80 z5N!s}c}lhi;V-MjS++v0=p$m?+ag*cj+lMC=*!C1#wqwk(DPwhMf{=phRUif_e}zG z&d&?wPV*!Ea*w+4`N+8&!-McjVyC<^E?UZO)h{8XyjhCAS@AakuR<1OTcIYhxB&8e zPEM4W@Y81B%`sZk%m?7v{)A%3xB#^cR`QG1E&GN*LROYJ95;C4lpKOGg+HQA;o6H< z{6*VqcEFkK9j>owmb;Yb=7d62{f-EhKHjIT>R($zrsnEauPEeu+;I!)##{+WP8#yJsVx z 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 diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 503007bb2e..00a754e330 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,12 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 368 +INVENTREE_API_VERSION = 369 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v369 -> 2025-07-15 : https://github.com/inventree/InvenTree/pull/10023 + - Adds "note", "updated", "updated_by" fields to the PartParameter API endpoints + v368 -> 2025-07-11 : https://github.com/inventree/InvenTree/pull/9673 - Adds 'tax_id' to company model - Adds 'tax_id' to search fields in the 'CompanyList' API endpoint diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index 3a006fb962..1ece0f99a1 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -104,6 +104,42 @@ class MetaMixin(models.Model): ) +class UpdatedUserMixin(models.Model): + """A mixin which stores additional information about the user who created or last modified the object.""" + + class Meta: + """Meta options for MetaUserMixin.""" + + abstract = True + + def save(self, *args, **kwargs): + """Extract the user object from kwargs, if provided.""" + if updated_by := kwargs.pop('updated_by', None): + self.updated_by = updated_by + + self.updated = InvenTree.helpers.current_time() + + super().save(*args, **kwargs) + + updated = models.DateTimeField( + verbose_name=_('Updated'), + help_text=_('Timestamp of last update'), + default=None, + blank=True, + null=True, + ) + + updated_by = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name='%(class)s_updated', + verbose_name=_('Update By'), + help_text=_('User who last updated this object'), + ) + + class ProjectCode(InvenTree.models.InvenTreeMetadataModel): """A ProjectCode is a unique identifier for a project.""" diff --git a/src/backend/InvenTree/importer/serializers.py b/src/backend/InvenTree/importer/serializers.py index 234cbf41b6..ad33d6e6a1 100644 --- a/src/backend/InvenTree/importer/serializers.py +++ b/src/backend/InvenTree/importer/serializers.py @@ -125,9 +125,7 @@ class DataImportSessionSerializer(InvenTreeModelSerializer): """ session = super().create(validated_data) - request = self.context.get('request', None) - - if request: + if request := self.context.get('request', None): session.user = request.user session.save() diff --git a/src/backend/InvenTree/part/admin.py b/src/backend/InvenTree/part/admin.py index 82a0f9ab13..3907eae294 100644 --- a/src/backend/InvenTree/part/admin.py +++ b/src/backend/InvenTree/part/admin.py @@ -121,6 +121,8 @@ class ParameterAdmin(admin.ModelAdmin): list_display = ('part', 'template', 'data') + readonly_fields = ('updated', 'updated_by') + autocomplete_fields = ('part', 'template') diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index 90ee0e5197..3a78858122 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -1392,9 +1392,16 @@ class PartParameterAPIMixin: def get_queryset(self, *args, **kwargs): """Override get_queryset method to prefetch related fields.""" queryset = super().get_queryset(*args, **kwargs) - queryset = queryset.prefetch_related('part', 'template') + queryset = queryset.prefetch_related('part', 'template', 'updated_by') return queryset + def get_serializer_context(self): + """Pass the 'request' object through to the serializer context.""" + context = super().get_serializer_context() + context['request'] = self.request + + return context + def get_serializer(self, *args, **kwargs): """Return the serializer instance for this API endpoint. @@ -1420,7 +1427,7 @@ class PartParameterFilter(rest_filters.FilterSet): """Metaclass options for the filterset.""" model = PartParameter - fields = ['template'] + fields = ['template', 'updated_by'] part = rest_filters.ModelChoiceFilter( queryset=Part.objects.all(), method='filter_part' @@ -1453,7 +1460,7 @@ class PartParameterList(PartParameterAPIMixin, DataExportViewMixin, ListCreateAP filter_backends = SEARCH_ORDER_FILTER_ALIAS - ordering_fields = ['name', 'data', 'part', 'template'] + ordering_fields = ['name', 'data', 'part', 'template', 'updated', 'updated_by'] ordering_field_aliases = { 'name': 'template__name', diff --git a/src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py b/src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py new file mode 100644 index 0000000000..3867769f6f --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.23 on 2025-07-15 02:05 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("part", "0135_alter_part_link"), + ] + + operations = [ + migrations.AddField( + model_name="partparameter", + name="note", + field=models.CharField( + blank=True, + help_text="Optional note field", + max_length=500, + verbose_name="Note", + ), + ), + migrations.AddField( + model_name="partparameter", + name="updated", + field=models.DateTimeField( + blank=True, + default=None, + help_text="Timestamp of last update", + null=True, + verbose_name="Updated", + ), + ), + migrations.AddField( + model_name="partparameter", + name="updated_by", + field=models.ForeignKey( + blank=True, + help_text="User who last updated this object", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_updated", + to=settings.AUTH_USER_MODEL, + verbose_name="Update By", + ), + ), + ] diff --git a/src/backend/InvenTree/part/models.py b/src/backend/InvenTree/part/models.py index 166db4a1c3..57fe3f21d7 100644 --- a/src/backend/InvenTree/part/models.py +++ b/src/backend/InvenTree/part/models.py @@ -3976,13 +3976,19 @@ def post_save_part_parameter_template(sender, instance, created, **kwargs): ) -class PartParameter(InvenTree.models.InvenTreeMetadataModel): +class PartParameter( + common.models.UpdatedUserMixin, InvenTree.models.InvenTreeMetadataModel +): """A PartParameter is a specific instance of a PartParameterTemplate. It assigns a particular parameter pair to a part. Attributes: part: Reference to a single Part object template: Reference to a single PartParameterTemplate object data: The data (value) of the Parameter [string] + data_numeric: Numeric value of the parameter (if applicable) [float] + note: Optional note field for the parameter [string] + updated: Timestamp of when the parameter was last updated [datetime] + updated_by: Reference to the User who last updated the parameter [User] """ class Meta: @@ -4123,6 +4129,13 @@ class PartParameter(InvenTree.models.InvenTreeMetadataModel): data_numeric = models.FloatField(default=None, null=True, blank=True) + note = models.CharField( + max_length=500, + blank=True, + verbose_name=_('Note'), + help_text=_('Optional note field'), + ) + @property def units(self): """Return the units associated with the template.""" diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 049256cc8f..3fef3b1634 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -410,8 +410,14 @@ class PartParameterSerializer( 'template_detail', 'data', 'data_numeric', + 'note', + 'updated', + 'updated_by', + 'updated_by_detail', ] + read_only_fields = ['updated', 'updated_by'] + def __init__(self, *args, **kwargs): """Custom initialization method for the serializer. @@ -431,13 +437,27 @@ class PartParameterSerializer( if not template_detail: self.fields.pop('template_detail', None) + def save(self): + """Save the PartParameter instance.""" + instance = super().save() + + if request := self.context.get('request', None): + # If the request is provided, update the 'updated_by' field + instance.updated_by = request.user + instance.save() + + return instance + part_detail = PartBriefSerializer( source='part', many=False, read_only=True, allow_null=True ) + template_detail = PartParameterTemplateSerializer( source='template', many=False, read_only=True, allow_null=True ) + updated_by_detail = UserSerializer(source='updated_by', many=False, read_only=True) + class DuplicatePartSerializer(serializers.Serializer): """Serializer for specifying options when duplicating a Part. @@ -1134,9 +1154,8 @@ class PartSerializer( part=instance, quantity=quantity, location=location ) - request = self.context.get('request', None) - user = request.user if request else None - stockitem.save(user=user) + if request := self.context.get('request', None): + stockitem.save(user=request.user) # Create initial supplier information if initial_supplier: @@ -1302,7 +1321,7 @@ class PartStocktakeSerializer(InvenTree.serializers.InvenTreeModelSerializer): # Add in user information automatically request = self.context.get('request') data['user'] = request.user if request else None - super().save() + return super().save() class PartStocktakeReportSerializer(InvenTree.serializers.InvenTreeModelSerializer): diff --git a/src/backend/InvenTree/part/test_param.py b/src/backend/InvenTree/part/test_param.py index 4cf8965f00..af2c162d53 100644 --- a/src/backend/InvenTree/part/test_param.py +++ b/src/backend/InvenTree/part/test_param.py @@ -1,6 +1,7 @@ """Various unit tests for Part Parameters.""" import django.core.exceptions as django_exceptions +from django.contrib.auth.models import User from django.test import TestCase, TransactionTestCase from django.urls import reverse @@ -19,7 +20,7 @@ from .models import ( class TestParams(TestCase): """Unit test class for testing the PartParameter model.""" - fixtures = ['location', 'category', 'part', 'params'] + fixtures = ['location', 'category', 'part', 'params', 'users'] def test_str(self): """Test the str representation of the PartParameterTemplate model.""" @@ -32,6 +33,18 @@ class TestParams(TestCase): c1 = PartCategoryParameterTemplate.objects.get(pk=1) self.assertEqual(str(c1), 'Mechanical | Length | 2.8') + def test_updated(self): + """Test that the 'updated' field is correctly set.""" + p1 = PartParameter.objects.get(pk=1) + self.assertIsNone(p1.updated) + self.assertIsNone(p1.updated_by) + + user = User.objects.get(username='sam') + p1.save(updated_by=user) + + self.assertIsNotNone(p1.updated) + self.assertEqual(p1.updated_by, user) + def test_validate(self): """Test validation for part templates.""" n = PartParameterTemplate.objects.all().count() diff --git a/src/frontend/src/forms/PartForms.tsx b/src/frontend/src/forms/PartForms.tsx index 22341e49e1..db7dcba159 100644 --- a/src/frontend/src/forms/PartForms.tsx +++ b/src/frontend/src/forms/PartForms.tsx @@ -250,7 +250,8 @@ export function usePartParameterFields({ // Coerce boolean value into a string (required by backend) return value.toString(); } - } + }, + note: {} }; }, [editTemplate, fieldType, choices]); } diff --git a/src/frontend/src/tables/part/PartParameterTable.tsx b/src/frontend/src/tables/part/PartParameterTable.tsx index e2e3b1364e..0b2a2930bb 100644 --- a/src/frontend/src/tables/part/PartParameterTable.tsx +++ b/src/frontend/src/tables/part/PartParameterTable.tsx @@ -12,9 +12,11 @@ import { YesNoButton } from '@lib/components/YesNoButton'; import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; +import type { TableFilter } from '@lib/types/Filters'; import type { ApiFormFieldSet } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; import { AddItemButton } from '../../components/buttons/AddItemButton'; +import { RenderUser } from '../../components/render/User'; import { formatDecimal } from '../../defaults/formatters'; import { usePartParameterFields } from '../../forms/PartForms'; import { @@ -24,7 +26,13 @@ import { } from '../../hooks/UseForm'; import { useTable } from '../../hooks/UseTable'; import { useUserState } from '../../states/UserState'; -import { DescriptionColumn, PartColumn } from '../ColumnRenderers'; +import { + DateColumn, + DescriptionColumn, + NoteColumn, + PartColumn +} from '../ColumnRenderers'; +import { UserFilter } from '../Filter'; import { InvenTreeTable } from '../InvenTreeTable'; import { TableHoverCard } from '../TableHoverCard'; @@ -108,10 +116,45 @@ export function PartParameterTable({ accessor: 'template_detail.units', ordering: 'units', sortable: true + }, + NoteColumn({}), + DateColumn({ + accessor: 'updated', + title: t`Last Updated`, + sortable: true, + switchable: true + }), + { + accessor: 'updated_by', + title: t`Updated By`, + sortable: true, + switchable: true, + render: (record: any) => { + return record.updated_by_detail ? ( + + ) : ( + '-' + ); + } } ]; }, [partId]); + const tableFilters: TableFilter[] = useMemo(() => { + return [ + { + name: 'include_variants', + label: t`Include Variants`, + type: 'boolean' + }, + UserFilter({ + name: 'updated_by', + label: t`Updated By`, + description: t`Filter by user who last updated the parameter` + }) + ]; + }, []); + const partParameterFields: ApiFormFieldSet = usePartParameterFields({}); const newParameter = useCreateApiFormModal({ @@ -211,13 +254,7 @@ export function PartParameterTable({ rowActions: rowActions, enableDownload: true, tableActions: tableActions, - tableFilters: [ - { - name: 'include_variants', - label: t`Include Variants`, - type: 'boolean' - } - ], + tableFilters: tableFilters, params: { part: partId, template_detail: true, diff --git a/src/frontend/tests/pages/pui_part.spec.ts b/src/frontend/tests/pages/pui_part.spec.ts index 994ef8f574..fa2998aa19 100644 --- a/src/frontend/tests/pages/pui_part.spec.ts +++ b/src/frontend/tests/pages/pui_part.spec.ts @@ -419,6 +419,8 @@ test('Parts - Parameters', async ({ browser }) => { await page.getByLabel('choice-field-data').click(); await page.getByRole('option', { name: 'Green' }).click(); + await page.getByLabel('text-field-note').fill('A custom note field'); + // Select the "polarized" parameter template (should create a "checkbox" field) await page.getByLabel('related-field-template').fill('Polarized'); await page.getByText('Is this part polarized?').click();