[Refactor] Notes (#11971)

* Display note info

* Support user locale

* Add unit testing for HTML content

* Observe color mode

* Add link between Note and NotesImage

* Ensure image file is deleted when NotesImage is deleted

* Add support for image upload in editor

* Skeleton for data migration

* Updates

* Update data migration

- Find any NoteImage items which do not link to a model
- Try to associate them with an existing note

* Remove validator

* Updated API endpoints for NotesImage model

* Update server side sanitizing

* Specify max field length

* Remove old fields from NotesImage model

* Refactor clean_string

* Remove obsolete task

* Remove legacy "notes" field from older models

* Adjust search params when switching notes

* Remove NotesFieldMixin

* Change editor

* Resizable image support

* Support tables

* Add table style

* Adjust header actions

* Add data migration for SalesOrderShipment notes

* Adjust back-end sanitizing

* Adjust

* Use subtle editor variant

* Move undo/redo

* Enhance editing logic

* Add report tags for notes

* Add unit test for note image cleanup

* Render note to HTML

- Automatically replace images

* Fix migration order

* Adjust migration text

* Fix "dirty" trigger on notes

* Prevent navigate from dirty notes

* Add documentation

* Prevent image clicking if not in editing mode

* Update API

* Update migration files

* Fix migrations

* remove notes from test fixtures

* remove notes field that does not exsist anymore

* add missing ruleset

* fix assertation

* fix assertation

* Update docs/docs/concepts/notes.md

Co-authored-by: Matthias Mair <code@mjmair.com>

* Remove blocknote deps

* Move old helper functions

- Only used for this migration
- Will potentially be removed at some point in the future?

* Revert change

* Fix note image URL

* Fix migration conflicts

* Fix migrations

* Fix delete call

* Fix content mixin

* Fix note duplication

* Make save method atomic

* Fix double-save

* Add "template" field to Note model

* Adjust migrations

* Frontend updates

* Fix for NotesEditor

* Render Note instance in forms

* Fix button-within-button

* Fix migrations

* Fix missing import

* add docs

* docs for rendering notes in reports

* Restrict queryset based on user view permissions

* APi unit tests for note permissions

* Duplicate embedded images when copying notes

* Add unit test for note duplication

* Add CHANGELOG

* Add 'copy_note' option to duplicate serializer

* Add unit tests for data migrations

* implement note duplication serializers

* frontend UI elements

* Fix migration conflicts

* Use branch for playwrigh testing

* Implement duplicate action for stock item

* Fix import

* Updated playwright tests

* Bug fix for receiving stock items

* Add screenshot

* Fix api_version

* Update unit tests

* Fix docs

* Remove defunct tests

* Fix migration order

* Adjust import/export workflow

* Manual cleaning update

* Fix migrations

* Fix migration files

* Fix for note save

* Adjust save ordering

* Skip constraint checking in NoteSerializer

* Custom validate_constraints on Note model

* Revert "Skip constraint checking in NoteSerializer"

This reverts commit b42bc955c1.

* Fix for note search

* Fix for receive_line_items

* Shim model renderer for NoteTemplate

* Fix playwright tests

* Adjust frontend CI

* Fix import/export CI job

* Fix for data migration test

* Fix migration test

* Adjust unit test

* Fix conflicting migration

* Fix unit test

* Run migration tests in parallel

* Robustify migration test

* Disable parallel options

* Fix conflicting migration

* Remove extraneous unit test

* Fix conflicting migrations

* Additional regression tests

* Check permissions before deleting Note instance

* Updated docs

* Validate note model type

* Prevent discard of unsaved changes in note editor

* Clean up dead code

* Fix migration conflict

* Improved data migration

* Prefetch role groups

* UI refactoring

* Refactor permission checking code

* Further code refactoring

* use DuplicateField helper

* Refactoring

* Add prefetch

* Throw exception rather than assert

* Logic fix for notes editor

* reimplement old background task

* Adjust data migration

* Fix notes field when receiving items

* Fix existing report templates

* Fix save action for notes editor

* Refactoring: Add "instance-info" API endpoint for common model properties

* Fix indicator dots

* Tweak nav alert msg

* Adjust layout of buttons

* Sanitize notes during migration

* Fix for NotesImage delete cascade

* Fix caching

* Fix race condition in notes editor

* Fix distinct issue when searching notse

* Fix race condition when saving new note instance

* Fix improper error

* Refactor StockItem duplication

* Refactoring

* Increase query time

* Fix api_version.py

* Additional migration tests

* Fix CI workflow

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
Oliver
2026-08-31 16:46:33 +10:00
committed by GitHub
co-authored by Matthias Mair
parent d39d0262e6
commit 02da01dfec
108 changed files with 6902 additions and 1643 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

+147
View File
@@ -0,0 +1,147 @@
---
title: Notes
---
## Notes
*Notes* allow free-form rich-text content to be written and stored against a specific object within InvenTree. Notes can be used to record observations, instructions, historical context, or any other information associated with a model instance.
!!! note "Business Logic"
Notes are not to be used for any core business logic within InvenTree. They are intended to provide supplementary documentation and context for objects, which can be useful for reference, communication, or reporting purposes. Plugins should not use them for storage and opt for object metadata or custom models instead.
Notes can be associated with various InvenTree models, and each model can have multiple notes associated with it. The user interface provides a dedicated "Notes" tab on the detail page of any model that supports notes, allowing users to easily view and manage notes for that object.
### Notes Tab
Any model which supports notes will have a "Notes" tab on its detail page. This tab displays the content of the currently selected note, along with a sidebar listing all notes for that object by title:
{{ image("concepts/notes-tab.png", "Notes Tab Example") }}
## Note Fields
Each note has the following attributes:
| Field | Description |
| --- | --- |
| Title | A short title for the note (*required*) |
| Description | An optional brief description of the note's purpose |
| Content | The rich-text body of the note |
| Primary | Marks this note as the default note for the object |
## Primary Note
When a model has multiple notes, one may be designated as the *primary* note. The primary note is indicated by a {{ icon("star") }} icon in the note sidebar.
- When the first note is created for a model instance, it is automatically set as the primary note.
- Only one note per model instance can be marked as primary at any time.
- The primary note is opened by default when navigating to the Notes tab.
## Rich Text Editing
Note content is edited using a rich-text (WYSIWYG) editor. The following formatting options are available:
- **Text formatting**: Bold, italic, underline, strikethrough, inline code, code blocks
- **Headings**: H1 through H4
- **Structure**: Blockquotes, horizontal rules
- **Lists**: Bullet lists and ordered lists
- **Links**: Insert and remove hyperlinks
- **Tables**: Insert tables; add/remove rows and columns; toggle header rows
- **Images**: Embed images uploaded directly into the note
### Inserting Images
Images can be embedded in note content in the following ways:
- Click the {{ icon("photo") }} button in the editor toolbar to select a file from your device
- Paste an image directly from the clipboard
- Drag and drop an image file into the editor
Uploaded images are stored on the server and linked to the note. If a note is edited or deleted, any images that are no longer referenced by any note are automatically removed.
## Adding a Note
To add a note to an object:
1. Navigate to the object's detail page
2. Click on the **Notes** tab
3. Click the **Add Note** button
4. Fill in the `Title` (required) and optional `Description` fields
5. Click **Submit**
The new note will appear in the sidebar ready for editing.
## Editing Note Content
Note content is shown in read-only mode by default. To make changes:
1. Click the {{ icon("pencil") }} icon in the note header to enter edit mode
2. Use the toolbar to format content, insert images, or add tables
3. Click the {{ icon("device-floppy") }} icon, or press **Ctrl+S** / **Cmd+S**, to save changes
4. Click the {{ icon("check") }} icon to exit edit mode once all changes are saved
!!! warning "Unsaved Changes"
If you navigate away from the Notes panel or leave the page while in edit mode with unsaved changes, InvenTree will prompt you to confirm before proceeding.
### Resetting Changes
While in edit mode, clicking the {{ icon("reload") }} icon discards any unsaved changes and reloads the last saved version of the note.
## Editing Note Properties
To change a note's title or description, open the actions menu in the note header and select **Edit Note**.
## Deleting a Note
To delete a note, open the actions menu in the note header and select **Delete Note**.
!!! danger "Permanent Action"
Deleting a note is permanent and cannot be undone. Any images embedded in the note that are not referenced elsewhere will also be removed.
## Note Templates
Note templates are pre-defined notes that can be used as a starting point when adding a new note to any model instance. They allow administrators to standardize common note structures and reduce repetitive data entry.
### Creating Notes from Templates
When adding a new note to an object, an optional **From Template** field is available. Selecting a template pre-fills the **Title**, **Description**, and **Content** fields with the template's content. These fields can then be edited before saving.
To create a note from a template:
1. Navigate to the object's detail page and open the **Notes** tab
2. Click the **Add Note** button
3. In the **From Template** field, select an existing template from the dropdown
4. The **Title**, **Description**, and **Content** fields are automatically populated from the template
5. Edit any fields as needed
6. Click **Submit** to save the note
!!! info "Template Filters"
The template dropdown only shows templates that are applicable to the current model type, plus any templates that are not restricted to a specific model type.
### Managing Note Templates
Note templates are managed by staff users via the **Admin Center**.
To access note templates:
1. Navigate to **Settings** > **Admin Center**
2. Select the **Note Templates** panel
This panel provides the same rich-text editor interface used for regular notes. Templates created here are available to all users when adding notes across the system.
#### Creating a Template
1. In the **Note Templates** panel, click **Add Note Template**
2. Enter a **Title** (required) and optional **Description**
3. Optionally select a **Model Type** to restrict the template to a specific kind of object (e.g. *Part*, *Build Order*). Leave blank to make the template available for all model types
4. Click **Submit**, then edit the template content in the editor
#### Editing a Template
Select a template from the sidebar, then use the same edit workflow as for regular notes: click the {{ icon("pencil") }} icon, make changes, and save with {{ icon("device-floppy") }} or **Ctrl+S** / **Cmd+S**.
#### Deleting a Template
Open the actions menu in the template header and select **Delete Note Template**.
!!! note
Deleting a template does not affect any notes that were previously created from it.
+2 -2
View File
@@ -193,9 +193,9 @@ Files attachments can be uploaded against the build order, and displayed in the
### Notes
Build order notes (which support markdown formatting) are displayed in the *Notes* tab:
One or more rich-text notes can be attached to a build order, and are displayed in the *Notes* tab.
{{ image("build/build_notes.png", title="Notes") }}
[Read about notes](../concepts/notes.md).
## External Build Orders
+3 -1
View File
@@ -149,4 +149,6 @@ The *Part Attachments* tab displays file attachments associated with the selecte
### Notes
A part may have notes attached, which support markdown formatting.
A part may have one or more rich-text notes attached.
[Read about notes](../concepts/notes.md).
+1 -1
View File
@@ -23,7 +23,7 @@ Consider the use-case for your plugin and define the exact function of the plugi
- Do you need to run in the background ([ScheduleMixin](./mixins/schedule.md)) or when things in InvenTree change ([EventMixin](./mixins/event.md))?
- Does the plugin need configuration that should be user changeable ([SettingsMixin](./mixins/settings.md)) or static (just use a yaml in the config dir)?
- You want to receive webhooks? Do not code your own untested function, use the WebhookEndpoint model as a base and override the perform_action method.
- Do you need the full power of Django with custom models and all the complexity that comes with that welcome to the danger zone and [AppMixin](./mixins/app.md). The plugin will be treated as a app by django and can maybe rack the whole instance.
- Do you need the full power of Django with custom models and all the complexity that comes with that - welcome to the danger zone and [AppMixin](./mixins/app.md). The plugin will be treated as a app by django and can maybe rack the whole instance.
### Define Metadata
+92 -3
View File
@@ -979,9 +979,98 @@ Length: {{ length_value }}
{% endraw %}
```
## Notes
[Notes](../concepts/notes.md) are rich-text documents that can be attached to most InvenTree model instances. Two template tags are available for accessing note content in a report.
### note
The `note` tag returns the rendered HTML content of a note, ready to embed directly in a report. Any images embedded in the note are automatically resolved to their base64-encoded data so that they appear in the generated PDF.
::: report.templatetags.report.note
options:
show_docstring_description: false
show_source: False
If no `title` argument is given, the [primary note](../concepts/notes.md#primary-note) is returned. If a `title` is given, the note whose title matches (case-insensitively) is returned instead. An empty string is returned when no matching note exists.
#### Example
```html
{% raw %}
{% load report %}
<!-- Render the primary note for the part -->
{% note part as part_note %}
<div>{{ part_note }}</div>
<!-- Render a note by title -->
{% note part "Assembly Instructions" as instructions %}
<div>{{ instructions }}</div>
{% endraw %}
```
!!! info "Safe HTML Output"
The `note` tag returns pre-sanitized HTML and is marked safe for direct template rendering. Do **not** additionally wrap it with `| safe` or `| markdownify` — the content has already been processed.
### note_instance
The `note_instance` tag returns the `Note` object itself, giving access to its individual fields. This is useful when you need to display the note title, description, or metadata alongside its content.
::: report.templatetags.report.note_instance
options:
show_docstring_description: false
show_source: False
A `Note` object exposes the following attributes:
| Attribute | Description |
| --- | --- |
| `title` | The title of the note |
| `description` | An optional short description of the note |
| `content` | The raw HTML content of the note |
| `primary` | `True` if this is the primary note for the model instance |
| `updated` | Timestamp of the last modification |
| `updated_by` | The user who last modified the note |
#### Example
```html
{% raw %}
{% load report %}
{% note_instance part as primary_note %}
{% if primary_note %}
<h3>{{ primary_note.title }}</h3>
{% if primary_note.description %}<p><em>{{ primary_note.description }}</em></p>{% endif %}
{% note part as note_content %}
<div>{{ note_content }}</div>
{% endif %}
{% endraw %}
```
### Iterating Over All Notes
When a model has multiple notes and you want to render all of them, access the `notes` queryset directly:
```html
{% raw %}
{% load report %}
{% for n in part.notes.all %}
<h3>{{ n.title }}</h3>
{% note part n.title as note_content %}
<div>{{ note_content }}</div>
{% endfor %}
{% endraw %}
```
## Rendering Markdown
Some data fields (such as the *Notes* field available on many internal database models) support [markdown formatting](https://en.wikipedia.org/wiki/Markdown). To render markdown content in a custom report, there are template filters made available through the [django-markdownify](https://github.com/erwinmatijsen/django-markdownify) library. This library provides functionality for converting markdown content to HTML representation, allowing it to be then rendered to PDF by the InvenTree report generation pipeline.
Some data fields (such as those provided by custom plugin models) may support [markdown formatting](https://en.wikipedia.org/wiki/Markdown). To render markdown content in a custom report, there are template filters made available through the [django-markdownify](https://github.com/erwinmatijsen/django-markdownify) library. This library provides functionality for converting markdown content to HTML representation, allowing it to be then rendered to PDF by the InvenTree report generation pipeline.
!!! info "Notes"
[Notes](../concepts/notes.md) content is rich-text (stored as HTML) rather than markdown, and is already sanitized. Use the [note](#note) tag to render it - do not pass it through `markdownify`.
To render markdown content in a report, consider the following simplified example:
@@ -990,9 +1079,9 @@ To render markdown content in a report, consider the following simplified exampl
{% load markdownify %}
<h3>Part Notes</h3>
<h3>Description</h3>
<p>
{{ part.notes | markdownify }}
{{ some_markdown_field | markdownify }}
</p>
{% endraw %}
```
+1
View File
@@ -107,6 +107,7 @@ nav:
- Project Codes: concepts/project_codes.md
- Attachments: concepts/attachments.md
- Parameters: concepts/parameters.md
- Notes: concepts/notes.md
- Tags: concepts/tags.md
- Barcodes:
- Barcode Support: barcodes/index.md