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']
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)
+19
View File
@@ -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
@@ -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({
<Button
color='green'
leftSection={<IconCirclePlus />}
onClick={createNote.open}
onClick={() => {
resetNoteFields();
createNote.open();
}}
disabled={isEditing}
>
{t`Add Note`}
+10 -2
View File
@@ -330,13 +330,19 @@ export function useNoteFields({
}: {
modelType: ModelType;
modelId: number;
}): ApiFormFieldSet {
}): { fields: ApiFormFieldSet; resetFields: () => void } {
const api = useApi();
const [title, setTitle] = useState<string>('');
const [description, setDescription] = useState<string>('');
const [content, setContent] = useState<string>('');
const resetFields = useCallback(() => {
setTitle('');
setDescription('');
setContent('');
}, []);
const fetchTemplate = useCallback(
(pk: number | null) => {
if (!pk) return;
@@ -352,7 +358,7 @@ export function useNoteFields({
[api]
);
return useMemo(() => {
const fields = useMemo<ApiFormFieldSet>(() => {
return {
model_type: {
hidden: true,
@@ -392,6 +398,8 @@ export function useNoteFields({
}
};
}, [modelType, modelId, title, description, content, fetchTemplate]);
return { fields, resetFields };
}
export function selectionListFields(): ApiFormFieldSet {