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
This commit is contained in:
Oliver
2026-09-10 14:22:55 +10:00
committed by GitHub
parent 43be292db2
commit e8af71ae5a
4 changed files with 38 additions and 4 deletions
@@ -929,6 +929,10 @@ class NoteSerializer(FilterableSerializerMixin, InvenTreeModelSerializer):
read_only_fields = ['updated', 'updated_by'] 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): def validate(self, data):
"""Validate note data — templates need no model_id; regular notes require both.""" """Validate note data — templates need no model_id; regular notes require both."""
data = super().validate(data) data = super().validate(data)
+19
View File
@@ -1471,6 +1471,25 @@ class NoteAPITests(InvenTreeAPITestCase):
primary_pks = [n['pk'] for n in list_response.data if n['primary']] primary_pks = [n['pk'] for n in list_response.data if n['primary']]
self.assertEqual(primary_pks, [third.data['pk']]) 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): def test_primary_flag_isolated_per_model_instance(self):
"""Primary flag changes on one model instance do not affect notes on another.""" """Primary flag changes on one model instance do not affect notes on another."""
from part.models import Part from part.models import Part
@@ -306,7 +306,7 @@ export default function NotesEditor({
return notesQuery.data && notesQuery.data.length > 0; return notesQuery.data && notesQuery.data.length > 0;
}, [notesQuery.data]); }, [notesQuery.data]);
const noteFields = useNoteFields({ const { fields: noteFields, resetFields: resetNoteFields } = useNoteFields({
modelType: modelType!, modelType: modelType!,
modelId: modelId! modelId: modelId!
}); });
@@ -711,7 +711,10 @@ export default function NotesEditor({
<Button <Button
color='green' color='green'
leftSection={<IconCirclePlus />} leftSection={<IconCirclePlus />}
onClick={createNote.open} onClick={() => {
resetNoteFields();
createNote.open();
}}
disabled={isEditing} disabled={isEditing}
> >
{t`Add Note`} {t`Add Note`}
+10 -2
View File
@@ -330,13 +330,19 @@ export function useNoteFields({
}: { }: {
modelType: ModelType; modelType: ModelType;
modelId: number; modelId: number;
}): ApiFormFieldSet { }): { fields: ApiFormFieldSet; resetFields: () => void } {
const api = useApi(); const api = useApi();
const [title, setTitle] = useState<string>(''); const [title, setTitle] = useState<string>('');
const [description, setDescription] = useState<string>(''); const [description, setDescription] = useState<string>('');
const [content, setContent] = useState<string>(''); const [content, setContent] = useState<string>('');
const resetFields = useCallback(() => {
setTitle('');
setDescription('');
setContent('');
}, []);
const fetchTemplate = useCallback( const fetchTemplate = useCallback(
(pk: number | null) => { (pk: number | null) => {
if (!pk) return; if (!pk) return;
@@ -352,7 +358,7 @@ export function useNoteFields({
[api] [api]
); );
return useMemo(() => { const fields = useMemo<ApiFormFieldSet>(() => {
return { return {
model_type: { model_type: {
hidden: true, hidden: true,
@@ -392,6 +398,8 @@ export function useNoteFields({
} }
}; };
}, [modelType, modelId, title, description, content, fetchTemplate]); }, [modelType, modelId, title, description, content, fetchTemplate]);
return { fields, resetFields };
} }
export function selectionListFields(): ApiFormFieldSet { export function selectionListFields(): ApiFormFieldSet {