From e8af71ae5a6fbc9ef493462666d67715885364ab Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 10 Sep 2026 14:22:55 +1000 Subject: [PATCH] Bug fixes for Note model (#12835) * Bug fixes for Note model - Allow note to be created as "primary" - Reset note creation fields in editor * Tweak to ensure that the API spec does not change --- src/backend/InvenTree/common/serializers.py | 4 ++++ src/backend/InvenTree/common/test_api.py | 19 +++++++++++++++++++ .../src/components/editors/NotesEditor.tsx | 7 +++++-- src/frontend/src/forms/CommonForms.tsx | 12 ++++++++++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/backend/InvenTree/common/serializers.py b/src/backend/InvenTree/common/serializers.py index 99568eed6e..8a3d785e99 100644 --- a/src/backend/InvenTree/common/serializers.py +++ b/src/backend/InvenTree/common/serializers.py @@ -929,6 +929,10 @@ class NoteSerializer(FilterableSerializerMixin, InvenTreeModelSerializer): read_only_fields = ['updated', 'updated_by'] + def get_unique_together_validators(self): + """Suppress the auto-generated validator for 'unique_primary_note_per_model'.""" + return [] + def validate(self, data): """Validate note data — templates need no model_id; regular notes require both.""" data = super().validate(data) diff --git a/src/backend/InvenTree/common/test_api.py b/src/backend/InvenTree/common/test_api.py index fc12d89902..dff21d122c 100644 --- a/src/backend/InvenTree/common/test_api.py +++ b/src/backend/InvenTree/common/test_api.py @@ -1471,6 +1471,25 @@ class NoteAPITests(InvenTreeAPITestCase): primary_pks = [n['pk'] for n in list_response.data if n['primary']] self.assertEqual(primary_pks, [third.data['pk']]) + def test_creating_primary_note_demotes_existing_primary(self): + """Explicitly creating a new note with primary=True demotes the existing primary note. + + Regression test: DRF auto-generates a UniqueTogetherValidator from the + 'unique_primary_note_per_model' partial unique constraint, which used to + reject this at the serializer-validation stage (before Note.save()'s + demote-then-save logic ever ran), raising a spurious 'unique set' error. + """ + first = self._create_note('First Note') + self.assertTrue(first.data['primary']) + + second = self._create_note('Second Note', primary=True) + self.assertTrue(second.data['primary']) + + from common.models import Note + + self.assertFalse(Note.objects.get(pk=first.data['pk']).primary) + self.assertTrue(Note.objects.get(pk=second.data['pk']).primary) + def test_primary_flag_isolated_per_model_instance(self): """Primary flag changes on one model instance do not affect notes on another.""" from part.models import Part diff --git a/src/frontend/src/components/editors/NotesEditor.tsx b/src/frontend/src/components/editors/NotesEditor.tsx index 8c12e45ff5..a6b7d24d57 100644 --- a/src/frontend/src/components/editors/NotesEditor.tsx +++ b/src/frontend/src/components/editors/NotesEditor.tsx @@ -306,7 +306,7 @@ export default function NotesEditor({ return notesQuery.data && notesQuery.data.length > 0; }, [notesQuery.data]); - const noteFields = useNoteFields({ + const { fields: noteFields, resetFields: resetNoteFields } = useNoteFields({ modelType: modelType!, modelId: modelId! }); @@ -711,7 +711,10 @@ export default function NotesEditor({