[Feature] Changelog / Edit History (#13069)

* Initial app scaffolding

* Skeleton for AuditLogTable

* Add UI table

* Add audit history panel to admin center

* Add auditing for models in the 'company' app

* Gate API access of user staff status

* Display item history in UI

* Track 'attachment' history

* Track 'parameter' changes

* Audit support for "common" models

* Track changed for "build" models

* Track history for "order" models:

- PurchaseOrder
- SalesOrder
- SalesOrderShipment
- ReturnOrder

Also track changes against LineItem and ExtraLineItem objects

* Register custom admin class

* Support Part history

* BomItem changes

* Fix for BulkCreateMixin

* Tweak unit tests

* Use Viewset

* Adjust error code behavior

* Setting to keep summary entries

* Refactor helper for middlewares

* Tweak unit tests

* Fixes for ModelInformationDict

* Track pricing changes

* Add model type filter for auditlog table

* Specify filter field type

* Hide HistoryPanel if auding is disabled

* add docs

* Add "--summary" option to export_records task

* Playwright tests

* Track task responsible for data changes

* Updated unit tests

* record changes to global settings

* Update docs

* Ensure values are stringified

* Fix for globalsetting

* Patch unit test

* Tweak playwright tests

* playwright test fix

* use main branch for demo data

* Update CHANGELOG
This commit is contained in:
Oliver authored and GitHub committed 2026-10-25 12:36:13 +11:00
1 parent cdb3b1ecf7
commit c475e9bfcf
91 files changed
+7753 -102

No files matched your search

+1
View File
@@ -28,6 +28,7 @@ The `OPTIONS` endpoint provides the following information:
| name | The human-readable name of the API endpoint |
| description | Descriptive detail for the endpoint, extracted from the python docstring |
| actions | Contains the available HTTP actions and field information (see below) |
| filters | For list endpoints, the available choices for any choice-based query filters (e.g. `model_type` for the audit log). Only filters with a fixed set of choices are listed - filters on related models are not |
Specific details are provided on the available attributes of each field:
Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

+112
View File
@@ -0,0 +1,112 @@
---
title: Audit Log
---
## Audit Log
InvenTree keeps an *audit log* (change history) of the changes made to key database models. Each time a supported item is created, updated or deleted, an entry is written to the audit log, recording:
- **When** the change was made
- **Who** made the change (the user account associated with the request)
- **What** was changed (the item, and the old and new values of each modified field)
- **How** the change was made (the source of the change, e.g. an API request or a background task)
The audit log is useful for tracing the history of an item, understanding *why* a value is what it is, and identifying which user made a particular change.
!!! info "Version Information"
Audit history is not recorded prior to InvenTree version 1.7.0. Any changes made before upgrading to version 1.7.0 will not appear in the audit log.
### How Auditing Works
Auditing in InvenTree is implemented using the [django-auditlog](https://github.com/jazzband/django-auditlog) library. Changes are captured at the database model level, so they are recorded regardless of how the change was made - whether via the user interface, the [REST API](../api/index.md), the Python bindings, or a plugin.
Some notes on what is recorded:
- **Field changes** - For updates, only the fields which have actually changed are recorded. Saving an item without changing any values does not create an audit entry.
- **Parent linkage** - Many audited models are linked to a *parent* item. For example, a purchase order line item is linked to its purchase order, and a BOM item is linked to its assembly part. Changes to child items are displayed in the history of the parent item.
- **Files** - For file fields (such as attachments), only the filename is recorded, not the file contents.
- **Request information** - Each entry records the user who made the change, along with the remote IP address and a correlation ID which links together all changes made in a single request. The username is retained, even if the user account is later deleted.
- **Background tasks** - Changes made by background tasks are recorded against the name of the task which made the change.
- **System settings** - Changes to [global settings](../settings/global.md) are recorded (and are only visible to staff users). Internal settings which are updated automatically by the system are not recorded, and the values of protected settings are never stored.
- **Audit settings** - Changes to the audit log settings are *always* recorded, even if the audit log is disabled. This ensures that disabling the audit log (or adjusting the retention period) cannot go unnoticed.
#### What Is Not Recorded
To keep the audit log focused and performant, some changes are intentionally *not* recorded:
- **Derived fields** - Internal or calculated fields (such as tree structure fields, cached pricing values and metadata) are excluded.
- **Data import** - Changes made using the [data import](./data_import.md) tool are not recorded.
- **Stock items** - Changes to stock items are tracked separately using [stock tracking](../stock/tracking.md).
- **Unsupported models** - Only the models listed below are audited.
### Audited Models
The following models are currently audited. Where a model has a *parent*, changes to that model are also displayed in the history of the parent item.
{{ audited_models() }}
### Viewing Change History
#### Detail Pages
When enabled, a *History* panel is displayed on the detail page for supported items (such as parts, companies and orders). This panel shows all changes made to the item, including changes made to any linked child items.
{{ image("concepts/audit_part_history.png", "Part change history") }}
For example, the history panel for a purchase order shows changes made to the order itself, as well as changes to its line items:
{{ image("concepts/audit_po_history.png", "Purchase order change history") }}
The *History* panel is only displayed if **all** of the following conditions are met:
- The audit log is enabled (via the `Enable Audit Log` system setting)
- The user is a *staff* user
- The user has enabled the *Show Change History* user setting
{{ image("concepts/audit_user_setting.png", "Show change history setting") }}
#### Admin Center
Staff users can view the complete audit log in the *Audit Log* panel of the [Admin Center](../settings/admin.md). This panel shows all recorded changes across all audited models, and provides access to the audit log settings.
{{ image("concepts/audit_admin_center.png", "Audit log in the admin center") }}
The audit log table can be filtered by action, user, date range and model type:
{{ image("concepts/audit_filter.png", "Filter by model type") }}
### Permissions
Access to the audit log is restricted:
- The audit log (including the `/api/audit/` API endpoint) is only available to *staff* users.
- Audit entries are additionally filtered by the user's [role permissions](../settings/permissions.md). A user can only view audit entries for models which they have permission to view.
- Some models (e.g. attachments and parameters) require the user to also have permission to view the *parent* model.
- The remote IP address associated with each entry is only visible to staff users.
### Data Retention
By default, audit log entries are kept indefinitely. Over time, the audit log may grow to a significant size. InvenTree can be configured to automatically delete old audit log entries, via a scheduled task which runs daily.
When deleting old entries, the *Retain Audit Log Summary* setting can be used to keep a minimal history for each item - the creation entry, and the most recent change - while still removing older intermediate entries.
### Settings
The following settings are available to control the behavior of the audit log, and can be adjusted in the *Audit Log* panel of the Admin Center:
{{ image("concepts/audit_admin_settings.png", "Audit log settings") }}
#### System Settings
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ globalsetting("AUDIT_LOG_ENABLED") }}
{{ globalsetting("AUDIT_LOG_DELETE_OLD_ENTRIES") }}
{{ globalsetting("AUDIT_LOG_DELETE_DAYS") }}
{{ globalsetting("AUDIT_LOG_DELETE_KEEP_SUMMARY") }}
#### User Settings
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ usersetting("DISPLAY_HISTORY_TAB") }}
+7
View File
@@ -255,6 +255,13 @@ CORS settings only affect user browsers that respect them. Setting them correctl
Use the INVENTREE_CORS_ORIGIN_ALLOW_ALL and INVENTREE_CORS_ORIGIN_WHITELIST settings - see the [Server Access settings](../start/config.md#server-access).
#### INVE-W17
**Audit log middleware missing from configuration - Backend**
The `middleware` list provided in the configuration file does not include the audit log middleware (`audit.middleware.InvenTreeAuditlogMiddleware`). This middleware is required for audit log entries to record which user made each change.
The middleware has been added automatically. To remove this warning, add it to the `middleware` list in your configuration file (after `django_structlog.middlewares.RequestMiddleware`).
### INVE-I (InvenTree Information)
Information — These are not errors but information messages. They might point out potential issues or just provide information.
+29
View File
@@ -91,6 +91,9 @@ with open(gen_base.joinpath('inventree_report_context.json'), encoding='utf-8')
# User permission roles
with open(gen_base.joinpath('inventree_roles.json'), encoding='utf-8') as f:
ROLES = json.load(f)
# Audited models
with open(gen_base.joinpath('inventree_audited_models.json'), encoding='utf-8') as f:
AUDITED_MODELS = json.load(f)
def get_repo_url(raw=False):
@@ -495,6 +498,32 @@ def define_env(env):
return ret_data
@env.macro
def audited_models():
"""Render a markdown table of the models which are recorded in the audit log.
The table is built directly from `docs/generated/inventree_audited_models.json`
(produced by the `export_audited_models` management command, sourced from
the models which implement `InvenTreeAuditMixin`), so it can never drift out
of sync with the models actually audited in the source code.
"""
global AUDITED_MODELS
ret_data = '| Category | Model | Model Type | Parent | Notes |\n'
ret_data += '| --- | --- | --- | --- | --- |\n'
for model in AUDITED_MODELS:
parent = model['parent']
notes = model['parent_note']
if fields := model['include_fields']:
fields = ', '.join(f'`{field}`' for field in fields)
notes = f'Only the following fields are recorded: {fields}'
ret_data += f'| {model["app"]} | {model["name"]} | `{model["model_type"]}` | {parent} | {notes} |\n'
return ret_data
@env.macro
def report_context(type_: Literal['models', 'base'], model: str):
"""Extract information on a particular report context."""
+1
View File
@@ -109,6 +109,7 @@ nav:
- Parameters: concepts/parameters.md
- Notes: concepts/notes.md
- Tags: concepts/tags.md
- Audit Log: concepts/audit.md
- Barcodes:
- Barcode Support: barcodes/index.md
- Internal Barcodes: barcodes/internal.md