mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-10 06:37:17 +00:00
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:
@@ -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)
|
||||||
|
|||||||
@@ -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`}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user