Compare commits

...

128 Commits

Author SHA1 Message Date
Oliver
7b181bb5ae [API] Query improvements (#11034)
* Improve prefetch fields for API

* Cache ContentType queryset for getModelsWithMixin

- Called a LOT of times for an options request
- Store the list in the session cache
- Much faster than redis - and expires after the session is complete

* Skip optional prefetch for options requests

* Custom implementation of DjangoModelPermission

- Cache the queryset against the view
- Prevents multiple hits for OPTIONS request
- Saves > 100ms on /stock/ options request
2025-12-17 20:20:59 +11:00
dependabot[bot]
145f4751c2 chore(deps): bump filelock from 3.20.0 to 3.20.1 in /src/backend (#11032)
* chore(deps): bump filelock from 3.20.0 to 3.20.1 in /src/backend

Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.20.0 to 3.20.1.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.20.0...3.20.1)

---
updated-dependencies:
- dependency-name: filelock
  dependency-version: 3.20.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-17 10:34:10 +11:00
Matthias Mair
8a614f4501 deps(backend): bump allauth (#11030)
* bump allauth

* fix api schema generation

* bump api
2025-12-17 10:20:58 +11:00
Oliver
140c65b26c [API] Tags filters (#11021)
* Add optional "tags" field

* Refactor "tags" field

- Off by default
- Only prefetch when requested (expensive)
- Ref: https://github.com/inventree/InvenTree/pull/11012
- Ref: https://github.com/inventree/InvenTree/issues/11002
- Closes https://github.com/inventree/InvenTree/issues/10996

* Bump API version

* Tweak unit tests

* Ensure all fields are available when writing data

* Handle case where request has *no* method
2025-12-17 07:14:56 +11:00
Matthias Mair
2eccf13c93 Default Supplier Support Missing in 1.X.X (#10980)
Fixes #10979
2025-12-16 22:12:35 +11:00
Oliver
19239c8621 [API] API refactoring (#11023)
* API refactoring

- Specify prefetch_fields for optional child serializers
- Ref: https://github.com/inventree/InvenTree/pull/11012/

* Fixes for unit tests
2025-12-16 21:13:28 +11:00
Oliver
c78b03b6ff [ui] Table fix (#11022)
* Optimize printing actions

- Don't fire API request until dialog is opened
- This prevents a lot of unnecessary API calls from the UI

* Do not fire export options query until required
2025-12-16 18:15:32 +11:00
Oliver
ba7b776257 [refactor] Optional prefetch (#11012)
* Automatic prefetch of related fields for enable_filter

- Allows us to *not* prefetch fields (expensive) when they are not going to be used
- Enables re-usable components for common detail fields

* Refactor "project_code_detail" filter into common component

- Automatically apply correct prefetch fields

* Refactor 'parameters' annotation

- add 'enable_parameters_filter' function
- Prefetch parameters only when needed
- Refactor / consolidate code

* Refactor SupplierPartSerializer

- Make fields switchable
- Ensure correct prefetch_related

* Refactor serializer for ManufacturerPart

* Refactor BuildSerializer

* Refactor PurchaseOrderSerializer

* Refactor SalesOrderSerializer

* Refactor ReturnOrderSerializer

* Remove debug statements

* Tweaks

* Simplify custom filterable fields

* Bump API version

* Fix for data export

* Additional unit tests

* Remove unused "prefetch_func" option

* Refactor PurchaseOrderLineItemList

* Refactor SalesOrderLineItemList

* Refactor ReturnOrderLineItem

* Cleanup "pretty_name"

* Fix for build list

* Refactoring StockItem API endpoint

- Needs significant work still

* Refactoring for BuildLineSerializer

* Keep all optional fields when exporting data

* Improve "UserRoles" API endpoint

- Prefetch roles
- Prevents significant number of db hits

* Prefetch Parameter API list

* Bug fix for exporting logic

* Specify InvenTreeOutputOption

* Optional prefetch for primary_address

* Fix typing

* Fix unit test

* fixes for playwright tests

* Update Part API

- Improved prefetching

* Fix for prefetch
2025-12-16 14:46:17 +11:00
dependabot[bot]
1e120c3589 chore(deps): bump the dependencies group with 3 updates (#11018)
Bumps the dependencies group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [actions/stale](https://github.com/actions/stale) and [crowdin/github-action](https://github.com/crowdin/github-action).


Updates `actions/checkout` from 6.0.0 to 6.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](1af3b93b68...8e8c483db8)

Updates `actions/stale` from 10.1.0 to 10.1.1
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](5f858e3efb...997185467f)

Updates `crowdin/github-action` from 2.12.0 to 2.13.0
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](08713f00a5...60debf382e)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: actions/stale
  dependency-version: 10.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: crowdin/github-action
  dependency-version: 2.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 09:04:49 +11:00
Michael
6cbed50794 Remove dublicate lines (#11016) 2025-12-16 08:59:49 +11:00
Oliver
71c2f5ca73 [refactor] Improve primary_address annotation for Company API (#11006)
* Refactor primary_address annotation

- Remove SerializerMethodField
- Better cache introspection

* Allow address detail to be optional

* Refactor address caching

* Fix primary_address annotation

* Remove "address_count" field

- Pointless annotation which is not used anywhere

* Update API version

* Tweak docs page

* Tweak unit tests
2025-12-14 21:54:07 +11:00
Mitch Davis
a727c4e2f9 Improve the documentation installation instructions. (#11011)
Co-authored-by: Mitch Davis <mjd@afork.com>
2025-12-14 21:26:06 +11:00
Oliver
0460e81f9a [refactor] Build list (#11010)
- Prefetch project_code
- Annotate parameter data
2025-12-14 19:09:01 +11:00
Oliver
9c6d16baba [refactor] Generic status API (#11009)
* Fix docs formatting

* [refactor] cache custom states

- Generic state API endpoint executed  query for each state type
- We can run a single database query and cache these in memory
- Reduces query time by ~50%
2025-12-14 19:08:52 +11:00
Oliver
2a20eeb033 Remove prefetch_related from parametric data filter (#11007)
- Not required as we do not process the parameter fields in python
2025-12-14 19:08:42 +11:00
Oliver
be8911eed3 Fix docs formatting (#11008) 2025-12-14 17:58:59 +11:00
Michael
d3d957e924 Rearrange python package installs in are metal setup (#11005)
* Reorder pip installation steps in bare metal setup

* Reorder pip installation steps in bare metal setup

* remove unused lines
2025-12-14 09:38:49 +11:00
dependabot[bot]
2600690fc1 chore(deps): bump the dependencies group across 1 directory with 2 updates (#11003)
* chore(deps): bump the dependencies group across 1 directory with 2 updates

Bumps the dependencies group with 2 updates in the /src/backend directory: [django-q2](https://github.com/GDay/django-q2) and [sentry-sdk](https://github.com/getsentry/sentry-python).


Updates `django-q2` from 1.8.0 to 1.9.0
- [Release notes](https://github.com/GDay/django-q2/releases)
- [Changelog](https://github.com/django-q2/django-q2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GDay/django-q2/compare/v1.8.0...v1.9.0)

Updates `sentry-sdk` from 2.46.0 to 2.47.0
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.46.0...2.47.0)

---
updated-dependencies:
- dependency-name: django-q2
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: sentry-sdk
  dependency-version: 2.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-14 07:01:09 +11:00
Oliver
0a2b53789a [dev] django silk - advanced profiling (#11004)
* django silk - advanced profiling

- Adds option for enabling advanced silk profiling

* Enable binary file generation
2025-12-13 20:11:53 +11:00
Oliver
edc68b21ab [dev] Django silk (#11001)
* Add developer support for django-silk

* Update docs

* Fix typo

* Tweak docs

* Revert ty version
2025-12-13 16:45:17 +11:00
Oliver
2d14364a4d Add helper funcs for debugging (#10997)
* Add helper funcs for debugging

* Exclude from coverage

* Raise error if not in debug

* Raise error if not in DEBUG mode
2025-12-13 10:19:37 +11:00
Oliver
e4a6c1abfb Add information about building docs (#10998) 2025-12-12 21:14:25 +11:00
Oliver
20c7a5b5b8 Barcode scan tweaks (#10992)
* Remove duplicate tooltip

* Adjust default value

* docs update

* Tweak unit test

* Fix playwright tests
2025-12-11 16:19:47 +11:00
Oliver
0723c74567 [UI] Spotlight Updates (#10994)
- Fix broken "admin" action
- Add action to jump to plugins page
2025-12-11 16:19:37 +11:00
Oliver
c5548aabde [security] Bump react deps (#10991) 2025-12-10 23:02:06 +11:00
Oliver
dbb55a63fd [docs] Adjusting serial numbers (#10989)
Add brief docs on adjusting serial numbers for a stock item
2025-12-10 10:21:32 +11:00
Oliver
c489423c07 [docs] database update (#10985)
Instructions for updating from one database backend version to another
2025-12-09 19:33:38 +11:00
Oliver
bed83bc038 Improve docs for report merging (#10984) 2025-12-09 09:57:10 +11:00
Matthias Mair
dc409c4efb bump precommit (#10981)
* bump precommit

* also bump gitleaks
2025-12-09 07:52:16 +11:00
dependabot[bot]
1badf6557f chore(deps): bump docker/metadata-action in the dependencies group (#10982)
Bumps the dependencies group with 1 update: [docker/metadata-action](https://github.com/docker/metadata-action).


Updates `docker/metadata-action` from 5.9.0 to 5.10.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](318604b99e...c299e40c65)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 07:51:47 +11:00
github-actions[bot]
3ca124c1a9 New Crowdin translations by GitHub Action (#10960)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-08 15:27:59 +11:00
Oliver
ae70c22485 [UI] Barcode form inputs (#10973)
* Add barcode buttons to related fields

- Only field types which support barcodes

* Add per-user settings for barcode support

* Fill form field with scanned data

* Updated docs

* Fix duplicate setting

* Add playwright tests

* Fix duplicate setting in docs

* Fix broken link

* Fix memo deps

* Fix typo

* Remove setting

* Updated playwright tests

* Improved typing
2025-12-07 18:31:32 +11:00
Oliver
f4186e73ff Docs spelling fixes (#10972) 2025-12-07 08:11:26 +11:00
mlam19
27fd2bcb8d Enable input controls in PDF reports (#10969)
* Enable input controls in PDF reports

Modify options passed to WeasyPrint.

* Changelog update for issue #10969.

* Add usage notes for PDF forms to report docs (#10969).
2025-12-07 07:35:52 +11:00
Matthias Mair
a0cfdd72a5 fix(frontend): deps (#10970)
https://github.com/inventree/InvenTree/security/dependabot/260
https://github.com/inventree/InvenTree/security/dependabot/243
https://github.com/inventree/InvenTree/security/dependabot/258
https://github.com/inventree/InvenTree/security/dependabot/257
2025-12-07 07:04:06 +11:00
dependabot[bot]
0c4a637739 chore(deps): bump the dependencies group across 2 directories with 4 updates (#10959)
* chore(deps): bump the dependencies group across 2 directories with 4 updates

Bumps the dependencies group with 2 updates in the /docs directory: [mkdocstrings[python]](https://github.com/mkdocstrings/mkdocstrings) and [neoteroi-mkdocs](https://github.com/Neoteroi/mkdocs-plugins).
Bumps the dependencies group with 2 updates in the /src/backend directory: [sentry-sdk](https://github.com/getsentry/sentry-python) and [pre-commit](https://github.com/pre-commit/pre-commit).


Updates `mkdocstrings[python]` from 0.30.1 to 1.0.0
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.1...1.0.0)

Updates `neoteroi-mkdocs` from 1.1.3 to 1.2.0
- [Release notes](https://github.com/Neoteroi/mkdocs-plugins/releases)
- [Changelog](https://github.com/Neoteroi/mkdocs-plugins/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Neoteroi/mkdocs-plugins/compare/v1.1.3...v1.2.0)

Updates `sentry-sdk` from 2.45.0 to 2.46.0
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.45.0...2.46.0)

Updates `pre-commit` from 4.4.0 to 4.5.0
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v4.4.0...v4.5.0)

---
updated-dependencies:
- dependency-name: mkdocstrings[python]
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: dependencies
- dependency-name: neoteroi-mkdocs
  dependency-version: 1.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: sentry-sdk
  dependency-version: 2.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: pre-commit
  dependency-version: 4.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-07 00:23:22 +11:00
Matthias Mair
be9ec848c3 fix(backend): fix various vulns (#10967)
* fix urllib

* fix fonttools

* fix django
2025-12-07 00:13:57 +11:00
Oliver
d7caddb135 Update email docs (#10966)
* Map shorthand email backends

* Revert settings changes

* Update email docs

* Tweak docs
2025-12-07 00:13:45 +11:00
Oliver
efc8fb816d Fix for string form fields (#10814)
* Fix for string form fields

- replace null values with empty strings

* Expose more serializer metadata

* Check if null values are not allowed

* Fix type

* Try removing feature

* Reduce deltas

* Remove extra field attrs entirely (for testing)

* Comment out changes

* Tweak form values

* Fix for form validation
2025-12-06 22:54:29 +11:00
dependabot[bot]
ffec087618 chore(deps): bump urllib3 from 2.5.0 to 2.6.0 in /contrib/dev_reqs (#10961)
* chore(deps): bump urllib3 from 2.5.0 to 2.6.0 in /contrib/dev_reqs

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.5.0...2.6.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-06 22:38:44 +11:00
dependabot[bot]
e98a612d9a chore(deps): bump urllib3 from 2.5.0 to 2.6.0 in /docs (#10962)
* chore(deps): bump urllib3 from 2.5.0 to 2.6.0 in /docs

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.5.0...2.6.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-06 22:38:30 +11:00
Oliver
3a18934b83 Obvserve default values for part forms (#10964)
- Closes https://github.com/inventree/InvenTree/issues/10909
- Use global setting values as defaults
2025-12-06 20:20:39 +11:00
Oliver
7028bb84ff Ignore ready warning when calling getModelsWithMixin (#10963) 2025-12-06 19:48:14 +11:00
github-actions[bot]
d0c23bd523 New Crowdin translations by GitHub Action (#10957)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-06 10:19:00 +11:00
Oliver
92099bab3c Fix for crowdin sync (#10956)
- Something going on with credentials
- Set persist to false
2025-12-05 13:22:25 +11:00
Oliver
744af5ba42 Update version checks: (#10954)
* Update version checks:

- Add error code for old python version
- Fix min python version in docs
- Various spelling fixes in docs

* Fix docs link

* Revert change to docs version string

* Bug fix
2025-12-05 12:50:32 +11:00
Oliver
fa0d892a62 [WIP] Generic parameters (#10699)
* Add ParameterTemplate model

- Data structure duplicated from PartParameterTemplate

* Apply data migration for templates

* Admin integration

* API endpoints for ParameterTemplate

* Scaffolding

* Add validator for ParameterTemplate model type

- Update migrations
- Make Parameter class abstract (for now)
- Validators

* API updates

- Fix options for model_type
- Add API filters

* Add definition for Parameter model

* Add django admin site integration

* Update InvenTreeParameterMixin class

- Fetch queryset of all linked Parameter instances
- Ensure deletion of linked instances

* API endpoints for Parameter instances

* Refactor UI table for parameter templates

* Add comment for later

* Add "enabled" field to ParameterTemplate model

* Add new field to serializer

* Rough-in new table

* Implement generic "parameter" table

* Enable parameters for Company model

* Change migration for part parameter

- Make it "universal"

* Remove code for ManufacturerPartParameter

* Fix for filters

* Add data import for parameter table

* Add verbose name to ParameterTemplate model

* Removed dead API code

* Update global setting

* Fix typos

* Check global setting for unit validation

* Use GenericForeignKey

* Add generic relationship to allow reverse lookups

* Fixes for table structure

* Add custom serializer field for ContentType with choices

* Adds ContentTypeField

- Handles representation of content type
- Provides human-readable options

* Refactor API filtering for endpoints

- Specify ContentType by ID, model or app label

* Revert change to parameters property

* Define GenericRelationship for linking model

* Refactoring some code

* Add a generic way to back-annotate and prefetch parameters for any model type

* Change panel position

* Directly annotate parameters against different model serializers

* remove defunct admin classes

* Run plugin validation against parameter

* Fix prefetching for PartSerializer

* Implement generic "filtering" against queryset

* Implement generic "ordering" by parameter

* Make parametric table generic

* Refactor segmented panels

* Consolidate part table views

* Fix for parametric part table

- Only display parameters for which we know there is a value

* Add parametric tables for company views

* Fix typo in file name

* Prefetch to reduce hits

* Add generic API mixin for filtering and ordering by parameter

* Fix hook for rebuilding template parameters

* Remove serializer

* Remove old models

* Fix code for copying parameters from category

* Implement more parametric tables:

- ManufacturerPart
- SupplierPart
- Fixes and enhancements

* Add parameter support for orders

* Add UI support for parameters against orders

* Update API version

* Update CHANGELOG.md

* Add parameter support for build orders

* Tweak frontend

* Add renderer

* Remove defunct endpoints

* Add migration requirement

* Require contenttypes to be updated

* Update migration

* Try using ID val

* Adjust migration dependencies

* fix params fixture

* fix schema export

* fix modelset

* Fixes for data migration

* tweak table

* Fix for Category Parameters

* Use branch of demo dataset for testing

* Add parameteric build order table

* disable broken imports

* remove old model from ruleset

* correct test

* Table tweaks

* fix test

* Remove old model type

* fix test

* fix test

* Refactor mixin to avoid specifying model type manually

* fix test

* fix resolve name

* remove unneeded import

* Tweak unit testing

* Fix unit test

* Enable bulk-create

* More fixes

* More unit test tweaks

* Enhancements

* Unit test fixes

* Add some migration tests

* Fix admin tests

* Fix part tests

* adapt expectation

* fix remaining typecheck

* Docs updates

* Rearrange models

* fix paramater caching

* fix doc links

* adjust assumption

* Adjust data migration unit tests

* docs fixes

* Fix docs link

* Fixes

* Tweak formatting

* Add doc for setting

* Add metadata view for parameters

* Add metadata view for ParamterTemplate

* Update CHANGELOG file

* Deconflict model_type fields

* Invert key:value

* Revert "Invert key:value"

This reverts commit d555658db2.

* fix assert

* Update API rev notes

* Initial unit tests for API

* Test parameter create / edit / delete via the API

* Add some more unit tests for the API

* Validate queryset annotation

- Add unit test with large dataset
- Ensure number of queries is fixed
- Fix for prefetching check

* Add breaking change info to CHANGELOG.md

* Ensure that parameters are removed when deleting the linked object

* Enhance type hinting

* Refactor part parameter exporter plugin

- Any model which supports parameters can use this now
- Update documentation

* Improve serializer field

* Adjust unit test

* Reimplement checks for locked parts

* Fix unit test for data migration

* Fix for unit test

* Allow disable edit for ParameterTable

* Fix supplier part import wizard

* Add unit tests for template API filtering

* Add playwright tests for purchasing index

* Add tests for manufacturing index page

* ui tests for sales index

* Add data migration tests for ManufacturerPartParameter

* Pull specific branch for python binding tests

* Specify target migration

* Remove debug statement

* Tweak migration unit tests

* Add options for spectacular

* Add explicit choice options

* Ensure empty string values are converted to None

* Don't use custom branch for python checks

* Fix for migration test

* Fix migration test

* Fix reference target

* Remove duplicate enum in spectactular.py

* Add null choice to custom serializer class

* [UI] Edit shipment details

- Pass "pending" status through to the form

* New migration strategy:

part.0144:
- Add new "enabled" field to PartParameterTemplate model
- Add new ContentType fields to the "PartParameterTemplate" and "PartParameter" models
- Data migration for existing "PartParameter" records

part.0145:
- Set NOT NULL constraints on new fields
- Remove the obsolete "part" field from the "PartParameter" model

* More migration updates:

- Create new "models" (without moving the existing tables)
- Data migration for PartCataegoryParameterTemplate model
- Remove PartParameterTemplate and PartParameter models

* Overhaul of migration strategy

- New models simply point to the old database tables
- Perform schema and data migrations on the old models first (in the part app)
- Swap model references in correct order

* Improve checks for data migrations

* Bug fix for data migration

* Add migration unit test to ensure that primary keys are maintained

* Add playwright test for company parameters

* Rename underlying database tables

* Fixes for migration unit tests

* Revert "Rename underlying database tables"

This reverts commit 477c692076.

* Fix for migration sequencing

* Simplify new playwright test

* Remove spectacular collision

* Monkey patch the drf-spectacular warn function

* Do not use custom branch for playwright testing

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-04 20:41:36 +11:00
Oliver
c443b4e9b8 App ready warning (#10938)
* Fix for currency functions

- Prevent database access until after the 'common' app has loaded

* Add decorator to selectively ignore warnings

* Add reference to PR

* Fix variable assignment

* Use functools.wraps

* Add wrapper for loading machine registry

* Move decorator to ready.py

* Add missing code

* Set backup values to match default currency codes

* Bump API version
2025-12-04 19:30:14 +11:00
Tyler Tracy
2ffc2cb9fc Fixed typo in shebang interpreter directive (#10952) 2025-12-04 11:03:56 +11:00
dependabot[bot]
8715935bb9 chore(deps): bump django from 5.2.8 to 5.2.9 in /src/backend (#10950)
* chore(deps): bump django from 5.2.8 to 5.2.9 in /src/backend

Bumps [django](https://github.com/django/django) from 5.2.8 to 5.2.9.
- [Commits](https://github.com/django/django/compare/5.2.8...5.2.9)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.9
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-04 11:03:29 +11:00
Oliver
7920b0e670 Allow null values for InvenTreeDecimalField (#10948)
- Fixes bug related to importing null "rounding_multiple" BOM field
2025-12-04 07:04:07 +11:00
Oliver
3e35f439c0 [UI] Edit shipment details (#10944)
- Pass "pending" status through to the form
2025-12-03 17:28:13 +11:00
Matthias Mair
c8b1bfb716 refactor (frontend): address code scanning issues (#10935)
* remove unused defs

* optimize

* revert rename
2025-12-02 18:11:02 +11:00
Oliver
38b27271ac [bug] Handle TransactionManagementError (#10942)
In the case where we try to call refresh_from_db within an atomic transaction block, it will throw a TransactionManagementError
2025-12-02 17:21:24 +11:00
dependabot[bot]
4c45716843 chore(deps): bump actions/setup-python in the dependencies group (#10940)
Bumps the dependencies group with 1 update: [actions/setup-python](https://github.com/actions/setup-python).


Updates `actions/setup-python` from 6.0.0 to 6.1.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](e797f83bcb...83679a892e)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-02 16:31:59 +11:00
Oliver
c224606d8d [UI] refactor "inactive alerts" panel (#10913)
* Remove "inactive alerts" panel

* Improve messaging / colors

* Refactor to "system status" display

* remove duplicate messages

* Revert alert messages

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-12-01 23:07:26 +11:00
Oliver
ee4e200cf3 [devcontainer] revert postgres version (#10937)
- Our debian image (bookworm) supports only postgres:15
- Revert from postgres:17 to postgres:15 in the devcontainer
2025-12-01 16:46:59 +11:00
github-actions[bot]
bc5c306b6d New Crowdin translations by GitHub Action (#10871)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2025-11-30 21:03:53 +11:00
Oliver
3958e10875 Fix for python bindings CI check (#10934) 2025-11-30 20:01:35 +11:00
Matthias Mair
7b592f157c feat: add cooldown to dependabot (#10930) 2025-11-29 09:14:45 +11:00
Matthias Mair
be5814112d refactor(backend): port typo fixes from #10699 (#10926)
* typo fxes from #10699

* bump api version
2025-11-29 07:03:02 +11:00
Oliver
3b6b702bd5 Stock availability docs (#10927)
* Add deficit stock badge

* screenshots

* Add "stock availability" page

* Update stock index page
2025-11-28 12:54:16 +11:00
Oliver
3a4981056b Add note regarding redis password restrictions (#10925) 2025-11-28 09:05:47 +11:00
Oliver
5713cff1cb [bug] Stock adjust (#10914)
* Extra checks on backend

* Bug fix for adjustment forms

- Set default quantity of zero

* Additional unit testing (to ensure no regression)
2025-11-26 22:32:57 +11:00
Oliver
16b600af88 Docs updates (#10912)
- Small tweaks / additions
2025-11-26 21:27:13 +11:00
Karl Q.
0746a1131f docs: brief writeup for env-vars on webserver in Dockerfile (#10906)
* docs: brief writeup for env-vars on webserver in Dockerfile

Mates with https://github.com/inventree/InvenTree/pull/10900

nit, fix: spacing

2 -> 1

* docs: relocate

And simplify

---------

Co-authored-by: Karl Quinsland <contact@kquinsland.com>
2025-11-26 14:26:14 +11:00
Oliver
df6cbca197 Check / uncheck shipments from table (#10907) 2025-11-25 23:19:18 +11:00
dependabot[bot]
0542f0608d chore(deps): bump the dependencies group with 4 updates (#10903)
Bumps the dependencies group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [github/codeql-action](https://github.com/github/codeql-action), [anchore/sbom-action](https://github.com/anchore/sbom-action) and [svenstaro/upload-release-action](https://github.com/svenstaro/upload-release-action).


Updates `actions/checkout` from 5.0.0 to 6.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](08c6903cd8...1af3b93b68)

Updates `github/codeql-action` from 4.31.2 to 4.31.5
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](0499de31b9...fdbfb4d275)

Updates `anchore/sbom-action` from 0.20.9 to 0.20.10
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](8e94d75ddd...fbfd9c6c18)

Updates `svenstaro/upload-release-action` from 2.11.2 to 2.11.3
- [Release notes](https://github.com/svenstaro/upload-release-action/releases)
- [Changelog](https://github.com/svenstaro/upload-release-action/blob/master/CHANGELOG.md)
- [Commits](81c65b7cd4...6b7fa9f267)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: dependencies
- dependency-name: github/codeql-action
  dependency-version: 4.31.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: anchore/sbom-action
  dependency-version: 0.20.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: svenstaro/upload-release-action
  dependency-version: 2.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-25 22:02:18 +11:00
dependabot[bot]
bb5bd85716 chore(deps): bump pypdf from 6.3.0 to 6.4.0 in /src/backend (#10905)
* chore(deps): bump pypdf from 6.3.0 to 6.4.0 in /src/backend

Bumps [pypdf](https://github.com/py-pdf/pypdf) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.3.0...6.4.0)

---
updated-dependencies:
- dependency-name: pypdf
  dependency-version: 6.4.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-11-25 22:01:50 +11:00
dependabot[bot]
74e368b85b chore(deps): bump the dependencies group across 1 directory with 5 updates (#10879)
* chore(deps): bump the dependencies group across 1 directory with 5 updates

Bumps the dependencies group with 5 updates in the /src/backend directory:

| Package | From | To |
| --- | --- | --- |
| [blessed](https://github.com/jquast/blessed) | `1.23.0` | `1.25.0` |
| [django-flags](https://github.com/cfpb/django-flags) | `5.0.14` | `5.1.0` |
| [pypdf](https://github.com/py-pdf/pypdf) | `6.2.0` | `6.3.0` |
| [sentry-sdk](https://github.com/getsentry/sentry-python) | `2.44.0` | `2.45.0` |
| [coverage[toml]](https://github.com/coveragepy/coveragepy) | `7.11.3` | `7.12.0` |



Updates `blessed` from 1.23.0 to 1.25.0
- [Release notes](https://github.com/jquast/blessed/releases)
- [Changelog](https://github.com/jquast/blessed/blob/master/docs/history.rst)
- [Commits](https://github.com/jquast/blessed/compare/1.23...1.25)

Updates `django-flags` from 5.0.14 to 5.1.0
- [Release notes](https://github.com/cfpb/django-flags/releases)
- [Changelog](https://github.com/cfpb/django-flags/blob/main/docs/releasenotes.md)
- [Commits](https://github.com/cfpb/django-flags/compare/5.0.14...5.1.0)

Updates `pypdf` from 6.2.0 to 6.3.0
- [Release notes](https://github.com/py-pdf/pypdf/releases)
- [Changelog](https://github.com/py-pdf/pypdf/blob/main/CHANGELOG.md)
- [Commits](https://github.com/py-pdf/pypdf/compare/6.2.0...6.3.0)

Updates `sentry-sdk` from 2.44.0 to 2.45.0
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.44.0...2.45.0)

Updates `coverage[toml]` from 7.11.3 to 7.12.0
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.3...7.12.0)

---
updated-dependencies:
- dependency-name: blessed
  dependency-version: 1.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: django-flags
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: pypdf
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: sentry-sdk
  dependency-version: 2.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: coverage[toml]
  dependency-version: 7.12.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-11-24 23:36:39 +11:00
Karl Q.
5df42eda74 chore, fix: allow gunicorn webserver bind address to be adjusted (#10900)
In production mode.
Retain default IPv4 exclusive bind behavior.

Co-authored-by: Karl Quinsland <contact@kquinsland.com>
2025-11-24 18:38:31 +11:00
Oliver
7c7d7b6a21 Spelling fix (#10896)
* spwan -> spawn

* Prioriy -> Priority

* Bump API version

* Fix spelling in migration file
2025-11-24 17:49:13 +11:00
Oliver
89e3d605c5 Update postgres version (#10895)
- docker setup uses postgres:17
- Documentation incorrectly stated postgres:16
- Update CI to use postgres:17 also
2025-11-24 16:48:59 +11:00
Oliver
276041ae54 [bug] Migration test fix (#10899)
* Pop instead of get

* Better error handling for unit registry

* Prevent caching during database migrations

* Remove debug msg

* Revert changes
2025-11-24 14:52:13 +11:00
Matthias Mair
fcea1383d0 Installer missing some required packages from REQS (#10897)
Fixes #10813
2025-11-24 09:12:21 +11:00
Oliver
7d5429303e Error messages (#10894)
* Add INVE-E14 - Error in config file

* Add INVE-E14

* Fix duplicate code

* Fix numbering
2025-11-23 23:11:12 +11:00
Oliver
e590522909 [UI] Fix for PartTestResultTable (#10886)
* [UI] Fix for PartTestResultTable

- Filter properly by part instance

* Add a simple unit test

* Tweak unit test
2025-11-22 17:20:49 +11:00
Oliver
a7ff1250ba [bug] Serialize location (#10882)
* Properly set location id when serializing stock

* Add correct tracking entries

* Add unit test
2025-11-22 12:56:31 +11:00
Oliver
f50d568b23 [plugin] Add printing delay to sample machines (#10873)
* [plugin] Add printing delay to sample machines

- Useful for testing
- Default is zero

* Add backup_value to MachineSetting.get_setting

* Fix call to machine.get_setting

* Fix for printing logic

* Simplify code
2025-11-21 14:56:23 +11:00
Oliver
d5ee647c6e [ui] Label fix (#10874)
* Fix for sales order allocations:

- Improve UX
- Clearer intent

* Same fix for build order

* Also for build line sub table
2025-11-21 12:43:01 +11:00
Oliver
a76ec0a7b8 [UI] Suggested pricing (#10867)
* Refactor NumberField into separate component

* Add helper func to ensure a number is a number

* Use placeholder value for suggested sale price

* Fix for auto-fill

* Tweak price calculation

* Add UI testing for sales order price breaks

* Fix aria label name

* Annotate price breaks to supplier part

* Fetch price break data

* Support price breaks for purchase order pricing

* Fix required to prevent circular imports

* Add playwright tests for purchase order price breaks

* Bump API version

* Re-add output options for SupplierPriceBreakList

* Revert change

* Simplify unit test
2025-11-20 22:51:27 +11:00
Oliver
835c7784f9 Implement caching for unit registry: (#10870)
* Implement caching for unit registry:

- Registry could become out of sync across sessions
- Implement a simple caching system

* Simplify code
2025-11-20 22:31:33 +11:00
github-actions[bot]
f72efb804e New Crowdin translations by GitHub Action (#10736)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-20 21:39:11 +11:00
Oliver
468efbacfc [UI] Delete stock fix (#10868)
* Add helper func getOverviewUrl

* Redirect to parent page when stock item is counted to zero
2025-11-20 06:34:25 +11:00
Oliver
18c2a934b8 Better handling of an invalid config file (#10857)
* Better handling of an invalid config file

* Remove dud line
2025-11-19 16:21:17 +11:00
Oliver
a8f2a02d69 [bug] Media url fix (#10855)
* Bug fix for getMediaUrl

- Proper check for file type
- Fix "fully_qualified_url" for Attachment model

* Add unit test

* Fix typo

* Fix unit test

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
2025-11-19 15:50:05 +11:00
Oliver
7b38fa30bb Fix for shipping virtual parts (#10853)
* Additional checks for virtual parts in sales order process

* Prevent allocation against virtual parts

* Fix order of operations

* Adjust part form fields based on selections

* Prevent order locking

* Updated playwright tests

* Add unit test
2025-11-19 15:40:41 +11:00
Oliver
40fbb4d810 Offload stock consume operations (#10856)
- These can be expensive if there are a lot of allocated items
- Offload to the background worker
2025-11-19 15:40:24 +11:00
Oliver
d06d80fb99 Bug fix for sales order pricing (#10858)
* Bug fix for sales order pricing

- Clear sale price field if no pricing

* Adjust playwright tests
2025-11-19 14:49:51 +11:00
Oliver
2799c4d1fe Specify python version in netlify.toml (#10859) 2025-11-19 13:55:34 +11:00
Oliver
dab7223245 Updates for data exporting (#10854)
- Add option to exclude email logs from import / export
- Fix for pluginusersetting
2025-11-19 07:58:22 +11:00
Oliver
57a2de6ffc [UI] Remove duplicate action (#10844)
- "Build Output" is same as "stock item" in this case
2025-11-17 19:52:35 +11:00
Oliver
770f7a292e [UI] Fix for form OPTIONS query (#10840)
* [UI] Fix for form OPTIONS query

- Fetch OPTIONs each time form is opened
- Ensure default values are filled correctly
- Prevent issues with latching form state

* Add comment

* Add playwright test

- Check that the reference field increments properly

* Fix other Playwright tests
2025-11-16 21:59:27 +11:00
dependabot[bot]
2c508feeec chore(deps): bump the dependencies group across 2 directories with 7 updates (#10838)
* chore(deps): bump the dependencies group across 2 directories with 7 updates

Bumps the dependencies group with 2 updates in the /docs directory: [mkdocs-macros-plugin](https://github.com/fralau/mkdocs_macros_plugin) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).
Bumps the dependencies group with 5 updates in the /src/backend directory:

| Package | From | To |
| --- | --- | --- |
| [dulwich](https://github.com/dulwich/dulwich) | `0.24.8` | `0.24.10` |
| [sentry-sdk](https://github.com/getsentry/sentry-python) | `2.43.0` | `2.44.0` |
| [xmlsec](https://github.com/mehcode/python-xmlsec) | `1.3.14` | `1.3.17` |
| [coverage[toml]](https://github.com/coveragepy/coveragepy) | `7.11.2` | `7.11.3` |
| [pip-tools](https://github.com/jazzband/pip-tools) | `7.5.1` | `7.5.2` |



Updates `mkdocs-macros-plugin` from 1.4.1 to 1.5.0
- [Release notes](https://github.com/fralau/mkdocs_macros_plugin/releases)
- [Changelog](https://github.com/fralau/mkdocs-macros-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/fralau/mkdocs_macros_plugin/compare/v1.4.1...v1.5.0)

Updates `mkdocs-material` from 9.6.22 to 9.7.0
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.6.22...9.7.0)

Updates `dulwich` from 0.24.8 to 0.24.10
- [Release notes](https://github.com/dulwich/dulwich/releases)
- [Changelog](https://github.com/jelmer/dulwich/blob/master/NEWS)
- [Commits](https://github.com/dulwich/dulwich/compare/dulwich-0.24.8...dulwich-0.24.10)

Updates `sentry-sdk` from 2.43.0 to 2.44.0
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.43.0...2.44.0)

Updates `xmlsec` from 1.3.14 to 1.3.17
- [Release notes](https://github.com/mehcode/python-xmlsec/releases)
- [Commits](https://github.com/mehcode/python-xmlsec/compare/1.3.14...1.3.17)

Updates `coverage[toml]` from 7.11.2 to 7.11.3
- [Release notes](https://github.com/coveragepy/coveragepy/releases)
- [Changelog](https://github.com/coveragepy/coveragepy/blob/main/CHANGES.rst)
- [Commits](https://github.com/coveragepy/coveragepy/compare/7.11.2...7.11.3)

Updates `pip-tools` from 7.5.1 to 7.5.2
- [Release notes](https://github.com/jazzband/pip-tools/releases)
- [Changelog](https://github.com/jazzband/pip-tools/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jazzband/pip-tools/compare/v7.5.1...v7.5.2)

---
updated-dependencies:
- dependency-name: mkdocs-macros-plugin
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: mkdocs-material
  dependency-version: 9.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: dulwich
  dependency-version: 0.24.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: sentry-sdk
  dependency-version: 2.44.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: xmlsec
  dependency-version: 1.3.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: coverage[toml]
  dependency-version: 7.11.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dependencies
- dependency-name: pip-tools
  dependency-version: 7.5.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

* remove old pin message

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-11-16 07:32:03 +11:00
Oliver
aa9958bf11 [bug] State change fixes (#10832)
* Fix for setting custom status

* Fix for setting custom status when receiving stock items

* Allow caching for set_status

* Updated code and unit tests
2025-11-15 07:42:48 +11:00
Oliver
af4d9efd1d devcontainer: Expose debug server on all ports (#10833)
- Required for new docker setup in devcontainer
2025-11-14 19:28:31 +11:00
Oliver
39d181ae5f Make icons red again (#10816) 2025-11-14 18:12:41 +11:00
Oliver
ba9b5438b4 Char fix (#10827)
* Remove debouncing from text field

* Add debounce to data import field

* Only apply for strings values

* Fix unit test

* More unit test tweaks
2025-11-14 17:35:59 +11:00
Oliver
8cb808f613 Bug fix for loading boolean settings (#10826)
- Do not just cast to bool
- The string "False" casts to True in this case
- Use the function that supports strings
2025-11-14 08:26:24 +11:00
Oliver
0790dfff5b [UI] Auto-fill stock allocation for build order (#10819)
* Auto-fill stock allocation for build order

* Auto-fill allocation for sales orders

* Prevent recursive auto-fills
2025-11-13 23:16:32 +11:00
Oliver
af6cce3aba [UI] Duplicate supplier part (#10809)
* Allow duplication of supplier part

* Bug fix for pack quantity display

* Allow duplication of ManufacturerPart
2025-11-12 00:08:45 +11:00
Oliver
d829d3a548 [UI] Order form improvements (#10802)
* Auto-fill supplier parts in order wizard

* Copy supplier part SKU from order parts wizard

* Add "on_order" filter to BuildLine table

* Allow ordering by production and ordering quantities

* Allow specification of purchase price

* Bump API version

* Adjust UI testings
2025-11-11 17:29:18 +11:00
Oliver
f3c1cc12af PO receive fix (#10807)
* Extract note field when receiving stock items against PO

* Fix tracking entry when receiving item
2025-11-11 17:29:09 +11:00
Oliver
77f80385c9 Fix for pricing display (#10804) 2025-11-11 16:19:20 +11:00
Oliver
246c084e6e Reduce log output for Pint library (#10803)
- Reduce clutter in app startup due to unit redefinition
2025-11-11 13:33:43 +11:00
dependabot[bot]
682dd79326 chore(deps): bump the dependencies group with 2 updates (#10798)
Bumps the dependencies group with 2 updates: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) and [docker/metadata-action](https://github.com/docker/metadata-action).


Updates `docker/setup-qemu-action` from 3.6.0 to 3.7.0
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](29109295f8...c7c5346462)

Updates `docker/metadata-action` from 5.8.0 to 5.9.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](c1e51972af...318604b99e)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
- dependency-name: docker/metadata-action
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-11 12:54:14 +11:00
Matthias Mair
5d21bf2679 feat(backend)!: bump to dj 5.2 lts / py 3.11 (#10730)
* feat(backend): bump to 5.2 lts / python 3.11

This will give us support till 2027-10 (PEP 664)

* bump dependencies

* fix dflt version

* remove 3.9 precaution

* changes for 5.2

* changes for py 3.10

* debug command

* lower crypto again

* another lowering

* fix version string

* lower minimum version to 3.11

* update refs

* fix text

* reaking: remove now unsupported OS

* disable break

* remove temp changes

* fix ruff call

* fix remaining ruff warnings

* remove old arg

* lower allauth reqs

* replace old method

* fix issue with args passing beeing depreceated

* add changelog entries

* bump dependencies a bit further

* fix broken image init for now
might need a refactor

* fix another test

* refactor image name lookup

* mroe refactoring

* ensure str does not cause an issue

* update referenced function

* fix cal sig

* simplify method and add test

* refactor

* ignore wrong typings

* fix deprecated feature

* simplify

* ensure image tests do their job

* simplify

* re-add type check

* fix test

* fix assertations - wonder how long this was broken

* bump to newer versions

* bump deps

* fix assertation
2025-11-11 11:45:25 +11:00
Matthias Mair
f3e8482469 fix(backend): auth check middleware for specific media access (#10784)
* simplify

* fix return type

* handle token (app access)

* reduce lookup amount

* add positive test again

* add poisitive test

* move out settings

* add tests for Check2FAMiddleware

* add test for auth_request

* add a reverse name for auth_request

* auth tests refactors

* move test

* disable check for things that do not trigger

* fix typing for python 3.9

* make names clearer and add comments

* finish tests

* fix call

* re-enable mfa test without the timing component

* cleanup helper

* ignore easy out

* ignore scenario that can not happen
2025-11-10 08:58:58 +11:00
dependabot[bot]
726e852b7b chore(deps): bump django from 4.2.25 to 4.2.26 in /src/backend (#10781)
* chore(deps): bump django from 4.2.25 to 4.2.26 in /src/backend

Bumps [django](https://github.com/django/django) from 4.2.25 to 4.2.26.
- [Commits](https://github.com/django/django/compare/4.2.25...4.2.26)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 4.2.26
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix style

* also bump docker version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Mair <code@mjmair.com>
2025-11-08 22:08:11 +01:00
Matthias Mair
b048ca3a04 refactor (frontend): reduce message and time in recovery codes (#10778)
* reduce showing of wrong info boxes

* stop waiting 30 sec when there is an error
2025-11-06 10:44:03 +11:00
Matthias Mair
2fc7c7eb54 improve docker dx with empty migration set (#10774)
* if we are in docker and empty - init db

* use structlog

* remove logging change

* reduce diff

* ignore in test mode

* add changes from review
2025-11-06 10:42:34 +11:00
Oliver
2dfe6b5f41 [UI] MFA Refactor (#10775)
* Install otpauth package

* Add separate MFASettings components

* Refresh methods after registering token

* Simplify layout

* Add modal for deleting TOTP code

* Display recovery codes

* Adjust text

* Register webauthn

* Add longer timeouts

* Add workflow for removing webauthn

* Cleanup SecurityContext.tsx

* Add playwright testing for TOTP registration

* Spelling fixes

* Delete unused file

* Better clipboard copy
2025-11-05 22:54:47 +11:00
Oliver
d12102ba96 PurchaseOrderForms tweaks (#10773)
- Small visual changes
- Small bug fix for null record
2025-11-05 10:21:55 +11:00
Oliver
dbb799a0e0 [UI] BOM part category (#10772)
* Add "Category" column to BOM Table

* Enable sorting by category in BOM table

* Add Category column to Buildline table

* Add Category filter to BuildLineTable

* Adjust queryset prefetch

* Bump API version and update CHANGELOG
2025-11-05 10:04:28 +11:00
Oliver
fc3d130888 Refactor settings.py (#10766)
- Move drf-spectacular settings into separate file
- Cleanup settings.py core file
2025-11-05 07:00:58 +11:00
Oliver
7e943293c7 MFA middleware tweaks (#10768)
* MFA middleware tweaks

- Remove Check2FAMiddleware if MFA not enabled
- Refactor into middleware.py

* Update settings.py

- Disable MFA_SUPPORTED_TYPES if MFA_ENABLED is False

* Update docs
2025-11-05 07:00:42 +11:00
Oliver
2bc2966d22 Reduce text size (#10764)
- Smaller text for table hovercards
2025-11-04 15:00:52 +11:00
Oliver
8e20bc53db Consolidate data actions for BOM table (#10763)
* Consolidate data actions for BOM table

* Adjust playwright tests
2025-11-04 12:02:00 +11:00
Oliver
901846272b Auth Improvements (#10752)
* Return more detail in MFA failure response

* Reject auth requests for users who are inactive

* Move markdown config out of settings.py
2025-11-04 10:08:04 +11:00
dependabot[bot]
a22e163b19 chore(deps): bump github/codeql-action in the dependencies group (#10758)
Bumps the dependencies group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 4.31.0 to 4.31.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](4e94bd11f7...0499de31b9)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 07:28:55 +11:00
Lê Hoàng Nam
39f0054cd5 code (#10751)
Co-authored-by: Le Hoang Nam <lehoangnam040>
2025-11-03 21:14:38 +11:00
Oliver
153bcc1d5c Search stock items by supplier data (#10750)
* Search stock items by supplier data

* Search by MPN or Manufacturer

* Update API version
2025-11-03 15:03:50 +11:00
Oliver
0997a18a62 Move "view" actions to the end of the list (#10749) 2025-11-03 14:28:31 +11:00
Oliver
91489e986c UI table updates (#10748)
- Add "IPN" col to SupplierPartTable
- Add "IPN" col to ManufacturerPartTable
2025-11-03 13:47:26 +11:00
Matthias Mair
e1bf67b32c fix: typo ins installer stops script from working (#10744) 2025-11-03 10:31:52 +11:00
Matthias Mair
d7daf660ef fix uv (#10742)
https://github.com/inventree/InvenTree/security/dependabot/248
2025-11-03 09:35:35 +11:00
Oliver
46615e447b Docs fix (#10738)
* Fix heading in helpers.md

* Helper func docs
2025-11-02 15:30:21 +11:00
Oliver
551da5a51f Bump version number to 1.2.0 dev (#10734)
* Bump version number to 1.2.0 dev

* Add release tag to CHANGELOG.md

* Add entry for upcoming 1.2.0

* Cleanup

* Remove links
2025-11-02 10:01:16 +11:00
424 changed files with 163773 additions and 143231 deletions

View File

@@ -14,7 +14,7 @@ pool:
strategy:
matrix:
Python39:
PYTHON_VERSION: '3.9'
PYTHON_VERSION: '3.11'
maxParallel: 3
steps:

View File

@@ -57,7 +57,7 @@ runs:
run: |
python3 -m pip install -U pip
pip3 install -U invoke wheel
pip3 install 'uv>=0.9.4'
pip3 install 'uv>=0.9.6'
- name: Allow uv to use the system Python by default
run: echo "UV_SYSTEM_PYTHON=1" >> $GITHUB_ENV
shell: bash

View File

@@ -4,6 +4,8 @@ updates:
directory: /
schedule:
interval: weekly
cooldown:
default-days: 7
groups:
dependencies:
patterns:
@@ -13,11 +15,15 @@ updates:
directory: /contrib/container
schedule:
interval: weekly
cooldown:
default-days: 7
- package-ecosystem: docker
directory: /.devcontainer
schedule:
interval: weekly
cooldown:
default-days: 7
- package-ecosystem: pip
directories:
@@ -28,6 +34,8 @@ updates:
schedule:
interval: weekly
day: friday
cooldown:
default-days: 7
groups:
dependencies:
patterns:
@@ -41,6 +49,8 @@ updates:
- /src/frontend
schedule:
interval: weekly
cooldown:
default-days: 7
groups:
dependencies:
patterns:

View File

@@ -9,7 +9,7 @@ on:
- l10
env:
python_version: 3.9
python_version: 3.11
permissions:
contents: read
@@ -31,7 +31,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false

View File

@@ -39,7 +39,7 @@ jobs:
docker: ${{ steps.filter.outputs.docker }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
@@ -67,7 +67,7 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Test Docker Image
@@ -129,7 +129,7 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Run Migration Tests
@@ -153,11 +153,11 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Set Up Python ${{ env.python_version }}
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # pin@v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # pin@v6.1.0
with:
python-version: ${{ env.python_version }}
- name: Version Check
@@ -168,7 +168,7 @@ jobs:
echo "git_commit_date=$(git show -s --format=%ci)" >> $GITHUB_ENV
- name: Set up QEMU
if: github.event_name != 'pull_request'
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # pin@v3.6.0
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # pin@v3.7.0
- name: Set up Docker Buildx
if: github.event_name != 'pull_request'
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # pin@v3.11.1
@@ -201,7 +201,7 @@ jobs:
- name: Extract Docker metadata
if: github.event_name != 'pull_request'
id: meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # pin@v5.8.0
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # pin@v5.10.0
with:
images: |
inventree/inventree

View File

@@ -9,7 +9,7 @@ on:
branches-ignore: ["l10*"]
env:
python_version: 3.9
python_version: 3.11
node_version: 20
# The OS version must be set per job
server_start_sleep: 60
@@ -41,7 +41,7 @@ jobs:
requirements: ${{ steps.filter.outputs.requirements }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
@@ -82,11 +82,11 @@ jobs:
if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Set up Python ${{ env.python_version }}
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # pin@v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # pin@v6.1.0
with:
python-version: ${{ env.python_version }}
cache: "pip"
@@ -104,7 +104,7 @@ jobs:
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -126,11 +126,11 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Set up Python ${{ env.python_version }}
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # pin@v6.0.0
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # pin@v6.1.0
with:
python-version: ${{ env.python_version }}
- name: Check Config
@@ -164,7 +164,7 @@ jobs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -249,7 +249,7 @@ jobs:
version: ${{ needs.schema.outputs.version }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
name: Checkout Code
with:
repository: inventree/schema
@@ -302,9 +302,9 @@ jobs:
INVENTREE_LOG_LEVEL: WARNING
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: true
persist-credentials: false
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -334,8 +334,8 @@ jobs:
continue-on-error: true # continue if a step fails so that coverage gets pushed
strategy:
matrix:
python_version: [3.9]
# python_version: [3.9, 3.12] # Disabled due to requirement issues
python_version: [3.11]
# python_version: [3.11, 3.14] # Disabled due to requirement issues
env:
INVENTREE_DB_NAME: ./inventree.sqlite
@@ -346,7 +346,7 @@ jobs:
python_version: ${{ matrix.python_version }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -397,7 +397,7 @@ jobs:
services:
postgres:
image: postgres:14
image: postgres:17
env:
POSTGRES_USER: inventree
POSTGRES_PASSWORD: password
@@ -410,7 +410,7 @@ jobs:
- 6379:6379
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -458,7 +458,7 @@ jobs:
- 3306:3306
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -492,7 +492,7 @@ jobs:
services:
postgres:
image: postgres:14
image: postgres:17
env:
POSTGRES_USER: inventree
POSTGRES_PASSWORD: password
@@ -500,7 +500,7 @@ jobs:
- 5432:5432
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -534,7 +534,7 @@ jobs:
INVENTREE_PLUGINS_ENABLED: false
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
name: Checkout Code
@@ -588,7 +588,7 @@ jobs:
if: needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
services:
postgres:
image: postgres:15
image: postgres:17
env:
POSTGRES_DB: inventree
POSTGRES_USER: inventree_user
@@ -613,7 +613,7 @@ jobs:
VITE_COVERAGE_BUILD: true
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -663,7 +663,7 @@ jobs:
timeout-minutes: 60
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -696,7 +696,7 @@ jobs:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- uses: hynek/setup-cached-uv@757bedc3f972eb7227a1aa657651f15a8527c817 # pin@v2
@@ -705,7 +705,7 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # pin@v3
uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # pin@v3
with:
sarif_file: results.sarif
category: zizmor

View File

@@ -7,7 +7,7 @@ on:
permissions:
contents: read
env:
python_version: 3.9
python_version: 3.11
jobs:
stable:
@@ -20,7 +20,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout Code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Version Check
@@ -43,7 +43,7 @@ jobs:
contents: write
attestations: write
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -55,7 +55,7 @@ jobs:
- name: Build frontend
run: cd src/frontend && npm run compile && npm run build
- name: Create SBOM for frontend
uses: anchore/sbom-action@8e94d75ddd33f69f691467e42275782e4bfefe84 # pin@v0
uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # pin@v0
with:
artifact-name: frontend-build.spdx
path: src/frontend
@@ -76,7 +76,7 @@ jobs:
subject-path: "${{ github.workspace }}/src/backend/InvenTree/web/static/frontend-build.zip"
- name: Upload frontend
uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # pin@2.11.2
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: src/backend/InvenTree/web/static/frontend-build.zip
@@ -84,7 +84,7 @@ jobs:
tag: ${{ github.ref }}
overwrite: true
- name: Upload Attestation
uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # pin@2.11.2
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
asset_name: frontend-build.intoto.jsonl
@@ -107,7 +107,7 @@ jobs:
INVENTREE_DEBUG: true
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: false
- name: Environment Setup
@@ -127,7 +127,7 @@ jobs:
cd docs/site
zip -r docs-html.zip *
- name: Publish documentation
uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # pin@2.11.2
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: docs/site/docs-html.zip

View File

@@ -32,7 +32,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
with:
sarif_file: results.sarif

View File

@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # pin@v10.1.0
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # pin@v10.1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "This issue seems stale. Please react to show this is still important."

View File

@@ -6,7 +6,7 @@ on:
- master
env:
python_version: 3.9
python_version: 3.11
node_version: 20
permissions:
@@ -32,9 +32,9 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # pin@v5.0.0
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # pin@v6.0.1
with:
persist-credentials: true
persist-credentials: false
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -56,7 +56,7 @@ jobs:
echo "Resetting to HEAD~"
git reset HEAD~ || true
- name: crowdin action
uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # pin@v2
uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # pin@v2
with:
upload_sources: true
upload_translations: false

5
.gitignore vendored
View File

@@ -37,6 +37,11 @@ local_settings.py
*.backup
*.old
# Files generated by profiling tools
*.prof
*.log
*.sql
# Files used for testing
inventree-demo-dataset/
inventree-data/

View File

@@ -20,9 +20,9 @@ before:
- contrib/packager.io/before.sh
dependencies:
- curl
- "python3.9 | python3.10 | python3.11 | python3.12 | python3.13 | python3.14"
- "python3.9-venv | python3.10-venv | python3.11-venv | python3.12-venv | python3.13-venv | python3.14-venv"
- "python3.9-dev | python3.10-dev | python3.11-dev | python3.12-dev | python3.13-dev | python3.14-dev"
- "python3.11 | python3.12 | python3.13 | python3.14"
- "python3.11-venv | python3.12-venv | python3.13-venv | python3.14-venv"
- "python3.11-dev | python3.12-dev | python3.13-dev | python3.14-dev"
- python3-pip
- python3-cffi
- python3-brotli
@@ -35,8 +35,6 @@ dependencies:
- jq
- "libffi7 | libffi8"
targets:
ubuntu-20.04: true
ubuntu-22.04: true
ubuntu-24.04: true
debian-11: true
debian-12: true

View File

@@ -10,7 +10,7 @@ exclude: |
)$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -18,18 +18,18 @@ repos:
exclude: mkdocs.yml
- id: mixed-line-ending
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.13
rev: v0.14.8
hooks:
- id: ruff-format
args: [--preview]
- id: ruff
- id: ruff-check
args: [
--fix,
# --unsafe-fixes,
--preview
]
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.7.12
rev: 0.9.16
hooks:
- id: pip-compile
name: pip-compile requirements-dev.in
@@ -71,16 +71,16 @@ repos:
src/frontend/vite.config.ts |
)$
- repo: https://github.com/biomejs/pre-commit
rev: v2.0.0-beta.5
rev: v2.3.8
hooks:
- id: biome-check
additional_dependencies: ["@biomejs/biome@1.9.4"]
files: ^src/frontend/.*\.(js|ts|tsx)$
- repo: https://github.com/gitleaks/gitleaks
rev: v8.27.2
rev: v8.30.0
hooks:
- id: gitleaks
language_version: 1.23.6
language_version: 1.25.4
#- repo: https://github.com/jumanjihouse/pre-commit-hooks
# rev: 3.0.0
# hooks:

7
.vscode/launch.json vendored
View File

@@ -11,7 +11,7 @@
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
"args": [
"runserver",
// "0.0.0.0:8000", // expose server in network (useful for testing with mobile app)
"0.0.0.0:8000", // expose server in network (useful for testing with mobile app)
// "--noreload" // disable auto-reload
],
"django": true,
@@ -35,7 +35,8 @@
"request": "launch",
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
"args": [
"runserver"
"runserver",
"0.0.0.0:8000"
],
"django": true,
"justMyCode": false
@@ -44,7 +45,7 @@
"name": "InvenTree invoke schema",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/.venv/lib/python3.9/site-packages/invoke/__main__.py",
"program": "${workspaceFolder}/.venv/lib/python3.11/site-packages/invoke/__main__.py",
"cwd": "${workspaceFolder}",
"args": [
"dev.schema","--ignore-warnings"

View File

@@ -5,7 +5,30 @@ All notable changes to this project will be documented in this file (starting wi
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] - yyyy-mm-dd (in UTC)
## Unreleased - YYYY-MM-DD
### Breaking Changes
- [#10699](https://github.com/inventree/InvenTree/pull/10699) removes the `PartParameter` and `PartParameterTempalate` models (and associated API endpoints). These have been replaced with generic `Parameter` and `ParameterTemplate` models (and API endpoints). Any external client applications which made use of the old endpoints will need to be updated.
### Added
- Adds "Category" columns to BOM and Build Item tables and APIs in [#10722](https://github.com/inventree/InvenTree/pull/10772)
- Adds generic "Parameter" and "ParameterTemplate" models (and associated API endpoints) in [#10699](https://github.com/inventree/InvenTree/pull/10699)
- Adds parameter support for multiple new model types in [#10699](https://github.com/inventree/InvenTree/pull/10699)
- Allows report generator to produce PDF input controls in [#10969](https://github.com/inventree/InvenTree/pull/10969)
- UI overhaul of parameter management in [#10699](https://github.com/inventree/InvenTree/pull/10699)
### Changed
-
### Removed
- Removed python 3.9 / 3.10 support as part of Django 5.2 upgrade in [#10730](https://github.com/inventree/InvenTree/pull/10730)
- Removed the "PartParameter" and "PartParameterTemplate" models (and associated API endpoints) in [#10699](https://github.com/inventree/InvenTree/pull/10699)
- Removed the "ManufacturerPartParameter" model (and associated API endpoints) [#10699](https://github.com/inventree/InvenTree/pull/10699)
## 1.1.0 - 2025-11-02
### Added
@@ -30,10 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Changed site URL check to allow protocol mismatches if `INVENTREE_SITE_LAX_PROTOCOL` is set to `True` (default) in [#10454](https://github.com/inventree/InvenTree/pull/10454)
- Changed call signature of `get_global_setting` to use `environment_key` instead of `enviroment_key` in [#10557](https://github.com/inventree/InvenTree/pull/10557)
### Removed
## [1.0.0 ] - 2025-09-15
## 1.0.0 - 2025-09-15
The first "stable" release following semver but not extensively other than the previous releases. The use of 1.0 indicates the stability that users already expect from InvenTree.

View File

@@ -149,7 +149,7 @@ COPY --from=builder_stage ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web ${IN
COPY --from=builder_stage /root/.local /root/.local
# Launch the production server
CMD ["sh", "-c", "exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8000 --chdir ${INVENTREE_BACKEND_DIR}/InvenTree"]
CMD ["sh", "-c", "exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} --chdir ${INVENTREE_BACKEND_DIR}/InvenTree"]
FROM builder_stage AS dev

View File

@@ -1,4 +1,4 @@
#!/bin/ash
#!/bin/bash
# exit when any command fails
set -e

View File

@@ -17,7 +17,7 @@ gunicorn>=22.0.0
# LDAP required packages
django-auth-ldap # Django integration for ldap auth
python-ldap # LDAP auth support
django<5.0 # Force lower to match main project
django<6.0 # Force lower to match main project
# Upgraded python package installer
uv

View File

@@ -4,9 +4,9 @@ asgiref==3.10.0 \
--hash=sha256:aef8a81283a34d0ab31630c9b7dfe70c812c95eba78171367ca8745e88124734 \
--hash=sha256:d89f2d8cd8b56dada7d52fa7dc8075baa08fb836560710d38c292a7a3f78c04e
# via django
django==4.2.25 \
--hash=sha256:2391ab3d78191caaae2c963c19fd70b99e9751008da22a0adcc667c5a4f8d311 \
--hash=sha256:9584cf26b174b35620e53c2558b09d7eb180a655a3470474f513ff9acb494f8c
django==5.2.9 \
--hash=sha256:16b5ccfc5e8c27e6c0561af551d2ea32852d7352c67d452ae3e76b4f6b2ca495 \
--hash=sha256:3a4ea88a70370557ab1930b332fd2887a9f48654261cdffda663fef5976bb00a
# via
# -r contrib/container/requirements.in
# django-auth-ldap
@@ -53,77 +53,77 @@ packaging==25.0 \
# via
# gunicorn
# mariadb
psycopg[binary, pool]==3.2.11 \
--hash=sha256:217231b2b6b72fba88281b94241b2f16043ee67f81def47c52a01b72ff0c086a \
--hash=sha256:398bb484ed44361e041c8f804ed7af3d2fcefbffdace1d905b7446c319321706
psycopg[binary, pool]==3.2.12 \
--hash=sha256:85c08d6f6e2a897b16280e0ff6406bef29b1327c045db06d21f364d7cd5da90b \
--hash=sha256:8a1611a2d4c16ae37eada46438be9029a35bb959bb50b3d0e1e93c0f3d54c9ee
# via -r contrib/container/requirements.in
psycopg-binary==3.2.11 \
--hash=sha256:00221bfeb9594ca6e01207b032c300fa6f889d918bf0de47f4571c1f9f6e1578 \
--hash=sha256:110a2036007230416fcc2c17bfe7aaa2c1fa9b6e9d21e2cd551523e3f6489759 \
--hash=sha256:199f88a05dd22133eab2deb30348ef7a70c23d706c8e63fdc904234163c63517 \
--hash=sha256:1db270e6bdbd183e3908cd9bb832506b99e1f2222a2fc2145f980c3ba1c8c30f \
--hash=sha256:20d41bcd9ac289d44ac1f6151594f7883483b4ad14680a63e04b639dc90c3349 \
--hash=sha256:23c77dbbffe8ba679213877f7204f4599bd545b65d2d69982fd685a3fea35b11 \
--hash=sha256:260738ae222b41dbefd0d84cb2e150a112f90b41688630f57fdac487ab6d6f38 \
--hash=sha256:27eb6367350b75fef882c40cd6f748bfd976db2f8651f7511956f11efc15154f \
--hash=sha256:2a438fad4cc081b018431fde0e791b6d50201526edf39522a85164f606c39ddb \
--hash=sha256:30e2c114d26554ae677088de5d4133cc112344d7a233200fdbf4a2ca5754c7ec \
--hash=sha256:31f1d5630afa673c37a6327f8e3efa1f17d4e4e42972643b3478b52275233529 \
--hash=sha256:32bd319a68420631a320bb450921c8320641621a92556c97b38b1e116010c344 \
--hash=sha256:3bd2c8fb1dec6f93383fbaa561591fa3d676e079f9cb9889af17c3020a19715f \
--hash=sha256:3f32b09fba85d9e239229bdc5b6254420c02054f6954fe7fbd1ecf1ca93009ed \
--hash=sha256:478a68d50f34f6203642d245e2046d266c719ab4e593a1bb94c3be5f82e1aee1 \
--hash=sha256:47f6cf8a1d02d25238bdb8741ac641ff0ec22b1c6ff6a2acd057d0da5c712842 \
--hash=sha256:49d76391b225f72dd63fcab87937ccf307ae0f093b5a382eeacf05f19a57c176 \
--hash=sha256:4cae9bdc482e36e825d5102a9f3010e729f33a4ca83fc8a1f439ba16eb61e1f1 \
--hash=sha256:54a30f00a51b9043048b3e7ee806ffd31fc5fbd02a20f0e69d21306ff33dc473 \
--hash=sha256:566d02a0b85b994e40b4f6276b3423c59e8157f10b73bd2e634f8e0a3dfb1890 \
--hash=sha256:5768a9e7d393b2edd3a28de5a6d5850d054a016ed711f7044a9072f19f5e50d5 \
--hash=sha256:581358e770a4536e546841b78fd0fe318added4a82443bf22d0bbe3109cf9582 \
--hash=sha256:58997db1aa48a1119e26c1c2f893d1c92339bd3be5d1f25334f22eaeaeeca90e \
--hash=sha256:58d8f9f80ae79ba7f2a0509424939236220d7d66a4f8256ae999b882cc58065b \
--hash=sha256:592fb928efe0674a7400af914bcf931eb5267d36237925947aaecf63bd9a91aa \
--hash=sha256:5bc571786a256a2fa2d8f13b5ecf714020b753bc76c2fa6d308e46751946dc31 \
--hash=sha256:5f6f948ff1cd252003ff534d7b50a2b25453b4212b283a7514ff8751bdb68c37 \
--hash=sha256:5fb27dd9c52ae13cb4de90244207155b694f76a75a816115ead2d573f40e1e36 \
--hash=sha256:6688807ed07436c18e9946d01372bc80b9d20b7732cde27de9313e0860910c84 \
--hash=sha256:6b9632c42f76d5349e7dd50025cff02688eb760b258e891ad2c6428e7e4917d5 \
--hash=sha256:720e19ff2d1c425b6be18bd20ba35010c7927e492bcfecbae1085a89caa7db7c \
--hash=sha256:749d23fbfd642a7abfef5fc0f6ca185fa82a2c0f895e6eab42c3f2a5d88f6011 \
--hash=sha256:7608c9fa58b85426093ab8777080e8f134d89915c05c51fa270e7aee317f2b38 \
--hash=sha256:766089fdaa8af1b5f7e2ec9fd7ad190c865e226b4fb0e7b1bd8dbcd62b5b923e \
--hash=sha256:7744b4ed1f3b76fe37de7e9ef98014482fe74b6d3dfe1026cc4cfb4b4404e74f \
--hash=sha256:7b3c5474dbad63bcccb8d14d4d4c7c19f1dc6f8e8c1914cbc771d261cf8eddca \
--hash=sha256:81e57d1f00af9b7414c8d00ac77892b3786ddd69a23c27dee47cae8fd3543b07 \
--hash=sha256:82fe30afbdd66fbdad583b02baad5c15930a3dc8a3756d2ae15fc874e9be8ec8 \
--hash=sha256:8792e502a16a0b28d9fd23571fe492271a00c4b2b55f6c0b8377e47032758cd3 \
--hash=sha256:91268f04380964a5e767f8102d05f1e23312ddbe848de1a9514b08b3fc57d354 \
--hash=sha256:9b4b0fc4e774063ae64c92cc57e2b10160150de68c96d71743218159d953869d \
--hash=sha256:9bdc762600fcc8e4ad3224734a4e70cc226207fd8f2de47c36b115efeed01782 \
--hash=sha256:9ea3ebe1706fd78d6ac0dd1cf692a77cfacd5ba967c82128f9863a5e48f63c47 \
--hash=sha256:9f12a34bddaeffa7840a61163595ec0d70a9db855896865dcfbb731510014484 \
--hash=sha256:a3a59d404e1fb8ec47116f66f5adf48a8993a8aac0dad0395a923155fd55ee38 \
--hash=sha256:b051aa1e67f0d03ccdb4503d716f22da56229896526f0aa721e5a199baa9e5d4 \
--hash=sha256:b2fa94ce40bc4b408149d83a6204fc5e53c3e9d3cd5b749de2e7e9671a049cf7 \
--hash=sha256:c45f61202e5691090a697e599997eaffa3ec298209743caa4fd346145acabafe \
--hash=sha256:c594c199869099c59c85b9f4423370b6212491fb929e7fcda0da1768761a2c2c \
--hash=sha256:d27f51b8ce291da4af749ef850adb4520bfe52c2ff4677402c719ff35af03f00 \
--hash=sha256:d59db908d9baaa057a43dd5aa8352f3e3de4b8c57f295172d5fe521e97d6c39d \
--hash=sha256:d7e490848d7bedf6c1d2180233a33d31d554a1b0823f80a0236ebb7d3b6caf12 \
--hash=sha256:e3b6328bc2f3ca233f9a5f08d266089b96a534eca9ee4e45cb92d0a8d4629d9c \
--hash=sha256:e3f5887019dfb094c60e7026968ca3a964ca16305807ba5e43f9a78483767d5f \
--hash=sha256:e7575ca710277cc3e9257ff803a3e0e3cb7cc1b7851639cb783a7cd55ebfc815 \
--hash=sha256:e7f4dff472a529c9027f294c8842ab535bbed7e2928fe1f4fc28b27f2463d6d5 \
--hash=sha256:eab6959fade522e586b8ec37d3fe337ce10861965edef3292f52e66e36dc375d \
--hash=sha256:f5e7415b5d0f58edf2708842c66605092df67f3821161d861b09695fc326c4de \
--hash=sha256:f72146ad5b69ea177c2707578e5a4a9422b79e50d5a80992dabc5619b0929771 \
--hash=sha256:fa2aa5094dc962967ca0978c035b3ef90329b802501ef12a088d3bac6a55598e \
--hash=sha256:fe5e3648e855df4fba1d70c18aef18c9880ea8d123fdfae754c18787c8cb37b3 \
--hash=sha256:ff64883cff66fe797cd958c0ff7f53fc36a28239b9e0dc80189ce1c03ce47153
psycopg-binary==3.2.12 \
--hash=sha256:095ccda59042a1239ac2fefe693a336cb5cecf8944a8d9e98b07f07e94e2b78d \
--hash=sha256:0afb71a99871a41dd677d207c6a988d978edde5d6a018bafaed4f9da45357055 \
--hash=sha256:100fdfee763d701f6da694bde711e264aca4c2bc84fb81e1669fb491ce11d219 \
--hash=sha256:13cd057f406d2c8063ae8b489395b089a7f23c39aff223b5ea39f0c4dd640550 \
--hash=sha256:15e226f0d8af85cc8b2435b2e9bc6f0d40febc79eef76cf20fceac4d902a6a7b \
--hash=sha256:16db2549a31ccd4887bef05570d95036813ce25fd9810b523ba1c16b0f6cfd90 \
--hash=sha256:1c1dbeb8e97d00a33dfa9987776ce3d1c1e4cc251dfbd663b8f9e173f5c89d17 \
--hash=sha256:1d7cedecbe0bb60a2e72b1613fba4072a184a6472d6cc9aa99e540217f544e3e \
--hash=sha256:2598d0e4f2f258da13df0560187b3f1dfc9b8688c46b9d90176360ae5212c3fc \
--hash=sha256:26b5927b5880b396231ab6190ee5c8fb47ed3f459b53504ed5419faaf16d3bfb \
--hash=sha256:294f08b014f08dfd3c9b72408f5e1a0fd187bd86d7a85ead651e32dbd47aa038 \
--hash=sha256:2aa80ca8d17266507bef853cecefa7d632ffd087883ee7ca92b8a7ea14a1e581 \
--hash=sha256:2d55009eeddbef54c711093c986daaf361d2c4210aaa1ee905075a3b97a62441 \
--hash=sha256:310c95a68a9b948b89d6d187622757d57b6c26cece3c3f7c2cbb645ee36531b2 \
--hash=sha256:32b3e12d9441508f9c4e1424f4478b1a518a90a087cd54be3754e74954934194 \
--hash=sha256:356b4266e5cde7b5bbcf232f549dedf7fbed4983daa556042bdec397780e044d \
--hash=sha256:385c7b5cfffac115f413b8e32c941c85ea0960e0b94a6ef43bb260f774c54893 \
--hash=sha256:3c1e38b1eda54910628f68448598139a9818973755abf77950057372c1fe89a6 \
--hash=sha256:3e9c9e64fb7cda688e9488402611c0be2c81083664117edcc709d15f37faa30f \
--hash=sha256:442f20153415f374ae5753ca618637611a41a3c58c56d16ce55f845d76a3cf7b \
--hash=sha256:489b154891f1c995355adeb1077ee3479e9c9bada721b93270c20243bbad6542 \
--hash=sha256:48a8e29f3e38fcf8d393b8fe460d83e39c107ad7e5e61cd3858a7569e0554a39 \
--hash=sha256:49582c3b6d578bdaab2932b59f70b1bd93351ed4d594b2c97cea1611633c9de1 \
--hash=sha256:58ed30d33c25d7dc8d2f06285e88493147c2a660cc94713e4b563a99efb80a1f \
--hash=sha256:5b6e505618cb376a7a7d6af86833a8f289833fe4cc97541d7100745081dc31bd \
--hash=sha256:66a031f22e4418016990446d3e38143826f03ad811b9f78f58e2afbc1d343f7a \
--hash=sha256:6a898717ab560db393355c6ecf39b8c534f252afc3131480db1251e061090d3a \
--hash=sha256:7130effd0517881f3a852eff98729d51034128f0737f64f0d1c7ea8343d77bd7 \
--hash=sha256:72fd979e410ba7805462817ef8ed6f37dd75f9f4ae109bdb8503e013ccecb80b \
--hash=sha256:77690f0bf08356ca00fc357f50a5980c7a25f076c2c1f37d9d775a278234fefd \
--hash=sha256:79de3cc5adbf51677009a8fda35ac9e9e3686d5595ab4b0c43ec7099ece6aeb5 \
--hash=sha256:7b9a99ded7d19b24d3b6fa632b58e52bbdecde7e1f866c3b23d0c27b092af4e3 \
--hash=sha256:802bd01fb18a0acb0dea491f69a9a2da6034f33329a62876ab5b558a1fb66b45 \
--hash=sha256:8335d989a4e94df2ccd8a1acbba9d03c4157ea8d73b65b79d447c6dc10b001d8 \
--hash=sha256:89b3c5201ca616d69ca0c3c0003ca18f7170a679c445c7e386ebfb4f29aa738e \
--hash=sha256:8ffe75fe6be902dadd439adf4228c98138a992088e073ede6dd34e7235f4e03e \
--hash=sha256:909de94de7dd4d6086098a5755562207114c9638ec42c52d84c8a440c45fe084 \
--hash=sha256:940ac69ef6e89c17b3d30f3297a2ad03efdd06a4b1857f81bc533a9108a90eb9 \
--hash=sha256:95f2806097a49bfd57e0c6a178f77b99487c53c157d9d507aee9c40dd58efdb4 \
--hash=sha256:9c674887d1e0d4384c06c822bc7fcfede4952742e232ec1e76b5a6ae39a3ddd4 \
--hash=sha256:9fdf3a0c24822401c60c93640da69b3dfd4d9f29c3a8d797244fe22bfe592823 \
--hash=sha256:ab02b7d138768fd6ac4230e45b073f7b9fd688d88c04f24c34df4a250a94d066 \
--hash=sha256:acb1811219a4144539f0baee224a11a2aa323a739c349799cf52f191eb87bc52 \
--hash=sha256:bfd632f7038c76b0921f6d5621f5ba9ecabfad3042fa40e5875db11771d2a5de \
--hash=sha256:ce68839da386f137bc8d814fdbeede8f89916b8605e3593a85b504a859243af9 \
--hash=sha256:d369e79ad9647fc8217cbb51bbbf11f9a1ffca450be31d005340157ffe8e91b3 \
--hash=sha256:dc68094e00a5a7e8c20de1d3a0d5e404a27f522e18f8eb62bbbc9f865c3c81ef \
--hash=sha256:deeb06b7141f3a577c3aa8562307e2747580ae43d705a0482603a2c1f110d046 \
--hash=sha256:e0b5ccd03ca4749b8f66f38608ccbcb415cbd130d02de5eda80d042b83bee90e \
--hash=sha256:ea049c8d33c4f4e6b030d5a68123c0ccd2ffb77d4035f073db97187b49b6422f \
--hash=sha256:ea9751310b840186379c949ede5a5129b31439acdb929f3003a8685372117ed8 \
--hash=sha256:ec82fa5134517af44e28a30c38f34384773a0422ffd545fd298433ea9f2cc5a9 \
--hash=sha256:eedc410f82007038030650aa58f620f9fe0009b9d6b04c3dc71cbd3bae5b2675 \
--hash=sha256:ef40601b959cc1440deaf4d53472ab54fa51036c37189cf3fe5500559ac25347 \
--hash=sha256:ef92d5ba6213de060d1390b1f71f5c3b2fbb00b4d55edee39f3b07234538b64a \
--hash=sha256:efab679a2c7d1bf7d0ec0e1ecb47fe764945eff75bb4321f2e699b30a12db9b3 \
--hash=sha256:f33c9e12ed05e579b7fb3c8fdb10a165f41459394b8eb113e7c377b2bd027f61 \
--hash=sha256:f3bae4be7f6781bf6c9576eedcd5e1bb74468126fa6de991e47cdb1a8ea3a42a \
--hash=sha256:f6ba1fe35fd215813dac4544a5ffc90f13713b29dd26e9e5be97ba53482bf6d6 \
--hash=sha256:f7c81bc60560be9eb3c23601237765069ebfa9881097ce19ca6b5ea17c5faa8f \
--hash=sha256:f8107968a9eadb451cfa6cf86036006fdde32a83cd39c26c9ca46765e653b547 \
--hash=sha256:f821e0c8a8fdfddfa71acb4f462d7a4c5aae1655f3f5e078970dbe9f19027386
# via psycopg
psycopg-pool==3.2.6 \
--hash=sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5 \
--hash=sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7
psycopg-pool==3.2.7 \
--hash=sha256:4b47bb59d887ef5da522eb63746b9f70e2faf967d34aac4f56ffc65e9606728f \
--hash=sha256:a77d531bfca238e49e5fb5832d65b98e69f2c62bfda3d2d4d833696bdc9ca54b
# via psycopg
pyasn1==0.6.1 \
--hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \
@@ -229,26 +229,26 @@ typing-extensions==4.15.0 \
# via
# psycopg
# psycopg-pool
uv==0.9.4 \
--hash=sha256:03a85b02e6ccf1b705ce78bd98da78c90d5a0d0f941756ee842825d850cada2f \
--hash=sha256:0840346084d28aa5345eeabcb7f9e727448b56b3b399300447a9155066909925 \
--hash=sha256:253133f7f2eac8fed10ad601c56ddcd13d8d81d9343ed9e95873d19b149199f2 \
--hash=sha256:2feb2adc0a2eb41a757b9cef3226f649452423badf20d68d177b6649342d021d \
--hash=sha256:39f6b459fdabc80c0afc080ba8bce86e048afa799bc6c5c372f78b14195cf49c \
--hash=sha256:3e1b5df83e96a8128b81a9f2bd72a4db752f691515914471b76df994339d2c35 \
--hash=sha256:42012fcfdbaec08e1c009bbdbf96296b05e0e86feb83e1182d9335ae86a288d2 \
--hash=sha256:57582a149de7788a83f998ddad2dfc50a328aae7a474fbb1617c73a9e2b42ebf \
--hash=sha256:610a219a6d92cc56c1a24888118a5ae1b07233b93dde0565d64fe198a2c7c376 \
--hash=sha256:787cf63c2f5c97cc6b30915632351eac655fcd4ec19620bc67cbd6855975817b \
--hash=sha256:79efd533016d9bf077056cac72e68fa501e9d0e09576a2c375f7c286d19be9d6 \
--hash=sha256:9ee7695b6632b74ea62d67fcef732e519d1fdb3f9ecf81c99bfd5a354ff925fb \
--hash=sha256:aa0e144df0276945cbe49e30b577cf51e19b808e5ca55e23b8a1a354857e1629 \
--hash=sha256:b7f7d3fd51627fbcca06cf75d327e060db924d4ca054e1e934b71682d58f1f51 \
--hash=sha256:c353be83686f769bf50e6c6bc8591ad59752b492c6bb51296e378e55521482f5 \
--hash=sha256:d89f88df09d571f6d06228b32a6a71100905eb64343247317d363bcd774ee870 \
--hash=sha256:dcbcc963232e13e279002844e983cd6d0f53560e75d8a3f7a68e7d68a6021235 \
--hash=sha256:df3288f85bd6bfb4b8722bb7223d6723de7c32d213596573d92803f89af9007c \
--hash=sha256:fa33399d5e3e31b753910cfaa6f87022736339cadb140c8896dccb7c6a855e32
uv==0.9.8 \
--hash=sha256:0f03bc413c933dbf850ad0dc2dba3df6b80c860a5c65cd767add49da19dadef0 \
--hash=sha256:14670bf55ecb5cfd0f3654fbf51c58a21dec3ad8ab531079b3ed8599271dc77b \
--hash=sha256:1b8b5bdcda3e10ea70b618d0609acddc5c725cb58d4caf933030ddedd7c2e98f \
--hash=sha256:40253d00c1e900a0a61b132b1e0dd4aa83575cfd5302d3671899b6de29b1ef67 \
--hash=sha256:50d130c46d97d7f10675ebea8608b7b4722c84b5745cd1bb0c8ae6d7984c05d5 \
--hash=sha256:543693def38fa41b9706aba391111fe8d9dd6be86899d76f9581faf045ac1cb6 \
--hash=sha256:5af28f1645eb3c50fd34a78508792db2d0799816f4eb5f55e1e6e2c724dfb125 \
--hash=sha256:6a01d7cd41953ffac583139b10ad1df004a67c0246a6b694eb5bcdbc8c99deaf \
--hash=sha256:6df2e16f6df32018047c60bab2c0284868ad5c309addba9183ea2eeb71746bf0 \
--hash=sha256:7038a552159f2291dd0d1f4f66a36261b5f3ed5fcd92e2869186f8e910b2c935 \
--hash=sha256:75671150d6eb9d5ee829e1fdb8cf86b8e44a66d27cbb996fe807e986c4107b5d \
--hash=sha256:87c3b65b6d5fcbdeab199d54c74fbf75de19cb534a690c936c5616478a038576 \
--hash=sha256:99b18bfe92c33c3862b65d74677697e799763e669e0064685f405e7e27517f25 \
--hash=sha256:9f2f3576c4518ff4f15e48dbca70585a513523c4738bc8cc2e48b20fd1190ce3 \
--hash=sha256:a4010b3fdabbb3c4f2cf2f7aa3bf6002d00049dcbc54ce0ee5ada32a933b2290 \
--hash=sha256:bb0f8e83c2a2fc5a802e930cc8a7b71ab068180300a3f27ba38037f9fcb3d430 \
--hash=sha256:cdbfadca9522422ab9820f5ada071c9c5c869bcd6fee719d20d91d5ec85b2a7d \
--hash=sha256:d93a2227d23e81ab3a16c30363559afc483e8aca40ea9343b3f326a9a41718c9 \
--hash=sha256:f52c6a99197028a314d4c1825f7ccb696eb9a88b822d2e2f17046266c75e543e
# via -r contrib/container/requirements.in
wheel==0.45.1 \
--hash=sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729 \

View File

@@ -214,66 +214,72 @@ ruamel-yaml==0.18.15 \
--hash=sha256:148f6488d698b7a5eded5ea793a025308b25eca97208181b6a026037f391f701 \
--hash=sha256:dbfca74b018c4c3fba0b9cc9ee33e53c371194a9000e694995e620490fd40700
# via jc
ruamel-yaml-clib==0.2.14 \
--hash=sha256:090782b5fb9d98df96509eecdbcaffd037d47389a89492320280d52f91330d78 \
--hash=sha256:0a54e5e40a7a691a426c2703b09b0d61a14294d25cfacc00631aa6f9c964df0d \
--hash=sha256:10d9595b6a19778f3269399eff6bab642608e5966183abc2adbe558a42d4efc9 \
--hash=sha256:16a60d69f4057ad9a92f3444e2367c08490daed6428291aa16cefb445c29b0e9 \
--hash=sha256:18c041b28f3456ddef1f1951d4492dbebe0f8114157c1b3c981a4611c2020792 \
--hash=sha256:1c1acc3a0209ea9042cc3cfc0790edd2eddd431a2ec3f8283d081e4d5018571e \
--hash=sha256:1f118b707eece8cf84ecbc3e3ec94d9db879d85ed608f95870d39b2d2efa5dca \
--hash=sha256:2070bf0ad1540d5c77a664de07ebcc45eebd1ddcab71a7a06f26936920692beb \
--hash=sha256:26a8de280ab0d22b6e3ec745b4a5a07151a0f74aad92dd76ab9c8d8d7087720d \
--hash=sha256:275f938692013a3883edbd848edde6d9f26825d65c9a2eb1db8baa1adc96a05d \
--hash=sha256:27c070cf3888e90d992be75dd47292ff9aa17dafd36492812a6a304a1aedc182 \
--hash=sha256:29757bdb7c142f9595cc1b62ec49a3d1c83fab9cef92db52b0ccebaad4eafb98 \
--hash=sha256:4ccba93c1e5a40af45b2f08e4591969fa4697eae951c708f3f83dcbf9f6c6bb1 \
--hash=sha256:4f4a150a737fccae13fb51234d41304ff2222e3b7d4c8e9428ed1a6ab48389b8 \
--hash=sha256:557df28dbccf79b152fe2d1b935f6063d9cc431199ea2b0e84892f35c03bb0ee \
--hash=sha256:5ac5ff9425d8acb8f59ac5b96bcb7fd3d272dc92d96a7c730025928ffcc88a7a \
--hash=sha256:5bae1a073ca4244620425cd3d3aa9746bde590992b98ee8c7c8be8c597ca0d4e \
--hash=sha256:5e56ac47260c0eed992789fa0b8efe43404a9adb608608631a948cee4fc2b052 \
--hash=sha256:6aeadc170090ff1889f0d2c3057557f9cd71f975f17535c26a5d37af98f19c27 \
--hash=sha256:6d5472f63a31b042aadf5ed28dd3ef0523da49ac17f0463e10fda9c4a2773352 \
--hash=sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83 \
--hash=sha256:7df6f6e9d0e33c7b1d435defb185095386c469109de723d514142632a7b9d07f \
--hash=sha256:7e4f9da7e7549946e02a6122dcad00b7c1168513acb1f8a726b1aaf504a99d32 \
--hash=sha256:803f5044b13602d58ea378576dd75aa759f52116a0232608e8fdada4da33752e \
--hash=sha256:808c7190a0fe7ae7014c42f73897cf8e9ef14ff3aa533450e51b1e72ec5239ad \
--hash=sha256:81f6d3b19bc703679a5705c6a16dabdc79823c71d791d73c65949be7f3012c02 \
--hash=sha256:83bbd8354f6abb3fdfb922d1ed47ad8d1db3ea72b0523dac8d07cdacfe1c0fcf \
--hash=sha256:8dd3c2cc49caa7a8d64b67146462aed6723a0495e44bf0aa0a2e94beaa8432f6 \
--hash=sha256:915748cfc25b8cfd81b14d00f4bfdb2ab227a30d6d43459034533f4d1c207a2a \
--hash=sha256:94f3efb718f8f49b031f2071ec7a27dd20cbfe511b4dfd54ecee54c956da2b31 \
--hash=sha256:9bd8fe07f49c170e09d76773fb86ad9135e0beee44f36e1576a201b0676d3d1d \
--hash=sha256:9bf6b699223afe6c7fe9f2ef76e0bfa6dd892c21e94ce8c957478987ade76cd8 \
--hash=sha256:a05ba88adf3d7189a974b2de7a9d56731548d35dc0a822ec3dc669caa7019b29 \
--hash=sha256:a0ac90efbc7a77b0d796c03c8cc4e62fd710b3f1e4c32947713ef2ef52e09543 \
--hash=sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27 \
--hash=sha256:a37f40a859b503304dd740686359fcf541d6fb3ff7fc10f539af7f7150917c68 \
--hash=sha256:a911aa73588d9a8b08d662b9484bc0567949529824a55d3885b77e8dd62a127a \
--hash=sha256:aef953f3b8bd0b50bd52a2e52fb54a6a2171a1889d8dea4a5959d46c6624c451 \
--hash=sha256:b28caeaf3e670c08cb7e8de221266df8494c169bd6ed8875493fab45be9607a4 \
--hash=sha256:b30110b29484adc597df6bd92a37b90e63a8c152ca8136aad100a02f8ba6d1b6 \
--hash=sha256:b5b0f7e294700b615a3bcf6d28b26e6da94e8eba63b079f4ec92e9ba6c0d6b54 \
--hash=sha256:c099cafc1834d3c5dac305865d04235f7c21c167c8dd31ebc3d6bbc357e2f023 \
--hash=sha256:d73a0187718f6eec5b2f729b0f98e4603f7bd9c48aa65d01227d1a5dcdfbe9e8 \
--hash=sha256:d8354515ab62f95a07deaf7f845886cc50e2f345ceab240a3d2d09a9f7d77853 \
--hash=sha256:dba72975485f2b87b786075e18a6e5d07dc2b4d8973beb2732b9b2816f1bad70 \
--hash=sha256:dd7546c851e59c06197a7c651335755e74aa383a835878ca86d2c650c07a2f85 \
--hash=sha256:df3ec9959241d07bc261f4983d25a1205ff37703faf42b474f15d54d88b4f8c9 \
--hash=sha256:e1d1735d97fd8a48473af048739379975651fab186f8a25a9f683534e6904179 \
--hash=sha256:e501c096aa3889133d674605ebd018471bc404a59cbc17da3c5924421c54d97c \
--hash=sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640 \
--hash=sha256:f4e97a1cf0b7a30af9e1d9dad10a5671157b9acee790d9e26996391f49b965a2 \
--hash=sha256:f8b2acb0ffdd2ce8208accbec2dca4a06937d556fdcaefd6473ba1b5daa7e3c4 \
--hash=sha256:fb04c5650de6668b853623eceadcdb1a9f2fee381f5d7b6bc842ee7c239eeec4 \
--hash=sha256:fbc08c02e9b147a11dfcaa1ac8a83168b699863493e183f7c0c8b12850b7d259 \
--hash=sha256:ff86876889ea478b1381089e55cf9e345707b312beda4986f823e1d95e8c0f59
ruamel-yaml-clib==0.2.15 \
--hash=sha256:014181cdec565c8745b7cbc4de3bf2cc8ced05183d986e6d1200168e5bb59490 \
--hash=sha256:04d21dc9c57d9608225da28285900762befbb0165ae48482c15d8d4989d4af14 \
--hash=sha256:05c70f7f86be6f7bee53794d80050a28ae7e13e4a0087c1839dcdefd68eb36b6 \
--hash=sha256:0ba6604bbc3dfcef844631932d06a1a4dcac3fee904efccf582261948431628a \
--hash=sha256:11e5499db1ccbc7f4b41f0565e4f799d863ea720e01d3e99fa0b7b5fcd7802c9 \
--hash=sha256:1b45498cc81a4724a2d42273d6cfc243c0547ad7c6b87b4f774cb7bcc131c98d \
--hash=sha256:1bb7b728fd9f405aa00b4a0b17ba3f3b810d0ccc5f77f7373162e9b5f0ff75d5 \
--hash=sha256:1f66f600833af58bea694d5892453f2270695b92200280ee8c625ec5a477eed3 \
--hash=sha256:27dc656e84396e6d687f97c6e65fb284d100483628f02d95464fd731743a4afe \
--hash=sha256:2812ff359ec1f30129b62372e5f22a52936fac13d5d21e70373dbca5d64bb97c \
--hash=sha256:2b216904750889133d9222b7b873c199d48ecbb12912aca78970f84a5aa1a4bc \
--hash=sha256:331fb180858dd8534f0e61aa243b944f25e73a4dae9962bd44c46d1761126bbf \
--hash=sha256:3cb75a3c14f1d6c3c2a94631e362802f70e83e20d1f2b2ef3026c05b415c4900 \
--hash=sha256:3eb199178b08956e5be6288ee0b05b2fb0b5c1f309725ad25d9c6ea7e27f962a \
--hash=sha256:424ead8cef3939d690c4b5c85ef5b52155a231ff8b252961b6516ed7cf05f6aa \
--hash=sha256:45702dfbea1420ba3450bb3dd9a80b33f0badd57539c6aac09f42584303e0db6 \
--hash=sha256:468858e5cbde0198337e6a2a78eda8c3fb148bdf4c6498eaf4bc9ba3f8e780bd \
--hash=sha256:46895c17ead5e22bea5e576f1db7e41cb273e8d062c04a6a49013d9f60996c25 \
--hash=sha256:46e4cc8c43ef6a94885f72512094e482114a8a706d3c555a34ed4b0d20200600 \
--hash=sha256:480894aee0b29752560a9de46c0e5f84a82602f2bc5c6cde8db9a345319acfdf \
--hash=sha256:4b293a37dc97e2b1e8a1aec62792d1e52027087c8eea4fc7b5abd2bdafdd6642 \
--hash=sha256:4be366220090d7c3424ac2b71c90d1044ea34fca8c0b88f250064fd06087e614 \
--hash=sha256:4d1032919280ebc04a80e4fb1e93f7a738129857eaec9448310e638c8bccefcf \
--hash=sha256:4d3b58ab2454b4747442ac76fab66739c72b1e2bb9bd173d7694b9f9dbc9c000 \
--hash=sha256:4dcec721fddbb62e60c2801ba08c87010bd6b700054a09998c4d09c08147b8fb \
--hash=sha256:512571ad41bba04eac7268fe33f7f4742210ca26a81fe0c75357fa682636c690 \
--hash=sha256:542d77b72786a35563f97069b9379ce762944e67055bea293480f7734b2c7e5e \
--hash=sha256:56ea19c157ed8c74b6be51b5fa1c3aff6e289a041575f0556f66e5fb848bb137 \
--hash=sha256:5d3c9210219cbc0f22706f19b154c9a798ff65a6beeafbf77fc9c057ec806f7d \
--hash=sha256:5fea0932358e18293407feb921d4f4457db837b67ec1837f87074667449f9401 \
--hash=sha256:617d35dc765715fa86f8c3ccdae1e4229055832c452d4ec20856136acc75053f \
--hash=sha256:64da03cbe93c1e91af133f5bec37fd24d0d4ba2418eaf970d7166b0a26a148a2 \
--hash=sha256:65f48245279f9bb301d1276f9679b82e4c080a1ae25e679f682ac62446fac471 \
--hash=sha256:6f1d38cbe622039d111b69e9ca945e7e3efebb30ba998867908773183357f3ed \
--hash=sha256:713cd68af9dfbe0bb588e144a61aad8dcc00ef92a82d2e87183ca662d242f524 \
--hash=sha256:71845d377c7a47afc6592aacfea738cc8a7e876d586dfba814501d8c53c1ba60 \
--hash=sha256:753faf20b3a5906faf1fc50e4ddb8c074cb9b251e00b14c18b28492f933ac8ef \
--hash=sha256:7e74ea87307303ba91073b63e67f2c667e93f05a8c63079ee5b7a5c8d0d7b043 \
--hash=sha256:88eea8baf72f0ccf232c22124d122a7f26e8a24110a0273d9bcddcb0f7e1fa03 \
--hash=sha256:923816815974425fbb1f1bf57e85eca6e14d8adc313c66db21c094927ad01815 \
--hash=sha256:9b6f7d74d094d1f3a4e157278da97752f16ee230080ae331fcc219056ca54f77 \
--hash=sha256:a8220fd4c6f98485e97aea65e1df76d4fed1678ede1fe1d0eed2957230d287c4 \
--hash=sha256:ab0df0648d86a7ecbd9c632e8f8d6b21bb21b5fc9d9e095c796cacf32a728d2d \
--hash=sha256:ac9b8d5fa4bb7fd2917ab5027f60d4234345fd366fe39aa711d5dca090aa1467 \
--hash=sha256:badd1d7283f3e5894779a6ea8944cc765138b96804496c91812b2829f70e18a7 \
--hash=sha256:bdc06ad71173b915167702f55d0f3f027fc61abd975bd308a0968c02db4a4c3e \
--hash=sha256:bf0846d629e160223805db9fe8cc7aec16aaa11a07310c50c8c7164efa440aec \
--hash=sha256:bfd309b316228acecfa30670c3887dcedf9b7a44ea39e2101e75d2654522acd4 \
--hash=sha256:c583229f336682b7212a43d2fa32c30e643d3076178fb9f7a6a14dde85a2d8bd \
--hash=sha256:cb15a2e2a90c8475df45c0949793af1ff413acfb0a716b8b94e488ea95ce7cff \
--hash=sha256:d290eda8f6ada19e1771b54e5706b8f9807e6bb08e873900d5ba114ced13e02c \
--hash=sha256:da3d6adadcf55a93c214d23941aef4abfd45652110aed6580e814152f385b862 \
--hash=sha256:dcc7f3162d3711fd5d52e2267e44636e3e566d1e5675a5f0b30e98f2c4af7974 \
--hash=sha256:def5663361f6771b18646620fca12968aae730132e104688766cf8a3b1d65922 \
--hash=sha256:e5e9f630c73a490b758bf14d859a39f375e6999aea5ddd2e2e9da89b9953486a \
--hash=sha256:e9fde97ecb7bb9c41261c2ce0da10323e9227555c674989f8d9eb7572fc2098d \
--hash=sha256:ef71831bd61fbdb7aa0399d5c4da06bea37107ab5c79ff884cc07f2450910262 \
--hash=sha256:f4421ab780c37210a07d138e56dd4b51f8642187cdfb433eb687fe8c11de0144 \
--hash=sha256:f6d3655e95a80325b84c4e14c080b2470fe4f33b6846f288379ce36154993fb1 \
--hash=sha256:fd4c928ddf6bce586285daa6d90680b9c291cfd045fc40aad34e445d57b1bf51 \
--hash=sha256:fe239bdfdae2302e93bd6e8264bd9b71290218fff7084a9db250b55caaccf43f
# via ruamel-yaml
urllib3==2.5.0 \
--hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \
--hash=sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc
urllib3==2.6.0 \
--hash=sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f \
--hash=sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1
# via requests
xmltodict==1.0.2 \
--hash=sha256:54306780b7c2175a3967cad1db92f218207e5bc1aba697d887807c0fb68b7649 \

View File

@@ -15,7 +15,7 @@ root_command() {
no_call=${args[--no-call]}
dry_run=${args[--dry-run]}
REQS="wget apt-transport-https"
REQS="wget apt-transport-https curl gpg"
function do_call() {
if [[ $dry_run ]]; then
@@ -64,7 +64,7 @@ root_command() {
Ubuntu)
if [[ $VER == "24.04" ]]; then
SUPPORTED=true
if [[ $VER == "22.04" ]]; then
elif [[ $VER == "22.04" ]]; then
SUPPORTED=true
elif [[ $VER == "20.04" ]]; then
SUPPORTED=true

View File

@@ -5,7 +5,7 @@ publisher=${args[publisher]}
no_call=${args[--no-call]}
dry_run=${args[--dry-run]}
REQS="wget apt-transport-https"
REQS="wget apt-transport-https curl gpg"
function do_call() {
if [[ $dry_run ]]; then
@@ -54,7 +54,7 @@ case "$OS" in
Ubuntu)
if [[ $VER == "24.04" ]]; then
SUPPORTED=true
if [[ $VER == "22.04" ]]; then
elif [[ $VER == "22.04" ]]; then
SUPPORTED=true
elif [[ $VER == "20.04" ]]; then
SUPPORTED=true

View File

@@ -4,8 +4,8 @@
#
Color_Off='\033[0m'
On_Red='\033[41m'
PYTHON_FROM=9
PYTHON_TO=14
PYTHON_FROM=11
PYTHON_TO=15
function detect_docker() {
if [ -n "$(grep docker </proc/1/cgroup)" ]; then
@@ -61,7 +61,7 @@ function detect_python() {
echo "# POI07| No python environment found - using environment variable: ${SETUP_PYTHON}"
fi
# Try to detect a python between 3.9 and 3.12 in reverse order
# Try to detect a python between lowest and highest supported in reverse order
if [ -z "$(which ${SETUP_PYTHON})" ]; then
echo "# POI07| Trying to detecting python3.${PYTHON_FROM} to python3.${PYTHON_TO} - using newest version"
for i in $(seq $PYTHON_TO -1 $PYTHON_FROM); do
@@ -79,7 +79,7 @@ function detect_python() {
echo "${On_Red}"
echo "# POI07| Python ${SETUP_PYTHON} not found - aborting!"
echo "# POI07| Please ensure python can be executed with the command '$SETUP_PYTHON' by the current user '$USER'."
echo "# POI07| If you are using a different python version, please set the environment variable SETUP_PYTHON to the correct command - eg. 'python3.10'."
echo "# POI07| If you are using a different python version, please set the environment variable SETUP_PYTHON to the correct command - eg. 'python3.11'."
echo "${Color_Off}"
exit 1
fi

View File

@@ -26,7 +26,7 @@ export DATA_DIR=${APP_HOME}/data
export SETUP_NGINX_FILE=${SETUP_NGINX_FILE:-/etc/nginx/sites-enabled/inventree.conf}
export SETUP_ADMIN_PASSWORD_FILE=${CONF_DIR}/admin_password.txt
export SETUP_NO_CALLS=${SETUP_NO_CALLS:-false}
export SETUP_PYTHON=${SETUP_PYTHON:-python3.9}
export SETUP_PYTHON=${SETUP_PYTHON:-python3.11}
export SETUP_ADMIN_NOCREATION=${SETUP_ADMIN_NOCREATION:-false}
# SETUP_DEBUG can be set to get debug info
# SETUP_EXTRA_PIP can be set to install extra pip packages

View File

@@ -4,28 +4,104 @@
This repository hosts the [official documentation](https://inventree.readthedocs.io/) for [InvenTree](https://github.com/inventree/inventree), an open source inventory management system.
To serve this documentation locally (e.g. for development), you will need to have Python 3 installed on your system.
## Prerequisites
## Setup
InvenTree uses [MkDocs](https://www.mkdocs.org/) to convert [Markdown](https://www.mkdocs.org/user-guide/writing-your-docs/#writing-with-markdown) format `.md` files into HTML suitable for viewing in a web browser.
Run the following commands from the top-level project directory:
!!! info "Prerequisites"
To build and serve this documentation locally (e.g. for development), you will need:
* Python 3 installed on your system.
* An existing InvenTree installation containing the virtual environment that was created during installation.
These instructions assume you followed the [InvenTree bare metal installation instructions](./docs/start/install.md), so you'll have an `inventree` user, a home directory at `/home/inventree`, the InvenTree source code cloned from [GitHub](https://github.com/inventree/inventree) into `/home/inventree/src`, and a virtual environment at `/home/inventree/env`. If you installed InvenTree some other way, this might vary, and you'll have to adjust these instructions accordingly.
!!! warning "Your InvenTree install will be updated!"
Some of the commands that follow will make changes to your install, for example, by running any pending database migrations. There's a small risk this may cause issues with your existing installation. If you can't risk this, consider setting up a separate InvenTree installation specifically for documentation development.
## Building the documentation locally
To build the documentation locally, run these commands as the `inventree` user:
```
$ git clone https://github.com/inventree/inventree
$ cd /home/inventree
$ source env/bin/activate
```
!!! info "(env) prefix"
The shell prompt should now display the `(env)` prefix, showing that you are operating within the context of the python virtual environment
You can now install the additional packages needed by mkdocs:
```
$ cd src
$ pip install --require-hashes -r docs/requirements.txt
```
## Serve Locally
## Build Documentation
To serve the pages locally, run the following command (from the top-level project directory):
Before serving the documentation, you will need to build the API schema files from the source code, so they can be included in the documentation:
```
invoke build-docs
```
!!! tip
This command is only required when building the documentation for the first time, or when changes have been made to the API schema.
## Serve Local files
```
$ invoke build-docs
```
You will see output similar to this (truncated for brevity):
```
Running InvenTree database migrations...
Exporting definitions...
Exporting settings definition to '/home/inventree/src/docs/generated/inventree_settings.json'...
Exported InvenTree settings definitions to '/home/inventree/src/docs/generated/inventree_settings.json'
Exported InvenTree tag definitions to '/home/inventree/src/docs/generated/inventree_tags.yml'
Exported InvenTree filter definitions to '/home/inventree/src/docs/generated/inventree_filters.yml'
Exported InvenTree report context definitions to '/home/inventree/src/docs/generated/inventree_report_context.json'
Exporting definitions complete
Exporting schema file to '/home/inventree/src/docs/generated/schema.yml'
Schema export completed: /home/inventree/src/docs/generated/schema.yml
Documentation build complete, but mkdocs not requested
```
If that worked, you can now generate the HTML format documentation pages:
```
$ mkdocs build -f docs/mkdocs.yml
```
## Viewing the generated output
To view the documentation locally, run the following command to start the MkDocs webpage server:
```
$ mkdocs serve -f docs/mkdocs.yml -a localhost:8080
```
## Edit Documentation Files
Alternatively, you can use the `invoke` command:
Once the server is running, it will monitor the documentation files for any changes, and update the served pages.
```
invoke dev.docs-server
```
To see all the available options:
```
invoke dev.docs-server --help
```
You can then point your web browser at http://localhost:8080/
## Editing the Documentation Files
Once the server is running, it will monitor the documentation files for any changes, and regenerate the HTML pages.
### Admonitions

View File

@@ -106,7 +106,7 @@ response = request.get('http://localhost:8080/api/part/', data=data, headers=hea
InvenTree has built-in support for using [oAuth2](https://oauth.net/2/) and OpenID Connect (OIDC) for authentication to the API. This enables using the instance as a very limited identity provider.
A default application using a public client with PKCE enabled ships with each instance. Intended to be used with the python api and configured with very wide scopes this can also be used for quick tests - the cliend_id is `zDFnsiRheJIOKNx6aCQ0quBxECg1QBHtVFDPloJ6`.
A default application using a public client with PKCE enabled ships with each instance. Intended to be used with the python api and configured with very wide scopes this can also be used for quick tests - the client_id is `zDFnsiRheJIOKNx6aCQ0quBxECg1QBHtVFDPloJ6`.
#### Managing applications

View File

@@ -67,7 +67,7 @@ print("Minimum stock:", part.minimum_stock)
### Adding Parameters
Each [part](../../part/index.md) can have multiple [parameters](../../part/parameter.md). For the example of the sofa (above) *length* and *weight* make sense. Each parameter has a parameter template that combines the parameter name with a unit. So we first have to create the parameter templates and afterwards add the parameter values to the sofa.
Each [part](../../part/index.md) can have multiple [parameters](../../concepts/parameters.md). For the example of the sofa (above) *length* and *weight* make sense. Each parameter has a parameter template that combines the parameter name with a unit. So we first have to create the parameter templates and afterwards add the parameter values to the sofa.
```python
from inventree.part import Parameter

View File

@@ -73,7 +73,7 @@ the *Scan Items Into Location* action allows you to scan items into the selected
### Stock Item Actions
From the [Stock Item detail page](./stock.md#stock-item-detail-view), the following barcode actions may be available:
From the [Stock Item detail page](./stock.md#details-tab), the following barcode actions may be available:
{{ image("app/barcode_stock_item_actions.png", "Stock item barcode actions") }}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -69,6 +69,27 @@ To access this page, select *Scan Barcode* from the main navigation menu:
{{ image("barcode/barcode_nav_menu.png", "Barcode menu item") }}
{{ image("barcode/barcode_scan_page.png", "Barcode scan page") }}
## Barcodes in Forms
The InvenTree user interface supports direct scanning of barcodes within certain forms in the web UI. This means that any form field which points to a model which supports barcodes can accept barcode input. If barcode scanning is supported for a particular field, a barcode icon will be displayed next to the input field:
{{ image("barcode/barcode_field.png", "Barcode form field") }}
To scan a barcode into a form field, click this barcode icon. A barcode scanning dialog will be displayed, allowing the user to scan a barcode using their preferred input method:
{{ image("barcode/barcode_field_dialog.png", "Barcode field scan dialog") }}
Once scanned, the form field will be automatically populated with the correct item.
{{ image("barcode/barcode_field_filled.png", "Barcode field populated") }}
Any field which supports barcode input will have this functionality, such as allocating stock items to an order:
{{ image("barcode/barcode_allocate_stock.png", "Allocate stock via barcode") }}
### User Configuration
By default, barcode scanning in form fields is disabled. Each user can enable this feature via their [user preferences](../settings/user.md#display-settings).
## App Integration
@@ -76,8 +97,15 @@ Barcode scanning is a key feature of the [companion mobile app](../app/barcode.m
## Barcode History
If enabled, InvenTree can retain logs of the most recent barcode scans. This can be very useful for debugging or auditing purpopes.
If enabled, InvenTree can retain logs of the most recent barcode scans. This can be very useful for debugging or auditing purposes.
Refer to the [barcode settings](../settings/global.md#barcodes) to enable barcode history logging.
The barcode history can be viewed via the admin panel in the web interface.
## Barcode Settings
There are a number of settings which control the behavior of barcodes within InvenTree. For more information, refer to the links below:
- [Global Barcode Settings](../settings/global.md#barcodes)
- [User Preferences for Barcode Scanning](../settings/user.md#display-settings)

View File

@@ -0,0 +1,18 @@
---
title: Attachments
---
## Attachments
An *attachment* is a file which has been uploaded and linked to a specific object within InvenTree. Attachments can be used to store additional documentation, images, or other relevant files associated with various InvenTree models.
!!! note "Business Logic"
Attachments are not used for any core business logic within InvenTree. They are intended to provide additional metadata for objects, which can be useful for documentation, reference, or reporting purposes.
Parameters can be associated with various InvenTree models.
### Attachments Tab
Any model which supports attachments will have an "Attachments" tab on its detail page. This tab displays all attachments associated with that object:
{{ image("concepts/attachments-tab.png", "Order Attachments Example") }}

View File

@@ -1,17 +1,21 @@
---
title: Part Parameters
title: Parameters
---
## Part Parameters
## Parameters
A part *parameter* describes a particular "attribute" or "property" of a specific part.
A *parameter* describes a particular "attribute" or "property" of a specific object in InvenTree. Parameters allow for flexible and customizable data to be stored against various InvenTree models.
Part parameters are located in the "Parameters" tab, on each part detail page.
There is no limit for the number of part parameters and they are fully customizable through the use of [parameter templates](#parameter-templates).
!!! note "Business Logic"
Parameters are not used for any core business logic within InvenTree. They are intended to provide additional metadata for objects, which can be useful for documentation, filtering, or reporting purposes.
Here is an example of parameters for a capacitor:
Parameters can be associated with various InvenTree models.
{{ image("part/part_parameters_example.png", "Part Parameters Example") }}
### Parameter Tab
Any model which supports parameters will have a "Parameters" tab on its detail page. This tab displays all parameters associated with that object:
{{ image("concepts/parameter-tab.png", "Part Parameters Example") }}
## Parameter Templates
@@ -22,13 +26,16 @@ Parameter templates are used to define the different types of parameters which a
| Name | The name of the parameter template (*must be unique*) |
| Description | Optional description for the template |
| Units | Optional units field (*must be a valid [physical unit](#parameter-units)*) |
| Model Type | The InvenTree model to which this parameter template applies (e.g. Part, Company, etc). If this is left blank, the template can be used for any model type. |
| Choices | A comma-separated list of valid choices for parameter values linked to this template. |
| Checkbox | If set, parameters linked to this template can only be assigned values *true* or *false* |
| Selection List | If set, parameters linked to this template can only be assigned values from the linked [selection list](#selection-lists) |
{{ image("concepts/parameter-template.png", "Parameters Template") }}
### Create Template
Parameter templates are created and edited via the [settings interface](../settings/global.md).
Parameter templates are created and edited via the [admin interface](../settings/admin.md).
To create a template:
@@ -54,11 +61,11 @@ Select the parameter `Template` you would like to use for this parameter, fill-o
## Parametric Tables
Parametric tables gather all parameters from all parts inside a particular [part category](./index.md#part-category) to be sorted and filtered.
Parametric tables gather all parameters from all objects of a particular type, to be sorted and filtered.
To access a category's parametric table, click on the "Parameters" tab within the category view:
Tables views which support parametric filtering and sorting will have a "Parametric View" button above the table:
{{ image("part/parametric_table_tab.png", "Parametric Table Tab") }}
{{ image("concepts/parametric-parts.png", "Parametric Parts Table") }}
### Sorting by Parameter Value
@@ -139,7 +146,7 @@ Parameter sorting takes unit conversion into account, meaning that values provid
### Selection Lists
Selection Lists can be used to add a large number of predefined values to a parameter template. This can be useful for parameters which must be selected from a large predefined list of values (e.g. a list of standardised colo codes). Choices on templates are limited to 5000 characters, selection lists can be used to overcome this limitation.
Selection Lists can be used to add a large number of predefined values to a parameter template. This can be useful for parameters which must be selected from a large predefined list of values (e.g. a list of standardized color codes). Choices on templates are limited to 5000 characters, selection lists can be used to overcome this limitation.
It is possible that plugins lock selection lists to ensure a known state.

View File

@@ -59,9 +59,9 @@ The [unit of measure](../part/index.md#units-of-measure) field for the [Part](..
The [supplier part](../part/index.md/#supplier-parts) model uses real-world units to convert between supplier part quantities and internal stock quantities. Unit conversion rules ensure that only compatible unit types can be supplied
### Part Parameter
### Parameter
The [part parameter template](../part/parameter.md#parameter-templates) model can specify units of measure, and part parameters can be specified against these templates with compatible units
The [parameter template](../concepts/parameters.md#parameter-templates) model can specify units of measure, and part parameters can be specified against these templates with compatible units
## Custom Units

View File

@@ -0,0 +1,56 @@
---
title: InvenTree Development
---
## Introduction
If you are interested in contributing to InvenTree, then this section is for you! Here you will find information about the architecture of InvenTree, how to set up a development environment, and guidelines for contributing code or documentation.
### Architecture Overview
Read the [architecture overview](./architecture.md) to understand the high-level architecture of InvenTree, including how requests are processed, the backend and frontend architecture, and various components of the system.
### Contribution Guide
Start with the [contribution guide](./contributing.md) to understand how to get involved with the InvenTree project.
### Devcontainer Setup
We provide a [devcontainer](./devcontainer.md) configuration to help you quickly set up a development environment using vscode.
### Frontend Development
For information on developing the InvenTree frontend, refer to the [frontend development guide](./react-frontend.md).
## Profiling Tools
The InvenTree project supports integrated profiling tools to help developers analyze and optimize performance. Note that the following tools are intended for development use only and should not be enabled in production environments. In fact, they are explicitly disabled unless the server is running in [debug mode](../start/index.md#debug-mode).
### Django Silk
[django-silk](https://silk.readthedocs.io/en/latest/) is a profiling tool that can be used to monitor and analyze the performance of Django applications. It provides insights into SQL queries, request/response times, and more.
To enable django-silk profiling, ensure that the `debug_silk` option is set to `True` in your [config file](../start/config.md#configuration-file). Alternative, you can set the `INVENTREE_DEBUG_SILK` environment variable to enable this feature.
Once enabled, you can access the silk interface at the `/silk/` endpoint of your InvenTree instance.
!!! tip "Run Migrations"
If you are enabling django-silk for the first time, you may need to run database migrations to create the necessary tables. You can do this by running `invoke migrate`.
#### Detailed Profiling
To enable detailed profiling in django-silk, set the `INVENTREE_DEBUG_SILK_PROFILING` environment variable to `True`, or set the `debug_silk_profiling` option to `True` in your config file. This will enable more granular profiling features within django-silk. Refer to the [django-silk documentation](https://github.com/jazzband/django-silk#profiling) for more information.
### Django QueryCount
Enabling the `INVENTREE_DEBUG_QUERYCOUNT` setting will log (to the terminal) the number of database queries executed for each page load. This can be useful for identifying performance bottlenecks in the InvenTree server. Note that this setting is only available if `INVENTREE_DEBUG` is also enabled.
### Database Logging
Enabling the `INVENTREE_DB_LOGGING` setting will log all database queries to the terminal. This can be useful for debugging database-related issues.
### Internal Profiling Tools
In addition to the above third-party tools, InvenTree includes some internal profiling tools that can be enabled in debug mode. These tools can be used to provide additional insights into the performance of various components of the InvenTree server.
These profiling tools can be found in `./src/backend/InvenTree/profiling.py`.

View File

@@ -98,7 +98,7 @@ Sometimes, users may encounter unexpected error messages when updating their Inv
The most common problem here is that the correct sequence of steps has not been followed:
1. Ensure that the InvenTree web server and background worker processes are *halted*
1. Ensure that the InvenTree [web server](./start/processes.md#web-server) and [background worker](./start/processes.md#background-worker) processes are *halted*
1. Update the InvenTree software (e.g. using git or docker, depending on installation method)
1. Run the `invoke update` command
1. Restart the web server and background worker processes
@@ -150,7 +150,7 @@ or
### Background Worker "Not Running"
The background worker process must be started separately to the web-server application.
The [background worker process](./start/processes.md#background-worker) must be started separately to the web-server application.
From the top-level source directory, run the following command from a separate terminal, while the server is already running:

View File

@@ -31,7 +31,7 @@ Before continuing, it is important that the difference between *untracked* and *
#### BOM Considerations
A [Bill of Materials](./bom.md) to generate an assembly may consist of a mixture of *untracked* and *tracked* components. The build order process can facilitate this, as documentated in the sections below.
A [Bill of Materials](./bom.md) to generate an assembly may consist of a mixture of *untracked* and *tracked* components. The build order process can facilitate this, as documented in the sections below.
### Tracked Build Outputs

View File

@@ -4,7 +4,7 @@ title: Trackable Parts
## Stock Tracking
Denoting a part as *Trackble* changes the way that [stock items](../stock/index.md) associated with the particular part are handled in the database. A trackable part also has more restrictions imposed by the database scheme.
Denoting a part as *Trackable* changes the way that [stock items](../stock/index.md) associated with the particular part are handled in the database. A trackable part also has more restrictions imposed by the database scheme.
For many parts in an InvenTree database, simply tracking current stock levels (and locations) is sufficient. However, some parts require more extensive tracking than simple stock level knowledge.

View File

@@ -39,12 +39,6 @@ A Part is defined in the system by the following parameters:
The Part view page organizes part data into sections, displayed as tabs. Each tab has its own function, which is described in this section.
### Parameters
Parts can have multiple defined parameters.
[Read about Part parameters](./parameter.md)
### Variants
If a part is a *Template Part* then the *Variants* tab will be visible.
@@ -125,10 +119,18 @@ Related parts can be added and are shown under a table of the same name in the "
This feature can be enabled or disabled in the global part settings.
### Parameters
Parts can have multiple defined parameters.
[Read about parameters](../concepts/parameters.md).
### Attachments
The *Part Attachments* tab displays file attachments associated with the selected *Part*. Multiple file attachments (such as datasheets) can be uploaded for each *Part*.
[Read about attachments](../concepts/attachments.md).
### Notes
A part may have notes attached, which support markdown formatting.

View File

@@ -21,7 +21,7 @@ The following builtin plugins are available in InvenTree:
| Barcodes | [TME](./barcode_tme.md) | TME barcode support | No |
| Data Export | [BOM Exporter](./bom_exporter.md) | Custom [exporter](../mixins/export.md) for BOM data | Yes |
| Data Export | [InvenTree Exporter](./inventree_exporter.md) | Custom [exporter](../mixins/export.md) for InvenTree data | Yes |
| Data Export | [Parameter Exporter](./part_parameter_exporter.md) | Custom [exporter](../mixins/export.md) for part parameter data | Yes |
| Data Export | [Parameter Exporter](./parameter_exporter.md) | Custom [exporter](../mixins/export.md) for parameter data | Yes |
| Data Export | [Stocktake Exporter](./stocktake_exporter.md) | Custom [exporter](../mixins/export.md) for stocktake data | No |
| Events | [Auto Create Child Builds](./auto_create_builds.md) | Automatically create child build orders for sub-assemblies | No |
| Events | [Auto Issue Orders](./auto_issue.md) | Automatically issue pending orders when target date is reached | No |

View File

@@ -0,0 +1,27 @@
---
title: Parameter Exporter
---
## Parameter Exporter
The **Parameter Exporter** plugin provides custom export functionality for models which support custom [Parameter](../../concepts/parameters.md) data.
It utilizes the [ExporterMixin](../mixins/export.md) mixin to provide a custom export format for part parameter data.
In addition to the standard exported fields, this plugin also exports all associated parameter data for each row of the export.
### Activation
This plugin is a *mandatory* plugin, and is always enabled.
### Plugin Settings
This plugin has no configurable settings.
## Usage
This plugin is used in the same way as the [InvenTree Exporter Plugin](./inventree_exporter.md), but provides a custom export format for part parameter data.
When exporting parameter data, the *Parameter Exporter* plugin is available for selection in the export dialog. When selected, the plugin provides some additional export options to control the data export process.
{{ image("parameter_export_options.png", base="plugin/builtin", title="Parameter Export Options") }}

View File

@@ -1,25 +0,0 @@
---
title: Part Parameter Exporter
---
## Part Parameter Exporter
The **Part Parameter Exporter** plugin provides custom export functionality for [Part Parameter](../../part/parameter.md) data.
It utilizes the [ExporterMixin](../mixins/export.md) mixin to provide a custom export format for part parameter data.
### Activation
This plugin is a *mandatory* plugin, and is always enabled.
### Plugin Settings
This plugin has no configurable settings.
## Usage
This plugin is used in the same way as the [InvenTree Exporter Plugin](./inventree_exporter.md), but provides a custom export format for part parameter data.
When exporting part parameter data, the *Part Parameter Exporter* plugin is available for selection in the export dialog. When selected, the plugin provides some additional export options to control the data export process.
{{ image("parameter_export_options.png", base="plugin/builtin", title="Part Parameter Export Options") }}

View File

@@ -134,11 +134,11 @@ Validation of the Part IPN (Internal Part Number) field is exposed to custom plu
summary: False
members: []
### Part Parameter Values
### Parameter Values
[Part parameters](../../part/parameter.md) can also have custom validation rules applied, by implementing the `validate_part_parameter` method. A plugin which implements this method should raise a `ValidationError` with an appropriate message if the part parameter value does not match a required convention.
[Parameters](../../concepts/parameters.md) can also have custom validation rules applied, by implementing the `validate_parameter` method. A plugin which implements this method should raise a `ValidationError` with an appropriate message if the parameter value does not match a required convention.
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_parameter
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_parameter
options:
show_bases: False
show_root_heading: False

View File

@@ -6,7 +6,7 @@ The InvenTree project is grateful to receive resources from various vendors free
Individuals and companies can also support via [GitHub sponsors](https://github.com/sponsors/inventree).
## Current supporters
## Current Supporters
- [DigitalOcean](https://inventree.org/digitalocean) - Cloud hosting provider, used to host the InvenTree demo instance
- [Crowdin](https://crowdin.com/) - Translation platform, used to manage the [InvenTree translations](../develop/contributing.md#translations) across backend, frontend and app
- [SonarQube Cloud](https://sonarcloud.io/) - Code quality and security analysis, used to track the code quality of the various components
@@ -15,7 +15,8 @@ Individuals and companies can also support via [GitHub sponsors](https://github.
- [Netlify](https://www.netlify.com/) - Static site hosting provider, used to test deploy the frontend and website
- [Depot](https://depot.dev/?utm_source=inventree) - Docker build accelerator, used to build the multi-arch images for the InvenTree docker image
## Past supporters
Non cromprehensive list of past supporters. The project stops consuming resources for various reasons, this does not mean they are not good resources.
## Past Supporters
Non comprehensive list of past supporters. The project stops consuming resources for various reasons, this does not mean they are not good resources.
- [Coveralls](https://coveralls.io/) - Code coverage as a service
- [Deepsource](https://deepsource.io/) - Code quality and security analysis

View File

@@ -273,7 +273,7 @@ To convert a currency value from one currency to another, use the `convert_curre
!!! info "Data Types"
The `money` parameter must be `Money` class instance. If not, an error will be raised.
#### create_currency
### create_currency
Create a `currency` instance using the `create_currency` helper function. This returns a `Money` class instance based on the provided amount and currency type.
@@ -286,12 +286,13 @@ Create a `currency` instance using the `create_currency` helper function. This r
Simple mathematical operators are available, as demonstrated in the example template below. These operators can be used to perform basic arithmetic operations within the report template.
### Input Types
These mathematical functions accept inputs of various input types, and attempt to perform the operation accordingly. Note that any inputs which are provided as strings or numbers will be converted to `Decimal` class types before the operation is performed.
!!! info "Input Types"
These mathematical functions accept inputs of various input types, and attempt to perform the operation accordingly. Note that any inputs which are provided as strings or numbers will be converted to `Decimal` class types before the operation is performed.
### add
Add two numbers together using the `add` helper function:
::: report.templatetags.report.add
options:
show_docstring_description: false
@@ -299,6 +300,8 @@ These mathematical functions accept inputs of various input types, and attempt t
### subtract
Subtract one number from another using the `subtract` helper function:
::: report.templatetags.report.subtract
options:
show_docstring_description: false
@@ -306,6 +309,8 @@ These mathematical functions accept inputs of various input types, and attempt t
### multiply
Multiply two numbers together using the `multiply` helper function:
::: report.templatetags.report.multiply
options:
show_docstring_description: false
@@ -313,6 +318,8 @@ These mathematical functions accept inputs of various input types, and attempt t
### divide
Divide one number by another using the `divide` helper function:
::: report.templatetags.report.divide
options:
show_docstring_description: false
@@ -320,6 +327,8 @@ These mathematical functions accept inputs of various input types, and attempt t
### modulo
Perform a modulo operation using the `modulo` helper function:
::: report.templatetags.report.modulo
options:
show_docstring_description: false
@@ -536,11 +545,11 @@ You can add asset images to the reports and labels by using the `{% raw %}{% ass
{% endraw %}
```
## Part Parameters
## Parameters
If you need to load a part parameter for a particular Part, within the context of your template, you can use the `part_parameter` template tag:
If you need to load a parameter value for a particular model instance, within the context of your template, you can use the `parameter` template tag:
::: report.templatetags.report.part_parameter
::: report.templatetags.report.parameter
options:
show_docstring_description: false
show_source: False
@@ -553,7 +562,7 @@ The following example assumes that you have a report or label which contains a v
{% raw %}
{% load report %}
{% part_parameter part "length" as length %}
{% parameter part "length" as length %}
Part: {{ part.name }}<br>
Length: {{ length.data }} [{{ length.units }}]
@@ -561,7 +570,7 @@ Length: {{ length.data }} [{{ length.units }}]
{% endraw %}
```
A [Part Parameter](../part/parameter.md) has the following available attributes:
A [Parameter](../concepts/parameters.md) has the following available attributes:
| Attribute | Description |
| --- | --- |
@@ -569,7 +578,7 @@ A [Part Parameter](../part/parameter.md) has the following available attributes:
| Description | The *description* of the parameter |
| Data | The *value* of the parameter (e.g. "123.4") |
| Units | The *units* of the parameter (e.g. "km") |
| Template | A reference to a [PartParameterTemplate](../part/parameter.md#parameter-templates) |
| Template | A reference to a [ParameterTemplate](../concepts/parameters.md#parameter-templates) |
## Rendering Markdown

View File

@@ -44,5 +44,45 @@ For example, rendering the name of a part (which is available in the particular
</p></i>
{% endraw %}
```
#### Rendering a single report vs. multiple report from selection
Users can select multiple items such as `part`, `stockItem`,...etc to render from a report template. By default, the `merge` attribute of report template is disabled, which means an independent report will be generated for each item in the list of selected items. If `merge` is enabled, all selected items will be available in the `instances` context variable of the report template. Users are free to access them by indexing or in a loop. For more details, visit [context variable](./context_variables.md)
## Merging Reports
When rendering reports for multiple items, the default behaviour is that each item is rendered as a separate report. The chosen templeate is rendered multiple times, once for each item selected, and expects a single item in the context variable.
However, it is possible to merge multiple items into a single report document. This is achieved by enabling the `merge` attribute of the report template:
{{ image("report/report_merge.png", alt="Report Merge Option") }}
When the `merge` is enabled, all selected items are passed to the report template in the `instances` context variable, which is a list of all selected items. The user can then iterate over this list to render multiple items in a single report document.
### Instance Context
When rendering a single template against multiple *instances* of a particular model (e.g. multiple parts, multiple stock items, etc), each instance being rendered has its own unique context data.
Each "instance" is provided to the report template as a dictionary of context variables, which can be accessed using standard django template syntax.
Refer to the [context variable documentation](./context_variables.md) for more information about the available context variables for each model type.
The `instances` variable is provided as a list of all selected items, where each item in the list is a dictionary of context variables for that particular instance. Within the report template, the user can iterate over the `instances` list to render each item in turn.
### Example
As an example, let's consider a report template where we are printing multiple parts in a single report document.
When the `merge` option is enabled, the report template is provided with an `instances` variable, which is a list of all selected parts.
Each *instance* in the `instances` list is a dictionary of context variables for that particular part, which conforms to the standard [part context structure](./context_variables.md#part).
```django
{% raw %}
{% for instance in instances %}
Part Name: {{ instance.part.name }} <br>
IPN: {{ instance.part.IPN }} <br>
Description: {{ instance.part.description }} <br>
{% endraw %}
```
!!! tip "Instance Prefix"
Note that the context variable is prefixed with `instance.` when accessing variables for each item in the `instances` list.

View File

@@ -13,6 +13,9 @@ We use the powerful [WeasyPrint](https://weasyprint.org/) PDF generation engine
Templates are rendered using standard HTML / CSS - if you are familiar with web page layout, you're ready to go!
HTML form elements (`input`, `select`, `textarea`, `button`) are converted into PDF form controls. Use CSS to limit which elements are converted; refer to the
[WeasyPrint docs](https://doc.courtbouillon.org/weasyprint/stable/common_use_cases.html#include-pdf-forms) for further information.
### Template Language
Uploaded report template files are passed through the [django template rendering framework]({% include "django.html" %}/topics/templates/), and as such accept the same variable template strings as any other django template file. Different variables are passed to the report template (based on the context of the report) and can be used to customize the contents of the generated PDF.

View File

@@ -4,7 +4,7 @@ title: InvenTree Multi Factor Authentication
## Multi Factor Authentication
InvenTree gives the option to use TOTP or statically generated backup tokens as an additional factor to password or SSO authentication. This is a widely adopted security feature on enterprise web services. We highly encourage to enable it if you expose your instance to the public internet.
InvenTree gives the option to use [TOTP](https://en.wikipedia.org/wiki/Time-based_One-Time_Password) or statically generated backup tokens as an additional factor to password or SSO authentication. This is a widely adopted security feature on enterprise web services. We highly encourage to enable it if you expose your instance to the public internet.
As TOTP is an [open standard](https://datatracker.ietf.org/doc/html/rfc6238) there are a lot of different ways to hold your key and generate the time based tokens needed for authentication. That ranges from physical devices to password managers and mobile apps. We do not advertise any method but recommend to keep password and token generator separate from each other.
@@ -16,4 +16,4 @@ To make MFA mandatory for all users:
### Security Consideration
A user can lock themself out if they lose access to both the device with their TOTP app and their backup tokens. An admin can delete their tokens from the admin pages (they exist under the 'TOTP devices' / 'static devices' models) . This should be a last resort and only done by people knowledgeable about the [admin pages](../settings/admin.md) as changes there might circumvent InvneTrees business and security logic.
A user can lock themselves out if they lose access to both the device with their TOTP app and their backup tokens. An admin can delete their tokens from the admin pages (they exist under the 'TOTP devices' / 'static devices' models) . This should be a last resort and only done by people knowledgeable about the [admin pages](../settings/admin.md) as changes there might circumvent InvenTree's business and security logic.

View File

@@ -9,7 +9,7 @@ InvenTree can be configured to send emails to users, for various purposes.
To enable this, email configuration settings must be supplied to the InvenTree [configuration options](../start/config.md#email-settings).
!!! info "Functionality might be degraded"
Multiple functions of InvenTree require functioning email delivery, including *Password Reset*, *Notififications*, *Update Infos*
Multiple functions of InvenTree require functioning email delivery, including *Password Reset*, *Notifications*, *Update Infos*
### Outgoing

View File

@@ -83,6 +83,24 @@ This is a security measure to prevent plugins from changing the core functionali
An error occurred when discovering or initializing a machine type from a plugin. This likely indicates a faulty or incompatible plugin.
#### INVE-E13
**Error reading InvenTree configuration file**
An error occurred while reading the InvenTree configuration file. This might be caused by a syntax error or invalid value in the configuration file.
#### INVE-E14
**Could not import Django**
Django is not installed in the current Python environment. This means that the InvenTree backend is not running within the correct [python virtual environment](../start/index.md#virtual-environment) or that the required Python packages were not installed correctly.
#### INVE-E15
**Python version not supported**
This error occurs attempting to run InvenTree on a version of Python which is older than the minimum required version. We [require Python {{ config.extra.min_python_version }} or newer](../start/index.md#python-requirements)
### INVE-W (InvenTree Warning)
Warnings - These are non-critical errors which should be addressed when possible.
@@ -153,7 +171,7 @@ The warning text will show the recommended command for intended use.
#### INVE-W10
**Config not in recommended directory - Backend**
A configuration file is not in the recommended directory. This might lead to issues with the deployment method you are using. It might also lead to confusinon.
A configuration file is not in the recommended directory. This might lead to issues with the deployment method you are using. It might also lead to confusion.
The warning text will show the recommended directory for your deployment method.

View File

@@ -78,7 +78,7 @@ If this setting is enabled, users can reset their password via email. This requi
If this setting is enabled, users must have multi-factor authentication enabled to log in.
#### Auto Fil SSO Users
#### Auto Fill SSO Users
Automatically fill out user-details from SSO account-data. If this feature is enabled the user is only asked for their username, first- and surname if those values can not be gathered from their SSO profile. This might lead to unwanted usernames bleeding over.
@@ -174,11 +174,14 @@ Configuration of label printing:
{{ globalsetting("PART_COPY_TESTS") }}
{{ globalsetting("PART_CATEGORY_PARAMETERS") }}
{{ globalsetting("PART_CATEGORY_DEFAULT_ICON") }}
{{ globalsetting("PART_PARAMETER_ENFORCE_UNITS") }}
#### Part Parameter Templates
#### Parameter Templates
Refer to the section describing [how to create part parameter templates](../part/parameter.md#create-template).
| Name | Description | Default | Units |
| ---- | ----------- | ------- | ----- |
{{ globalsetting("PARAMETER_ENFORCE_UNITS") }}
For more information on parameters, refer to the [parameter documentation](../concepts/parameters.md).
### Categories

View File

@@ -22,6 +22,7 @@ The *Display Settings* screen shows general display configuration options:
{{ usersetting("STICKY_HEADER") }}
{{ usersetting("STICKY_TABLE_HEADER") }}
{{ usersetting("SHOW_SPOTLIGHT") }}
{{ usersetting("BARCODE_IN_FORM_FIELDS") }}
{{ usersetting("DATE_DISPLAY_FORMAT") }}
{{ usersetting("FORMS_CLOSE_USING_ESCAPE") }}
{{ usersetting("DISPLAY_STOCKTAKE_TAB") }}

View File

@@ -143,7 +143,7 @@ pip install django-storages[google]
You will need to change the storage backend, which is set via the `INVENTREE_BACKUP_STORAGE` environment variable, or via `backup_storage` in the configuration file:
```yaml
backup_stoage: storages.backends.gcloud.GoogleCloudStorage
backup_storage: storages.backends.gcloud.GoogleCloudStorage
```
### Configure Backend Options

View File

@@ -92,7 +92,9 @@ The following debugging / logging options are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_DEBUG | debug | Enable [debug mode](./index.md#debug-mode) | False |
| INVENTREE_DEBUG_QUERYCOUNT | debug_querycount | Enable [query count logging](https://github.com/bradmontgomery/django-querycount) in the terminal | False |
| INVENTREE_DEBUG_QUERYCOUNT | debug_querycount | Enable support for [django-querycount](../develop/index.md#django-querycount) middleware. | False |
| INVENTREE_DEBUG_SILK | debug_silk | Enable support for [django-silk](../develop/index.md#django-silk) profiling tool. | False |
| INVENTREE_DEBUG_SILK_PROFILING | debug_silk_profiling | Enable detailed profiling in django-silk | False |
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
| INVENTREE_LOG_LEVEL | log_level | Set level of logging to terminal | WARNING |
| INVENTREE_JSON_LOG | json_log | log as json | False |
@@ -103,13 +105,7 @@ The following debugging / logging options are available:
Enabling the `INVENTREE_DEBUG` setting will turn on [Django debug mode]({% include "django.html" %}/ref/settings/#debug). This mode is intended for development purposes, and should not be enabled in a production environment. Read more about [InvenTree debug mode](./index.md#debug-mode).
### Query Count Logging
Enabling the `INVENTREE_DEBUG_QUERYCOUNT` setting will log the number of database queries executed for each page load. This can be useful for identifying performance bottlenecks in the InvenTree server. Note that this setting is only available if `INVENTREE_DEBUG` is also enabled.
### Database Logging
Enabling the `INVENTREE_DB_LOGGING` setting will log all database queries to the terminal. This can be useful for debugging database-related issues.
In debug mode, there are additional [profiling tools](../develop/index.md#profiling-tools) available to help developers analyze and optimize performance.
## Server Access
@@ -266,6 +262,9 @@ The following database options can be configured:
| INVENTREE_DB_HOST | database.HOST | Database host address (if required) | *Not specified* |
| INVENTREE_DB_PORT | database.PORT | Database host port (if required) | *Not specified* |
!!! tip "Database Password"
The value specified for `INVENTREE_DB_PASSWORD` should not contain comma `,` or colon `:` characters, otherwise the connection to the database may fail.
### PostgreSQL Settings
If running with a PostgreSQL database backend, the following additional options are available:
@@ -318,6 +317,9 @@ The following cache settings are available:
| INVENTREE_CACHE_KEEPALIVE_INTERVAL | cache.keepalive_interval | Cache keepalive interval | 1 |
| INVENTREE_CACHE_USER_TIMEOUT | cache.user_timeout | Cache user timeout | 1000 |
!!! tip "Cache Password"
The value specified for `INVENTREE_CACHE_PASSWORD` should not contain comma `,` or colon `:` characters, otherwise the connection to the cache server may fail.
## Email Settings
To enable [email functionality](../settings/email.md), email settings must be configured here, either via environment variables or within the configuration file.
@@ -336,6 +338,10 @@ The following email settings are available:
| INVENTREE_EMAIL_SENDER | email.sender | Sending email address | *Not specified* |
| INVENTREE_EMAIL_PREFIX | email.prefix | Prefix for subject text | [InvenTree] |
### Email Backend
The default email implementation uses the Django STMP backend. This should be sufficient for most implementations, although other backends can be used if required. Note that selection of a different backend requires must use fully qualified module path, and requires advanced knowledge.
### Sender Email
The "sender" email address is the address from which InvenTree emails are sent (by default) and must be specified for outgoing emails to function:
@@ -425,7 +431,7 @@ InvenTree provides allowance for additional sign-in options. The following optio
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_MFA_ENABLED | mfa_enabled | Enable or disable multi-factor authentication support for the InvenTree server | True |
| INVENTREE_MFA_SUPPORTED_TYPES | mfa_supported_types | List of supported multi-factor authentication types | recovery_codes,totp |
| INVENTREE_MFA_SUPPORTED_TYPES | mfa_supported_types | List of supported multi-factor authentication types | recovery_codes,totp,webauthn |
### Single Sign On
@@ -482,7 +488,7 @@ The INVENTREE_CUSTOMIZE environment variable must contain a json object with the
the wanted values. Example:
```
INVENTREE_CUSTOMIZE={"login_message":"Hallo Michi"}
INVENTREE_CUSTOMIZE={"login_message":"Hello World"}
```
This example sets a login message. Take care of the double quotes.

View File

@@ -235,6 +235,21 @@ The [Caddy](./docker.md#ssl-certificates) container will automatically generate
Any persistent files generated by the Caddy container (such as certificates, etc) will be stored in the `caddy` directory within the external volume.
### Web Server Bind Address
By default, the Dockerized InvenTree web server binds to all available network interfaces and listens for IPv4 traffic on port 8000.
This can be adjusted using the following environment variables:
| Environment Variable | Default |
| --- | --- | --- | --- |
| INVENTREE_WEB_ADDR | 0.0.0.0 |
| INVENTREE_WEB_PORT | 8000 |
These variables are combined in the [Dockerfile]({{ sourcefile("contrib/container/Dockerfile") }}) to build the bind string passed to the InvenTree server on startup.
!!! tip "IPv6 Support"
To enable IPv6/Dual Stack support, set `INVENTREE_WEB_ADDR` to `[::]` when you create/start the container.
### Demo Dataset
To quickly get started with a [demo dataset](../demo.md), you can run the following command:

View File

@@ -123,11 +123,14 @@ By default, a production InvenTree installation is configured to run with [DEBUG
Running in DEBUG mode provides many handy development features, however it is strongly recommended *NOT* to run in DEBUG mode in a production environment. This recommendation is made because DEBUG mode leaks a lot of information about your installation and may pose a security risk.
So, for a production setup, you should set `INVENTREE_DEBUG=false` in the [configuration options](./config.md).
So, for a production setup, you should ensure that `INVENTREE_DEBUG=false` in the [configuration options](./config.md).
!!! warning "Security Risk"
Running InvenTree in DEBUG mode in a production environment is a significant security risk, and should be avoided at all costs.
### Turning Debug Mode off
When running in DEBUG mode, the InvenTree web server natively manages *static* and *media* files, which means that when DEBUG mode is *disabled*, the proxy setup has to be configured to handle this.
When running in DEBUG mode, the InvenTree web server natively manages *static* and *media* files, which means that when DEBUG mode is *disabled* (which is the default for a production setup), the proxy setup has to be configured to handle this.
!!! info "Read More"
Refer to the [proxy server documentation](./processes.md#proxy-server) for more details

View File

@@ -108,6 +108,27 @@ The Python packages required by the InvenTree server must be installed into the
```
pip install --upgrade --ignore-installed invoke
```
#### Install Python Bindings
Depending on your database the python bindings must also be installed (into your virtual environment).
For PostgreSQL install:
```
pip3 install psycopg pgcli
```
For MySQL install:
```
pip3 install mysqlclient mariadb
```
If all packages have been installed run:
```
invoke install
```
@@ -155,14 +176,6 @@ grant all privileges on database inventree to myuser;
!!! info "Username / Password"
You should change the username and password from the values specified above. This username and password will also be for the InvenTree database connection configuration.
#### Install Python Bindings
The PostgreSQL python binding must also be installed (into your virtual environment):
```
pip3 install psycopg pgcli
```
#### Install Postgresql client
If PostgreSQL and InvenTree are installed on separate servers / containers the PostgreSQL client has to be installed also where InvenTree is running.
@@ -184,14 +197,6 @@ To run InvenTree with the MySQL or MariaDB backends, a number of extra packages
sudo apt-get install mysql-server libmysqlclient-dev
```
#### Install Python Bindings
Install the python bindings for MySQL (into the python virtual environment).
```
pip3 install mysqlclient mariadb
```
#### Create Database
Assuming the MySQL server is installed and running, login to the MySQL server as follows:

View File

@@ -114,3 +114,86 @@ Once the migration process completes, the database records are now updated! Rest
!!! tip "Example: Docker"
If running under docker, run `docker compose up -d`
## Migrating Between Incompatible Database Versions
There may be occasions when InvenTree data needs to be migrated between two database installations running *incompatible* versions of the database software. For example, InvenTree may be running on a Postgres database running on version 12, and the administrator wishes to migrate the data to a Postgres version 17 database.
!!! warning "Advanced Procedure"
The following procedure is *advanced*, and should only be attempted by experienced database administrators. Always ensure that database backups are made before attempting any migration procedure.
Due to inherit incompatibilities between different major versions of database software, it is not always possible to directly migrate data between two different database versions. In such cases, the following procedure can be used as a workaround.
!!! warning "InvenTree Version"
It is *crucial* that both InvenTree database installations are running the same version of InvenTree software! If this is not the case, data migration may fail, and there is a possibility that data corruption can occur. Ensure that the original database is up to date, by running `invoke update`.
The following instructions assume that the source (old) database is Postgres version 15, and the target (new) database is Postgres version 17. Additionally, it assumes that the InvenTree installation is running under [docker / docker compose](./docker.md), for simplicity. Adjust commands as required for other InvenTree configurations or database software.
The overall procedure is as follows:
### Backup Old Database
Run the following command to create a backup dump of the old database:
```
docker compose run --rm inventree-server invoke backup
```
This will create a database backup file in the InvenTree backup directory.
!!! tip "Secondary Backup"
It may be prudent to create a secondary backup of the database, separate to the one created by InvenTree.
### Shutdown Old Database
Stop the old InvenTree installation, to ensure that the database is not being accessed during the migration process:
```
docker compose down
```
### Remove Old Database Files
The raw database files are incompatible between different major versions of Postgres. Thus, the old database files must be removed before starting the new database.
!!! warning "Data Loss"
Ensure that a complete backup of the old database has been made before proceeding! Removing the database files will result in data loss if a backup does not exist.
```
rm -rf ./path/to/database/*
```
!!! info "Database Location"
The location of the database files depends on how InvenTree was configured.
### Start New Database
Update the InvenTree docker configuration to use the new version of Postgres (e.g. `postgres:17`), and then start the InvenTree installation:
```
docker compose up -d
```
This will initialize a new, empty database using the new version of Postgres.
### Run Database Migrations
Run the database migration process to ensure that the new database schema is correctly initialized:
```
docker compose run --rm inventree-server invoke update
```
### Restore Database Backup
Finally, restore the database backup created earlier into the new database:
```
docker compose run --rm inventree-server invoke restore
```
This will load the database records from the backup file into the new database.
### Caveats
The process described here is a *suggested* procedure for migrating between incompatible database versions. However, due to the complexity of database software, there may be unforeseen complications that arise during the process.

View File

@@ -0,0 +1,88 @@
---
title: Stock Availability
---
## Stock Availability
Each stock item represents a physical quantity of a particular part in a specific location. Given the complexities of tracking stock across multiple locations, reservations, and allocations, it is important to understand whether a stock item is actually available for use.
There are multiple terms used in InvenTree to describe stock availability:
### Required
The *Required* quantity indicates the total number of a certain [part](../part/index.md) that is needed to fulfill all current orders, builds, or other commitments. This is independent of the actual stock levels.
### In Stock
The *In Stock* quantity represents the total number of items physically present in the stock location, regardless of their allocation or reservation status.
For example, if a stock item has a quantity of 100, it means there are 100 units of that part physically present in the specified location.
### Allocated
The *Allocated* quantity indicates the number of stock items that have been reserved for specific orders, builds, or other commitments. This number is specified against each stock item when it is allocated.
Note that the *Allocated* quantity is always less than or equal to the *In Stock* quantity.
For example, if a stock item has an *In Stock* quantity of 100, and 30 units have been allocated to fulfill orders, then the *Allocated* quantity is 30. In this case the *In Stock* quantity remains 100, until the alloacted quantity is actually consumed.
### Available
The *Available* quantity for a given stock item is the difference between the *In Stock* quantity and the *Allocated* quantity.
For example, if a stock item has an *In Stock* quantity of 100, and 30 units have been allocated, then the *Available* quantity is 70.
## Consumed or Depleted Stock
Once allocated stock quantities are consumed (e.g. when fulfilling an order or completing a build), the *In Stock* quantity of the stock item is reduced accordingly. This means that the *Available* quantity is also reduced.
### Delete on Deplete
Stock items can be designated with the `Delete on Deplete` flag. When this flag is set, if the stock quantity of the item reaches zero, the stock item will be automatically deleted from the system.
## Part Stock Levels
There are multiple ways to view the overall stock levels for a given part across all stock items and locations.
### Stock Overview
Each [part](../part/index.md) page displays an overview of the stocktotal stock levels across all locations. This page shows the total *In Stock*, *Allocated*, and *Available* quantities for the part, aggregated across all stock items:
{{ image("stock/stock_overview.png", title="Stock overview") }}
In the example above, the "Red Widget" part has the following stock levels:
| Metric | Quantity | Description |
| ------ | -------- | ----------- |
| In Stock | 138 | There are 138 physical units of the Red Widget part across all stock locations. |
| Available | 123 | Only 123 units are available for allocation, as 15 units have already been allocated to fulfill orders. |
| Required | 657 | A total of 657 units of the Red Widget part are needed to fulfill all current orders and builds. |
| Deficit | 519 | There is a deficit of 519 units, meaning that the current available stock is insufficient to meet the required quantity. |
### Stock Details
The *Part Detail* view displays additional information regarding the stock levels for the part:
{{ image("stock/stock_detail.png", title="Stock detail") }}
Here we can see that:
- There are 138 units of the Red Widget part physically in stock across all locations.
- Of this quantity, 15 units have been allocated to fulfill orders, leaving 123 units available for allocation.
- A total of 657 units are required to fulfill all current orders and builds.
- 645 units are required to fulfil outstanding build orders, of which 15 units have already been allocated.
- 12 units are required to fulfil outstanding sales orders, none of which have been allocated yet.
### Allocations Tab
To view further details regarding which stock items have been allocated, the *Allocations* tab on the part view can be used.
This tab displays a complete overview of where the stock items for this part are allocated, against any open orders:
#### Build Order Allocations
{{ image("stock/build_order_allocations.png", title="Build order allocations") }}
#### Sales Order Allocations
{{ image("stock/sales_order_allocations.png", title="Sales order allocations") }}

View File

@@ -26,11 +26,15 @@ The *Stock Item* detail view shows information regarding the particular stock it
**Status** - Status of this stock item
### Stock Availability
InvenTree has a number of different mechanisms to determine whether stock is available for use. See the [Stock Availability](./availability.md) page for more information.
### Stock Tracking
Every time a *Stock Item* is adjusted, a *Stock Tracking* entry is automatically created. This ensures a complete history of the *Stock Item* is maintained as long as the item is in the system.
Each stock tracking historical item records the user who performed the action.
Each stock tracking historical item records the user who performed the action. [Read more about stock tracking here](./tracking.md).
## Stock Location
@@ -49,12 +53,10 @@ If there are some icons missing in the tabler icons package, users can even inst
A stock location type represents a specific type of location (e.g. one specific size of drawer, shelf, ... or box) which can be assigned to multiple stock locations. In the first place, it is used to specify an icon and having the icon in sync for all locations that use this location type, but it also serves as a data field to quickly see what type of location this is. It is planned to add e.g. drawer dimension information to the location type to add a "find a matching, empty stock location" tool.
## External Stock Location
An external stock location can be used to indicate that items in there might not be available
for immediate usage. Stock items in an external location are marked with an additional icon
It may be useful to mark certain stock locations as *external*. An external stock location can be used to indicate that items in there might not be available for immediate usage. Stock items in an external location are marked with an additional icon
in the build order line items view where the material is allocated.
{{ image("stock/stock_external_icon.png", title="External stock indication") }}
Anyhow there is no limitation on the stock item. It can be allocated as usual.
The external flag does not get inherited to sublocations.

View File

@@ -132,6 +132,17 @@ If a provided serial number (or group of numbers) is not considered valid, an er
!!! tip "Serial Number Validation"
Custom serial number validation can be implemented using an external plugin
#### Adjusting Serial Numbers
Once a stock item has been created with a serial number, it is possible to adjust that serial number value if required. This can be achieved by navigating to the stock item's detail page, and selecting the "Edit" option from the actions menu. Here, the serial number value can be modified as required:
{{ image("stock/serial_edit.png", title="Editing a serial number") }}
Note that any serial number adjustments are subject to the same validation rules as when the stock item was created. If the new serial number value is not valid, an error message will be displayed to the user:
{{ image("stock/serial_edit_error.png", title="Error while editing a serial number") }}
#### Plugin Support
Custom serial number functionality, with any arbitrary requirements or level of complexity, can be implemented using the [Validation Plugin Mixin class](../plugins/mixins/validation.md#serial-numbers). Refer to the documentation for this plugin for technical details.

View File

@@ -9,6 +9,12 @@ If you are struggling with an issue which is not covered in the FAQ above, pleas
Even if you cannot immediately resolve the issue, the information below will be very useful when reporting the issue on GitHub.
## Error Codes
InvenTree uses a system of error codes to help identify specific issues. Each error code is prefixed with `INVE-`, followed by a letter indicating the error type, and a number.
Refer to the [error code documentation](./settings/error_codes.md) for more information on specific error codes.
## Recent Update
If you have recently updated your InvenTree instance, please ensure that you have followed all update instructions carefully. In particular, make sure that you have run any required database migrations using the `invoke update` command.

View File

@@ -78,6 +78,7 @@ nav:
- Demo: demo.md
- Release Notes: releases/release_notes.md
- Development:
- InvenTree Development: develop/index.md
- Contributing: develop/contributing.md
- Architecture: develop/architecture.md
- Roadmap: develop/roadmap.md
@@ -96,6 +97,8 @@ nav:
- Custom States: concepts/custom_states.md
- Pricing: concepts/pricing.md
- Project Codes: concepts/project_codes.md
- Attachments: concepts/attachments.md
- Parameters: concepts/parameters.md
- Barcodes:
- Barcode Support: barcodes/index.md
- Internal Barcodes: barcodes/internal.md
@@ -125,7 +128,6 @@ nav:
- Virtual Parts: part/virtual.md
- Part Views: part/views.md
- Tracking: part/trackable.md
- Parameters: part/parameter.md
- Revisions: part/revision.md
- Templates: part/template.md
- Tests: part/test.md
@@ -134,8 +136,9 @@ nav:
- Notifications: part/notification.md
- Stock:
- Stock Items: stock/index.md
- Stock Status: stock/status.md
- Availability: stock/availability.md
- Stock Tracking: stock/tracking.md
- Stock Status: stock/status.md
- Adjusting Stock: stock/adjust.md
- Stock Expiry: stock/expiry.md
- Stock Ownership: stock/owner.md
@@ -237,7 +240,7 @@ nav:
- Export Plugins:
- BOM Exporter: plugins/builtin/bom_exporter.md
- InvenTree Exporter: plugins/builtin/inventree_exporter.md
- Parameter Exporter: plugins/builtin/part_parameter_exporter.md
- Parameter Exporter: plugins/builtin/parameter_exporter.md
- Stocktake Exporter: plugins/builtin/stocktake_exporter.md
- Label Printing:
- Label Printer: plugins/builtin/inventree_label.md
@@ -357,10 +360,10 @@ extra:
# provider: google
# property: UA-143467500-1
min_python_version: 3.9
min_python_version: 3.11
min_invoke_version: 2.0.0
django_version: 4.2
docker_postgres_version: 16
django_version: 5.2
docker_postgres_version: 17
version:
default: stable

View File

@@ -6,5 +6,5 @@ mkdocs-redirects
mkdocs-simple-hooks>=0.1,<1.0
mkdocs-include-markdown-plugin
neoteroi-mkdocs
mkdocstrings[python]>=0.25.0,<=0.30.1
mkdocstrings[python]>=0.25.0,<=1.0.0
mkdocs-mermaid2-plugin

View File

@@ -148,14 +148,10 @@ essentials==1.1.6 \
--hash=sha256:3fd26923f5f2ece51a219dbb17b1fb22c9190d70fa2104919be92a6419521877 \
--hash=sha256:a470e693d83c13369ebf1f488d60236b4ea99400f38db6b7224e2808c1369256
# via essentials-openapi
essentials-openapi==1.2.1 \
--hash=sha256:70b06d80a8d9cb7634b702903cfe8fcfc48e6fc054e744eab514bb7bc37f00c9 \
--hash=sha256:d0085cde3ceaa25e6fc4983d8372ac0185909e3fa2791f498280379bb3c86c62
essentials-openapi==1.3.0 \
--hash=sha256:453327a0a847a431133f4472ced7e4a9180bf667437049b57381ddf88079e886 \
--hash=sha256:9c2a88531e2c70c565d5b526d74043941e46f60c114f7a0e3ae91e9e6bef4dae
# via neoteroi-mkdocs
exceptiongroup==1.3.0 \
--hash=sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10 \
--hash=sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88
# via anyio
ghp-import==2.1.0 \
--hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \
--hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343
@@ -197,14 +193,6 @@ idna==3.10 \
# anyio
# httpx
# requests
importlib-metadata==8.7.0 \
--hash=sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000 \
--hash=sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd
# via
# markdown
# mkdocs
# mkdocs-get-deps
# mkdocstrings
jinja2==3.1.6 \
--hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \
--hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
@@ -342,13 +330,13 @@ mkdocs-include-markdown-plugin==7.2.0 \
--hash=sha256:4a67a91ade680dc0e15f608e5b6343bec03372ffa112c40a4254c1bfb10f42f3 \
--hash=sha256:d56cdaeb2d113fb66ed0fe4fb7af1da889926b0b9872032be24e19bbb09c9f5b
# via -r docs/requirements.in
mkdocs-macros-plugin==1.4.1 \
--hash=sha256:55a9c93871e3744cdeb0736316783d60830a6d5d97b1132364e6b491607f2332 \
--hash=sha256:5a9e483f6056fe7ad0923802affe699233ca468672e20a9640dba237165b3240
mkdocs-macros-plugin==1.5.0 \
--hash=sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f \
--hash=sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f
# via -r docs/requirements.in
mkdocs-material==9.6.22 \
--hash=sha256:14ac5f72d38898b2f98ac75a5531aaca9366eaa427b0f49fc2ecf04d99b7ad84 \
--hash=sha256:87c158b0642e1ada6da0cbd798a3389b0bc5516b90e5ece4a0fb939f00bacd1c
mkdocs-material==9.7.0 \
--hash=sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec \
--hash=sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887
# via -r docs/requirements.in
mkdocs-material-extensions==1.3.1 \
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
@@ -366,9 +354,9 @@ mkdocs-simple-hooks==0.1.5 \
--hash=sha256:dddbdf151a18723c9302a133e5cf79538be8eb9d274e8e07d2ac3ac34890837c \
--hash=sha256:efeabdbb98b0850a909adee285f3404535117159d5cb3a34f541d6eaa644d50a
# via -r docs/requirements.in
mkdocstrings[python]==0.30.1 \
--hash=sha256:41bd71f284ca4d44a668816193e4025c950b002252081e387433656ae9a70a82 \
--hash=sha256:84a007aae9b707fb0aebfc9da23db4b26fc9ab562eb56e335e9ec480cb19744f
mkdocstrings[python]==1.0.0 \
--hash=sha256:351a006dbb27aefce241ade110d3cd040c1145b7a3eb5fd5ac23f03ed67f401a \
--hash=sha256:4c50eb960bff6e05dfc631f6bc00dfabffbcb29c5ff25f676d64daae05ed82fa
# via
# -r docs/requirements.in
# mkdocstrings-python
@@ -376,9 +364,9 @@ mkdocstrings-python==1.16.12 \
--hash=sha256:22ded3a63b3d823d57457a70ff9860d5a4de9e8b1e482876fc9baabaf6f5f374 \
--hash=sha256:9b9eaa066e0024342d433e332a41095c4e429937024945fea511afe58f63175d
# via mkdocstrings
neoteroi-mkdocs==1.1.3 \
--hash=sha256:3ecfb825e898d10a6d703a3ef3f0484d823b7b5660425e76af421e316ac18036 \
--hash=sha256:772aee317c9bb10a89d67e71e322730f92cc349d5eecc8a08e8fb079398e514b
neoteroi-mkdocs==1.2.0 \
--hash=sha256:58e25cb1b9db093ffa8d12bdb33264bf567cac30fb964b56e0a493efa749ad6e \
--hash=sha256:91b6aa95a4e46c9bb93e00e021d2044cb0c7d80c0b4600434ff8f440d613a0a8
# via -r docs/requirements.in
packaging==25.0 \
--hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 \
@@ -551,12 +539,9 @@ typing-extensions==4.14.1 \
# via
# anyio
# beautifulsoup4
# exceptiongroup
# gitpython
# mkdocstrings-python
urllib3==2.5.0 \
--hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \
--hash=sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc
urllib3==2.6.0 \
--hash=sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f \
--hash=sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1
# via requests
watchdog==6.0.0 \
--hash=sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a \
@@ -594,7 +579,3 @@ wcmatch==10.1 \
--hash=sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a \
--hash=sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af
# via mkdocs-include-markdown-plugin
zipp==3.23.0 \
--hash=sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e \
--hash=sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166
# via importlib-metadata

View File

@@ -97,12 +97,12 @@ skip-magic-trailing-comma = true
line-ending = "auto"
[tool.uv.pip]
python-version = "3.9.2"
python-version = "3.11.0"
no-strip-extras=true
generate-hashes=true
[tool.ty.src]
root = "src/backend/InvenTree"
[tool.ty.environment]
root = ["src/backend/InvenTree"]
[tool.ty.rules]
unresolved-reference="ignore" # 21 # see https://github.com/astral-sh/ty/issues/220

View File

@@ -11,7 +11,7 @@ python:
build:
os: "ubuntu-22.04"
tools:
python: "3.9"
python: "3.11"
jobs:
post_install:
- pip install -U invoke

View File

@@ -1,77 +0,0 @@
"""Middleware to require 2FA for users."""
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils.deprecation import MiddlewareMixin
from django.utils.translation import gettext_lazy as _
from allauth.account.authentication import get_authentication_records
from allauth.mfa.utils import is_mfa_enabled
from allauth.mfa.webauthn.internal.flows import did_use_passwordless_login
def is_multifactor_logged_in(request: HttpRequest) -> bool:
"""Check if the user is logged in with multifactor authentication."""
authns = get_authentication_records(request)
return is_mfa_enabled(request.user) and (
did_use_passwordless_login(request)
or any(record.get('method') == 'mfa' for record in authns)
)
class AllUserRequire2FAMiddleware(MiddlewareMixin):
"""Ensure that users have two-factor authentication enabled before they have access restricted endpoints.
Adapted from https://github.com/pennersr/django-allauth/issues/3649
"""
allowed_pages = [
'api-user-meta',
'api-user-me',
'api-user-roles',
'api-inventree-info',
'api-token',
# web platform urls
'password_reset_confirm',
'web',
'web-wildcard',
'web-assets',
]
app_names = ['headless']
require_2fa_message = _(
'You must enable two-factor authentication before doing anything else.'
)
def on_require_2fa(self, request: HttpRequest) -> HttpResponse:
"""Force user to mfa activation."""
return JsonResponse({'id': 'mfa_register'}, status=401)
def is_allowed_page(self, request: HttpRequest) -> bool:
"""Check if the current page can be accessed without mfa."""
match = request.resolver_match
return (
None
if match is None
else any(ref in self.app_names for ref in match.app_names)
or match.url_name in self.allowed_pages
or match.route == 'favicon.ico'
)
def enforce_2fa(self, request):
"""Check if 2fa should be enforced for this request."""
return True
def process_view(
self, request: HttpRequest, view_func, view_args, view_kwargs
) -> HttpResponse:
"""If set up enforce 2fa registration."""
if request.user.is_anonymous:
return None
if self.is_allowed_page(request):
return None
if is_multifactor_logged_in(request):
return None
if self.enforce_2fa(request):
return self.on_require_2fa(request)
return None

View File

@@ -53,7 +53,7 @@ def read_license_file(path: Path) -> list:
return []
try:
data = json.loads(path.read_text())
data = json.loads(path.read_text(encoding='utf-8'))
except Exception as e:
logger.exception("Failed to parse license file '%s': %s", path, e)
return []
@@ -600,6 +600,34 @@ class BulkUpdateMixin(BulkOperationMixin):
return Response({'success': f'Updated {n} items'}, status=200)
class ParameterListMixin:
"""Mixin class which supports filtering against parametric fields."""
def filter_queryset(self, queryset):
"""Perform filtering against parametric fields."""
import common.filters
queryset = super().filter_queryset(queryset)
# Filter by parametric data
queryset = common.filters.filter_parametric_data(
queryset, self.request.query_params
)
serializer_class = (
getattr(self, 'serializer_class', None) or self.get_serializer_class()
)
model_class = serializer_class.Meta.model
# Apply ordering based on query parameter
queryset = common.filters.order_by_parameter(
queryset, model_class, self.request.query_params.get('ordering', None)
)
return queryset
class BulkDeleteMixin(BulkOperationMixin):
"""Mixin class for enabling 'bulk delete' operations for various models.

View File

@@ -1,11 +1,66 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 421
INVENTREE_API_VERSION = 435
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v435 -> 2025-12-16 : https://github.com/inventree/InvenTree/pull/11030
- Adds token refresh endpoint to auth API
v434 -> 2025-12-16 : https://github.com/inventree/InvenTree/pull/11021
- The "tags" fields (on various API endpoints) is now optional, and disabled by default
- To request tags information, add "tags=true" to the API request query parameters
v433 -> 2025-12-16 : https://github.com/inventree/InvenTree/pull/11023
- "substitutes" field on the BomItem API endpoint is now excluded by default
- Add "?substitutes=true" query parameter to include substitute parts in BomItem API endpoint(s)
v432 -> 2025-12-15 : https://github.com/inventree/InvenTree/pull/11012
- The "part_detail" field on the SupplierPart API endpoint is now optional
- The "supplier_detail" field on the SupplierPart API endpoint is now optional
- The "manufacturer_detail" field on the ManufacturerPart API endpoint is now optional
- The "part_detail" field on the StockItem API is now disabled by default
v431 -> 2025-12-14 : https://github.com/inventree/InvenTree/pull/11006
- Remove duplicate "address" field on the Company API endpoint
- Make "primary_address" field optional on the Company API endpoint
- Remove "address_count" field from the Company API endpoint
v430 -> 2025-12-04 : https://github.com/inventree/InvenTree/pull/10699
- Removed the "PartParameter" and "PartParameterTemplate" API endpoints
- Removed the "ManufacturerPartParameter" API endpoint
- Added generic "Parameter" and "ParameterTemplate" API endpoints
v429 -> 2025-12-04 : https://github.com/inventree/InvenTree/pull/10938
- Adjust default values for currency codes in the API schema
- Note that this does not change any functional behavior, only the schema documentation
v428 -> 2025-11-28 : https://github.com/inventree/InvenTree/pull/10926
- Various typo fixes in API - no functional changes
v427 -> 2025-11-24 : https://github.com/inventree/InvenTree/pull/10896
- Fixes a spelling mistake in the API field labels
v426 -> 2025-11-19 : https://github.com/inventree/InvenTree/pull/10867
- Adds optional "price_breaks" filter to the SupplierPart API endpoint
v425 -> 2025-11-11 : https://github.com/inventree/InvenTree/pull/10802
- Adds "on_order" filter to the BuildLine API endpoint
- Allow BuildLine list to be ordered by "on_order" and "in_production" fields
v424 -> 2025-11-11 : https://github.com/inventree/InvenTree/pull/10730
- Adds more lower / upper bounds to integer fields in the API schema due to bump to Django 5.2- no functional changes
v423 -> 2025-11-05 : https://github.com/inventree/InvenTree/pull/10772
- Adds "category_detail" field to BomItem API endpoints
- Adds "category_detail" field to BuildLine API endpoints
v422 -> 2025-11-03 : https://github.com/inventree/InvenTree/pull/10750
- Adds ability to search StockItem API by supplier SKU
- Adds ability to search StockItem API by manufacturer MPN
v421 -> 2025-10-31 : https://github.com/inventree/InvenTree/pull/10724
- Allow upload of attachments against SupplierPart objects via the API
@@ -21,7 +76,7 @@ v418 -> 2025-10-24 : https://github.com/inventree/InvenTree/pull/10657
v417 -> 2025-10-22 : https://github.com/inventree/InvenTree/pull/10654
- Adds "checked" filter to SalesOrderShipment API endpoint
- Adds "order_status" filter to SalesOrdereShipment API endpoint
- Adds "order_status" filter to SalesOrderShipment API endpoint
- Adds "order_outstanding" filter to SalesOrderShipment API endpoint
v416 -> 2025-10-22 : https://github.com/inventree/InvenTree/pull/10651
@@ -79,7 +134,7 @@ v401 -> 2025-10-05 : https://github.com/inventree/InvenTree/pull/10381
- Adds machine properties to machine API endpoints
v400 -> 2025-10-05 : https://github.com/inventree/InvenTree/pull/10486
- Adds return datatypes for admin/config and flags entpoints
- Adds return datatypes for admin/config and flags endpoints
v399 -> 2025-10-05 : https://github.com/inventree/InvenTree/pull/10445
- Refactors 'customer_detail' param in SalesOrder API endpoint

View File

@@ -18,6 +18,7 @@ import InvenTree.conversion
import InvenTree.ready
import InvenTree.tasks
from InvenTree.config import get_setting
from InvenTree.ready import ignore_ready_warning
logger = structlog.get_logger('inventree')
MIGRATIONS_CHECK_DONE = False
@@ -70,9 +71,7 @@ class InvenTreeConfig(AppConfig):
InvenTree.tasks.offload_task(InvenTree.tasks.check_for_migrations)
self.update_site_url()
# Ensure the unit registry is loaded
InvenTree.conversion.get_unit_registry()
self.load_unit_registry()
if InvenTree.ready.canAppAccessDatabase() or settings.TESTING_ENV:
self.add_user_on_startup()
@@ -84,6 +83,7 @@ class InvenTreeConfig(AppConfig):
social_account_updated.connect(sso.ensure_sso_groups)
@ignore_ready_warning
def remove_obsolete_tasks(self):
"""Delete any obsolete scheduled tasks in the database."""
obsolete = [
@@ -112,6 +112,7 @@ class InvenTreeConfig(AppConfig):
except Exception:
logger.exception('Failed to remove obsolete tasks - database not ready')
@ignore_ready_warning
def start_background_tasks(self):
"""Start all background tests for InvenTree."""
logger.info('Starting background tasks...')
@@ -171,6 +172,7 @@ class InvenTreeConfig(AppConfig):
logger.info('Started %s scheduled background tasks...', len(tasks))
@ignore_ready_warning
def add_heartbeat(self):
"""Ensure there is at least one background task in the queue."""
import django_q.models
@@ -185,6 +187,7 @@ class InvenTreeConfig(AppConfig):
except Exception:
pass
@ignore_ready_warning
def collect_tasks(self):
"""Collect all background tasks."""
for app_name, app in apps.app_configs.items():
@@ -197,6 +200,7 @@ class InvenTreeConfig(AppConfig):
except Exception as e: # pragma: no cover
logger.exception('Error loading tasks for %s: %s', app_name, e)
@ignore_ready_warning
def update_site_url(self):
"""Update the site URL setting.
@@ -223,6 +227,12 @@ class InvenTreeConfig(AppConfig):
except Exception:
pass
@ignore_ready_warning
def load_unit_registry(self):
"""Ensure the unit registry is loaded."""
InvenTree.conversion.get_unit_registry()
@ignore_ready_warning
def add_user_on_startup(self):
"""Add a user on startup."""
# stop if checks were already created
@@ -277,10 +287,11 @@ class InvenTreeConfig(AppConfig):
new_user = user.objects.create_superuser(
add_user, add_email, add_password
)
logger.info('User %s was created!', str(new_user))
logger.info('User %s was created!', new_user)
except IntegrityError:
logger.warning('The user "%s" could not be created', add_user)
@ignore_ready_warning
def add_user_from_file(self):
"""Add the superuser from a file."""
# stop if checks were already created
@@ -321,6 +332,18 @@ class InvenTreeConfig(AppConfig):
return
if not InvenTree.tasks.check_for_migrations():
logger.error('INVE-W8: Database Migrations required')
sys.exit(1)
# Detect if this an empty database - if so, start with a fresh migration
if (
settings.DOCKER
and not InvenTree.ready.isInTestMode()
and not InvenTree.ready.isRunningMigrations()
and InvenTree.tasks.get_migration_count() == 0
):
logger.warning(
'INVE-W8: Empty database detected - trying to run migrations'
)
InvenTree.tasks.check_for_migrations(force_run=True)
else:
logger.error('INVE-W8: Database Migrations required')
sys.exit(1)
MIGRATIONS_CHECK_DONE = True

View File

@@ -2,7 +2,6 @@
import datetime
import time
from typing import Union
from django.conf import settings
from django.core.exceptions import ValidationError
@@ -109,7 +108,7 @@ class InvenTreeMailLoggingBackend(BaseEmailBackend):
return self.backend.open()
def send_messages(
self, email_messages: list[Union[EmailMessage, EmailMultiAlternatives]]
self, email_messages: list[EmailMessage | EmailMultiAlternatives]
) -> int:
"""Send email messages and log them to the database.

View File

@@ -4,6 +4,8 @@ import socket
import threading
from typing import Any
from django.db.utils import OperationalError, ProgrammingError
import structlog
import InvenTree.config
@@ -169,3 +171,22 @@ def set_session_cache(key: str, value: Any) -> None:
if request_cache is not None:
request_cache[key] = value
def get_cached_content_types(cache_key: str = 'all_content_types') -> list:
"""Return a list of all ContentType objects, using session cache if possible."""
from django.contrib.contenttypes.models import ContentType
# Attempt to retrieve a list of ContentType objects from session cache
if content_types := get_session_cache(cache_key):
return content_types
try:
content_types = list(ContentType.objects.all())
if len(content_types) > 0:
set_session_cache(cache_key, content_types)
except (OperationalError, ProgrammingError):
# Database is likely not yet ready
content_types = []
return content_types

View File

@@ -7,8 +7,9 @@ import os
import random
import shutil
import string
import sys
from pathlib import Path
from typing import Optional, Union
from typing import Optional
logger = logging.getLogger('inventree')
CONFIG_DATA = None
@@ -177,7 +178,7 @@ def get_config_file(create=True) -> Path:
return cfg_filename
def load_config_data(set_cache: bool = False) -> Union[map, None]:
def load_config_data(set_cache: bool = False) -> map | None:
"""Load configuration data from the config file.
Arguments:
@@ -195,7 +196,15 @@ def load_config_data(set_cache: bool = False) -> Union[map, None]:
cfg_file = get_config_file()
with open(cfg_file, encoding='utf-8') as cfg:
data = yaml.safe_load(cfg)
try:
data = yaml.safe_load(cfg)
except yaml.parser.ParserError as error:
logger.error(
"INVE-E13: Error reading InvenTree configuration file '%s': %s",
cfg_file,
error,
)
sys.exit(1)
# Set the cache if requested
if set_cache:
@@ -223,6 +232,11 @@ def do_typecast(value, type, var_name=None):
elif type is dict:
value = to_dict(value)
# Special handling for boolean typecasting
elif type is bool:
val = is_true(value)
return val
elif type is not None:
# Try to typecast the value
try:
@@ -392,7 +406,7 @@ def get_plugin_dir():
return get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
def get_secret_key(return_path: bool = False) -> Union[str, Path]:
def get_secret_key(return_path: bool = False) -> str | Path:
"""Return the secret key value which will be used by django.
Following options are tested, in descending order of preference:
@@ -436,7 +450,7 @@ def get_secret_key(return_path: bool = False) -> Union[str, Path]:
return secret_key_file.read_text().strip()
def get_oidc_private_key(return_path: bool = False) -> Union[str, Path]:
def get_oidc_private_key(return_path: bool = False) -> str | Path:
"""Return the private key for OIDC authentication.
Following options are tested, in descending order of preference:

View File

@@ -1,27 +1,96 @@
"""Helper functions for converting between units."""
import logging
import re
from hashlib import md5
from typing import Optional
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
import pint
_unit_registry = None
import structlog
from common.settings import get_global_setting, set_global_setting
from InvenTree.cache import get_session_cache, set_session_cache
_UNIT_REG_CACHE_KEY = 'unit_registry_hash'
_unit_registry = None
_unit_registry_hash: str = ''
logger = structlog.get_logger('inventree')
# Disable log output for Pint library
logging.getLogger('pint').setLevel(logging.ERROR)
def can_cache_registry() -> bool:
"""Return True if it is appropriate to cache the unit registry.
Prevent caching under certain conditions (such as database migration)
to prevent database access.
"""
import InvenTree.ready
return not any([
InvenTree.ready.isImportingData(),
InvenTree.ready.isRunningBackup(),
InvenTree.ready.isRunningMigrations(),
InvenTree.ready.isInTestMode(),
])
def get_unit_registry_hash():
"""Return a hash representing the current state of the unit registry.
We use this to determine if we need to reload the unit registry,
due to changes in the database.
"""
# Look in the session cache first (faster, and potentially newer)
registry_hash = get_session_cache(_UNIT_REG_CACHE_KEY)
if registry_hash is None:
registry_hash = get_global_setting(
'_UNIT_REGISTRY_HASH', create=False, backup_value=''
)
if registry_hash:
set_session_cache(_UNIT_REG_CACHE_KEY, registry_hash)
return registry_hash
def set_unit_registry_hash(registry_hash: str):
"""Save the hash representing the current state of the unit registry.
Because most of the registry is static, we only need to consider the
CustomUnit entries in the database.
"""
global _unit_registry_hash
_unit_registry_hash = registry_hash
if not can_cache_registry():
return
# Save to both the global settings and the session cache
set_global_setting('_UNIT_REGISTRY_HASH', registry_hash)
set_session_cache(_UNIT_REG_CACHE_KEY, registry_hash)
def get_unit_registry():
"""Return a custom instance of the Pint UnitRegistry."""
global _unit_registry
global _unit_registry_hash
# Cache the unit registry for speedier access
if _unit_registry is None:
return reload_unit_registry()
# Check if the unit registry has changed
if can_cache_registry() and _unit_registry_hash != get_unit_registry_hash():
logger.info('Unit registry hash has changed, reloading unit registry')
return reload_unit_registry()
return _unit_registry
@@ -53,23 +122,32 @@ def reload_unit_registry():
reg.define('thousand = 1000')
# Allow for custom units to be defined in the database
# Calculate a hash of all custom units
hash_md5 = md5()
try:
from common.models import CustomUnit
for cu in CustomUnit.objects.all():
try:
reg.define(cu.fmt_string())
except Exception as e:
logger.exception(
'Failed to load custom unit: %s - %s', cu.fmt_string(), e
)
# Once custom units are loaded, save registry
_unit_registry = reg
custom_units = list(CustomUnit.objects.all())
except Exception:
# Database is not ready, or CustomUnit model is not available
pass
# Database is likely not ready
custom_units = []
for cu in custom_units:
try:
fmt = cu.fmt_string()
reg.define(fmt)
hash_md5.update(fmt.encode('utf-8'))
except Exception as e:
logger.exception('Failed to load custom unit: %s - %s', cu.fmt_string(), e)
# Once custom units are loaded, save registry
_unit_registry = reg
# Update the unit registry hash
set_unit_registry_hash(hash_md5.hexdigest())
dt = time.time() - t_start
logger.debug('Loaded unit registry in %.3f s', dt)

View File

@@ -22,7 +22,7 @@ class InvenTreeDateFilter(rest_filters.DateFilter):
if settings.USE_TZ and value is not None:
tz = timezone.get_current_timezone()
value = datetime(value.year, value.month, value.day)
value = make_aware(value, timezone=tz, is_dst=True)
value = make_aware(value, timezone=tz)
return super().filter(qs, value)

View File

@@ -5,11 +5,10 @@ import hashlib
import inspect
import io
import json
import os
import os.path
import re
from decimal import Decimal, InvalidOperation
from typing import Optional, TypeVar, Union
from typing import Optional, TypeVar
from wsgiref.util import FileWrapper
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
@@ -17,6 +16,7 @@ from django.conf import settings
from django.contrib.staticfiles.storage import StaticFilesStorage
from django.core.exceptions import FieldError, ValidationError
from django.core.files.storage import default_storage
from django.db.models.fields.files import FieldFile, ImageFieldFile
from django.http import StreamingHttpResponse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
@@ -28,6 +28,7 @@ import structlog
from bleach import clean
from djmoney.money import Money
from PIL import Image
from stdimage.models import StdImageField, StdImageFieldFile
from common.currency import currency_code_default
@@ -127,7 +128,7 @@ def extract_int(
return ref_int
def generateTestKey(test_name: Union[str, None]) -> str:
def generateTestKey(test_name: str | None) -> str:
"""Generate a test 'key' for a given test name. This must not have illegal chars as it will be used for dict lookup in a template.
Tests must be named such that they will have unique keys.
@@ -175,11 +176,52 @@ def constructPathString(path: list[str], max_chars: int = 250) -> str:
return pathstring
def getMediaUrl(filename):
def getMediaUrl(
file: FieldFile | ImageFieldFile | StdImageFieldFile, name: str | None = None
):
"""Return the qualified access path for the given file, under the media directory."""
if not isinstance(file, (FieldFile, ImageFieldFile, StdImageFieldFile)):
raise TypeError(
'file must be one of FileField, ImageFileField, StdImageFieldFile'
)
if name is not None:
file = regenerate_imagefile(file, name)
if settings.STORAGE_TARGET == StorageBackends.S3:
return str(filename)
return os.path.join(MEDIA_URL, str(filename))
return str(file.url)
return os.path.join(MEDIA_URL, str(file.url))
def regenerate_imagefile(_file, _name: str):
"""Regenerate a file object for a given variation name.
Arguments:
_file: Original file object
_name: Name of the variation (e.g. 'thumbnail', 'preview')
"""
name = _file.field.attr_class.get_variation_name(_file.name, _name)
return ImageFieldFile(_file.instance, _file, name) # type: ignore
def image2name(img_obj: StdImageField, do_preview: bool, do_thumbnail: bool):
"""Convert an image object to a filename string.
Arguments:
img_obj: Image object
do_preview: Return preview image name
do_thumbnail: Return thumbnail image name
"""
def extract(ref: str):
return None if not hasattr(img_obj, ref) else getattr(img_obj, ref).name
if not img_obj:
return None
elif do_preview:
return extract('preview')
elif do_thumbnail:
return extract('thumbnail')
else:
return img_obj.name
def getStaticUrl(filename):

View File

@@ -1,6 +1,6 @@
"""Code for managing email functionality in InvenTree."""
from typing import Optional, Union
from typing import Optional
from django.conf import settings
@@ -8,7 +8,6 @@ import structlog
from allauth.account.models import EmailAddress
import InvenTree.ready
import InvenTree.tasks
from common.models import Priority, issue_mail
logger = structlog.get_logger('inventree')
@@ -64,7 +63,7 @@ def is_email_configured() -> bool:
def send_email(
subject: str,
body: str,
recipients: Union[str, list],
recipients: str | list,
from_email: Optional[str] = None,
html_message=None,
prio: Priority = Priority.NORMAL,

View File

@@ -0,0 +1,22 @@
"""Helper functions for allauth MFA testing."""
from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes
from allauth.mfa.totp.internal import auth as allauth_totp_auth
def get_codes(user):
"""Generate active TOTP and recovery codes for a user.
Args:
user: User instance
Returns:
Tuple of (TOTP Authenticator instance, list of recovery codes, TOTP secret)
"""
secret = allauth_totp_auth.generate_totp_secret()
totp_auth = allauth_totp_auth.TOTP.activate(user, secret).instance
rc_auth = RecoveryCodes.activate(user).instance
# Get usable codes
rc_codes = rc_auth.wrap().get_unused_codes()
return totp_auth, rc_codes, secret

View File

@@ -1,8 +1,9 @@
"""Provides helper mixins that are used throughout the InvenTree project."""
import inspect
from collections.abc import Callable
from pathlib import Path
from typing import Any, Callable
from typing import Any
from django.conf import settings
from django.core.cache import cache

View File

@@ -24,7 +24,13 @@ from common.notifications import (
trigger_notification,
)
from common.settings import get_global_setting
from InvenTree.cache import (
get_cached_content_types,
get_session_cache,
set_session_cache,
)
from InvenTree.format import format_money
from InvenTree.ready import ignore_ready_warning
logger = structlog.get_logger('inventree')
@@ -260,6 +266,7 @@ def render_currency(
)
@ignore_ready_warning
def getModelsWithMixin(mixin_class) -> list:
"""Return a list of database models that inherit from the given mixin class.
@@ -268,17 +275,23 @@ def getModelsWithMixin(mixin_class) -> list:
Returns:
List of models that inherit from the given mixin class
"""
from django.contrib.contenttypes.models import ContentType
# First, look in the session cache - to prevent repeated expensive comparisons
cache_key = f'models_with_mixin_{mixin_class.__name__}'
try:
db_models = [
x.model_class() for x in ContentType.objects.all() if x is not None
]
except (OperationalError, ProgrammingError):
# Database is likely not yet ready
db_models = []
if cached_models := get_session_cache(cache_key):
return cached_models
return [x for x in db_models if x is not None and issubclass(x, mixin_class)]
content_types = get_cached_content_types()
db_models = [x.model_class() for x in content_types if x is not None]
models_with_mixin = [
x for x in db_models if x is not None and issubclass(x, mixin_class)
]
# Store the result in the session cache
set_session_cache(cache_key, models_with_mixin)
return models_with_mixin
def notify_responsible(

View File

@@ -35,10 +35,6 @@ def get_type_str(type_obj):
if type_obj.__module__ == 'builtins':
return type_obj.__name__
# in python3.9, typing.Union has no __name__
if not hasattr(type_obj, '__module__') or not hasattr(type_obj, '__name__'):
return str(type_obj)
return f'{type_obj.__module__}.{type_obj.__name__}'

View File

@@ -1,7 +1,7 @@
"""Extended schema generator."""
from pathlib import Path
from typing import TypeVar, Union
from typing import TypeVar
from django.conf import settings
@@ -26,7 +26,7 @@ def prep_name(ref):
return f'{dja_ref_prefix}.{ref}'
def sub_component_name(name: T) -> Union[T, str]:
def sub_component_name(name: T) -> T | str:
"""Clean up component references."""
if not isinstance(name, str):
return name
@@ -70,8 +70,10 @@ class Command(spectacular.Command):
# Reformat paths
for p_name, p_spec in spec['paths'].items():
# strip path name
p_name = p_name.removeprefix(dja_path_prefix).removeprefix(
'/_allauth/browser/v1/'
p_name = (
p_name.removeprefix(dja_path_prefix)
.removeprefix('/_allauth/browser/v1/')
.removeprefix('/_allauth/app/v1/')
)
# fix refs

View File

@@ -23,7 +23,7 @@ logger = structlog.get_logger('inventree')
class InvenTreeMetadata(SimpleMetadata):
"""Custom metadata class for the DRF API.
This custom metadata class imits the available "actions",
This custom metadata class limits the available "actions",
based on the user's role permissions.
Thus when a client send an OPTIONS request to an API endpoint,
@@ -50,6 +50,10 @@ class InvenTreeMetadata(SimpleMetadata):
for method in {'PUT', 'POST', 'GET'} & set(view.allowed_methods):
view.request = clone_request(request, method)
# Mark this request, to prevent expensive prefetching
view.request._metadata_requested = True
try:
# Test global permissions
if hasattr(view, 'check_permissions'):
@@ -420,6 +424,13 @@ class InvenTreeMetadata(SimpleMetadata):
if field_info['type'] == 'dependent field':
field_info['depends_on'] = field.depends_on
# Extends with extra attributes from the serializer
extra_field_attributes = ['allow_blank', 'allow_null']
for attr in extra_field_attributes:
if hasattr(field, attr):
field_info[attr] = getattr(field, attr)
# Extend field info if the field has a get_field_info method
if (
not field_info.get('read_only')

View File

@@ -1,21 +1,22 @@
"""Middleware for InvenTree."""
import sys
from typing import Optional
from urllib.parse import urlsplit
from django.conf import settings
from django.contrib.auth.middleware import PersistentRemoteUserMiddleware
from django.http import HttpResponse
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import redirect, render
from django.urls import resolve, reverse_lazy
from django.urls import resolve, reverse, reverse_lazy
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import is_same_domain
from django.utils.translation import gettext_lazy as _
import structlog
from error_report.middleware import ExceptionProcessor
from common.settings import get_global_setting
from InvenTree.AllUserRequire2FAMiddleware import AllUserRequire2FAMiddleware
from InvenTree.cache import create_session_cache, delete_session_cache
from InvenTree.config import CONFIG_LOOKUPS, inventreeInstaller
from users.models import ApiToken
@@ -40,6 +41,15 @@ def get_token_from_request(request):
return None
def ensure_slashes(path: str):
"""Ensure that slashes are suroudning the passed path."""
if not path.startswith('/'):
path = f'/{path}'
if not path.endswith('/'):
path = f'{path}/'
return path
# List of target URL endpoints where *do not* want to redirect to
urls = [
reverse_lazy('account_login'),
@@ -47,8 +57,46 @@ urls = [
reverse_lazy('admin:logout'),
]
# Do not redirect requests to any of these paths
paths_ignore = ['/api/', '/auth/', settings.MEDIA_URL, settings.STATIC_URL]
paths_ignore_handling = [
'/api/',
reverse('auth-check'),
settings.MEDIA_URL,
settings.STATIC_URL,
]
"""Paths that should not use InvenTrees own auth rejection behaviour, no host checking or redirecting. Security
are still enforced."""
paths_own_security = [
'/api/', # DRF handles API
'/o/', # oAuth2 library - has its own auth model
'/anymail/', # Mails - wehbhooks etc
'/accounts/', # allauth account management - has its own auth model
'/assets/', # Web assets - only used for testing, no security model needed
ensure_slashes(
settings.STATIC_URL
), # Static files - static files are considered safe to serve
ensure_slashes(
settings.FRONTEND_URL_BASE
), # Frontend files - frontend paths have their own security model
]
"""Paths that handle their own security model."""
pages_mfa_bypass = [
'api-user-meta',
'api-user-me',
'api-user-roles',
'api-inventree-info',
'api-token',
# web platform urls
'password_reset_confirm',
'index',
'web',
'web-wildcard',
'web-assets',
]
"""Exact page names that bypass MFA enforcement - normal security model is still enforced."""
apps_mfa_bypass = [
'headless' # Headless allauth app - has its own security model
]
"""App namespaces that bypass MFA enforcement - normal security model is still enforced."""
class AuthRequiredMiddleware:
@@ -61,6 +109,7 @@ class AuthRequiredMiddleware:
def check_token(self, request) -> bool:
"""Check if the user is authenticated via token."""
if token := get_token_from_request(request):
request.token = token
# Does the provided token match a valid user?
try:
token = ApiToken.objects.get(key=token)
@@ -69,8 +118,10 @@ class AuthRequiredMiddleware:
# Provide the user information to the request
request.user = token.user
return True
except ApiToken.DoesNotExist:
logger.warning('Access denied for unknown token %s', token)
except ApiToken.DoesNotExist: # pragma: no cover
logger.warning(
'Access denied for unknown token %s', token
) # pragma: no cover
return False
@@ -79,79 +130,113 @@ class AuthRequiredMiddleware:
Redirects to login if not authenticated.
"""
path: str = request.path_info
# Code to be executed for each request before
# the view (and later middleware) are called.
assert hasattr(request, 'user')
# API requests are handled by the DRF library
if request.path_info.startswith('/api/'):
return self.get_response(request)
# oAuth2 requests are handled by the oAuth2 library
if request.path_info.startswith('/o/'):
return self.get_response(request)
# anymail requests are handled by the anymail library
if request.path_info.startswith('/anymail/'):
# API requests that are handled elsewhere
if any(path.startswith(a) for a in paths_own_security):
return self.get_response(request)
# Is the function exempt from auth requirements?
path_func = resolve(request.path).func
if getattr(path_func, 'auth_exempt', False) is True:
return self.get_response(request)
if not request.user.is_authenticated:
if not request.user.is_authenticated and not (
path == f'/{settings.FRONTEND_URL_BASE}' or self.check_token(request)
):
"""
Normally, a web-based session would use csrftoken based authentication.
However when running an external application (e.g. the InvenTree app or Python library),
we must validate the user token manually.
"""
authorized = False
# Allow static files to be accessed without auth
# Important for e.g. login page
if (
request.path_info.startswith('/static/')
or request.path_info.startswith('/accounts/')
or (
request.path_info.startswith(f'/{settings.FRONTEND_URL_BASE}/')
or request.path_info.startswith('/assets/')
or request.path_info == f'/{settings.FRONTEND_URL_BASE}'
)
or self.check_token(request)
if path not in urls and not any(
path.startswith(p) for p in paths_ignore_handling
):
authorized = True
# Save the 'next' parameter to pass through to the login view
# No authorization was found for the request
if not authorized:
path = request.path_info
if path not in urls and not any(
path.startswith(p) for p in paths_ignore
):
# Save the 'next' parameter to pass through to the login view
return redirect(
f'{reverse_lazy("account_login")}?next={request.path}'
)
# Return a 401 (Unauthorized) response code for this request
return HttpResponse('Unauthorized', status=401)
return redirect(f'{reverse_lazy("account_login")}?next={request.path}')
# Return a 401 (Unauthorized) response code for this request
return HttpResponse('Unauthorized', status=401)
response = self.get_response(request)
return response
class Check2FAMiddleware(AllUserRequire2FAMiddleware):
"""Ensure that mfa is enforced if set so."""
class Check2FAMiddleware(MiddlewareMixin):
"""Ensure that users have two-factor authentication enabled before they have access restricted endpoints.
Adapted from https://github.com/pennersr/django-allauth/issues/3649
"""
require_2fa_message = _(
'You must enable two-factor authentication before doing anything else.'
)
def on_require_2fa(self, request: HttpRequest) -> HttpResponse:
"""Force user to mfa activation."""
return JsonResponse(
{'id': 'mfa_register', 'error': self.require_2fa_message}, status=401
)
def is_allowed_page(self, request: HttpRequest) -> bool:
"""Check if the current page can be accessed without mfa."""
match = request.resolver_match
return (
False
if match is None
else any(ref in apps_mfa_bypass for ref in match.app_names)
or match.url_name in pages_mfa_bypass
or match.route == 'favicon.ico'
)
def is_multifactor_logged_in(self, request: HttpRequest) -> bool:
"""Check if the user is logged in with multifactor authentication."""
from allauth.account.authentication import get_authentication_records
from allauth.mfa.utils import is_mfa_enabled
from allauth.mfa.webauthn.internal.flows import did_use_passwordless_login
authns = get_authentication_records(request)
return is_mfa_enabled(request.user) and (
did_use_passwordless_login(request)
or any(record.get('method') == 'mfa' for record in authns)
)
def process_view(
self, request: HttpRequest, view_func, view_args, view_kwargs
) -> Optional[HttpResponse]:
"""Determine if the server is set up enforce 2fa registration."""
from django.conf import settings
# Exit early if MFA is not enabled
if not settings.MFA_ENABLED:
return None
if request.user.is_anonymous:
return None
if self.is_allowed_page(request):
return None
if self.is_multifactor_logged_in(request):
return None
if getattr(
request, 'token', get_token_from_request(request)
): # Token based login can not do MFA
return None
if self.enforce_2fa(request):
return self.on_require_2fa(request)
return None
def enforce_2fa(self, request):
"""Use setting to check if MFA should be enforced."""
return get_global_setting('LOGIN_ENFORCE_MFA')
return get_global_setting(
'LOGIN_ENFORCE_MFA', None, 'INVENTREE_LOGIN_ENFORCE_MFA'
)
class InvenTreeRemoteUserMiddleware(PersistentRemoteUserMiddleware):
@@ -232,7 +317,7 @@ class InvenTreeHostSettingsMiddleware(MiddlewareMixin):
# Handle commonly ignored paths that might also work without a correct setup (api, auth)
path = request.path_info
if path in urls or any(path.startswith(p) for p in paths_ignore):
if path in urls or any(path.startswith(p) for p in paths_ignore_handling):
return None
# treat the accessed scheme and host

View File

@@ -258,6 +258,19 @@ class OutputOptionsMixin:
return serializer
def get_queryset(self):
"""Return the queryset with output options applied.
This automatically applies any prefetching defined against the optional fields.
"""
queryset = super().get_queryset()
serializer = self.get_serializer()
if isinstance(serializer, FilterableSerializerMixin):
queryset = serializer.prefetch_queryset(queryset)
return queryset
class SerializerContextMixin:
"""Mixin to add context to serializer."""

View File

@@ -1,14 +1,18 @@
"""Generic models which provide extra functionality over base Django model types."""
from collections.abc import Callable
from datetime import datetime
from string import Formatter
from typing import Optional
from typing import Any, Optional
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models
from django.db import models, transaction
from django.db.models import QuerySet
from django.db.models.signals import post_save
from django.db.transaction import TransactionManagementError
from django.dispatch import receiver
from django.urls import resolve, reverse
from django.urls.exceptions import NoReverseMatch
@@ -19,6 +23,7 @@ from django_q.models import Task
from error_report.models import Error
from mptt.exceptions import InvalidMove
from mptt.models import MPTTModel, TreeForeignKey
from stdimage.models import StdImageField
import common.settings
import InvenTree.exceptions
@@ -164,10 +169,14 @@ class MetadataMixin(models.Model):
abstract = True
def save(self, *args, **kwargs):
def save(self, force_insert=False, force_update=False, *args, **kwargs):
"""Save the model instance, and perform validation on the metadata field."""
self.validate_metadata()
super().save(*args, **kwargs)
if len(args) > 0:
raise TypeError(
'save() takes no positional arguments anymore'
) # pragma: no cover
super().save(force_insert=force_insert, force_update=force_update, **kwargs)
def clean(self, *args, **kwargs):
"""Perform model validation on the metadata field."""
@@ -443,7 +452,18 @@ class ReferenceIndexingMixin(models.Model):
reference_int = models.BigIntegerField(default=0)
class InvenTreeModel(PluginValidationMixin, models.Model):
class ContentTypeMixin:
"""Mixin class which supports retrieval of the ContentType for a model instance."""
@classmethod
def get_content_type(cls):
"""Return the ContentType object associated with this model."""
from django.contrib.contenttypes.models import ContentType
return ContentType.objects.get_for_model(cls)
class InvenTreeModel(ContentTypeMixin, PluginValidationMixin, models.Model):
"""Base class for InvenTree models, which provides some common functionality.
Includes the following mixins by default:
@@ -466,7 +486,167 @@ class InvenTreeMetadataModel(MetadataMixin, InvenTreeModel):
abstract = True
class InvenTreeAttachmentMixin:
class InvenTreePermissionCheckMixin:
"""Provides an abstracted class for managing permissions against related fields."""
@classmethod
def check_related_permission(cls, permission, user) -> bool:
"""Check if the user has permission to perform the specified action on the attachment.
The default implementation runs a permission check against *this* model class,
but this can be overridden in the implementing class if required.
Arguments:
permission: The permission to check (add / change / view / delete)
user: The user to check against
Returns:
bool: True if the user has permission, False otherwise
"""
perm = f'{cls._meta.app_label}.{permission}_{cls._meta.model_name}'
return user.has_perm(perm)
class InvenTreeParameterMixin(InvenTreePermissionCheckMixin, models.Model):
"""Provides an abstracted class for managing parameters.
Links the implementing model to the common.models.Parameter table,
and provides the following methods:
"""
class Meta:
"""Metaclass options for InvenTreeParameterMixin."""
abstract = True
# Define a reverse relation to the Parameter model
parameters_list = GenericRelation(
'common.Parameter', content_type_field='model_type', object_id_field='model_id'
)
@staticmethod
def annotate_parameters(queryset: QuerySet) -> QuerySet:
"""Annotate a queryset with pre-fetched parameters.
Args:
queryset: Queryset to annotate
Returns:
Annotated queryset
"""
return queryset.prefetch_related(
'parameters_list',
'parameters_list__model_type',
'parameters_list__updated_by',
'parameters_list__template',
'parameters_list__template__model_type',
)
@property
def parameters(self) -> QuerySet:
"""Return a QuerySet containing all the Parameter instances for this model.
This will return pre-fetched data if available (i.e. in a serializer context).
"""
# Check the query cache for pre-fetched parameters
if cache := getattr(self, '_prefetched_objects_cache', None):
if 'parameters_list' in cache:
return cache['parameters_list']
return self.parameters_list.all()
def delete(self, *args, **kwargs):
"""Handle the deletion of a model instance.
Before deleting the model instance, delete any associated parameters.
"""
self.parameters_list.all().delete()
super().delete(*args, **kwargs)
@transaction.atomic
def copy_parameters_from(self, other, clear=True, **kwargs):
"""Copy all parameters from another model instance.
Arguments:
other: The other model instance to copy parameters from
clear: If True, clear existing parameters before copying
**kwargs: Additional keyword arguments to pass to the Parameter constructor
"""
import common.models
if clear:
self.parameters_list.all().delete()
parameters = []
content_type = ContentType.objects.get_for_model(self.__class__)
template_ids = [parameter.template.pk for parameter in other.parameters.all()]
# Remove all conflicting parameters first
self.parameters_list.filter(template__pk__in=template_ids).delete()
for parameter in other.parameters.all():
parameter.pk = None
parameter.model_id = self.pk
parameter.model_type = content_type
parameters.append(parameter)
if len(parameters) > 0:
common.models.Parameter.objects.bulk_create(parameters)
def get_parameter(self, name: str):
"""Return a Parameter instance for the given parameter name.
Args:
name: Name of the parameter template
Returns:
Parameter instance if found, else None
"""
return self.parameters_list.filter(template__name=name).first()
def get_parameters(self) -> QuerySet:
"""Return all Parameter instances for this model."""
return (
self.parameters_list.all()
.prefetch_related('template', 'model_type')
.order_by('template__name')
)
def parameters_map(self) -> dict:
"""Return a map (dict) of parameter values associated with this Part instance, of the form.
Example:
{
"name_1": "value_1",
"name_2": "value_2",
}
"""
params = {}
for parameter in self.parameters.all().prefetch_related('template'):
params[parameter.template.name] = parameter.data
return params
def check_parameter_delete(self, parameter):
"""Run a check to determine if the provided parameter can be deleted.
The default implementation always returns True, but this can be overridden in the implementing class.
"""
return True
def check_parameter_save(self, parameter):
"""Run a check to determine if the provided parameter can be saved.
The default implementation always returns True, but this can be overridden in the implementing class.
"""
return True
class InvenTreeAttachmentMixin(InvenTreePermissionCheckMixin):
"""Provides an abstracted class for managing file attachments.
Links the implementing model to the common.models.Attachment table,
@@ -484,33 +664,15 @@ class InvenTreeAttachmentMixin:
super().delete(*args, **kwargs)
@property
def attachments(self):
def attachments(self) -> QuerySet:
"""Return a queryset containing all attachments for this model."""
return self.attachments_for_model().filter(model_id=self.pk)
@classmethod
def check_attachment_permission(cls, permission, user) -> bool:
"""Check if the user has permission to perform the specified action on the attachment.
The default implementation runs a permission check against *this* model class,
but this can be overridden in the implementing class if required.
Arguments:
permission: The permission to check (add / change / view / delete)
user: The user to check against
Returns:
bool: True if the user has permission, False otherwise
"""
perm = f'{cls._meta.app_label}.{permission}_{cls._meta.model_name}'
return user.has_perm(perm)
def attachments_for_model(self):
def attachments_for_model(self) -> QuerySet:
"""Return all attachments for this model class."""
from common.models import Attachment
model_type = self.__class__.__name__.lower()
return Attachment.objects.filter(model_type=model_type)
def create_attachment(self, attachment=None, link=None, comment='', **kwargs):
@@ -526,7 +688,7 @@ class InvenTreeAttachmentMixin:
Attachment.objects.create(**kwargs)
class InvenTreeTree(MPTTModel):
class InvenTreeTree(ContentTypeMixin, MPTTModel):
"""Provides an abstracted self-referencing tree model, based on the MPTTModel class.
Our implementation provides the following key improvements:
@@ -757,7 +919,15 @@ class InvenTreeTree(MPTTModel):
if len(trees) > 0:
# A tree update was performed, so we need to refresh the instance
self.refresh_from_db()
try:
self.refresh_from_db()
except TransactionManagementError:
# If we are inside a transaction block, we cannot refresh from db
pass
except Exception as e:
# Any other error is unexpected
InvenTree.sentry.report_exception(e)
InvenTree.exceptions.log_error(f'{self.__class__.__name__}.save')
def partial_rebuild(self, tree_id: int) -> bool:
"""Perform a partial rebuild of the tree structure.
@@ -1293,3 +1463,55 @@ def after_error_logged(sender, instance: Error, created: bool, **kwargs):
'link': url,
},
)
class InvenTreeImageMixin(models.Model):
"""A mixin class for adding image functionality to a model class.
The following fields are added to any model which implements this mixin:
- image : An image field for storing an image
"""
IMAGE_RENAME: Callable | None = None
class Meta:
"""Metaclass options for this mixin.
Note: abstract must be true, as this is only a mixin, not a separate table
"""
abstract = True
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Custom init method for InvenTreeImageMixin to ensure IMAGE_RENAME is implemented."""
if self.IMAGE_RENAME is None:
raise NotImplementedError(
'IMAGE_RENAME must be implemented in the model class'
)
super().__init__(*args, **kwargs)
def rename_image(self, filename):
"""Rename the uploaded image file using the IMAGE_RENAME function."""
return self.IMAGE_RENAME(filename) # type: ignore
image = StdImageField(
upload_to=rename_image,
null=True,
blank=True,
variations={'thumbnail': (128, 128), 'preview': (256, 256)},
delete_orphans=False,
verbose_name=_('Image'),
)
def get_image_url(self):
"""Return the URL of the image for this object."""
if self.image:
return InvenTree.helpers.getMediaUrl(self.image)
return InvenTree.helpers.getBlankImage()
def get_thumbnail_url(self) -> str:
"""Return the URL of the image thumbnail for this object."""
if self.image:
return InvenTree.helpers.getMediaUrl(self.image, 'thumbnail')
return InvenTree.helpers.getBlankThumbnail()

Some files were not shown because too many files have changed in this diff Show More