update next-breaking (#11999)
@@ -13,5 +13,5 @@ runs:
|
||||
invoke export-records -f data.json
|
||||
python3 ./src/backend/InvenTree/manage.py flush --noinput
|
||||
invoke migrate
|
||||
invoke import-records -c -f data.json
|
||||
invoke import-records -c -f data.json
|
||||
invoke import-records -c -f data.json --strict
|
||||
invoke import-records -c -f data.json --strict
|
||||
|
||||
@@ -15,6 +15,10 @@ inputs:
|
||||
required: false
|
||||
description: 'Install the InvenTree requirements?'
|
||||
default: 'false'
|
||||
static:
|
||||
required: false
|
||||
description: 'Should the static files be built?'
|
||||
default: 'false'
|
||||
dev-install:
|
||||
required: false
|
||||
description: 'Install the InvenTree development requirements?'
|
||||
@@ -103,3 +107,7 @@ runs:
|
||||
if: ${{ inputs.update == 'true' }}
|
||||
shell: bash
|
||||
run: invoke update --skip-backup --skip-static
|
||||
- name: Collect static files
|
||||
if: ${{ inputs.static == 'true' }}
|
||||
shell: bash
|
||||
run: invoke static --skip-plugins
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"""Script to check a data file exported using the 'export-records' command.
|
||||
|
||||
This script is intended to be used as part of the CI workflow,
|
||||
in conjunction with the "workflows/import_export.yaml" workflow.
|
||||
|
||||
In reads the exported data file, to ensure that:
|
||||
|
||||
- The file can be read and parsed as JSON
|
||||
- The file contains the expected metadata
|
||||
- The file contains the expected plugin configuration
|
||||
- The file contains the expected plugin database records
|
||||
|
||||
"""
|
||||
|
||||
PLUGIN_KEY = 'dummy_app_plugin'
|
||||
PLUGIN_SLUG = 'dummy-app-plugin'
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Check exported data file')
|
||||
parser.add_argument('datafile', help='Path to the exported data file (JSON)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isfile(args.datafile):
|
||||
print(f'Error: File not found: {args.datafile}')
|
||||
exit(1)
|
||||
|
||||
with open(args.datafile, encoding='utf-8') as f:
|
||||
try:
|
||||
data = json.load(f)
|
||||
print(f'Successfully loaded data from {args.datafile}')
|
||||
print(f'Number of records: {len(data)}')
|
||||
except json.JSONDecodeError as e:
|
||||
print(f'Error: Failed to parse JSON file: {e}')
|
||||
exit(1)
|
||||
|
||||
found_metadata = False
|
||||
found_installed_apps = False
|
||||
found_plugin_config = False
|
||||
plugin_data_records = {}
|
||||
|
||||
# Inspect the data and check that it has the expected structure and content.
|
||||
for entry in data:
|
||||
# Check metadata entry for expected values
|
||||
if entry.get('metadata', False):
|
||||
print('Found metadata entry')
|
||||
found_metadata = True
|
||||
|
||||
expected_apps = ['InvenTree', 'allauth', 'dbbackup', PLUGIN_KEY]
|
||||
|
||||
apps = entry.get('installed_apps', [])
|
||||
|
||||
for app in expected_apps:
|
||||
if app not in apps:
|
||||
print(f'- Expected app "{app}" not found in installed apps list')
|
||||
exit(1)
|
||||
|
||||
found_installed_apps = True
|
||||
|
||||
elif entry.get('model', None) == 'plugin.pluginconfig':
|
||||
key = entry['fields']['key']
|
||||
|
||||
if key == PLUGIN_SLUG:
|
||||
print(f'Found plugin configuration for plugin "{PLUGIN_KEY}"')
|
||||
found_plugin_config = True
|
||||
|
||||
elif entry.get('model', None) == f'{PLUGIN_KEY}.examplemodel':
|
||||
key = entry['fields']['key']
|
||||
value = entry['fields']['value']
|
||||
|
||||
plugin_data_records[key] = value
|
||||
|
||||
if not found_metadata:
|
||||
print('Error: No metadata entry found in exported data')
|
||||
exit(1)
|
||||
|
||||
if not found_installed_apps:
|
||||
print(
|
||||
f'Error: Plugin "{PLUGIN_KEY}" not found in installed apps list in metadata'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
if not found_plugin_config:
|
||||
print(f'Error: No plugin configuration found for plugin "{PLUGIN_KEY}"')
|
||||
exit(1)
|
||||
|
||||
# Check the extracted plugin records
|
||||
expected_keys = ['alpha', 'beta', 'gamma', 'delta']
|
||||
|
||||
for key in expected_keys:
|
||||
if key not in plugin_data_records:
|
||||
print(
|
||||
f'Error: Expected plugin record with key "{key}" not found in exported data'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
print('All checks passed successfully!')
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # pin@v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -168,13 +168,13 @@ 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@c7c53464625b32c7a7e944ae62b3e17d2b600130 # pin@v3.7.0
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # pin@v4.0.0
|
||||
- name: Set up Docker Buildx
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # pin@v3.12.0
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # pin@v4.0.0
|
||||
- name: Set up cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # pin@v4.0.0
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # pin@v4.1.2
|
||||
- name: Check if Dockerhub login is required
|
||||
id: docker_login
|
||||
run: |
|
||||
@@ -185,14 +185,14 @@ jobs:
|
||||
fi
|
||||
- name: Login to Dockerhub
|
||||
if: github.event_name != 'pull_request' && steps.docker_login.outputs.skip_dockerhub_login != 'true'
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # pin@v3.7.0
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # pin@v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log into registry ghcr.io
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # pin@v3.7.0
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # pin@v4.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -201,7 +201,7 @@ jobs:
|
||||
- name: Extract Docker metadata
|
||||
if: github.event_name != 'pull_request'
|
||||
id: meta
|
||||
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # pin@v5.10.0
|
||||
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # pin@v6.0.0
|
||||
with:
|
||||
images: |
|
||||
inventree/inventree
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Ensure that data import / export functionality works as expected.
|
||||
# - Create a dataset in a Postgres database (including plugin data)
|
||||
# - Export the dataset to an agnostic format (JSON)
|
||||
# - Import the dataset into a Sqlite database
|
||||
|
||||
name: Import / Export
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: ["l10*"]
|
||||
pull_request:
|
||||
branches-ignore: ["l10*"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
python_version: 3.11
|
||||
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
INVENTREE_DEBUG: false
|
||||
INVENTREE_LOG_LEVEL: WARNING
|
||||
INVENTREE_MEDIA_ROOT: /home/runner/work/InvenTree/test_inventree_media
|
||||
INVENTREE_STATIC_ROOT: /home/runner/work/InvenTree/test_inventree_static
|
||||
INVENTREE_BACKUP_DIR: /home/runner/work/InvenTree/test_inventree_backup
|
||||
INVENTREE_SITE_URL: http://localhost:8000
|
||||
|
||||
INVENTREE_PLUGINS_ENABLED: true
|
||||
INVENTREE_AUTO_UPDATE: true
|
||||
INVENTREE_PLUGINS_MANDATORY: "dummy-app-plugin"
|
||||
INVENTREE_GLOBAL_SETTINGS: '{"ENABLE_PLUGINS_APP": true}'
|
||||
|
||||
DATA_FILE: /home/runner/work/InvenTree/test_inventree_data.json
|
||||
|
||||
INVENTREE_DB_ENGINE: postgresql
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_DB_USER: inventree
|
||||
INVENTREE_DB_PASSWORD: password
|
||||
INVENTREE_DB_HOST: "127.0.0.1"
|
||||
INVENTREE_DB_PORT: 5432
|
||||
|
||||
jobs:
|
||||
|
||||
paths-filter:
|
||||
name: filter
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
server: ${{ steps.filter.outputs.server }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # pin@v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
server:
|
||||
- .github/workflows/import_export.yaml
|
||||
- .github/scripts/check_exported_data.py
|
||||
- 'src/backend/**'
|
||||
- 'tasks.py'
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: paths-filter
|
||||
if: needs.paths-filter.outputs.server == 'true' || contains(github.event.pull_request.labels.*.name, 'full-run')
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
env:
|
||||
POSTGRES_USER: inventree
|
||||
POSTGRES_PASSWORD: password
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
apt-dependency: gettext poppler-utils libpq-dev
|
||||
pip-dependency: psycopg
|
||||
update: true
|
||||
static: false
|
||||
- name: Setup Postgres Database
|
||||
run: |
|
||||
invoke migrate
|
||||
invoke dev.setup-test -i
|
||||
- name: Create Plugin Data
|
||||
run: |
|
||||
pip install -U inventree-dummy-app-plugin==0.1.0
|
||||
invoke migrate
|
||||
cd src/backend/InvenTree && python manage.py create_dummy_data
|
||||
- name: Export Postgres Dataset
|
||||
run: |
|
||||
invoke export-records -o -f ${{ env.DATA_FILE }}
|
||||
python .github/scripts/check_exported_data.py ${{ env.DATA_FILE }}
|
||||
invoke dev.delete-data --force
|
||||
- name: Update Environment Variables for Sqlite
|
||||
run: |
|
||||
echo "Updating environment variables for Sqlite"
|
||||
echo "INVENTREE_DB_ENGINE=sqlite" >> $GITHUB_ENV
|
||||
echo "INVENTREE_DB_NAME=/home/runner/work/InvenTree/test_inventree_db.sqlite3" >> $GITHUB_ENV
|
||||
- name: Setup Sqlite Database
|
||||
run: |
|
||||
invoke migrate
|
||||
test -f /home/runner/work/InvenTree/test_inventree_db.sqlite3 || (echo "Sqlite database not created" && exit 1)
|
||||
- name: Import Sqlite Dataset
|
||||
run: |
|
||||
invoke import-records -c -f ${{ env.DATA_FILE }} --strict
|
||||
cd src/backend/InvenTree && python manage.py check_dummy_data
|
||||
- name: Export Sqlite Dataset
|
||||
run: |
|
||||
invoke export-records -o -f ${{ env.DATA_FILE }}
|
||||
python .github/scripts/check_exported_data.py ${{ env.DATA_FILE }}
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
python_version: 3.11
|
||||
node_version: 20
|
||||
node_version: 24
|
||||
# The OS version must be set per job
|
||||
server_start_sleep: 60
|
||||
|
||||
@@ -23,8 +23,6 @@ env:
|
||||
INVENTREE_SITE_URL: http://localhost:8000
|
||||
INVENTREE_DEBUG: true
|
||||
|
||||
use_performance: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -42,12 +40,14 @@ jobs:
|
||||
cicd: ${{ steps.filter.outputs.cicd }}
|
||||
requirements: ${{ steps.filter.outputs.requirements }}
|
||||
runner-perf: ${{ steps.runner-perf.outputs.runner }}
|
||||
performance: ${{ steps.performance.outputs.force-performance }}
|
||||
submit-performance: ${{ steps.runner-perf.outputs.submit-performance }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
|
||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # pin@v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -77,13 +77,32 @@ jobs:
|
||||
if: |
|
||||
contains(github.event.pull_request.labels.*.name, 'dependency') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'full-run')
|
||||
- name: Is performance testing being forced?
|
||||
run: echo "force-performance=true" >> $GITHUB_OUTPUT
|
||||
id: performance
|
||||
if: |
|
||||
contains(github.event.pull_request.labels.*.name, 'performance-run')
|
||||
- name: Which runner to use?
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
PERFORMANCE: ${{ steps.performance.outputs.force-performance }}
|
||||
id: runner-perf
|
||||
# decide if we are running in inventree/inventree -> use codspeed-macro runner else ubuntu-24.04
|
||||
run: echo "runner=$([[ '${{ github.repository }}' == 'inventree/InvenTree' && '${{ env.use_performance }}' == 'true' ]] && echo 'codspeed-macro' || echo 'ubuntu-24.04')" >> $GITHUB_OUTPUT
|
||||
run: |
|
||||
is_main_push=false
|
||||
if [[ '${{ github.event_name }}' == 'push' && "$GITHUB_REF" == 'refs/heads/master' ]]; then
|
||||
is_main_push=true
|
||||
fi
|
||||
if [[ '${{ github.repository }}' == 'inventree/InvenTree' && ( "$is_main_push" == 'true' || "$PERFORMANCE" == 'true' ) ]]; then
|
||||
echo "runner=codspeed-macro" >> "$GITHUB_OUTPUT"
|
||||
echo "submit-performance=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "runner=ubuntu-24.04" >> "$GITHUB_OUTPUT"
|
||||
echo "submit-performance=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
pre-commit:
|
||||
name: Style [pre-commit]
|
||||
code-style:
|
||||
name: Style [prek]
|
||||
runs-on: ubuntu-24.04
|
||||
needs: paths-filter
|
||||
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'
|
||||
@@ -97,8 +116,8 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: "pip"
|
||||
- name: Run pre-commit Checks
|
||||
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # pin@v3.0.1
|
||||
- name: Run pre commit hook Checks
|
||||
uses: j178/prek-action@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # pin@v2
|
||||
- name: Check Version
|
||||
run: |
|
||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
||||
@@ -107,7 +126,7 @@ jobs:
|
||||
typecheck:
|
||||
name: Style [Typecheck]
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [paths-filter, pre-commit]
|
||||
needs: [code-style, paths-filter]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
steps:
|
||||
@@ -183,7 +202,7 @@ jobs:
|
||||
- name: Export API Documentation
|
||||
run: invoke dev.schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
|
||||
- name: Upload schema
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: schema.yml
|
||||
path: src/backend/InvenTree/schema.yml
|
||||
@@ -203,7 +222,7 @@ jobs:
|
||||
echo "Downloaded api.yaml"
|
||||
- name: Running OpenAPI Spec diff action
|
||||
id: breaking_changes
|
||||
uses: oasdiff/oasdiff-action/diff@1c611ffb1253a72924624aa4fb662e302b3565d3 # pin@main
|
||||
uses: oasdiff/oasdiff-action/diff@6147a58e5d1249a12f42fc864ab791d571a30015 # pin@main
|
||||
with:
|
||||
base: "api.yaml"
|
||||
revision: "src/backend/InvenTree/schema.yml"
|
||||
@@ -232,17 +251,17 @@ jobs:
|
||||
- name: Extract settings / tags
|
||||
run: invoke int.export-definitions --basedir docs
|
||||
- name: Upload settings
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: inventree_settings.json
|
||||
path: docs/generated/inventree_settings.json
|
||||
- name: Upload tags
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: inventree_tags.yml
|
||||
path: docs/generated/inventree_tags.yml
|
||||
- name: Upload filters
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: inventree_filters.yml
|
||||
path: docs/generated/inventree_filters.yml
|
||||
@@ -265,7 +284,7 @@ jobs:
|
||||
- name: Create artifact directory
|
||||
run: mkdir -p artifact
|
||||
- name: Download schema artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # pin@v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # pin@v8.0.1
|
||||
with:
|
||||
path: artifact
|
||||
merge-multiple: true
|
||||
@@ -291,8 +310,8 @@ jobs:
|
||||
name: Tests - inventree-python
|
||||
runs-on: ${{ needs.paths-filter.outputs.runner-perf }}
|
||||
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true' || needs.paths-filter.outputs.performance == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -310,7 +329,7 @@ jobs:
|
||||
INVENTREE_SITE_URL: http://127.0.0.1:12345
|
||||
INVENTREE_DEBUG: true
|
||||
INVENTREE_LOG_LEVEL: WARNING
|
||||
node_version: '>=20.19.6'
|
||||
node_version: '>=24'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||
@@ -322,6 +341,7 @@ jobs:
|
||||
apt-dependency: gettext poppler-utils
|
||||
dev-install: true
|
||||
update: true
|
||||
static: true
|
||||
npm: true
|
||||
- name: Download Python Code For `${WRAPPER_NAME}`
|
||||
run: git clone --depth 1 https://github.com/inventree/${WRAPPER_NAME} ./${WRAPPER_NAME}
|
||||
@@ -341,10 +361,11 @@ jobs:
|
||||
pip uninstall pytest-django -y
|
||||
cd ${WRAPPER_NAME}
|
||||
pip install .
|
||||
if: needs.paths-filter.outputs.submit-performance == 'true'
|
||||
- name: Performance Reporting
|
||||
uses: CodSpeedHQ/action@dbda7111f8ac363564b0c51b992d4ce76bb89f2f # pin@v4
|
||||
uses: CodSpeedHQ/action@3194d9a39c4d46684cb44bf7207fc56626aad8fd # pin@v4.15.1
|
||||
# check if we are in inventree/inventree - reporting only works in that OIDC context
|
||||
if: github.repository == 'inventree/InvenTree'
|
||||
if: github.repository == 'inventree/InvenTree' && needs.paths-filter.outputs.submit-performance == 'true'
|
||||
with:
|
||||
mode: walltime
|
||||
run: pytest ./src/performance --codspeed
|
||||
@@ -353,7 +374,7 @@ jobs:
|
||||
name: Tests - DB [SQLite] + Coverage ${{ matrix.python_version }}
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
continue-on-error: true # continue if a step fails so that coverage gets pushed
|
||||
strategy:
|
||||
@@ -378,6 +399,7 @@ jobs:
|
||||
apt-dependency: gettext poppler-utils
|
||||
dev-install: true
|
||||
update: true
|
||||
static: true
|
||||
- name: Data Export Test
|
||||
uses: ./.github/actions/migration
|
||||
- name: Test Translations
|
||||
@@ -387,13 +409,13 @@ jobs:
|
||||
- name: Coverage Tests
|
||||
run: invoke dev.test --check --coverage --translations
|
||||
- name: Upload raw coverage to artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: coverage
|
||||
path: .coverage
|
||||
retention-days: 14
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # pin@v5.5.2
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # pin@v6.0.0
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -404,9 +426,9 @@ jobs:
|
||||
name: Tests - Performance
|
||||
runs-on: ${{ needs.paths-filter.outputs.runner-perf }}
|
||||
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
needs: ["code-style", "paths-filter"]
|
||||
# check if we are in inventree/inventree - reporting only works in that OIDC context
|
||||
if: (needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true') && github.repository == 'inventree/InvenTree'
|
||||
if: (needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true') && github.repository == 'inventree/InvenTree' && needs.paths-filter.outputs.submit-performance == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
@@ -430,9 +452,9 @@ jobs:
|
||||
update: true
|
||||
npm: true
|
||||
env:
|
||||
node_version: '>=20.19.0'
|
||||
node_version: '>=24'
|
||||
- name: Performance Reporting
|
||||
uses: CodSpeedHQ/action@dbda7111f8ac363564b0c51b992d4ce76bb89f2f # pin@v4
|
||||
uses: CodSpeedHQ/action@3194d9a39c4d46684cb44bf7207fc56626aad8fd # pin@v4.15.1
|
||||
with:
|
||||
mode: walltime
|
||||
run: inv dev.test --pytest
|
||||
@@ -440,7 +462,7 @@ jobs:
|
||||
postgres:
|
||||
name: Tests - DB [PostgreSQL]
|
||||
runs-on: ubuntu-24.04
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
env:
|
||||
@@ -480,6 +502,7 @@ jobs:
|
||||
pip-dependency: psycopg django-redis>=5.0.0
|
||||
dev-install: true
|
||||
update: true
|
||||
static: true
|
||||
- name: Run Tests
|
||||
run: invoke dev.test --check --translations
|
||||
- name: Data Export Test
|
||||
@@ -489,7 +512,7 @@ jobs:
|
||||
name: Tests - DB [MySQL]
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
env:
|
||||
@@ -528,6 +551,7 @@ jobs:
|
||||
pip-dependency: mysqlclient
|
||||
dev-install: true
|
||||
update: true
|
||||
static: true
|
||||
- name: Run Tests
|
||||
run: invoke dev.test --check --translations
|
||||
- name: Data Export Test
|
||||
@@ -573,7 +597,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
run: invoke dev.test --check --migrations --report --coverage --translations
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # pin@v5.5.2
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # pin@v6.0.0
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -644,7 +668,7 @@ jobs:
|
||||
name: Tests - Web UI
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
services:
|
||||
postgres:
|
||||
@@ -682,7 +706,7 @@ jobs:
|
||||
npm: true
|
||||
install: true
|
||||
update: true
|
||||
apt-dependency: postgresql-client libpq-dev
|
||||
apt-dependency: gettext postgresql-client libpq-dev
|
||||
pip-dependency: psycopg2
|
||||
- name: Set up test data
|
||||
run: |
|
||||
@@ -701,7 +725,7 @@ jobs:
|
||||
invoke static
|
||||
env INVENTREE_CUSTOM_SPLASH="img/playwright_custom_splash.png" INVENTREE_CUSTOM_LOGO="img/playwright_custom_logo.png" npx nyc playwright test --project=customization
|
||||
npx nyc playwright test --project=chromium --project=firefox
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
|
||||
with:
|
||||
name: playwright-report
|
||||
@@ -710,7 +734,7 @@ jobs:
|
||||
- name: Report coverage
|
||||
run: cd src/frontend && npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --exclude-after-remap false
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # pin@v5.5.2
|
||||
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # pin@v6.0.0
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: inventree/InvenTree
|
||||
@@ -739,14 +763,14 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: cd src/frontend && yarn install
|
||||
- name: Build frontend
|
||||
run: cd src/frontend && yarn run compile && yarn run build
|
||||
run: cd src/frontend && yarn run compile && yarn run lib && yarn run build
|
||||
- name: Write version file - SHA
|
||||
run: cd src/backend/InvenTree/web/static/web/.vite && echo "$GITHUB_SHA" > sha.txt
|
||||
- name: Zip frontend
|
||||
run: |
|
||||
cd src/backend/InvenTree/web/static
|
||||
zip -r frontend-build.zip web/ web/.vite
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: frontend-build
|
||||
path: src/backend/InvenTree/web/static/web
|
||||
@@ -755,7 +779,7 @@ jobs:
|
||||
zizmor:
|
||||
name: Security [Zizmor]
|
||||
runs-on: ubuntu-24.04
|
||||
needs: ["pre-commit", "paths-filter"]
|
||||
needs: ["code-style", "paths-filter"]
|
||||
if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
permissions:
|
||||
@@ -765,13 +789,5 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: hynek/setup-cached-uv@757bedc3f972eb7227a1aa657651f15a8527c817 # pin@v2
|
||||
- name: Run zizmor
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # pin@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
- name: Run zizmor 🌈
|
||||
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
||||
python3 .github/scripts/version_check.py
|
||||
- name: Push to Stable Branch
|
||||
uses: ad-m/github-push-action@77c5b412c50b723d2a4fbc6d71fb5723bcd439aa # pin@v1.0.0
|
||||
uses: ad-m/github-push-action@4cc74773234f74829a8c21bc4d69dd4be9cfa599 # pin@v1.1.0
|
||||
if: env.stable_release == 'true'
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -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@28d71544de8eaf1b958d335707167c5f783590ad # pin@v0
|
||||
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # pin@v0
|
||||
with:
|
||||
artifact-name: frontend-build.spdx
|
||||
path: src/frontend
|
||||
@@ -73,31 +73,26 @@ jobs:
|
||||
zip -r ../frontend-build.zip * .vite
|
||||
- name: Attest Build Provenance
|
||||
id: attest
|
||||
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # pin@v1
|
||||
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # pin@v1
|
||||
with:
|
||||
subject-path: "${{ github.workspace }}/src/backend/InvenTree/web/static/frontend-build.zip"
|
||||
|
||||
- name: Upload frontend
|
||||
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
|
||||
asset_name: frontend-build.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
run: gh release upload ${REF} src/backend/InvenTree/web/static/frontend-build.zip#frontend-build.zip
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload frontend to artifacts
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # pin@v7.0.1
|
||||
with:
|
||||
name: frontend-build
|
||||
path: src/backend/InvenTree/web/static/frontend-build.zip
|
||||
- name: Upload Attestation
|
||||
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
asset_name: frontend-build.intoto.jsonl
|
||||
file: ${{ steps.attest.outputs.bundle-path}}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
run: gh release upload ${REF} ${BUNDLE_PATH}#frontend-build.intoto.jsonl
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path}}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
docs:
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -134,13 +129,10 @@ jobs:
|
||||
cd docs/site
|
||||
zip -r docs-html.zip *
|
||||
- name: Publish documentation
|
||||
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: docs/site/docs-html.zip
|
||||
asset_name: docs-html.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
run: gh release upload ${REF} docs/site/docs-html.zip#docs-html.zip
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-pkgr:
|
||||
if: github.repository == 'inventree/InvenTree'
|
||||
@@ -163,7 +155,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get frontend artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # pin@v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # pin@v8.0.1
|
||||
with:
|
||||
name: frontend-build
|
||||
- name: Setup
|
||||
@@ -244,10 +236,9 @@ jobs:
|
||||
channel: ${{ env.pkg_channel }}
|
||||
file: ${{ steps.package.outputs.package_path }}
|
||||
- name: Publish to artifact
|
||||
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.package.outputs.package_path }}
|
||||
asset_name: ${{ matrix.target }}-{{ steps.setup.outputs.version }}.tar.gz
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
run: gh release upload ${REF} ${PACKAGE_PATH}#${PACKAGE_NAME}
|
||||
env:
|
||||
REF: ${{ github.ref_name }}
|
||||
PACKAGE_PATH: ${{ steps.package.outputs.package_path }}
|
||||
PACKAGE_NAME: ${{ matrix.target }}-{{ steps.setup.outputs.version }}.tar.gz
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -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@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
|
||||
uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
env:
|
||||
python_version: 3.11
|
||||
node_version: 20
|
||||
node_version: 24
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
echo "Resetting to HEAD~"
|
||||
git reset HEAD~ || true
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # pin@v2
|
||||
uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # pin@v2
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
|
||||
@@ -20,6 +20,7 @@ before:
|
||||
- contrib/packager.io/before.sh
|
||||
dependencies:
|
||||
- curl
|
||||
- poppler-utils
|
||||
- "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"
|
||||
|
||||
@@ -18,7 +18,7 @@ repos:
|
||||
exclude: mkdocs.yml
|
||||
- id: mixed-line-ending
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.1
|
||||
rev: v0.15.12
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
args: [--preview]
|
||||
@@ -29,7 +29,7 @@ repos:
|
||||
--preview
|
||||
]
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
rev: 0.10.2
|
||||
rev: 0.11.12
|
||||
hooks:
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements-dev.in
|
||||
@@ -48,15 +48,15 @@ repos:
|
||||
args: [src/backend/requirements.in, -o, src/backend/requirements-3.14.txt, --python-version=3.14, -c, src/backend/requirements.txt]
|
||||
files: src/backend/requirements\.(in|txt)$
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements.txt
|
||||
name: pip-compile contrib/dev_reqs/requirements.txt
|
||||
args: [contrib/dev_reqs/requirements.in, -o, contrib/dev_reqs/requirements.txt, -c, src/backend/requirements.txt]
|
||||
files: contrib/dev_reqs/requirements\.(in|txt)$
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements.txt
|
||||
name: pip-compile docs/requirements.txt
|
||||
args: [docs/requirements.in, -o, docs/requirements.txt, -c, src/backend/requirements.txt]
|
||||
files: docs/requirements\.(in|txt)$
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements.txt
|
||||
name: pip-compile contrib/container/requirements.txt
|
||||
args: [contrib/container/requirements.in, -o, contrib/container/requirements.txt, --python-version=3.14, -c, src/backend/requirements.txt]
|
||||
files: contrib/container/requirements\.(in|txt)$
|
||||
- repo: https://github.com/Riverside-Healthcare/djLint
|
||||
@@ -64,7 +64,7 @@ repos:
|
||||
hooks:
|
||||
- id: djlint-django
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.1
|
||||
rev: v2.4.2
|
||||
hooks:
|
||||
- id: codespell
|
||||
additional_dependencies:
|
||||
@@ -79,13 +79,13 @@ repos:
|
||||
src/frontend/vite.config.ts |
|
||||
)$
|
||||
- repo: https://github.com/biomejs/pre-commit
|
||||
rev: v2.3.15
|
||||
rev: v2.4.14
|
||||
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.30.0
|
||||
rev: v8.30.1
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
language_version: 1.25.4
|
||||
@@ -97,3 +97,4 @@ repos:
|
||||
rev: 0.4.3
|
||||
hooks:
|
||||
- id: teyit
|
||||
language_version: python3.11
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
|
||||
"args": [
|
||||
"runserver",
|
||||
"0.0.0.0:8000"
|
||||
"0.0.0.0:8000",
|
||||
// "--sync",// Synchronize worker tasks to foreground thread
|
||||
// "--noreload", // disable auto-reload
|
||||
],
|
||||
"django": true,
|
||||
"justMyCode": false
|
||||
|
||||
@@ -19,20 +19,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [#11303](https://github.com/inventree/InvenTree/pull/11303) removes the `default_supplier` field from the `Part` model. Instead, the `SupplierPart` model now has a `primary` field which is used to indicate which supplier is the default for a given part. Any external client applications which made use of the old `default_supplier` field will need to be updated.
|
||||
- [#9604](https://github.com/inventree/InvenTree/pull/9604) - refactors user API endpoint to be less ambiguous
|
||||
- [#11893](https://github.com/inventree/InvenTree/pull/11893) bumps Node environment to version 24 LTS - this is only relevant if you build the frontend assets yourself
|
||||
|
||||
### Added
|
||||
|
||||
- [#11920](https://github.com/inventree/InvenTree/pull/11920) adds support for renaming attachments after they have been uploaded. This includes both backend and frontend changes, allowing users to rename attachments via the API or through the UI.
|
||||
- [#11914](https://github.com/inventree/InvenTree/pull/11914) adds a "maximum_stock" field to the Part model, allowing users to specify a maximum preferred stock level for each part. This is used in conjunction with the existing "minimum_stock" field to allow users to define a preferred stock range for each part. The "high_stock" filter has also been added to the Part API endpoint, allowing users to filter parts which are above their maximum stock level.
|
||||
- [#11631](https://github.com/inventree/InvenTree/pull/11631) adds "raw_amount" field to the BomItem model, allowing BOM quantities to account for the units of measure of the underlying part.
|
||||
- [#11872](https://github.com/inventree/InvenTree/pull/11872) adds a global setting to allow or disallow the deletion of serialized stock items.
|
||||
- [#11861](https://github.com/inventree/InvenTree/pull/11861) adds support for bulk-replacing a component in multiple BOMs simultaneously
|
||||
- [#11853](https://github.com/inventree/InvenTree/pull/11853) adds BOM comparison functionality, allowing users to compare the BOM of one assembly with another assembly.
|
||||
- [#11809](https://github.com/inventree/InvenTree/pull/11809) adds multi-level subassembly display mode to the BOM table, allowing users to view multiple levels of subassemblies in a single table view. This is an optional display mode which can be toggled on or off by the user.
|
||||
- [#11778](https://github.com/inventree/InvenTree/pull/11778) adds inline supplier part creation to po line item addition dialog.
|
||||
- [#11772](https://github.com/inventree/InvenTree/pull/11772) the UI now warns if you navigate away from a note panel with unsaved changes
|
||||
- [#11788](https://github.com/inventree/InvenTree/pull/11788) adds support for custom permissions checks on database models defined in plugins. If a model defines a `check_user_permission` classmethod, this will be called to determine if a user has permission to view the model. This is required for plugin models which do not have the required ruleset definitions for the standard permission system.
|
||||
- [#9570](https://github.com/inventree/InvenTree/pull/9570) adds support for defining primary actions via plugins
|
||||
|
||||
### Changed
|
||||
|
||||
- [#11990](https://github.com/inventree/InvenTree/pull/11990) build output operations performed via the API now offload the work to a background task, and now return a task ID which can be used to monitor the progress of the task. This allows for better performance and responsiveness when performing build output operations, as the work is performed asynchronously in the background.
|
||||
- [#11825](https://github.com/inventree/InvenTree/pull/11825) adds a new "bom" ruleset and associated permissions for BOM management, separate from the "part" ruleset which remains focused on part management. This allows for more granular control over user permissions, allowing users to have different levels of access to part management and BOM management functionality.
|
||||
- [#11816](https://github.com/inventree/InvenTree/pull/11816) makes the `issued_by` field on the `Build` API read only, and instead sets the `issued_by` field to the current user when a build is created. This change was made to ensure that the `issued_by` field accurately reflects the user who created the build, and to prevent users from setting this field to an arbitrary value when creating or updating a build.
|
||||
|
||||
### Removed
|
||||
|
||||
- [#11962](https://github.com/inventree/InvenTree/pull/11962) removes the "remote_image" field from the Part API endpoint, which (previously) allowed the user to specify a remote URL for an image to be downloaded and associated with the part. This field was removed due to security concerns around downloading images from arbitrary URLs. If you were using this field in an external client application, you will need to update your application to use the new "download_image_from_url" API endpoint instead.
|
||||
|
||||
## 1.3.0 - 2026-04-11
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [#11303](https://github.com/inventree/InvenTree/pull/11303) removes the `default_supplier` field from the `Part` model. Instead, the `SupplierPart` model now has a `primary` field which is used to indicate which supplier is the default for a given part. Any external client applications which made use of the old `default_supplier` field will need to be updated.
|
||||
- [#11500](https://github.com/inventree/InvenTree/pull/11500) fixes a spelling mistake in the database configuration values, which may affect some users running the PostgreSQL database backend. The `tcp_keepalives_internal` option has been renamed to `tcp_keepalives_interval` to reflect the correct PostgreSQL configuration option name. If you are using PostgreSQL, and have set a custom value for the `tcp_keepalives_internal` option, you will need to update this to `tcp_keepalives_interval` in your configuration (either via environment variable or config file).
|
||||
|
||||
### Added
|
||||
|
||||
- [#11702](https://github.com/inventree/InvenTree/pull/11702) adds "last updated" and "updated by" fields for label and report templates, allowing users to track when a template was last modified and by whom.
|
||||
- [#11685](https://github.com/inventree/InvenTree/pull/11685) exposes the data importer wizard to the plugin interface, allowing plugins to trigger the data importer wizard and perform custom data imports from the UI.
|
||||
- [#11692](https://github.com/inventree/InvenTree/pull/11692) adds line item numbering for external orders (purchase, sales and return orders). This allows users to specify a line number for each line item on the order, which can be used for reference purposes. The line number is optional, and can be left blank if not required. The line number is stored as a string, to allow for more flexible formatting (e.g. "1", "1.1", "A", etc).
|
||||
- [#11641](https://github.com/inventree/InvenTree/pull/11641) adds support for custom parameters against the SalesOrderShipment model.
|
||||
- [#11527](https://github.com/inventree/InvenTree/pull/11527) adds a new API endpoint for monitoring the status of a particular background task. This endpoint allows clients to check the status of a background task and receive updates when the task is complete. This is useful for long-running tasks that may take some time to complete, allowing clients to provide feedback to users about the progress of the task.
|
||||
- [#11405](https://github.com/inventree/InvenTree/pull/11405) adds default table filters, which hide inactive items by default. The default table filters are overridden by user filter selection, and only apply to the table view initially presented to the user. This means that users can still view inactive items if they choose to, but they will not be shown by default.
|
||||
- [#11222](https://github.com/inventree/InvenTree/pull/11222) adds support for data import using natural keys, allowing for easier association of related objects without needing to know their internal database IDs.
|
||||
- [#11383](https://github.com/inventree/InvenTree/pull/11383) adds "exists_for_model_id", "exists_for_related_model", and "exists_for_related_model_id" filters to the ParameterTemplate API endpoint. These filters allow users to check for the existence of parameters associated with specific models or related models, improving the flexibility and usability of the API.
|
||||
- [#10887](https://github.com/inventree/InvenTree/pull/10887) adds the ability to auto-allocate tracked items against specific build outputs. Currently, this will only allocate items where the serial number of the tracked item matches the serial number of the build output, but in future this may be extended to allow for more flexible allocation rules.
|
||||
- [#11372](https://github.com/inventree/InvenTree/pull/11372) adds backup metadata setter and restore metadata validator functions to ensure common footguns are harder to trigger when using the backup and restore functionality.
|
||||
- [#11374](https://github.com/inventree/InvenTree/pull/11374) adds `updated_at` field on purchase, sales and return orders.
|
||||
- [#11074](https://github.com/inventree/InvenTree/pull/11074) adds "Keep form open" option on create form which leaves dialog with form opened after form submitting.
|
||||
|
||||
### Changed
|
||||
|
||||
- [#11648](https://github.com/inventree/InvenTree/pull/11648) improves the import/export process, allowing data records defined by plugins to be loaded when importing a database from a file.
|
||||
- [#11630](https://github.com/inventree/InvenTree/pull/11630) enhances the `import_records` and `export_records` system commands, by adding a metadata entry to the exported data file to allow for compatibility checks during data import.
|
||||
|
||||
### Removed
|
||||
|
||||
- [#11581](https://github.com/inventree/InvenTree/pull/11581) removes the ability to specify arbitrary filters when performing bulk operations via the API. This functionality represented a significant security risk, and was not required for any existing use cases. Bulk operations now only work with a provided list of primary keys.
|
||||
|
||||
## 1.2.0 - 2026-02-12
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -42,7 +42,6 @@ InvenTree/
|
||||
├─ CONTRIBUTING.md # Contribution guidelines and overview
|
||||
├─ Procfile # Process definition for Debian/Ubuntu packages
|
||||
├─ README.md # General project information and overview
|
||||
├─ runtime.txt # Python runtime settings for Debian/Ubuntu packages build
|
||||
├─ SECURITY.md # Project security policy
|
||||
├─ tasks.py # Action definitions for development, testing and deployment
|
||||
```
|
||||
|
||||
@@ -77,6 +77,7 @@ InvenTree is designed to be **extensible**, and provides multiple options for **
|
||||
<ul>
|
||||
<li><a href="https://www.postgresql.org/">PostgreSQL</a></li>
|
||||
<li><a href="https://www.mysql.com/">MySQL</a></li>
|
||||
<li><a href="https://www.mariadb.org/">MariaDB</a></li>
|
||||
<li><a href="https://www.sqlite.org/">SQLite</a></li>
|
||||
<li><a href="https://redis.io/">Redis</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
ignore:
|
||||
- "src/backend/InvenTree/**/test_migrations.py"
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
# - Runs InvenTree web server under django development server
|
||||
# - Monitors source files for any changes, and live-reloads server
|
||||
|
||||
# Base image last bumped 2026-02-23
|
||||
FROM python:3.14-slim-trixie@sha256:486b8092bfb12997e10d4920897213a06563449c951c5506c2a2cfaf591c599f AS inventree_base
|
||||
# Base image last bumped 2026-03-20
|
||||
FROM python:3.14-slim-trixie@sha256:fb83750094b46fd6b8adaa80f66e2302ecbe45d513f6cece637a841e1025b4ca AS inventree_base
|
||||
|
||||
# Build arguments for this image
|
||||
ARG commit_tag=""
|
||||
|
||||
@@ -6,9 +6,9 @@ asgiref==3.11.1 \
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# django
|
||||
django==5.2.11 \
|
||||
--hash=sha256:7f2d292ad8b9ee35e405d965fbbad293758b858c34bbf7f3df551aeeac6f02d3 \
|
||||
--hash=sha256:e7130df33ada9ab5e5e929bc19346a20fe383f5454acb2cc004508f242ee92c0
|
||||
django==5.2.14 \
|
||||
--hash=sha256:58a63ba841662e5c686b57ba1fec52ddd68c0b93bd96ac3029d55728f00bf8a2 \
|
||||
--hash=sha256:6f712143bd3064310d1f50fac859c3e9a274bdcfc9595339853be7779297fc76
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/container/requirements.in
|
||||
@@ -17,15 +17,15 @@ django-auth-ldap==5.3.0 \
|
||||
--hash=sha256:743d8107b146240b46f7e97207dc06cb11facc0cd70dce490b7ca09dd5643d19 \
|
||||
--hash=sha256:aa880415983149b072f876d976ef8ec755a438090e176817998263a6ed9e1038
|
||||
# via -r contrib/container/requirements.in
|
||||
gunicorn==25.1.0 \
|
||||
--hash=sha256:1426611d959fa77e7de89f8c0f32eed6aa03ee735f98c01efba3e281b1c47616 \
|
||||
--hash=sha256:d0b1236ccf27f72cfe14bce7caadf467186f19e865094ca84221424e839b8b8b
|
||||
gunicorn==25.3.0 \
|
||||
--hash=sha256:cacea387dab08cd6776501621c295a904fe8e3b7aae9a1a3cbb26f4e7ed54660 \
|
||||
--hash=sha256:f74e1b2f9f76f6cd1ca01198968bd2dd65830edc24b6e8e4d78de8320e2fe889
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/container/requirements.in
|
||||
invoke==2.2.1 \
|
||||
--hash=sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8 \
|
||||
--hash=sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707
|
||||
invoke==3.0.3 \
|
||||
--hash=sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c \
|
||||
--hash=sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/container/requirements.in
|
||||
@@ -44,92 +44,91 @@ mariadb==1.1.14 \
|
||||
--hash=sha256:98d552a8bb599eceaa88f65002ad00bd88aeed160592c273a7e5c1d79ab733dd \
|
||||
--hash=sha256:e6d702a53eccf20922e47f2f45cfb5c7a0c2c6c0a46e4ee2d8a80d0ff4a52f34
|
||||
# via -r contrib/container/requirements.in
|
||||
mysqlclient==2.2.7 \
|
||||
--hash=sha256:199dab53a224357dd0cb4d78ca0e54018f9cee9bf9ec68d72db50e0a23569076 \
|
||||
--hash=sha256:201a6faa301011dd07bca6b651fe5aaa546d7c9a5426835a06c3172e1056a3c5 \
|
||||
--hash=sha256:24ae22b59416d5fcce7e99c9d37548350b4565baac82f95e149cac6ce4163845 \
|
||||
--hash=sha256:2e3c11f7625029d7276ca506f8960a7fd3c5a0a0122c9e7404e6a8fe961b3d22 \
|
||||
--hash=sha256:4b4c0200890837fc64014cc938ef2273252ab544c1b12a6c1d674c23943f3f2e \
|
||||
--hash=sha256:92af368ed9c9144737af569c86d3b6c74a012a6f6b792eb868384787b52bb585 \
|
||||
--hash=sha256:977e35244fe6ef44124e9a1c2d1554728a7b76695598e4b92b37dc2130503069 \
|
||||
--hash=sha256:a22d99d26baf4af68ebef430e3131bb5a9b722b79a9fcfac6d9bbf8a88800687
|
||||
mysqlclient==2.2.8 \
|
||||
--hash=sha256:000c7ec3d11e7c411db832e4cfcd7f05db47464326381f5d5ae991b4bb572f93 \
|
||||
--hash=sha256:260cce0e81446c83bf0a389e0fae38d68547d9f8fc0833bc733014e10ce28a99 \
|
||||
--hash=sha256:60c9ed339dc09e3d5380cc2a9f42e86754fee25a661ced77a02df77990664c92 \
|
||||
--hash=sha256:86db31bba7b3480fec2751350e9790e24f016f89af33a87bab7e79f7196474e8 \
|
||||
--hash=sha256:8ed20c5615a915da451bb308c7d0306648a4fd9a2809ba95c992690006306199 \
|
||||
--hash=sha256:9bed7c8d3b629bdc09e17fb628d5b3b0a5fd1f12b09432b464b9126c727bedc0 \
|
||||
--hash=sha256:a81f5e12f8d05439709cb02fba97f9f76d1a6c528164f2260d8798fec969e300
|
||||
# via -r contrib/container/requirements.in
|
||||
packaging==26.0 \
|
||||
--hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \
|
||||
--hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529
|
||||
packaging==26.2 \
|
||||
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
|
||||
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# gunicorn
|
||||
# mariadb
|
||||
# wheel
|
||||
psycopg[binary, pool]==3.3.2 \
|
||||
--hash=sha256:3e94bc5f4690247d734599af56e51bae8e0db8e4311ea413f801fef82b14a99b \
|
||||
--hash=sha256:707a67975ee214d200511177a6a80e56e654754c9afca06a7194ea6bbfde9ca7
|
||||
psycopg[binary, pool]==3.3.3 \
|
||||
--hash=sha256:5e9a47458b3c1583326513b2556a2a9473a1001a56c9efe9e587245b43148dd9 \
|
||||
--hash=sha256:f96525a72bcfade6584ab17e89de415ff360748c766f0106959144dcbb38c698
|
||||
# via -r contrib/container/requirements.in
|
||||
psycopg-binary==3.3.2 \
|
||||
--hash=sha256:03b7cd73fb8c45d272a34ae7249713e32492891492681e3cf11dff9531cf37e9 \
|
||||
--hash=sha256:04bb2de4ba69d6f8395b446ede795e8884c040ec71d01dd07ac2b2d18d4153d1 \
|
||||
--hash=sha256:0611f4822674f3269e507a307236efb62ae5a828fcfc923ac85fe22ca19fd7c8 \
|
||||
--hash=sha256:0768c5f32934bb52a5df098317eca9bdcf411de627c5dca2ee57662b64b54b41 \
|
||||
--hash=sha256:07a5f030e0902ec3e27d0506ceb01238c0aecbc73ecd7fa0ee55f86134600b5b \
|
||||
--hash=sha256:083c2e182be433f290dc2c516fd72b9b47054fcd305cce791e0a50d9e93e06f2 \
|
||||
--hash=sha256:09b3014013f05cd89828640d3a1db5f829cc24ad8fa81b6e42b2c04685a0c9d4 \
|
||||
--hash=sha256:0ae60e910531cfcc364a8f615a7941cac89efeb3f0fffe0c4824a6d11461eef7 \
|
||||
--hash=sha256:136c43f185244893a527540307167f5d3ef4e08786508afe45d6f146228f5aa9 \
|
||||
--hash=sha256:1586e220be05547c77afc326741dd41cc7fba38a81f9931f616ae98865439678 \
|
||||
--hash=sha256:1e09d0d93d35c134704a2cb2b15f81ffc8174fd602f3e08f7b1a3d8896156cf0 \
|
||||
--hash=sha256:1ea41c0229f3f5a3844ad0857a83a9f869aa7b840448fa0c200e6bcf85d33d19 \
|
||||
--hash=sha256:23d2594af848c1fd3d874a9364bef50730124e72df7bb145a20cb45e728c50ed \
|
||||
--hash=sha256:3789d452a9d17a841c7f4f97bbcba51a21f957ea35641a4c98507520e6b6a068 \
|
||||
--hash=sha256:3ff7489df5e06c12d1829544eaec64970fe27fe300f7cf04c8495fe682064688 \
|
||||
--hash=sha256:43b130e3b6edcb5ee856c7167ccb8561b473308c870ed83978ae478613764f1c \
|
||||
--hash=sha256:44e89938d36acc4495735af70a886d206a5bfdc80258f95b69b52f68b2968d9e \
|
||||
--hash=sha256:458696a5fa5dad5b6fb5d5862c22454434ce4fe1cf66ca6c0de5f904cbc1ae3e \
|
||||
--hash=sha256:50ff10ab8c0abdb5a5451b9315538865b50ba64c907742a1385fdf5f5772b73e \
|
||||
--hash=sha256:522b79c7db547767ca923e441c19b97a2157f2f494272a119c854bba4804e186 \
|
||||
--hash=sha256:59d0163c4617a2c577cb34afbed93d7a45b8c8364e54b2bd2020ff25d5f5f860 \
|
||||
--hash=sha256:5a327327f1188b3fbecac41bf1973a60b86b2eb237db10dc945bd3dc97ec39e4 \
|
||||
--hash=sha256:649c1d33bedda431e0c1df646985fbbeb9274afa964e1aef4be053c0f23a2924 \
|
||||
--hash=sha256:716a586f99bbe4f710dc58b40069fcb33c7627e95cc6fc936f73c9235e07f9cf \
|
||||
--hash=sha256:742ce48cde825b8e52fb1a658253d6d1ff66d152081cbc76aa45e2986534858d \
|
||||
--hash=sha256:74bc306c4b4df35b09bc8cecf806b271e1c5d708f7900145e4e54a2e5dedfed0 \
|
||||
--hash=sha256:7c1feba5a8c617922321aef945865334e468337b8fc5c73074f5e63143013b5a \
|
||||
--hash=sha256:7c43a773dd1a481dbb2fe64576aa303d80f328cce0eae5e3e4894947c41d1da7 \
|
||||
--hash=sha256:8309ee4569dced5e81df5aa2dcd48c7340c8dee603a66430f042dfbd2878edca \
|
||||
--hash=sha256:8db9034cde3bcdafc66980f0130813f5c5d19e74b3f2a19fb3cfbc25ad113121 \
|
||||
--hash=sha256:8ea05b499278790a8fa0ff9854ab0de2542aca02d661ddff94e830df971ff640 \
|
||||
--hash=sha256:90ed9da805e52985b0202aed4f352842c907c6b4fc6c7c109c6e646c32e2f43b \
|
||||
--hash=sha256:94503b79f7da0b65c80d0dbb2f81dd78b300319ec2435d5e6dcf9622160bc2fa \
|
||||
--hash=sha256:9742580ecc8e1ac45164e98d32ca6df90da509c2d3ff26be245d94c430f92db4 \
|
||||
--hash=sha256:9ca24062cd9b2270e4d77576042e9cc2b1d543f09da5aba1f1a3d016cea28390 \
|
||||
--hash=sha256:a9387ab615f929e71ef0f4a8a51e986fa06236ccfa9f3ec98a88f60fbf230634 \
|
||||
--hash=sha256:ac230e3643d1c436a2dfb59ca84357dfc6862c9f372fc5dbd96bafecae581f9f \
|
||||
--hash=sha256:c3a9ccdfee4ae59cf9bf1822777e763bc097ed208f4901e21537fca1070e1391 \
|
||||
--hash=sha256:c5774272f754605059521ff037a86e680342e3847498b0aa86b0f3560c70963c \
|
||||
--hash=sha256:c6464150e25b68ae3cb04c4e57496ea11ebfaae4d98126aea2f4702dd43e3c12 \
|
||||
--hash=sha256:c749770da0947bc972e512f35366dd4950c0e34afad89e60b9787a37e97cb443 \
|
||||
--hash=sha256:cabb2a554d9a0a6bf84037d86ca91782f087dfff2a61298d0b00c19c0bc43f6d \
|
||||
--hash=sha256:d391b70c9cc23f6e1142729772a011f364199d2c5ddc0d596f5f43316fbf982d \
|
||||
--hash=sha256:d45acedcaa58619355f18e0f42af542fcad3fd84ace4b8355d3a5dea23318578 \
|
||||
--hash=sha256:d79b0093f0fbf7a962d6a46ae292dc056c65d16a8ee9361f3cfbafd4c197ab14 \
|
||||
--hash=sha256:d88f32ff8c47cb7f4e7e7a9d1747dcee6f3baa19ed9afa9e5694fd2fb32b61ed \
|
||||
--hash=sha256:d8c899a540f6c7585cee53cddc929dd4d2db90fd828e37f5d4017b63acbc1a5d \
|
||||
--hash=sha256:de9173f8cc0efd88ac2a89b3b6c287a9a0011cdc2f53b2a12c28d6fd55f9f81c \
|
||||
--hash=sha256:df65174c7cf6b05ea273ce955927d3270b3a6e27b0b12762b009ce6082b8d3fc \
|
||||
--hash=sha256:e22bf6b54df994aff37ab52695d635f1ef73155e781eee1f5fa75bc08b58c8da \
|
||||
--hash=sha256:e750afe74e6c17b2c7046d2c3e3173b5a3f6080084671c8aa327215323df155b \
|
||||
--hash=sha256:ea4fe6b4ead3bbbe27244ea224fcd1f53cb119afc38b71a2f3ce570149a03e30 \
|
||||
--hash=sha256:f26f113013c4dcfbfe9ced57b5bad2035dda1a7349f64bf726021968f9bccad3 \
|
||||
--hash=sha256:f3f601f32244a677c7b029ec39412db2772ad04a28bc2cbb4b1f0931ed0ffad7 \
|
||||
--hash=sha256:fc5a189e89cbfff174588665bb18d28d2d0428366cc9dae5864afcaa2e57380b
|
||||
psycopg-binary==3.3.3 \
|
||||
--hash=sha256:05f32239aec25c5fb15f7948cffdc2dc0dac098e48b80a140e4ba32b572a2e7d \
|
||||
--hash=sha256:07c7211f9327d522c9c47560cae00a4ecf6687f4e02d779d035dd3177b41cb12 \
|
||||
--hash=sha256:0dde92cfde09293fb63b3f547919ba7d73bd2654573c03502b3263dd0218e44e \
|
||||
--hash=sha256:111c59897a452196116db12e7f608da472fbff000693a21040e35fc978b23430 \
|
||||
--hash=sha256:162e5675efb4704192411eaf8e00d07f7960b679cd3306e7efb120bb8d9456cc \
|
||||
--hash=sha256:165f22ab5a9513a3d7425ffb7fcc7955ed8ccaeef6d37e369d6cc1dff1582383 \
|
||||
--hash=sha256:17bb6600e2455993946385249a3c3d0af52cd70c1c1cdbf712e9d696d0b0bf1b \
|
||||
--hash=sha256:19f93235ece6dbfc4036b5e4f6d8b13f0b8f2b3eeb8b0bd2936d406991bcdd40 \
|
||||
--hash=sha256:1bef235a50a80f6aba05147002bc354559657cb6386dbd04d8e1c97d1d7cbe84 \
|
||||
--hash=sha256:258d1ea53464d29768bf25930f43291949f4c7becc706f6e220c515a63a24edd \
|
||||
--hash=sha256:263a24f39f26e19ed7fc982d7859a36f17841b05bebad3eb47bb9cd2dd785351 \
|
||||
--hash=sha256:329ff393441e75f10b673ae99ab45276887993d49e65f141da20d915c05aafd8 \
|
||||
--hash=sha256:42961609ac07c232a427da7c87a468d3c82fee6762c220f38e37cfdacb2b178d \
|
||||
--hash=sha256:47f06fcbe8542b4d96d7392c476a74ada521c5aebdb41c3c0155f6595fc14c8d \
|
||||
--hash=sha256:48e500cf1c0984dacf1f28ea482c3cdbb4c2288d51c336c04bc64198ab21fc51 \
|
||||
--hash=sha256:497852c5eaf1f0c2d88ab74a64a8097c099deac0c71de1cbcf18659a8a04a4b2 \
|
||||
--hash=sha256:4d4606c84d04b80f9138d72f1e28c6c02dc5ae0c7b8f3f8aaf89c681ce1cd1b1 \
|
||||
--hash=sha256:5152d50798c2fa5bd9b68ec68eb68a1b71b95126c1d70adaa1a08cd5eefdc23d \
|
||||
--hash=sha256:533efe6dc3a7cba5e2a84e38970786bb966306863e45f3db152007e9f48638a6 \
|
||||
--hash=sha256:56c767007ca959ca32f796b42379fc7e1ae2ed085d29f20b05b3fc394f3715cc \
|
||||
--hash=sha256:5958dbf28b77ce2033482f6cb9ef04d43f5d8f4b7636e6963d5626f000efb23e \
|
||||
--hash=sha256:59aa31fe11a0e1d1bcc2ce37ed35fe2ac84cd65bb9036d049b1a1c39064d0f14 \
|
||||
--hash=sha256:642050398583d61c9856210568eb09a8e4f2fe8224bf3be21b67a370e677eead \
|
||||
--hash=sha256:6698dbab5bcef8fdb570fc9d35fd9ac52041771bfcfe6fd0fc5f5c4e36f1e99d \
|
||||
--hash=sha256:73eaaf4bb04709f545606c1db2f65f4000e8a04cdbf3e00d165a23004692093e \
|
||||
--hash=sha256:74eae563166ebf74e8d950ff359be037b85723d99ca83f57d9b244a871d6c13b \
|
||||
--hash=sha256:78c9ce98caaf82ac8484d269791c1b403d7598633e0e4e2fa1097baae244e2f1 \
|
||||
--hash=sha256:7c84f9d214f2d1de2fafebc17fa68ac3f6561a59e291553dfc45ad299f4898c1 \
|
||||
--hash=sha256:883d68d48ca9ff3cb3d10c5fdebea02c79b48eecacdddbf7cce6e7cdbdc216b8 \
|
||||
--hash=sha256:8e7e9eca9b363dbedeceeadd8be97149d2499081f3c52d141d7cd1f395a91f83 \
|
||||
--hash=sha256:90eecd93073922f085967f3ed3a98ba8c325cbbc8c1a204e300282abd2369e13 \
|
||||
--hash=sha256:97c839717bf8c8df3f6d983a20949c4fb22e2a34ee172e3e427ede363feda27b \
|
||||
--hash=sha256:9d6a1e56dd267848edb824dbeb08cf5bac649e02ee0b03ba883ba3f4f0bd54f2 \
|
||||
--hash=sha256:9f7d0cf072c6fbac3795b08c98ef9ea013f11db609659dcfc6b1f6cc31f9e181 \
|
||||
--hash=sha256:a39f34c9b18e8f6794cca17bfbcd64572ca2482318db644268049f8c738f35a6 \
|
||||
--hash=sha256:a4aab31bd6d1057f287c96c0effca3a25584eb9cc702f282ecb96ded7814e830 \
|
||||
--hash=sha256:a6af77b6626ce92b5817bf294b4d45ec1a6161dba80fc2d82cdffdd6814fd023 \
|
||||
--hash=sha256:a89bb9ee11177b2995d87186b1d9fa892d8ea725e85eab28c6525e4cc14ee048 \
|
||||
--hash=sha256:ae07a3114313dd91fce686cab2f4c44af094398519af0e0f854bc707e1aeedf1 \
|
||||
--hash=sha256:b27d3a23c79fa59557d2cc63a7e8bb4c7e022c018558eda36f9d7c4e6b99a6e0 \
|
||||
--hash=sha256:b3385b58b2fe408a13d084c14b8dcf468cd36cbbe774408250facc128f9fa75c \
|
||||
--hash=sha256:b62cf8784eb6d35beaee1056d54caf94ec6ecf2b7552395e305518ab61eb8fd2 \
|
||||
--hash=sha256:cab7bc3d288d37a80aa8c0820033250c95e40b1c2b5c57cf59827b19c2a8b69d \
|
||||
--hash=sha256:cb85b1d5702877c16f28d7b92ba030c1f49ebcc9b87d03d8c10bf45a2f1c7508 \
|
||||
--hash=sha256:d257c58d7b36a621dcce1d01476ad8b60f12d80eb1406aee4cf796f88b2ae482 \
|
||||
--hash=sha256:d593612758d0041cb13cb0003f7f8d3fabb7ad9319e651e78afae49b1cf5860e \
|
||||
--hash=sha256:da2f331a01af232259a21573a01338530c6016dcfad74626c01330535bcd8628 \
|
||||
--hash=sha256:dac7ee2f88b4d7bb12837989ca354c38d400eeb21bce3b73dac02622f0a3c8d6 \
|
||||
--hash=sha256:e77957d2ba17cada11be09a5066d93026cdb61ada7c8893101d7fe1c6e1f3925 \
|
||||
--hash=sha256:e7800e6c6b5dc4b0ca7cc7370f770f53ac83886b76afda0848065a674231e856 \
|
||||
--hash=sha256:e7b607f0e14f2a4cf7e78a05ebd13df6144acfba87cb90842e70d3f125d9f53f \
|
||||
--hash=sha256:eb072949b8ebf4082ae24289a2b0fd724da9adc8f22743409d6fd718ddb379df \
|
||||
--hash=sha256:eb36a08859b9432d94ea6b26ec41a2f98f83f14868c91321d0c1e11f672eeae7 \
|
||||
--hash=sha256:f24e8e17035200a465c178e9ea945527ad0738118694184c450f1192a452ff25 \
|
||||
--hash=sha256:fab6b5e37715885c69f5d091f6ff229be71e235f272ebaa35158d5a46fd548a0
|
||||
# via psycopg
|
||||
psycopg-pool==3.3.0 \
|
||||
--hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \
|
||||
--hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5
|
||||
# via psycopg
|
||||
pyasn1==0.6.2 \
|
||||
--hash=sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf \
|
||||
--hash=sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b
|
||||
pyasn1==0.6.3 \
|
||||
--hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \
|
||||
--hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# python-ldap
|
||||
@@ -219,9 +218,9 @@ pyyaml==6.0.3 \
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/container/requirements.in
|
||||
setuptools==82.0.0 \
|
||||
--hash=sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb \
|
||||
--hash=sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0
|
||||
setuptools==82.0.1 \
|
||||
--hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \
|
||||
--hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/container/requirements.in
|
||||
@@ -237,28 +236,28 @@ typing-extensions==4.15.0 \
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# psycopg-pool
|
||||
uv==0.9.22 \
|
||||
--hash=sha256:012bdc5285a9cdb091ac514b7eb8a707e3b649af5355fe4afb4920bfe1958c00 \
|
||||
--hash=sha256:0cdc653fb601aa7f273242823fa93024f5fd319c66cdf22f36d784858493564c \
|
||||
--hash=sha256:0d8f007616cac5962620252b56a1d8224e9b2de566e78558efe04cc18526d507 \
|
||||
--hash=sha256:1f45e1e0f26dd47fa01eb421c54cfd39de10fd52ac0a9d7ae45b92fce7d92b0b \
|
||||
--hash=sha256:1f979c9d313b4616d9865859ef520bea5df0d4f15c57214589f5676fafa440c1 \
|
||||
--hash=sha256:2a4155cf7d0231d0adae94257ee10d70c57c2f592207536ddd55d924590a8c15 \
|
||||
--hash=sha256:3422b093b8e6e8de31261133b420c34dbef81f3fd1d82f787ac771b00b54adf8 \
|
||||
--hash=sha256:369b55341c6236f42d8fc335876308e5c57c921850975b3019cc9f7ebbe31567 \
|
||||
--hash=sha256:3b2bcce464186f8fafa3bf2aa5d82db4e3229366345399cc3f5bcafd616b8fe0 \
|
||||
--hash=sha256:41c73a4938818ede30e601cd0be87953e5c6a83dc4762e04e626f2eb9b240ebe \
|
||||
--hash=sha256:59c4f6b3659a68c26c50865432a7134386f607432160aad51e2247f862902697 \
|
||||
--hash=sha256:77ec4c101d41d7738226466191a7d62f9fa4de06ea580e0801da2f5cd5fa08aa \
|
||||
--hash=sha256:8f73043ade8ff6335e19fe1f4e7425d5e28aec9cafd72d13d5b40bb1cbb85690 \
|
||||
--hash=sha256:9c238525272506845fe07c0b9088c5e33fcd738e1f49ef49dc3c8112096d2e3a \
|
||||
--hash=sha256:b1985559b38663642658069e8d09fa6c30ed1c67654b7e5765240d9e4e9cdd57 \
|
||||
--hash=sha256:b78f2605d65c4925631d891dec99b677b05f50c774dedc6ef8968039a5bcfdb0 \
|
||||
--hash=sha256:b807bafe6b65fc1fe9c65ffd0d4228db894872de96e7200c44943f24beb68931 \
|
||||
--hash=sha256:d9d4be990bb92a68781f7c98d2321b528667b61d565c02ba978488c0210aa768 \
|
||||
--hash=sha256:e4b61a9c8b8dcbf64e642d2052342d36a46886b8bc3ccc407282962b970101af
|
||||
uv==0.11.7 \
|
||||
--hash=sha256:0df59ab0c6a4b14a763e8445e1c303af9abeb53cdfa4428daf9ff9642c0a3cce \
|
||||
--hash=sha256:162fa961a9a081dcea6e889c79f738a5ae56507047e4672964972e33c301bea9 \
|
||||
--hash=sha256:23d457d6731ebdb83f1bffebe4894edab2ef43c1ec5488433c74300db4958924 \
|
||||
--hash=sha256:46d971489b00bdb27e0aa715e4a5cd4ef2c28ea5b6ef78f2b67bf861eb44b405 \
|
||||
--hash=sha256:4e4d5e31bea86e1b6e0f5a0f95e14e80018e6f6c0129256d2915a4b3d793644d \
|
||||
--hash=sha256:553e67cc766d013ce24353fecd4ea5533d2aedcfd35f9fac430e07b1d1f23ed4 \
|
||||
--hash=sha256:5674dfb5944513f4b3735b05c2deba6b1b01151f46729d533d413a9a905f8c5d \
|
||||
--hash=sha256:5985a15a92bd9a170fc1947abb1fbc3e9828c5a430ad85b5bed8356c20b67a71 \
|
||||
--hash=sha256:6158b7e39464f1aa1e040daa0186cae4749a78b5cd80ac769f32ca711b8976b1 \
|
||||
--hash=sha256:750ee5b96959b807cf442b73dd8b55111862d63f258f896787ea5f06b68aaca9 \
|
||||
--hash=sha256:7d6a17507b8139b8803f445a03fd097f732ce8356b1b7b13cdb4dd8ef7f4b2e0 \
|
||||
--hash=sha256:8b2fe1ec6775dad10183e3fdce430a5b37b7857d49763c884f3a67eaa8ca6f8a \
|
||||
--hash=sha256:ceae53b202ea92bc954759bc7c7570cdcd5c3512fce15701198c19fd2dfb8605 \
|
||||
--hash=sha256:dd48823ca4b505124389f49ae50626ba9f57212b9047738efc95126ed5f3844d \
|
||||
--hash=sha256:eb91f52ee67e10d5290f2c2897e2171357f1a10966de38d83eefa93d96843b0c \
|
||||
--hash=sha256:f394331f0507e80ee732cb3df737589de53bed999dd02a6d24682f08c2f8ac4f \
|
||||
--hash=sha256:f422d39530516b1dfb28bb6e90c32bb7dacd50f6a383cd6e40c1a859419fbc8c \
|
||||
--hash=sha256:f97e9f4e4d44fb5c4dfaa05e858ef3414a96416a2e4af270ecd88a3e5fb049a9 \
|
||||
--hash=sha256:fab0bb43fbbc0ee5b5fee212078d2300c371b725faff7cf72eeaafa0bff0606b
|
||||
# via -r contrib/container/requirements.in
|
||||
wheel==0.46.3 \
|
||||
--hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \
|
||||
--hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803
|
||||
wheel==0.47.0 \
|
||||
--hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \
|
||||
--hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3
|
||||
# via -r contrib/container/requirements.in
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Packages needed for CI/packages
|
||||
requests==2.32.5
|
||||
requests==2.33.1
|
||||
pyyaml==6.0.3
|
||||
jc==1.25.6
|
||||
|
||||
@@ -1,131 +1,147 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile contrib/dev_reqs/requirements.in -o contrib/dev_reqs/requirements.txt -c src/backend/requirements.txt
|
||||
certifi==2026.1.4 \
|
||||
--hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \
|
||||
--hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120
|
||||
certifi==2026.4.22 \
|
||||
--hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \
|
||||
--hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
charset-normalizer==3.4.4 \
|
||||
--hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \
|
||||
--hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \
|
||||
--hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \
|
||||
--hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \
|
||||
--hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \
|
||||
--hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \
|
||||
--hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \
|
||||
--hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \
|
||||
--hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \
|
||||
--hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \
|
||||
--hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \
|
||||
--hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \
|
||||
--hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \
|
||||
--hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \
|
||||
--hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \
|
||||
--hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \
|
||||
--hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \
|
||||
--hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \
|
||||
--hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \
|
||||
--hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \
|
||||
--hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \
|
||||
--hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \
|
||||
--hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \
|
||||
--hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \
|
||||
--hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \
|
||||
--hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \
|
||||
--hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \
|
||||
--hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \
|
||||
--hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \
|
||||
--hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \
|
||||
--hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \
|
||||
--hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \
|
||||
--hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \
|
||||
--hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \
|
||||
--hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \
|
||||
--hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \
|
||||
--hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \
|
||||
--hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \
|
||||
--hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \
|
||||
--hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \
|
||||
--hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \
|
||||
--hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \
|
||||
--hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \
|
||||
--hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \
|
||||
--hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \
|
||||
--hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \
|
||||
--hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \
|
||||
--hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \
|
||||
--hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \
|
||||
--hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \
|
||||
--hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \
|
||||
--hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \
|
||||
--hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \
|
||||
--hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \
|
||||
--hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \
|
||||
--hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \
|
||||
--hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \
|
||||
--hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \
|
||||
--hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \
|
||||
--hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \
|
||||
--hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \
|
||||
--hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \
|
||||
--hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \
|
||||
--hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \
|
||||
--hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \
|
||||
--hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \
|
||||
--hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \
|
||||
--hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \
|
||||
--hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \
|
||||
--hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \
|
||||
--hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \
|
||||
--hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \
|
||||
--hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \
|
||||
--hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \
|
||||
--hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \
|
||||
--hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \
|
||||
--hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \
|
||||
--hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \
|
||||
--hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \
|
||||
--hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \
|
||||
--hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \
|
||||
--hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \
|
||||
--hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \
|
||||
--hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \
|
||||
--hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \
|
||||
--hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \
|
||||
--hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \
|
||||
--hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \
|
||||
--hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \
|
||||
--hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \
|
||||
--hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \
|
||||
--hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \
|
||||
--hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \
|
||||
--hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \
|
||||
--hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \
|
||||
--hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \
|
||||
--hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \
|
||||
--hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \
|
||||
--hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \
|
||||
--hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \
|
||||
--hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \
|
||||
--hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \
|
||||
--hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \
|
||||
--hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \
|
||||
--hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \
|
||||
--hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \
|
||||
--hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \
|
||||
--hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \
|
||||
--hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \
|
||||
--hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \
|
||||
--hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \
|
||||
--hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \
|
||||
--hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608
|
||||
charset-normalizer==3.4.7 \
|
||||
--hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \
|
||||
--hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \
|
||||
--hash=sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67 \
|
||||
--hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \
|
||||
--hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \
|
||||
--hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \
|
||||
--hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \
|
||||
--hash=sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444 \
|
||||
--hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \
|
||||
--hash=sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9 \
|
||||
--hash=sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01 \
|
||||
--hash=sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217 \
|
||||
--hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \
|
||||
--hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \
|
||||
--hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \
|
||||
--hash=sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83 \
|
||||
--hash=sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5 \
|
||||
--hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \
|
||||
--hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \
|
||||
--hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \
|
||||
--hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \
|
||||
--hash=sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42 \
|
||||
--hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \
|
||||
--hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \
|
||||
--hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \
|
||||
--hash=sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207 \
|
||||
--hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \
|
||||
--hash=sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734 \
|
||||
--hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \
|
||||
--hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \
|
||||
--hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \
|
||||
--hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \
|
||||
--hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \
|
||||
--hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \
|
||||
--hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \
|
||||
--hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \
|
||||
--hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \
|
||||
--hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \
|
||||
--hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \
|
||||
--hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \
|
||||
--hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \
|
||||
--hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \
|
||||
--hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \
|
||||
--hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \
|
||||
--hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \
|
||||
--hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \
|
||||
--hash=sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776 \
|
||||
--hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \
|
||||
--hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \
|
||||
--hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \
|
||||
--hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \
|
||||
--hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \
|
||||
--hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \
|
||||
--hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \
|
||||
--hash=sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5 \
|
||||
--hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \
|
||||
--hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \
|
||||
--hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \
|
||||
--hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \
|
||||
--hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \
|
||||
--hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \
|
||||
--hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \
|
||||
--hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \
|
||||
--hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \
|
||||
--hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \
|
||||
--hash=sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4 \
|
||||
--hash=sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545 \
|
||||
--hash=sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706 \
|
||||
--hash=sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366 \
|
||||
--hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \
|
||||
--hash=sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a \
|
||||
--hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \
|
||||
--hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \
|
||||
--hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \
|
||||
--hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \
|
||||
--hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \
|
||||
--hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \
|
||||
--hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \
|
||||
--hash=sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319 \
|
||||
--hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \
|
||||
--hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \
|
||||
--hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \
|
||||
--hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \
|
||||
--hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \
|
||||
--hash=sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0 \
|
||||
--hash=sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686 \
|
||||
--hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \
|
||||
--hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \
|
||||
--hash=sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c \
|
||||
--hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \
|
||||
--hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \
|
||||
--hash=sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60 \
|
||||
--hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \
|
||||
--hash=sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274 \
|
||||
--hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \
|
||||
--hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \
|
||||
--hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \
|
||||
--hash=sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f \
|
||||
--hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \
|
||||
--hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \
|
||||
--hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \
|
||||
--hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \
|
||||
--hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \
|
||||
--hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \
|
||||
--hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \
|
||||
--hash=sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00 \
|
||||
--hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \
|
||||
--hash=sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3 \
|
||||
--hash=sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7 \
|
||||
--hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \
|
||||
--hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \
|
||||
--hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \
|
||||
--hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \
|
||||
--hash=sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259 \
|
||||
--hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \
|
||||
--hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \
|
||||
--hash=sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30 \
|
||||
--hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \
|
||||
--hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \
|
||||
--hash=sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24 \
|
||||
--hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \
|
||||
--hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \
|
||||
--hash=sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc \
|
||||
--hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \
|
||||
--hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \
|
||||
--hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \
|
||||
--hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \
|
||||
--hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \
|
||||
--hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
idna==3.11 \
|
||||
--hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \
|
||||
--hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902
|
||||
idna==3.15 \
|
||||
--hash=sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8 \
|
||||
--hash=sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
@@ -133,9 +149,9 @@ jc==1.25.6 \
|
||||
--hash=sha256:27f58befc7ae0a4c63322926c5f1ec892e3eac4a065eff3b07cfe420a6924a07 \
|
||||
--hash=sha256:7367b59e6e0da8babeede1e5b0da083f3c5aa6b6e585b4aed28dd7c4b2d76162
|
||||
# via -r contrib/dev_reqs/requirements.in
|
||||
pygments==2.19.2 \
|
||||
--hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \
|
||||
--hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b
|
||||
pygments==2.20.0 \
|
||||
--hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
|
||||
--hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
|
||||
# via jc
|
||||
pyyaml==6.0.3 \
|
||||
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
|
||||
@@ -214,9 +230,9 @@ pyyaml==6.0.3 \
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/dev_reqs/requirements.in
|
||||
requests==2.32.5 \
|
||||
--hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \
|
||||
--hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf
|
||||
requests==2.33.1 \
|
||||
--hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \
|
||||
--hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# -r contrib/dev_reqs/requirements.in
|
||||
@@ -224,13 +240,13 @@ ruamel-yaml==0.19.1 \
|
||||
--hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93 \
|
||||
--hash=sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993
|
||||
# via jc
|
||||
urllib3==2.6.3 \
|
||||
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
|
||||
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
|
||||
urllib3==2.7.0 \
|
||||
--hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \
|
||||
--hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
xmltodict==1.0.2 \
|
||||
--hash=sha256:54306780b7c2175a3967cad1db92f218207e5bc1aba697d887807c0fb68b7649 \
|
||||
--hash=sha256:62d0fddb0dcbc9f642745d8bbf4d81fd17d6dfaec5a15b5c1876300aad92af0d
|
||||
xmltodict==1.0.4 \
|
||||
--hash=sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61 \
|
||||
--hash=sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a
|
||||
# via jc
|
||||
|
||||
@@ -54,7 +54,7 @@ Each user is assigned an authentication token which can be used to access the AP
|
||||
|
||||
If a user does not know their access token, it can be requested via the API interface itself, using a basic authentication request.
|
||||
|
||||
To obtain a valid token, perform a GET request to `/api/user/token/`. No data are required, but a valid username / password combination must be supplied in the authentication headers.
|
||||
To obtain a valid token, perform a GET request to `/api/user/me/token/`. No data are required, but a valid username / password combination must be supplied in the authentication headers.
|
||||
|
||||
!!! info "Credentials"
|
||||
Ensure that a valid username:password combination are supplied as basic authorization headers.
|
||||
@@ -146,7 +146,7 @@ r:delete:stock
|
||||
Users can only perform REST API actions which align with their assigned [role permissions](../settings/permissions.md#roles).
|
||||
Once a user has *authenticated* via the API, a list of the available roles can be retrieved from:
|
||||
|
||||
`/api/user/roles/`
|
||||
`/api/user/me/roles/`
|
||||
|
||||
For example, when accessing the API from a *superuser* account:
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ The API schema as documented below is generated using the [drf-spectactular](htt
|
||||
|
||||
## API Version
|
||||
|
||||
This documentation is for API version: `449`
|
||||
This documentation is for API version: `459`
|
||||
|
||||
!!! tip "API Schema History"
|
||||
We track API schema changes, and provide a snapshot of each API schema version in the [API schema repository](https://github.com/inventree/schema/).
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Build Orders
|
||||
---
|
||||
|
||||
## Build Order List
|
||||
|
||||
The build order list display lists all build orders:
|
||||
|
||||
{{ image("app/build_list.png", "Build order list") }}
|
||||
|
||||
Select an individual build order to display the detail view for that order.
|
||||
|
||||
### Filtering
|
||||
|
||||
Displayed build orders can be subsequently filtered using the search input at the top of the screen
|
||||
|
||||
## Build Order Detail
|
||||
|
||||
{{ image("app/build_detail.png", "Build order detail") }}
|
||||
|
||||
### Edit Build Order Details
|
||||
|
||||
From the detail view, select the *Edit* button in the top-right of the screen. This opens the build order editing display.
|
||||
|
||||
### Required Parts Tab
|
||||
|
||||
The *Required Parts* tab shows the parts required to complete this build order:
|
||||
|
||||
{{ image("app/build_required_parts.png", "Build order required parts") }}
|
||||
|
||||
### Build Outputs Tab
|
||||
|
||||
The *Build Outputs* tab shows the stock items which have been produced as part of this build order:
|
||||
|
||||
{{ image("app/build_outputs.png", "Build order outputs") }}
|
||||
@@ -30,7 +30,7 @@ The *Line Items* tab shows the line items associated with this purchase order:
|
||||
|
||||
{{ image("app/po_lines.png", "Purchase order line items") }}
|
||||
|
||||
Long press on a particular line item to receive the item into stock.
|
||||
Select a particular line item to view the details for that line, and receive the line item into stock.
|
||||
|
||||
### Stock Items
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ The *Part Settings* view allows you to configure various options governing what
|
||||
|
||||
| Option | Description |
|
||||
| --- | --- |
|
||||
| Parameters | Enable display of part parameters in the part detail view |
|
||||
| Parameters | Enable display of parameters in the part detail view |
|
||||
| BOM | Enable bill of materials display in the part detail view |
|
||||
| Stock History | Enable display of stock history in the stock detail view |
|
||||
| Test Results | Enable display of test results in the stock detail view |
|
||||
|
||||
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 266 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 56 KiB |
@@ -16,3 +16,36 @@ Parameters can be associated with various InvenTree models.
|
||||
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") }}
|
||||
|
||||
## Attachments Types
|
||||
|
||||
The following types of attachments are supported:
|
||||
|
||||
### File Attachments
|
||||
|
||||
File attachments allow users to upload files directly to InvenTree. These files are stored on the server and can be downloaded or viewed by users with appropriate permissions.
|
||||
|
||||
### Image Thumbnails
|
||||
|
||||
When a file attachment is uploaded, InvenTree automatically determines whether the file is a valid image. If it is, a thumbnail is generated and stored alongside the attachment.
|
||||
|
||||
- The thumbnail is created with a reduced image size, while preserving the original aspect ratio.
|
||||
- Thumbnail generation is performed in the background after upload.
|
||||
- The `is_image` flag on the attachment record is set to `True` for valid images, and `False` for all other file types.
|
||||
- If the uploaded file has an image extension but contains invalid or corrupt image data, no thumbnail is generated and `is_image` remains `False`.
|
||||
- Link attachments (external URLs) are never assigned a thumbnail.
|
||||
|
||||
!!! info "Supported Formats"
|
||||
Any image format recognised by the [Pillow](https://pillow.readthedocs.io/) library (e.g. PNG, JPEG, GIF, BMP, WEBP) will be treated as a valid image and have a thumbnail generated automatically.
|
||||
|
||||
### Link Attachments
|
||||
|
||||
Link attachments allow users to associate external URLs with an object. This can be useful for linking to external documentation, resources, or other relevant web content.
|
||||
|
||||
## Adding Attachments
|
||||
|
||||
To add an attachment to an object, navigate to the object's detail page and click on the "Attachments" tab. From there, you can click the "Add attachment" button to upload a file or the "Add external link" button to add a link.
|
||||
|
||||
### Renaming Attachments
|
||||
|
||||
Once a file attachment has been uploaded, it can be renamed by clicking the "Edit" action associated with the attachment. This allows you to change the filename without needing to re-upload the file. The system will handle renaming the file on the server and updating the database record accordingly.
|
||||
|
||||
@@ -4,16 +4,70 @@ title: Custom States
|
||||
|
||||
## Custom States
|
||||
|
||||
Several models within InvenTree support the use of custom states. The custom states are display only - the business logic is not affected by the state.
|
||||
Several models within InvenTree support the use of *custom states*. Custom states extend the built-in status system by adding extra labels and colours that are displayed in the user interface.
|
||||
|
||||
States can be added in the [Admin Center](../settings/admin.md#admin-center) under the "Custom States" section. Each state has a name, label and a color that are used to display the state in the user interface. Changes to these settings will only be reflected in the user interface after a full reload of the interface.
|
||||
!!! info "Display Only"
|
||||
Custom states affect display only — they do not add new workflow steps or change business logic. Every custom state is mapped to an existing built-in state (its *logical key*), and the system uses that built-in state for all decisions such as availability counts, order transitions, and filtering.
|
||||
|
||||
States need to be assigned to a model, state (for example status on a StockItem) and a logical key - that will be used for business logic. These 3 values combined need to be unique throughout the system.
|
||||
### Example
|
||||
|
||||
Custom states can be defined for the following models:
|
||||
Suppose you want to track stock items that are physically present and available, but are waiting for a quality inspection before use. The built-in `OK` status is the closest match — the item is available — but you want it to appear distinctly in the interface.
|
||||
|
||||
- [Stock Item](../stock/index.md)
|
||||
- [Build Order](../manufacturing/build.md)
|
||||
- [Purchase Order](../purchasing/purchase_order.md)
|
||||
- [Sales Order](../sales/sales_order.md)
|
||||
- [Return Order](../sales/return_order.md)
|
||||
You would create a custom state:
|
||||
|
||||
- **Logical key**: `OK` — the system treats the item as available stock
|
||||
- **Label**: `Awaiting Inspection` — shown in the interface instead of "OK"
|
||||
- **Colour**: `warning` — displayed in amber to draw attention
|
||||
|
||||
The item is counted as available stock in all reports and filters, but is visually distinguished from items with a standard `OK` status.
|
||||
|
||||
## Managing Custom States
|
||||
|
||||
Custom states are managed in the [Admin Center](../settings/admin.md#admin-center) under the *Custom States* section.
|
||||
|
||||
!!! warning "Page Reload Required"
|
||||
Changes to custom states are only reflected in the user interface after a full page reload.
|
||||
|
||||
## State Fields
|
||||
|
||||
When creating a custom state, the following fields must be provided:
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **Model** | The model type this state applies to (e.g. *Stock Item*, *Build Order*) |
|
||||
| **Reference Status** | The status class being extended (e.g. `StockStatus`, `BuildStatus`) |
|
||||
| **Logical Key** | The built-in status value this custom state maps to for business logic |
|
||||
| **Key** | A unique integer that identifies this custom state in the database |
|
||||
| **Name** | An uppercase Python identifier for this state (e.g. `AWAITING_INSPECTION`) |
|
||||
| **Label** | The human-readable text displayed in the interface |
|
||||
| **Colour** | The badge colour used to display the state |
|
||||
|
||||
### Key
|
||||
|
||||
The *Key* is the integer value stored in the database when this custom state is active. It must satisfy all of the following:
|
||||
|
||||
- Must be a positive integer
|
||||
- Must not be equal to the *Logical Key*
|
||||
- Must not conflict with any existing built-in status values for the selected model
|
||||
|
||||
### Name
|
||||
|
||||
The *Name* field is used internally to identify the state. It must:
|
||||
|
||||
- Be uppercase (e.g. `AWAITING_INSPECTION`, not `awaiting_inspection`)
|
||||
- Be a valid Python identifier (letters, digits, underscores; no spaces or hyphens)
|
||||
- Not conflict with any existing status names for the selected model
|
||||
|
||||
### Colours
|
||||
|
||||
The following colour values are available:
|
||||
|
||||
| Colour | Appearance |
|
||||
|--------|------------|
|
||||
| `primary` | Blue |
|
||||
| `secondary` | Grey |
|
||||
| `success` | Green |
|
||||
| `warning` | Amber |
|
||||
| `danger` | Red |
|
||||
| `info` | Cyan |
|
||||
| `dark` | Dark grey / black |
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Exporting Data
|
||||
---
|
||||
|
||||
## Exporting Data
|
||||
|
||||
InvenTree provides data export functionality for a variety of data types. Most data tables in the web interface include a *Download* button in the table toolbar, which allows the currently displayed data to be exported to a file.
|
||||
|
||||
!!! info "Filtered Data"
|
||||
The export reflects the data currently visible in the table — any active filters, search terms, or sort order are carried through to the exported file. To export the full dataset, clear all filters before exporting.
|
||||
|
||||
!!! info "Paginated Data"
|
||||
In the user interface, data tables are paginated to improve performance. When exporting data, the export will include **all** records that match the current filters and search terms, not just the records visible on the current page.
|
||||
|
||||
## How to Export
|
||||
|
||||
**Step 1** — In any table view, click the {{ icon("cloud-download") }} *Download* button in the table toolbar:
|
||||
|
||||
{{ image("admin/export.png", "Download button") }}
|
||||
|
||||
**Step 2** — An export dialog is displayed. Select the desired *Export Format* and *Export Plugin*, then click *Export*:
|
||||
|
||||
{{ image("admin/export_options.png", "Export dialog") }}
|
||||
|
||||
**Step 3** — The export runs in the background. A loading indicator is shown while the export is being processed. When the export is complete, the file is automatically downloaded to your browser.
|
||||
|
||||
## Supported File Formats
|
||||
|
||||
The following file formats are available for export:
|
||||
|
||||
| Format | Description |
|
||||
|--------|-------------|
|
||||
| CSV | Comma-separated values. Portable plain-text format, compatible with most tools. |
|
||||
| Excel | Microsoft Excel format (`.xlsx`). Suitable for direct use in spreadsheet applications. |
|
||||
| TSV | Tab-separated values. Similar to CSV but uses tab characters as delimiters. |
|
||||
|
||||
## Export Plugins
|
||||
|
||||
InvenTree uses a plugin-based export system. The export dialog lists all plugins that are available for the data type being exported. Selecting a different plugin may provide additional export options or a different output format.
|
||||
|
||||
### Built-in Exporters
|
||||
|
||||
InvenTree includes the following built-in export plugins:
|
||||
|
||||
| Plugin | Description |
|
||||
|--------|-------------|
|
||||
| [InvenTree Exporter](../plugins/builtin/inventree_exporter.md) | General-purpose exporter for any tabulated dataset. Always enabled. |
|
||||
| [BOM Exporter](../plugins/builtin/bom_exporter.md) | Custom exporter for Bill of Materials data, with additional BOM-specific options. |
|
||||
| [Parameter Exporter](../plugins/builtin/parameter_exporter.md) | Exports part data including all associated custom parameter values as additional columns. |
|
||||
| [Stocktake Exporter](../plugins/builtin/stocktake_exporter.md) | Exports a comprehensive stock-level summary for parts, with optional pricing and variant data. |
|
||||
|
||||
Custom export plugins can also be developed using the [DataExportMixin](../plugins/mixins/export.md).
|
||||
|
||||
## API Export
|
||||
|
||||
Data can also be exported programmatically via the InvenTree REST API. To trigger an export, perform a `GET` request against any list endpoint with the following query parameters:
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `export` | Set to `true` to trigger an export |
|
||||
| `export_format` | File format: `csv`, `xlsx`, or `tsv` (default: `csv`) |
|
||||
| `export_plugin` | Slug of the export plugin to use (default: `inventree-exporter`) |
|
||||
|
||||
Additional `export_*` parameters may be accepted depending on the plugin selected.
|
||||
|
||||
**Example:**
|
||||
|
||||
```
|
||||
GET /api/part/?export=true&export_format=xlsx&export_plugin=inventree-exporter
|
||||
```
|
||||
|
||||
Refer to the [API documentation](../api/index.md) for further details.
|
||||
@@ -43,7 +43,7 @@ Importing data is a multi-step process, which is managed via an *import session*
|
||||
|
||||
The import session is managed by the InvenTree server, and all import session data is stored on the server. As the import process can be time-consuming, the user can navigate away from the import page and return later to check on the progress of the import.
|
||||
|
||||
Import sessions can be managed from the [Admin Center](./admin.md#admin-center) page, which lists all available import sessions
|
||||
Import sessions can be managed from the [Admin Center](../settings/admin.md#admin-center) page, which lists all available import sessions
|
||||
|
||||
### Context Sensitive Importing
|
||||
|
||||
@@ -55,9 +55,9 @@ An import session can be initiated from a number of different contexts within th
|
||||
|
||||
### Admin Center
|
||||
|
||||
Staff users can create an import session from within the [Admin Center](./admin.md#admin-center). This is a general-purpose import session, and the user will be required to select the type of data to import.
|
||||
Staff users can create an import session from within the [Admin Center](../settings/admin.md#admin-center). This is a general-purpose import session, and the user will be required to select the type of data to import.
|
||||
|
||||
Users can quickly navigate to the data import managemement page from the [spotlight search](../concepts/user_interface.md#spotlight), by searching for "import" and selecting the "Import data" option.
|
||||
Users can quickly navigate to the data import management page from the [spotlight search](../concepts/user_interface.md#spotlight), by searching for "import" and selecting the "Import data" option.
|
||||
|
||||
### Data Tables
|
||||
|
||||
@@ -123,7 +123,7 @@ The basic outline of this process is:
|
||||
### Create Import Session
|
||||
|
||||
!!! note "Admin Center"
|
||||
Updating existing records can only be performed when creating a new import session from the [Admin Center](./admin.md#admin-center).
|
||||
Updating existing records can only be performed when creating a new import session from the [Admin Center](../settings/admin.md#admin-center).
|
||||
|
||||
Create a new import session, and ensure that the *Update Existing Records* option is selected. This will allow the import session to update existing records in the database.
|
||||
|
||||
@@ -15,7 +15,7 @@ Parameters can be associated with various InvenTree models.
|
||||
|
||||
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") }}
|
||||
{{ image("concepts/parameter-tab.png", "Parameters Example") }}
|
||||
|
||||
## Parameter Templates
|
||||
|
||||
@@ -40,9 +40,9 @@ Parameter templates are created and edited via the [admin interface](../settings
|
||||
To create a template:
|
||||
|
||||
- Navigate to the "Settings" page
|
||||
- Click on the "Part Parameters" tab
|
||||
- Click on the "Parameters" tab
|
||||
- Click on the "New Parameter" button
|
||||
- Fill out the `Create Part Parameter Template` form: `Name` (required) and `Units` (optional) fields
|
||||
- Fill out the `Create Parameter Template` form: `Name` (required) and `Units` (optional) fields
|
||||
- Click on the "Submit" button.
|
||||
|
||||
An existing template can be edited by clicking on the "Edit" button associated with that template:
|
||||
@@ -53,9 +53,9 @@ An existing template can be edited by clicking on the "Edit" button associated w
|
||||
|
||||
After [creating a template](#create-template) or using the existing templates, you can add parameters to any part.
|
||||
|
||||
To add a parameter, navigate to a specific part detail page, click on the "Parameters" tab then click on the "New Parameters" button, the `Create Part Parameter` form will be displayed:
|
||||
To add a parameter, navigate to a specific part detail page, click on the "Parameters" tab then click on the "New Parameters" button, the `Create Parameter` form will be displayed:
|
||||
|
||||
{{ image("part/create_part_parameter.png", "Create Part Parameter Form") }}
|
||||
{{ image("part/create_part_parameter.png", "Create Parameter Form") }}
|
||||
|
||||
Select the parameter `Template` you would like to use for this parameter, fill-out the `Data` field (value of this specific parameter) and click the "Submit" button.
|
||||
|
||||
@@ -132,7 +132,7 @@ The in-built conversion functionality means that parameter values can be input i
|
||||
|
||||
### Incompatible Units
|
||||
|
||||
If a part parameter is created with a value which is incompatible with the units specified for the template, it will be rejected:
|
||||
If a parameter is created with a value which is incompatible with the units specified for the template, it will be rejected:
|
||||
|
||||
{{ image("part/part_invalid_units.png", "Invalid Parameter Units") }}
|
||||
|
||||
@@ -151,4 +151,4 @@ Selection Lists can be used to add a large number of predefined values to a para
|
||||
It is possible that plugins lock selection lists to ensure a known state.
|
||||
|
||||
|
||||
Administration of lists can be done through the Part Parameter section in the [Admin Center](../settings/admin.md#admin-center) or via the API.
|
||||
Administration of lists can be done through the `Parameter` section in the [Admin Center](../settings/admin.md#admin-center) or via the API.
|
||||
|
||||
@@ -16,7 +16,7 @@ Deploying InvenTree to production requires to knowledge of the security assumpti
|
||||
|
||||
2. All users are trusted - therefore user uploaded files can be assumed to be safe. There are basic checks in place to ensure that the files are not using common attack vectors but those are not exhaustive.
|
||||
|
||||
3. Superuser permissions are only given to trusted users and not used for daily operations. A superuser account can manipulate or extract all files on the server that the InvenTree server process have access to.
|
||||
3. Superuser or staff permissions are only given to trusted users and not used for daily operations. A superuser account can manipulate or extract all files on the server that the InvenTree server process have access to. See [dangerous user flags](../settings/permissions.md#dangerous-user-flags) for more details on user permissions and flags.
|
||||
|
||||
4. All templates and plugins are trusted.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ Support for real-world "physical" units of measure is implemented using the [pin
|
||||
|
||||
- Ensures consistent use of real units for your inventory management
|
||||
- Convert between compatible units of measure from suppliers
|
||||
- Enforce use of compatible units when creating part parameters
|
||||
- Enforce use of compatible units when creating parameters
|
||||
- Enable custom units as required
|
||||
|
||||
### Unit Conversion
|
||||
@@ -61,7 +61,7 @@ The [supplier part](../part/index.md/#supplier-parts) model uses real-world unit
|
||||
|
||||
### Parameter
|
||||
|
||||
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
|
||||
The [parameter template](../concepts/parameters.md#parameter-templates) model can specify units of measure, and parameters can be specified against these templates with compatible units
|
||||
|
||||
## Custom Units
|
||||
|
||||
|
||||
@@ -224,6 +224,8 @@ Example: Creating a new part via the "Add Part" form:
|
||||
|
||||
{{ image("concepts/ui_form_add_part.png", "Add Part Button") }}
|
||||
|
||||
On several forms is displayed option "Keep form open" in bottom part of the form on left side of Submit button (option is visible on the screenshot above). When this switch is turned on, form window is not closed after submit and filled form data is not reset. This is useful for creating more entries at one time with similar properties (e.g. only different number in name).
|
||||
|
||||
### Data Editing
|
||||
|
||||
Example: Editing an existing purchase order via the "Edit Purchase Order" form:
|
||||
@@ -280,11 +282,12 @@ Alternatively, the spotlight search can be opened using the keyboard shortcut `C
|
||||
|
||||
Users may opt to disable the spotlight search functionality if they do not find it useful or prefer not to use it. To disable the spotlight search, navigate to your [user settings](../settings/user.md) and locate the option to disable the spotlight feature. Once disabled, the spotlight search will no longer be accessible from the main menu or via keyboard shortcuts.
|
||||
|
||||
## Barcode Scanning
|
||||
## Copy Button
|
||||
|
||||
## Notifications
|
||||
Many fields within the InvenTree user interface include a "copy" button, which allows users to quickly copy the value of that field to their clipboard. This is particularly useful for fields that contain important identifiers, such as part numbers, stock item codes, or other relevant data that may need to be easily copied and pasted elsewhere.
|
||||
|
||||
## Customization
|
||||
!!! important "Secure Context"
|
||||
The "copy" button functionality relies on the browser's clipboard API, which may not be available in all contexts (e.g. if the user is accessing the InvenTree interface via a non-https connection, or through an embedded iframe or a non-standard browser). In such cases, the "copy" button may not function as intended.
|
||||
|
||||
## User Permissions
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ InvenTree roughly follow the [GitLab flow](https://about.gitlab.com/topics/versi
|
||||
There are nominally 5 active branches:
|
||||
- `master` - The main development branch
|
||||
- `stable` - The latest stable release
|
||||
- `next-breaking` - The next breaking release (e.g. 2.0, 3.0) with all deprecated features removed
|
||||
- `l10n` - Translation branch: Source to Crowdin
|
||||
- `l10_crowdin` - Translation branch: Source from Crowdin
|
||||
- `y.y.x` - Release branch for the currently supported version (e.g. `0.5.x`)
|
||||
@@ -115,6 +116,23 @@ The translation process is as follows:
|
||||
4. Translations made in Crowdin are automatically pushed back to the `l10_crowdin` branch by Crowdin once they are approved
|
||||
5. The `l10_crowdin` branch is merged back into `master` by a maintainer periodically
|
||||
|
||||
### `next-breaking` Branch
|
||||
|
||||
Used for easier testing of plugins and integrations against the next major release. It is branched from master when a major release is cut and updated on minor release. The branch is not build into docker images or packages and not meant to be run in production.
|
||||
|
||||
|
||||
All deprecated features (REST or python API endpoints mostly) are removed from this branch after each minor release. This allows plugin developers to test their plugins against the next major release early and identify any extensive changes before the major release is cut.
|
||||
|
||||
Only breaking changes are added to this branch. No new features should be added at any point to this branch, only breaking removals / changes.
|
||||
|
||||
Before a major release is cut (1.12.5 > 2.0.0), this branch is merged back into `master`.
|
||||
|
||||
|
||||
During the life-time of a major release line (1.0.1, 1.1.x, 1.2.x, 1.3.x, ..., 1.12.5) all deprecation removals are collected in this branch.
|
||||
On every minor release (1.11.8 > 1.12.0) the `master` is rebased onto the `next-breaking` branch.
|
||||
|
||||
Every time a change with depreations is merged into `master`, a follow up PR that removes the newly-introduced deprecation is created targeting the `next-breaking` branch. After the next minor is released and `master` was rebased into `next-breaking` all the PRs from the previous minor release line can be merged into the `next-breaking` branch. Deprecation removals for the - possibly - long running major release line can be collected this way without having a large number of deprecation removals PRs open.
|
||||
|
||||
## API versioning
|
||||
|
||||
The [API version]({{ sourcefile("src/backend/InvenTree/InvenTree/api_version.py") }}) needs to be bumped every time when the API is changed.
|
||||
@@ -144,7 +162,7 @@ The core software modules are targeting the following versions:
|
||||
| Python | {{ config.extra.min_python_version }} | Minimum required version |
|
||||
| Invoke | {{ config.extra.min_invoke_version }} | Minimum required version |
|
||||
| Django | {{ config.extra.django_version }} | Pinned version |
|
||||
| Node | 20 | Only needed for frontend development |
|
||||
| Node | 24 | Only needed for frontend development |
|
||||
|
||||
Any other software dependencies are handled by the project package config.
|
||||
|
||||
|
||||
@@ -60,7 +60,12 @@ If you only need a superuser, run the `superuser` task. It should prompt you for
|
||||
|
||||
#### Run background workers
|
||||
|
||||
If you need to process your queue with background workers, run the `worker` task. This is a foreground task which will execute in the terminal.
|
||||
If you need to process your queue with background workers, open a new terminal and run the `worker` task with `invoke worker`. This is a foreground task which will execute in the terminal.
|
||||
|
||||
If you are developing functions that will be executed by background workers there are a two debugging options.
|
||||
|
||||
- If the workers are started with the `worker` task you can add `print` or `logger` statements to the code and monitor the output in the terminal.
|
||||
- All tasks can be forced to run in the foreground worker by uncommenting the `--sync` and `--noreload` arguments under the `InvenTree Server - 3rd party` entry in `.vscode/launch.json`. With this setting you should not start a separate background worker, instead you start the `InvenTree Server - 3rd party` from the `Run and Debug` side panel. All task will now run in one single process and you can set breakpoints, inspect variables and single step also tasks that normally are offloaded to background workers. It should be noted that with this setting the GUI will be unresponsive while tasks are executed.
|
||||
|
||||
### Running InvenTree
|
||||
|
||||
|
||||
@@ -195,3 +195,14 @@ This means that either:
|
||||
- The docker user does not have write permission to the specified directory
|
||||
|
||||
In either case, ensure that the directory is available *on your local machine* and the user account has the required permissions.
|
||||
|
||||
|
||||
## Error Rendering Component
|
||||
|
||||
Sometimes, following a software update, you may find that certain components of the web interface are not rendering correctly, and presented with a message similar to the screenshow below:
|
||||
|
||||
{{ image("faq/boundary.png", "Error Rendering Component") }}
|
||||
|
||||
This is often due to a caching issue with your web browser. Try performing a hard refresh of the page to clear the cache, this should resolve the issue in most cases.
|
||||
|
||||
If the problem persists, refer to the [troubleshooting guide](./troubleshooting.md) for further assistance.
|
||||
|
||||
@@ -280,9 +280,27 @@ def on_post_build(*args, **kwargs):
|
||||
ignored_settings = {
|
||||
'global': ['SERVER_RESTART_REQUIRED'],
|
||||
'user': ['LAST_USED_PRINTING_MACHINES'],
|
||||
'config': [
|
||||
'INVENTREE_DB_TCP_KEEPALIVES',
|
||||
'INVENTREE_DB_TCP_KEEPALIVES_IDLE',
|
||||
'INVENTREE_DB_TCP_KEEPALIVES_INTERVAL',
|
||||
'INVENTREE_DB_TCP_KEEPALIVES_COUNT',
|
||||
'INVENTREE_DB_ISOLATION_SERIALIZABLE',
|
||||
'INVENTREE_DB_WAL_MODE',
|
||||
'INVENTREE_PLUGIN_DIR',
|
||||
'INVENTREE_DOCKER',
|
||||
'INVENTREE_FLAGS',
|
||||
'INVENTREE_REMOTE_LOGIN',
|
||||
'INVENTREE_REMOTE_LOGIN_HEADER',
|
||||
'TEST_TRANSLATIONS',
|
||||
'INVENTREE_FRONTEND_URL_BASE',
|
||||
'INVENTREE_FRONTEND_API_HOST',
|
||||
'INVENTREE_FRONTEND_SETTINGS',
|
||||
'INVENTREE_LOGOUT_REDIRECT_URL',
|
||||
],
|
||||
}
|
||||
|
||||
for group in ['global', 'user']:
|
||||
for group in ['global', 'user', 'config']:
|
||||
expected = expected_settings.get(group, {})
|
||||
observed = observed_settings.get(group, {})
|
||||
ignored = ignored_settings.get(group, [])
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Bill of Materials
|
||||
|
||||
## Bill of Materials
|
||||
|
||||
A Bill of Materials (BOM) defines the list of component parts required to make an assembly, [create builds](./build.md) and allocate inventory.
|
||||
A Bill of Materials (BOM) defines the list of component parts required to make an assembly, [create build orders](./build.md) and allocate inventory.
|
||||
|
||||
A part which can be built from other sub components is called an *Assembly*.
|
||||
|
||||
@@ -18,7 +18,8 @@ A BOM for a particular assembly is comprised of a number (zero or more) of BOM "
|
||||
| --- | --- |
|
||||
| Part | A reference to another *Part* object which is required to build this assembly |
|
||||
| Reference | Optional reference field to describe the BOM Line Item, e.g. part designator |
|
||||
| Quantity | The quantity of *Part* required for the assembly |
|
||||
| Raw Amount | The raw quantity of the part required for the assembly, which can be expressed in different units of measure, e.g. `2 cm`, `1/2 inch`, `200 kg`. |
|
||||
| Quantity | The quantity of *Part* required for the assembly - this value is automatically calculated from the "raw amount" field, taking into account the units of measure associated with the underlying part. |
|
||||
| Attrition | Estimated attrition losses for a production run. Expressed as a percentage of the base quantity (e.g. 2%) |
|
||||
| Setup Quantity | An additional quantity of the part which is required to account for fixed setup losses during the production process. This is added to the base quantity of the BOM line item |
|
||||
| Rounding Multiple | A value which indicates that the required quantity should be rounded up to the nearest multiple of this value. |
|
||||
@@ -27,6 +28,18 @@ A BOM for a particular assembly is comprised of a number (zero or more) of BOM "
|
||||
| Optional | A boolean field which indicates if this BOM Line Item is "optional" |
|
||||
| Note | Optional note field for additional information
|
||||
|
||||
### Units of Measure
|
||||
|
||||
The `raw_amount` field allows the user to specify the required quantity of a particular part in different [units of measure](../concepts/units.md). The units of measure are determined by the underlying part definition. For example, if the part is defined with a default unit of measure of "kg", the user can specify the required quantity in "g", "mg", "lb", etc.
|
||||
|
||||
The `raw_amount` field is stored as a string, and the `quantity` field is automatically calculated from the `raw_amount` field, taking into account the units of measure associated with the underlying part. This allows for greater flexibility in specifying the required quantity of a particular part, while still maintaining accurate tracking of inventory and production requirements.
|
||||
|
||||
If the underlying part does not have a defined unit of measure, the `raw_amount` field is not allowed to have any units of measure specified, and the `quantity` field is simply a numeric representation of the `raw_amount` field.
|
||||
|
||||
### Fractional Representation
|
||||
|
||||
The `raw_amount` field also allows for fractional representation of the required quantity. For example, if the required quantity is 0.5 kg, the user can specify this as `500 g`, `0.5 kg`, `1/2 kg`, etc. The `quantity` field will be automatically calculated as 0.5 kg, regardless of the specific representation used in the `raw_amount` field.
|
||||
|
||||
### Consumable BOM Line Items
|
||||
|
||||
If a BOM line item is marked as *consumable*, this means that while the part and quantity information is tracked in the BOM, this line item does not get allocated to a [Build Order](./build.md). This may be useful for certain items that the user does not wish to track through the build process, as they may be low value, in abundant stock, or otherwise complicated to track.
|
||||
@@ -37,6 +50,12 @@ In the example below, see that the *Wood Screw* line item is marked as consumabl
|
||||
|
||||
Further, in the [Build Order](./build.md) stock allocation table, we see that this line item cannot be allocated, as it is *consumable*.
|
||||
|
||||
### Optional BOM Line Items
|
||||
|
||||
If a BOM line item is marked as *optional*, this means that the part and quantity information is tracked in the BOM, but this line item is not required to be allocated to a [Build Order](./build.md). This may be useful for certain items which are not strictly required for the build process to be completed.
|
||||
|
||||
When completing a Build Order, the user can choose whether to include optional items in the build process or not. If optional items are included, they will be allocated to the Build Order as normal. If optional items are excluded, they will not be allocated to the Build Order, and the build process can be completed without them.
|
||||
|
||||
### Substitute BOM Line Items
|
||||
|
||||
Where alternative parts can be used when building an assembly, these parts are assigned as *Substitute* parts in the Bill of Materials. A particular line item may have multiple substitute parts assigned to it. When allocating stock to a [Build Order](./build.md), stock items associated with any of the substitute parts may be allocated against the particular line item.
|
||||
@@ -86,13 +105,20 @@ Note that inherited BOM Line Items only flow "downwards" in the variant inherita
|
||||
!!! info "Editing Inherited Items"
|
||||
When editing an inherited BOM Line Item for a template part, the changes are automatically reflected in the BOM of any variant parts.
|
||||
|
||||
## BOM Creation
|
||||
## BOM Editing
|
||||
|
||||
BOMs can be created manually, by adjusting individual line items, or by uploading (importing) an existing BOM file.
|
||||
Bills of Material (BOMs) can be created manually, by adjusting individual line items, or by uploading (importing) an existing BOM file.
|
||||
|
||||
### Editing Mode
|
||||
|
||||
By default, the BOM is displayed in "view" mode. To edit the BOM, click on the {{ icon("edit", color="blue", title="Edit") }} icon at the top of the BOM panel. This will enable editing mode, which allows you to add, adjust or delete BOM line items.
|
||||
|
||||
!!! warning "Permissions"
|
||||
Only users with the appropriate permissions can edit BOMs. If you do not have permission to edit the BOM, the "Edit" icon will not be visible.
|
||||
|
||||
### Importing a BOM
|
||||
|
||||
BOM data can be imported from an existing file (such as CSV or Excel) from the *BOM* panel for a particular part/assembly. This process is a special case of the more general [data import process](../settings/import.md).
|
||||
BOM data can be imported from an existing file (such as CSV or Excel) from the *BOM* panel for a particular part/assembly. This process is a special case of the more general [data import process](../concepts/data_import.md).
|
||||
|
||||
At the top of the *BOM* panel, click on the {{ icon("file-arrow-left", color="green", title="Import BOM Data") }} icon to open the import dialog.
|
||||
|
||||
@@ -156,89 +182,40 @@ If the BOM requires revalidation, the status will be displayed as "Not Validated
|
||||
|
||||
{{ image("build/bom_invalid.png", "BOM Not Validated") }}
|
||||
|
||||
## Required Quantity Calculation
|
||||
## BOM Comparison
|
||||
|
||||
When a new [Build Order](./build.md) is created, the required production quantity of each component part is calculated based on the BOM line items defined for the assembly being built. To calculate the required production quantity of a component part, the following considerations are made:
|
||||
It is possible to compare the BOM of one assembly with another assembly. This comparison can highlight different component parts, quantities and other properties of the BOM line items.
|
||||
|
||||
### Base Quantity
|
||||
To compare the BOM of one assembly with another, navigate to the "Bill of Materials" tab of the part detail page, then click on the {{ icon("git-compare", color="blue", title="Compare BOM") }} icon at the top of the BOM table:
|
||||
|
||||
The base quantity of a BOM line item is defined by the `Quantity` field of the BOM line item. This is the number of parts which are required to build one assembly. This value is multiplied by the number of assemblies which are being built to determine the total quantity of parts required.
|
||||
{{ image("build/bom_compare_icon.png", "BOM Compare") }}
|
||||
|
||||
```
|
||||
Required Quantity = Base Quantity * Number of Assemblies
|
||||
```
|
||||
This will open the BOM comparison view, which allows you to select a secondary assembly to compare with the primary assembly. The BOM line items of the two assemblies will be displayed side by side, with differences highlighted:
|
||||
|
||||
### Attrition
|
||||
{{ image("build/bom_compare.png", "BOM Compare") }}
|
||||
|
||||
The `Attrition` field of a BOM line item is used to account for expected losses during the production process. This is expressed as a percentage of the `Base Quantity` (e.g. 2%).
|
||||
### Display Mode
|
||||
|
||||
If a non-zero attrition percentage is specified, it is applied to the calculated `Required Quantity` value.
|
||||
When comparing BOMs from two different assemblies, the user can select from the following view modes:
|
||||
|
||||
```
|
||||
Required Quantity = Required Quantity * (1 + Attrition Percentage)
|
||||
```
|
||||
| View Mode | Description |
|
||||
| --- | --- |
|
||||
| *Show all parts* | Display all BOM line items from both assemblies. Differences are highlighted. |
|
||||
| *Show different parts* | Display only the BOM line items which are different between the two assemblies. |
|
||||
| *Show common parts* | Display only the BOM line items which are common between the two assemblies. |
|
||||
|
||||
!!! info "Optional"
|
||||
The attrition percentage is optional. If not specified, it defaults to 0%.
|
||||
In each case, any differences between the BOM line items are highlighted in red.
|
||||
|
||||
### Setup Quantity
|
||||
## Replacing Components
|
||||
|
||||
The `Setup Quantity` field of a BOM line item is used to account for fixed losses during the production process. This is an additional quantity of the part which is required to ensure that the production run can be completed successfully. This value is added to the calculated `Required Quantity`.
|
||||
When a component is used in the BOM for multiple assemblies, it can be time consuming to update the BOM for each assembly when a change is required. InvenTree provides a "Replace Component" function which streamlines the process of replacing a component part with another part across multiple BOMs.
|
||||
|
||||
```
|
||||
Required Quantity = Required Quantity + Setup Quantity
|
||||
```
|
||||
To replace a component part within multiple assemblies:
|
||||
|
||||
!!! info "Optional"
|
||||
The setup quantity is optional. If not specified, it defaults to 0.
|
||||
- Navigate to the [Used In](../part/views.md#used-in) tab of the component part detail page
|
||||
- Select the assemblies you wish to update by ticking the checkbox next to each assembly
|
||||
- Click on the {{ icon("replace", color="blue", title="Replace Component") }} icon to open the "Replace Component" dialog
|
||||
|
||||
### Rounding Multiple
|
||||
The following dialog will be displayed, which allows the user to select a new component part to replace the existing component part in the BOM of the selected assemblies:
|
||||
|
||||
The `Rounding Multiple` field of a BOM line item is used to round the calculated `Required Quantity` value to the nearest multiple of the specified value. This is useful for ensuring that the required quantity is a whole number, or to meet specific packaging requirements.
|
||||
|
||||
```
|
||||
Required Quantity = ceil(Required Quantity / Rounding Multiple) * Rounding Multiple
|
||||
```
|
||||
|
||||
!!! info "Optional"
|
||||
The rounding multiple is optional. If not specified, no rounding is applied to the calculated production quantity.
|
||||
|
||||
### Example Calculation
|
||||
|
||||
Consider a BOM line item with the following properties:
|
||||
|
||||
- Base Quantity: 3
|
||||
- Attrition: 2% (0.02)
|
||||
- Setup Quantity: 10
|
||||
- Rounding Multiple: 25
|
||||
|
||||
If we are building 100 assemblies, the required quantity would be calculated as follows:
|
||||
|
||||
```
|
||||
Required Quantity = Base Quantity * Number of Assemblies
|
||||
= 3 * 100
|
||||
= 300
|
||||
|
||||
Attrition Value = Required Quantity * Attrition Percentage
|
||||
= 300 * 0.02
|
||||
= 6
|
||||
|
||||
Required Quantity = Required Quantity + Attrition Value
|
||||
= 300 + 6
|
||||
= 306
|
||||
|
||||
Required Quantity = Required Quantity + Setup Quantity
|
||||
= 306 + 10
|
||||
= 316
|
||||
|
||||
Required Quantity = ceil(Required Quantity / Rounding Multiple) * Rounding Multiple
|
||||
= ceil(316 / 25) * 25
|
||||
= 13 * 25
|
||||
= 325
|
||||
|
||||
```
|
||||
|
||||
So the final required production quantity of the component part would be `325`.
|
||||
|
||||
!!! info "Calculation"
|
||||
The required quantity calculation is performed automatically when a new [Build Order](./build.md) is created.
|
||||
{{ image("build/replace_component.png", "Replace Component") }}
|
||||
|
||||
@@ -304,10 +304,11 @@ The following [global settings](../settings/global.md) are available for adjusti
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("BUILDORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("BUILDORDER_EXTERNAL_BUILDS") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_ACTIVE_PART") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_LOCKED_PART") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_VALID_BOM") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_CLOSED_CHILDS") }}
|
||||
{{ globalsetting("PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS") }}
|
||||
{{ globalsetting("BUILDORDER_EXTERNAL_BUILDS") }}
|
||||
{{ globalsetting("BUILDORDER_EXTERNAL_REQUIRED") }}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Required Build Quantity
|
||||
---
|
||||
|
||||
## Required Build Quantity
|
||||
|
||||
When a new [Build Order](./build.md) is created, the required production quantity of each component part is calculated based on the BOM line items defined for the assembly being built. To calculate the required production quantity of a component part, the following considerations are made:
|
||||
|
||||
### Base Quantity
|
||||
|
||||
The base quantity of a BOM line item is defined by the `Quantity` field of the BOM line item. This is the number of parts which are required to build one assembly. This value is multiplied by the number of assemblies which are being built to determine the total quantity of parts required.
|
||||
|
||||
```
|
||||
Required Quantity = Base Quantity * Number of Assemblies
|
||||
```
|
||||
|
||||
### Attrition
|
||||
|
||||
The `Attrition` field of a BOM line item is used to account for expected losses during the production process. This is expressed as a percentage of the `Base Quantity` (e.g. 2%).
|
||||
|
||||
If a non-zero attrition percentage is specified, it is applied to the calculated `Required Quantity` value.
|
||||
|
||||
```
|
||||
Required Quantity = Required Quantity * (1 + Attrition Percentage)
|
||||
```
|
||||
|
||||
!!! info "Optional"
|
||||
The attrition percentage is optional. If not specified, it defaults to 0%.
|
||||
|
||||
### Setup Quantity
|
||||
|
||||
The `Setup Quantity` field of a BOM line item is used to account for fixed losses during the production process. This is an additional quantity of the part which is required to ensure that the production run can be completed successfully. This value is added to the calculated `Required Quantity`.
|
||||
|
||||
```
|
||||
Required Quantity = Required Quantity + Setup Quantity
|
||||
```
|
||||
|
||||
!!! info "Optional"
|
||||
The setup quantity is optional. If not specified, it defaults to 0.
|
||||
|
||||
### Rounding Multiple
|
||||
|
||||
The `Rounding Multiple` field of a BOM line item is used to round the calculated `Required Quantity` value to the nearest multiple of the specified value. This is useful for ensuring that the required quantity is a whole number, or to meet specific packaging requirements.
|
||||
|
||||
```
|
||||
Required Quantity = ceil(Required Quantity / Rounding Multiple) * Rounding Multiple
|
||||
```
|
||||
|
||||
!!! info "Optional"
|
||||
The rounding multiple is optional. If not specified, no rounding is applied to the calculated production quantity.
|
||||
|
||||
### Example Calculation
|
||||
|
||||
Consider a BOM line item with the following properties:
|
||||
|
||||
- Base Quantity: 3
|
||||
- Attrition: 2% (0.02)
|
||||
- Setup Quantity: 10
|
||||
- Rounding Multiple: 25
|
||||
|
||||
If we are building 100 assemblies, the required quantity would be calculated as follows:
|
||||
|
||||
```
|
||||
Required Quantity = Base Quantity * Number of Assemblies
|
||||
= 3 * 100
|
||||
= 300
|
||||
|
||||
Attrition Value = Required Quantity * Attrition Percentage
|
||||
= 300 * 0.02
|
||||
= 6
|
||||
|
||||
Required Quantity = Required Quantity + Attrition Value
|
||||
= 300 + 6
|
||||
= 306
|
||||
|
||||
Required Quantity = Required Quantity + Setup Quantity
|
||||
= 306 + 10
|
||||
= 316
|
||||
|
||||
Required Quantity = ceil(Required Quantity / Rounding Multiple) * Rounding Multiple
|
||||
= ceil(316 / 25) * 25
|
||||
= 13 * 25
|
||||
= 325
|
||||
|
||||
```
|
||||
|
||||
So the final required production quantity of the component part would be `325`.
|
||||
|
||||
!!! info "Calculation"
|
||||
The required quantity calculation is performed automatically when a new [Build Order](./build.md) is created.
|
||||
@@ -20,7 +20,7 @@ New parts can be created manually by selecting the *Create Part* option from the
|
||||
{{ image("part/part_create_form.png", "New part form") }}
|
||||
|
||||
|
||||
Fill out the required part parameters and then press *Submit* to create the new part. If there are any form errors, you must fix these before the form can be successfully submitted.
|
||||
Fill out the required attributes and then press *Submit* to create the new part. If there are any form errors, you must fix these before the form can be successfully submitted.
|
||||
|
||||
Once the form is completed, the browser window is redirected to the new part detail page.
|
||||
|
||||
@@ -48,7 +48,7 @@ If the *Add Supplier Data* option is checked, then supplier part and manufacture
|
||||
|
||||
Parts can be imported from an external file, by selecting the *Import from File* option.
|
||||
|
||||
This action opens the [data import wizard](../settings/import.md), which steps the user through the process of importing parts from the selected file.
|
||||
This action opens the [data import wizard](../concepts/data_import.md), which steps the user through the process of importing parts from the selected file.
|
||||
|
||||
## Import from Supplier
|
||||
|
||||
|
||||
@@ -6,6 +6,18 @@ title: Parts
|
||||
|
||||
The *Part* is the core element of the InvenTree ecosystem. A Part object is the archetype of any stock item in your inventory. Parts are arranged in hierarchical categories which are used to organize and filter parts by function.
|
||||
|
||||
## Part Stock
|
||||
|
||||
Each part can have multiple [stock items](../stock/index.md) associated with it, which represent the physical quantity of that part in various locations. The total stock level for a given part is the sum of all stock items associated with that part.
|
||||
|
||||
### Minimum Stock
|
||||
|
||||
A part may have a specified "minimum stock" level. This is a user-defined value which indicates the minimum quantity of that part which should be kept in stock at all times. If the total stock level for a given part falls below the minimum stock level, the part is flagged as "low stock" and can be easily identified in the interface.
|
||||
|
||||
### Maximum Stock
|
||||
|
||||
A part may also have a specified "maximum stock" level. This is a user-defined value which indicates the maximum quantity of that part which should be kept in stock at all times. If the total stock level for a given part exceeds the maximum stock level, the part is flagged as "overstocked" and can be easily identified in the interface.
|
||||
|
||||
## Part Category
|
||||
|
||||
Part categories are very flexible and can be easily arranged to match a particular user requirement. Each part category displays a list of all parts *under* that given category. This means that any part belonging to a particular category, or belonging to a sub-category, will be displayed.
|
||||
@@ -27,7 +39,7 @@ Clicking on the part name links to the [*Part Detail*](./views.md) view.
|
||||
|
||||
## Part Attributes
|
||||
|
||||
Each *Part* defined in the database provides a number of different attributes which determine how that part can be used. Configuring these attributes for a given part will impact the available functions that can be perform on (or using) that part).
|
||||
Each *Part* defined in the database provides a number of different attributes which determine how that part can be used. Configuring these attributes for a given part will impact the available functions that can be perform on (or using) that part.
|
||||
|
||||
### Virtual
|
||||
|
||||
@@ -75,13 +87,16 @@ A [Purchase Order](../purchasing/purchase_order.md) allows parts to be ordered f
|
||||
|
||||
If a part is designated as *Salable* it can be sold to external customers. Setting this flag allows parts to be added to sales orders.
|
||||
|
||||
|
||||
## Locked Parts
|
||||
|
||||
Parts can be locked to prevent them from being modified. This is useful for parts which are in production and should not be changed. The following restrictions apply to parts which are locked:
|
||||
|
||||
- Locked parts cannot be deleted
|
||||
- BOM items cannot be created, edited, or deleted when they are part of a locked assembly
|
||||
- Part parameters linked to a locked part cannot be created, edited or deleted
|
||||
- Parameters linked to a locked part cannot be created, edited or deleted
|
||||
|
||||
The part locking functionality can be enabled or disabled globally via the [Part Locking](../settings/global.md#parts) system setting (`PART_ENABLE_LOCKING`). When disabled, the locked state of a part is ignored and all operations are permitted.
|
||||
|
||||
## Active Parts
|
||||
|
||||
@@ -145,4 +160,4 @@ The [InvenTree mobile app](../app/part.md#part-image-view) allows part images to
|
||||
|
||||
## Part Import
|
||||
|
||||
*Parts* can be imported by staff-members on the part-list-view (this feature must be enabled in the part-settings), in the part-settings or on the [admin-page for parts](../settings/import.md) (only accessible if you are also an admin). The first two options provide a multi-stage wizard that enables mapping fields from various spreadsheet or table-data formats while the latter requires a well-formatted file but is much more performant.
|
||||
*Part* data can be imported using the [data import wizard](../concepts/data_import.md). This allows part data to be imported from an external file, which can be useful for bulk importing of part data.
|
||||
|
||||
@@ -27,24 +27,22 @@ When creating a new revision of a part, there are some restrictions which must b
|
||||
|
||||
* **Circular References**: A part cannot be a revision of itself. This would create a circular reference which is not allowed.
|
||||
* **Unique Revisions**: A part cannot have two revisions with the same revision number. Each revision (of a given part) must have a unique revision code.
|
||||
* **Revisions of Revisions**: A single part can have multiple revisions, but a revision cannot have its own revision. This restriction is in place to prevent overly complex part relationships.
|
||||
* **Template Revisions**: A part which is a [template part](./template.md) cannot have revisions. This is because the template part is used to create variants, and allowing revisions of templates would create disallowed relationship states in the database. However, variant parts are allowed to have revisions.
|
||||
* **Template References**: A part which is a revision of a variant part must point to the same template as the original part. This is to ensure that the revision is correctly linked to the original part.
|
||||
|
||||
## Revision Settings
|
||||
|
||||
The following options are available to control the behavior of part revisions.
|
||||
The following [global settings](../settings/global.md) are available to control the behavior of part revisions:
|
||||
|
||||
Note that these options can be changed in the InvenTree settings:
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("PART_ENABLE_REVISION") }}
|
||||
{{ globalsetting("PART_REVISION_ASSEMBLY_ONLY") }}
|
||||
|
||||
{{ image("part/part_revision_settings.png", "Part revision settings") }}
|
||||
|
||||
* **Enable Revisions**: If this setting is enabled, parts can have revisions. If this setting is disabled, parts cannot have revisions.
|
||||
* **Assembly Revisions Only**: If this setting is enabled, only assembly parts can have revisions. This is useful if you only want to track revisions of assemblies, and not individual parts.
|
||||
|
||||
## Create a Revision
|
||||
|
||||
To create a new revision for a given part, navigate to the part detail page, and click on the "Revisions" tab.
|
||||
To create a new revision for a given part, navigate to the part detail page, and click on the part actions menu (three vertical dots on the top right of the page).
|
||||
|
||||
Select the "Duplicate Part" action, to create a new copy of the selected part. This will open the "Duplicate Part" form:
|
||||
|
||||
@@ -67,4 +65,5 @@ When multiple revisions exist for a particular part, you can navigate between re
|
||||
|
||||
{{ image("part/part_revision_select.png", "Select part revision") }}
|
||||
|
||||
Note that this revision selector is only visible when multiple revisions exist for the part.
|
||||
!!! info "Revision Selector Visibility"
|
||||
Note that this revision selector is only visible when multiple revisions exist for the part.
|
||||
|
||||
@@ -41,7 +41,7 @@ If you want to make your life easier, try to follow these guidelines; break wher
|
||||
from plugin import InvenTreePlugin, registry
|
||||
from plugin.mixins import APICallMixin, SettingsMixin, ScheduleMixin, BarcodeMixin
|
||||
```
|
||||
- Feliver as a package (see [below](#packaging))
|
||||
- Deliver as a package (see [below](#packaging))
|
||||
- If you need to use a private infrastructure, use the 'Releases' functions in GitHub or Gitlab. Point to the 'latest' release endpoint when installing to make sure the update function works
|
||||
- Tag your GitHub repo with `inventree` and `inventreeplugins` to make discovery easier. A discovery mechanism using these tags is on the roadmap.
|
||||
- Use GitHub actions to test your plugin regularly (you can [schedule actions](https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule)) against the 'latest' [docker-build](https://hub.docker.com/r/inventree/inventree) of InvenTree
|
||||
|
||||
@@ -10,6 +10,9 @@ Plugins can inherit from the [UserInterfaceMixin](./mixins/ui.md) class to provi
|
||||
|
||||
Note that the [InvenTree plugin creator](./creator.md) can be used to scaffold a new plugin with the necessary structure for frontend integration. This will automatically set up the necessary files and configurations to get started with frontend development.
|
||||
|
||||
!!! info "Simplified Development"
|
||||
Using the plugin creator can significantly simplify the process of developing a frontend plugin, as it provides a ready-made template with the necessary configurations for building and integrating the frontend code. Rolling your own frontend plugin from scratch is possible, but it requires a good understanding of the InvenTree frontend architecture and build process.
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
When designing a frontend plugin component, it is important to have at least a basic understanding of the InvenTree frontend architecture.
|
||||
@@ -76,20 +79,28 @@ The following properties are available in the `context` object:
|
||||
| Property | Description |
|
||||
| -------- | ----------- |
|
||||
| `version` | An object containing the current InvenTree version information. |
|
||||
| `api` | The Axios instance configured to communicate with the InvenTree API. |
|
||||
| `queryClient` | The query client instance used for managing API calls in the frontend. |
|
||||
| `user` | An object containing information about the currently logged-in user. |
|
||||
| `userSettings` | An object containing user-specific settings. |
|
||||
| `globalSettings` | An object containing global settings for the InvenTree instance. |
|
||||
| `modelInformation` | An object containing information about the models available in the InvenTree instance. |
|
||||
| `renderInstance` | A function to render a model instance |
|
||||
| `host` | An object containing information about the current host (server) configuration. |
|
||||
| `i18n` | An object containing internationalization (i18n) functions for translating text. |
|
||||
| `locale` | The current locale being used for the user interface. |
|
||||
| `api` | The Axios instance configured to communicate with the InvenTree API. |
|
||||
| `queryClient` | The query client instance used for managing API calls in the frontend. |
|
||||
| `navigate` | A function to navigate to a different page in the InvenTree web interface. |
|
||||
| `globalSettings` | An object containing global settings for the InvenTree instance. |
|
||||
| `userSettings` | An object containing user-specific settings. |
|
||||
| `modelInformation` | An object containing information about the models available in the InvenTree instance. |
|
||||
| `renderInstance` | A function to render a model instance |
|
||||
| `theme` | The current Mantine theme being used in the InvenTree web interface. |
|
||||
| `colorScheme` | The current color scheme being used in the InvenTree web interface. |
|
||||
| `forms` | A set of functional components for rendering forms in the InvenTree web interface. |
|
||||
| `tables` | A set of functional components for rendering tables in the InvenTree web interface. |
|
||||
| `importer` | A set of functions for controlling the global importer drawer in the InvenTree web interface. |
|
||||
| `model` | The model type associated with the rendered component (if applicable). |
|
||||
| `id` | The ID (primary key) of the model instance being rendered (if applicable). |
|
||||
| `instance` | The model instance data (if available). |
|
||||
| `reloadContent` | A function which can be called to reload the plugin content. |
|
||||
| `reloadInstance` | A function which can be called to reload the model instance. |
|
||||
| `context` | Any additional context data which may be passed to the plugin. |
|
||||
|
||||
This set of components is passed through at render time to the plugin function, allowing the plugin code to hook directly into the InvenTree web interface and access the necessary context for rendering.
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ Enter the package name into the form as shown below. You can add a path and a ve
|
||||
|
||||
{{ image("plugin/plugin_install_txt.png", "Plugin.txt file") }}
|
||||
|
||||
!!! info "Superuser Required"
|
||||
Only users with superuser privileges can manage plugins via the web interface.
|
||||
|
||||
#### Local Directory
|
||||
|
||||
Custom plugins can be placed in the `data/plugins/` directory, where they will be automatically discovered. This can be useful for developing and testing plugins, but can prove more difficult in production (e.g. when using Docker).
|
||||
|
||||
@@ -8,3 +8,27 @@ If this mixin is added to a plugin the directory the plugin class is defined in
|
||||
|
||||
!!! warning "Danger Zone"
|
||||
Only use this mixin if you have an understanding of Django's [app system]({% include "django.html" %}/ref/applications). Plugins with this mixin are deeply integrated into InvenTree and can cause difficult to reproduce or long-running errors. Use the built-in testing functions of Django to make sure your code does not cause unwanted behaviour in InvenTree before releasing.
|
||||
|
||||
## Custom Models
|
||||
|
||||
This mixin allows you to define custom database models within your plugin. These models will be automatically registered with the InvenTree server, and will be available for use within your plugin code.
|
||||
|
||||
### Model Permissions
|
||||
|
||||
Some database operations within the InvenTree ecosystem may require custom permissions checks - to determine which actions a user can perform against a given model. If your plugin defines custom models, you may need to implement a custom permission check method on your model class.
|
||||
|
||||
Each model class can implement a `check_user_permission` classmethod, which will be called by the InvenTree permission system when checking permissions for that model. This method should return `True` if the user has the required permissions, and `False` otherwise.
|
||||
|
||||
|
||||
```python
|
||||
class MyCustomModel(models.Model):
|
||||
# model fields here
|
||||
|
||||
@classmethod
|
||||
def check_user_permission(cls, user: User, permission: str) -> bool:
|
||||
# custom permission logic here
|
||||
return True # or False
|
||||
```
|
||||
|
||||
!!! warning "Default Permissions"
|
||||
By default, if the `check_user_permission` method is not implemented, the InvenTree permission system will return `False` for all permission checks against that model. This is to ensure that no permissions are granted by default, and that the plugin developer must explicitly define the required permissions for their custom models.
|
||||
|
||||
@@ -13,6 +13,9 @@ When a certain (server-side) event occurs, the background worker passes the even
|
||||
|
||||
{{ image("plugin/enable_events.png", "Enable event integration") }}
|
||||
|
||||
!!! info "Worker debugging"
|
||||
As the events are offloaded to a background worker debugging the `process_event()` function need some extra consideration. Please see the [Run background workers](../../develop/devcontainer.md#run-background-workers) section for further information.
|
||||
|
||||
## Events
|
||||
|
||||
Events are passed through using a string identifier, e.g. `build.completed`
|
||||
|
||||
@@ -183,6 +183,20 @@ The `get_ui_template_previews` feature type can be used to provide custom templa
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Primary Actions
|
||||
|
||||
The `get_ui_primary_actions` method can be used to provide custom primary action, which are rendered in the header of the page, next to the title/name and any status indicators. These primary actions are typically used to provide quick access to common actions related to the current page.
|
||||
|
||||
::: plugin.base.ui.mixins.UserInterfaceMixin.get_ui_primary_actions
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
extra:
|
||||
show_source: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
## Plugin Context
|
||||
|
||||
When rendering certain content in the user interface, the rendering functions are passed a `context` object which contains information about the current page being rendered. The type of the `context` object is defined in the `PluginContext` file:
|
||||
|
||||
@@ -9,11 +9,10 @@ For complicated plugins it makes sense to add unit tests the code to ensure that
|
||||
|
||||
For plugin testing the following environment variables must be set to True:
|
||||
|
||||
| Name | Function | Value |
|
||||
| ---- | -------- | ----- |
|
||||
| INVENTREE_PLUGINS_ENABLED | Enables the use of 3rd party plugins | True |
|
||||
| INVENTREE_PLUGIN_TESTING | Enables enables all plugins no matter of their active state in the db or built-in flag | True |
|
||||
| INVENTREE_PLUGIN_TESTING_SETUP | Enables the url mixin | True |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_PLUGINS_ENABLED") }} Enables the use of 3rd party plugins |
|
||||
{{ configsetting("INVENTREE_PLUGIN_TESTING") }} Enables enables all plugins no matter of their active state in the db or built-in flag |
|
||||
{{ configsetting("INVENTREE_PLUGIN_TESTING_SETUP") }} Enables the url mixin |
|
||||
|
||||
### Test Program
|
||||
|
||||
|
||||
@@ -173,8 +173,144 @@ Generate a list of all active customers:
|
||||
|
||||
More advanced database filtering should be achieved using a [report plugin](../plugins/mixins/report.md), and adding custom context data to the report template.
|
||||
|
||||
## List Helpers
|
||||
|
||||
The following helper functions are available for working with list (or list-like) data structures:
|
||||
|
||||
### length
|
||||
|
||||
Return the length of a list (or list-like) data structure. Note that this will also work for other data structures which support the `len()` function, such as strings, dictionaries or querysets:
|
||||
|
||||
::: report.templatetags.report.length
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### first
|
||||
|
||||
Return the first element of a list (or list-like) data structure:
|
||||
|
||||
::: report.templatetags.report.first
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
|
||||
### last
|
||||
|
||||
Return the last element of a list (or list-like) data structure:
|
||||
|
||||
::: report.templatetags.report.last
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### reverse
|
||||
|
||||
Return a list (or list-like) data structure in reverse order:
|
||||
|
||||
::: report.templatetags.report.reverse
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### truncate
|
||||
|
||||
Return a truncated version of a list (or list-like) data structure, containing only the first N elements:
|
||||
|
||||
::: report.templatetags.report.truncate
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
## String Formatting
|
||||
|
||||
### strip
|
||||
|
||||
Return a string with leading and trailing whitespace removed:
|
||||
|
||||
::: report.templatetags.report.strip
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
|
||||
### lstrip
|
||||
|
||||
Return a string with leading whitespace removed:
|
||||
|
||||
::: report.templatetags.report.lstrip
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### rstrip
|
||||
|
||||
Return a string with trailing whitespace removed:
|
||||
|
||||
::: report.templatetags.report.rstrip
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### split
|
||||
|
||||
Return a list of substrings by splitting a string based on a specified separator:
|
||||
|
||||
::: report.templatetags.report.split
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### join
|
||||
|
||||
Return a string by joining a list of strings into a single string, using a specified separator:
|
||||
|
||||
::: report.templatetags.report.join
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### replace
|
||||
|
||||
Return a string where occurrences of a specified substring are replaced with another substring:
|
||||
|
||||
::: report.templatetags.report.replace
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### lowercase
|
||||
|
||||
Return a string with all characters converted to lowercase:
|
||||
|
||||
::: report.templatetags.report.lowercase
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### uppercase
|
||||
|
||||
Return a string with all characters converted to uppercase:
|
||||
|
||||
::: report.templatetags.report.uppercase
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### titlecase
|
||||
|
||||
Return a string with the first character of each word converted to uppercase and the remaining characters converted to lowercase:
|
||||
|
||||
::: report.templatetags.report.titlecase
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
## Number Formatting
|
||||
|
||||
A number of helper functions are available for formatting numbers in a particular way. These can be used to format numbers according to a particular number of decimal places, or to add leading zeros, for example.
|
||||
|
||||
### format_number
|
||||
|
||||
The helper function `format_number` allows for some common number formatting options. It takes a number (or a number-like string) as an input, as well as some formatting arguments. It returns a *string* containing the formatted number:
|
||||
@@ -547,14 +683,18 @@ You can add asset images to the reports and labels by using the `{% raw %}{% ass
|
||||
|
||||
## Parameters
|
||||
|
||||
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:
|
||||
If you need to reference a parameter for a particular model instance, within the context of your template, you can use the `parameter` template tag:
|
||||
|
||||
### parameter
|
||||
|
||||
This returns a [Parameter](../concepts/parameters.md) object which contains the value of the parameter, as well as any associated metadata (e.g. units, description, etc).
|
||||
|
||||
::: report.templatetags.report.parameter
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### Example
|
||||
#### Example
|
||||
|
||||
The following example assumes that you have a report or label which contains a valid [Part](../part/index.md) instance:
|
||||
|
||||
@@ -580,6 +720,27 @@ A [Parameter](../concepts/parameters.md) has the following available attributes:
|
||||
| Units | The *units* of the parameter (e.g. "km") |
|
||||
| Template | A reference to a [ParameterTemplate](../concepts/parameters.md#parameter-templates) |
|
||||
|
||||
### parameter_value
|
||||
|
||||
To access just the value of a parameter, use the `parameter_value` template tag:
|
||||
|
||||
::: report.templatetags.report.parameter_value
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
#### Example
|
||||
|
||||
```
|
||||
{% raw %}
|
||||
{% load report %}
|
||||
|
||||
{% parameter_value part "length" backup_value="3"as length_value %}
|
||||
Part: {{ part.name }}<br>
|
||||
Length: {{ length_value }}
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
## Rendering Markdown
|
||||
|
||||
Some data fields (such as the *Notes* field available on many internal database models) support [markdown formatting](https://en.wikipedia.org/wiki/Markdown). To render markdown content in a custom report, there are template filters made available through the [django-markdownify](https://github.com/erwinmatijsen/django-markdownify) library. This library provides functionality for converting markdown content to HTML representation, allowing it to be then rendered to PDF by the InvenTree report generation pipeline.
|
||||
|
||||
@@ -234,3 +234,4 @@ The following [global settings](../settings/global.md) are available for sales o
|
||||
{{ globalsetting("SALESORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
{{ globalsetting("SALESORDER_SHIP_COMPLETE") }}
|
||||
{{ globalsetting("SALESORDER_SHIPMENT_REQUIRES_CHECK") }}
|
||||
{{ globalsetting("SALESORDER_BLOCK_INCOMPLETE_ITEM_TESTS")}}
|
||||
|
||||
@@ -29,10 +29,10 @@ The first step is to ensure that the required provider modules are installed, vi
|
||||
|
||||
There are two variables in the configuration file which define the operation of SSO:
|
||||
|
||||
| Environment Variable |Configuration File | Description | More Info |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SOCIAL_BACKENDS | `social_backends` | A *list* of provider backends enabled for the InvenTree instance | [django-allauth docs](https://docs.allauth.org/en/latest/installation/quickstart.html) |
|
||||
| INVENTREE_SOCIAL_PROVIDERS | `social_providers` | A *dict* of settings specific to the installed providers | [provider documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_SOCIAL_BACKENDS") }} A *list* of [social provider backends](https://docs.allauth.org/en/latest/installation/quickstart.html) enabled for the InvenTree instance |
|
||||
{{ configsetting("INVENTREE_SOCIAL_PROVIDERS") }} A *dict* of settings specific to the [installed providers](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) |
|
||||
|
||||
|
||||
In the example below, SSO provider modules are activated for *google*, *github* and *microsoft*. Specific configuration options are specified for the *microsoft* provider module:
|
||||
|
||||
|
||||
@@ -55,19 +55,12 @@ The admin interface allows *staff* users the ability to directly view / add / ed
|
||||
|
||||
#### Access Backend Admin Interface
|
||||
|
||||
To access the admin interface, select the "Admin" option from the drop-down user menu in the top-right corner of the screen.
|
||||
|
||||
|
||||
!!! info "Staff Only"
|
||||
Only users with staff access will be able to see the "Admin" option
|
||||
To directly access the admin interface, append /admin/ to the InvenTree site URL - e.g. http://localhost:8000/admin/.
|
||||
|
||||
An administration panel will be presented as shown below:
|
||||
|
||||
{{ image("admin/admin.png", "Admin panel") }}
|
||||
|
||||
!!! info "Admin URL"
|
||||
To directly access the admin interface, append /admin/ to the InvenTree site URL - e.g. http://localhost:8000/admin/
|
||||
|
||||
#### View Database Objects
|
||||
|
||||
Database objects can be listed and filtered directly. The image below shows an example of displaying existing part categories.
|
||||
|
||||
@@ -105,6 +105,11 @@ A potentially critical mismatch between the backup environment and the current r
|
||||
|
||||
While using [invoke](../start/invoke.md), this can be overridden with the `--restore-allow-newer-version` flag.
|
||||
|
||||
#### INVE-E17
|
||||
**Error rendering component**
|
||||
|
||||
An error occurred while rendering a component in the frontend. Typically this is caused by a browser caching issue, and can be resolved by clearing the browser cache and refreshing the page. If the issue persists, check the browser console for more information about the error.
|
||||
|
||||
### INVE-W (InvenTree Warning)
|
||||
Warnings - These are non-critical errors which should be addressed when possible.
|
||||
|
||||
@@ -210,6 +215,17 @@ The environment in which the backup was taken does not match the current environ
|
||||
|
||||
This warning will not prevent you from restoring the backup but it is recommended to ensure the mentioned issues are resolved before restoring the backup to prevent issues with the restored instance.
|
||||
|
||||
#### INVE-W14
|
||||
**Elevated privileges - Backend**
|
||||
|
||||
A user is logged in with elevated privileges. This might be a superuser or a administrator user. These types of users have elevated permissions and should not be used for regular usage.
|
||||
Use separate accounts for administrative tasks and regular usage to reduce risk. Make sure to review the [permission documentation](../settings/permissions.md#dangerous-user-flags).
|
||||
|
||||
#### INVE-W15
|
||||
**Process interrupted by user - Backend**
|
||||
|
||||
A process was interrupted by the user, likely by a keyboard interrupt. This might lead to issues with the process that was interrupted, as it might not have completed its task. This is especially relevant for processes that are not idempotent or that do not have a good rollback mechanism.
|
||||
|
||||
|
||||
### INVE-I (InvenTree Information)
|
||||
Information — These are not errors but information messages. They might point out potential issues or just provide information.
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
title: Exporting Data
|
||||
---
|
||||
|
||||
## Exporting Data
|
||||
|
||||
InvenTree provides data export functionality for a variety of data types. Most data tables provide an "Download" button, which allows the user to export the data in a variety of formats.
|
||||
|
||||
In the top right corner of the table, click the "Download" button to export the data in the table.
|
||||
|
||||
{{ image("admin/export.png", "Download") }}
|
||||
|
||||
This will present a dialog box with the available export options:
|
||||
|
||||
{{ image("admin/export_options.png", "Export Dialog") }}
|
||||
|
||||
## Plugin Support
|
||||
|
||||
InvenTree plugins can also provide custom export functionality for specific data types. If a plugin provides export functionality, it will be listed in the export options.
|
||||
|
||||
Refer to the [export plugin mixin documentation](../plugins/mixins/export.md) for more information on how to create export plugins.
|
||||
|
||||
## API Export
|
||||
|
||||
Data can also be exported via the InvenTree REST API, by appending the appropriate format suffix (and other export options) to the API endpoint URL.
|
||||
@@ -27,14 +27,20 @@ Configuration of basic server settings:
|
||||
{{ globalsetting("INVENTREE_INSTANCE_TITLE") }}
|
||||
{{ globalsetting("INVENTREE_INSTANCE_ID", default="Randomly generated value") }}
|
||||
{{ globalsetting("INVENTREE_ANNOUNCE_ID") }}
|
||||
{{ globalsetting("INVENTREE_SHOW_SUPERUSER_BANNER") }}
|
||||
{{ globalsetting("INVENTREE_SHOW_ADMIN_BANNER") }}
|
||||
{{ globalsetting("INVENTREE_RESTRICT_ABOUT") }}
|
||||
{{ globalsetting("DISPLAY_FULL_NAMES") }}
|
||||
{{ globalsetting("DISPLAY_PROFILE_INFO") }}
|
||||
{{ globalsetting("INVENTREE_UPDATE_CHECK_INTERVAL") }}
|
||||
{{ globalsetting("INVENTREE_DOWNLOAD_FROM_URL") }}
|
||||
{{ globalsetting("INVENTREE_DOWNLOAD_IMAGE_MAX_SIZE") }}
|
||||
{{ globalsetting("INVENTREE_DOWNLOAD_FROM_URL_USER_AGENT") }}
|
||||
{{ globalsetting("WEEK_STARTS_ON") }}
|
||||
{{ globalsetting("INVENTREE_UPLOAD_MAX_SIZE") }}
|
||||
{{ globalsetting("INVENTREE_STRICT_URLS") }}
|
||||
|
||||
Configuration of various scheduled tasks:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("INVENTREE_UPDATE_CHECK_INTERVAL") }}
|
||||
{{ globalsetting("INVENTREE_BACKUP_ENABLE") }}
|
||||
{{ globalsetting("INVENTREE_BACKUP_DAYS") }}
|
||||
{{ globalsetting("INVENTREE_DELETE_TASKS_DAYS") }}
|
||||
@@ -156,6 +162,7 @@ Configuration of label printing:
|
||||
{{ globalsetting("PART_ALLOW_DUPLICATE_IPN") }}
|
||||
{{ globalsetting("PART_ALLOW_EDIT_IPN") }}
|
||||
{{ globalsetting("PART_ALLOW_DELETE_FROM_ASSEMBLY") }}
|
||||
{{ globalsetting("PART_ENABLE_LOCKING") }}
|
||||
{{ globalsetting("PART_ENABLE_REVISION") }}
|
||||
{{ globalsetting("PART_REVISION_ASSEMBLY_ONLY") }}
|
||||
{{ globalsetting("PART_NAME_FORMAT") }}
|
||||
@@ -170,6 +177,7 @@ Configuration of label printing:
|
||||
{{ globalsetting("PART_SALABLE") }}
|
||||
{{ globalsetting("PART_VIRTUAL") }}
|
||||
{{ globalsetting("PART_COPY_BOM") }}
|
||||
{{ globalsetting("PART_BOM_ALLOW_ZERO_QUANTITY") }}
|
||||
{{ globalsetting("PART_COPY_PARAMETERS") }}
|
||||
{{ globalsetting("PART_COPY_TESTS") }}
|
||||
{{ globalsetting("PART_CATEGORY_PARAMETERS") }}
|
||||
@@ -203,6 +211,8 @@ Configuration of stock item options
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("SERIAL_NUMBER_GLOBALLY_UNIQUE") }}
|
||||
{{ globalsetting("STOCK_ALLOW_EDIT_SERIAL") }}
|
||||
{{ globalsetting("STOCK_ALLOW_DELETE_SERIALIZED") }}
|
||||
{{ globalsetting("STOCK_DELETE_DEPLETED_DEFAULT") }}
|
||||
{{ globalsetting("STOCK_BATCH_CODE_TEMPLATE") }}
|
||||
{{ globalsetting("STOCK_ENABLE_EXPIRY") }}
|
||||
@@ -232,6 +242,10 @@ Refer to the [sales order settings](../sales/sales_order.md#sales-order-settings
|
||||
|
||||
Refer to the [return order settings](../sales/return_order.md#return-order-settings).
|
||||
|
||||
### Transfer Orders
|
||||
|
||||
Refer to the [transfer order settings](../stock/transfer_order.md#transfer-order-settings).
|
||||
|
||||
### Plugin Settings
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
|
||||
@@ -28,7 +28,8 @@ InvenTree functionality is split into a number of distinct roles. A group will h
|
||||
| Role | Description |
|
||||
| ---- | ----------- |
|
||||
| **Admin** | The *admin* role is related to assigning user permissions. |
|
||||
| **Build** | The *build* role is related to accessing Build Order and Bill of Materials data |
|
||||
| **BOM** | The *bom* role is related to accessing Bill of Materials data |
|
||||
| **Build** | The *build* role is related to accessing manufacturing / Build Order |
|
||||
| **Part** | The *part* role is related to accessing Part data |
|
||||
| **Part Category** | The *part category* role is related to accessing Part Category data |
|
||||
| **Purchase Order** | The *purchase* role is related to accessing Purchase Order data |
|
||||
@@ -50,6 +51,17 @@ Within each role, there are four levels of available permissions:
|
||||
| **Add** | The *add* permission allows the user to add / create database records associated with the particular role |
|
||||
| **Delete** | The *delete* permission allows the user to delete / remove database records associated with the particular role |
|
||||
|
||||
## Dangerous User Flags
|
||||
|
||||
In addition to the above permissions, there are two special flags that can be assigned to a user:
|
||||
- **Staff** - A user with the *staff* flag is able to access the admin interface, and can trigger dangerous actions that might have a security impact such as changing parsable files on the server (templates / reports / plugins). Some of these actions require the *admin* role to be assigned as well.
|
||||
- **Superuser** - A user with the *superuser* flag is able to access and change all data and functions of InvenTree. A superuser can modify and access all data that the InvenTree installation / server has access to - including shell access on the server OS itself. This is a very powerful flag, and should be used with caution.
|
||||
|
||||
It is strongly recommended to register any users with staff / superuser flags with strong MFA methods to reduce the risk of unauthorized access. These accounts should be used with caution, and should not be used for day-to-day operations.
|
||||
|
||||
Practicing account tiering is strongly recommended.
|
||||
|
||||
|
||||
## Admin Interface Permissions
|
||||
|
||||
If a user does not have the required permissions to perform a certain action in the admin interface, those options not be displayed.
|
||||
|
||||
@@ -27,8 +27,10 @@ The *Display Settings* screen shows general display configuration options:
|
||||
{{ usersetting("FORMS_CLOSE_USING_ESCAPE") }}
|
||||
{{ usersetting("DISPLAY_STOCKTAKE_TAB") }}
|
||||
{{ usersetting("SHOW_FULL_CATEGORY_IN_TABLES")}}
|
||||
{{ usersetting("SHOW_BOM_SUBASSEMBLY_LEVELS")}}
|
||||
{{ usersetting("ENABLE_LAST_BREADCRUMB") }}
|
||||
{{ usersetting("SHOW_FULL_LOCATION_IN_TABLES") }}
|
||||
{{ usersetting("DISPLAY_ITEMS_FINAL_LEVEL") }}
|
||||
|
||||
### Search Settings
|
||||
|
||||
|
||||
@@ -51,46 +51,44 @@ You can link your InvenTree server to an LDAP server.
|
||||
|
||||
Next you can start configuring the connection. Either use the config file or set the environment variables.
|
||||
|
||||
| config key | ENV Variable | Description |
|
||||
| --- | --- | --- |
|
||||
| `ldap.enabled` | `INVENTREE_LDAP_ENABLED` | Set this to `True` to enable LDAP. |
|
||||
| `ldap.debug` | `INVENTREE_LDAP_DEBUG` | Set this to `True` to activate debug mode, useful for troubleshooting ldap configurations. |
|
||||
| `ldap.server_uri` | `INVENTREE_LDAP_SERVER_URI` | LDAP Server URI, e.g. `ldaps://example.org` |
|
||||
| `ldap.start_tls` | `INVENTREE_LDAP_START_TLS` | Enable TLS encryption over the standard LDAP port, [see](https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-start-tls). (You can set TLS options via `ldap.global_options`) |
|
||||
| `ldap.bind_dn` | `INVENTREE_LDAP_BIND_DN` | LDAP bind dn, e.g. `cn=admin,dc=example,dc=org` |
|
||||
| `ldap.bind_password` | `INVENTREE_LDAP_BIND_PASSWORD` | LDAP bind password |
|
||||
| `ldap.search_base_dn` | `INVENTREE_LDAP_SEARCH_BASE_DN` | LDAP search base dn, e.g. `cn=Users,dc=example,dc=org` |
|
||||
| `ldap.user_dn_template` | `INVENTREE_LDAP_USER_DN_TEMPLATE` | use direct bind as auth user, `ldap.bind_dn` and `ldap.bin_password` is not necessary then, e.g. `uid=%(user)s,dc=example,dc=org` |
|
||||
| `ldap.global_options` | `INVENTREE_LDAP_GLOBAL_OPTIONS` | set advanced options as dict, e.g. TLS settings. For a list of all available options, see [python-ldap docs](https://www.python-ldap.org/en/latest/reference/ldap.html#ldap-options). (keys and values starting with OPT_ get automatically converted to `python-ldap` keys) |
|
||||
| `ldap.search_filter_str`| `INVENTREE_LDAP_SEARCH_FILTER_STR` | LDAP search filter str, default: `uid=%(user)s` |
|
||||
| `ldap.user_attr_map` | `INVENTREE_LDAP_USER_ATTR_MAP` | LDAP <-> InvenTree user attribute map, can be json if used as env, in yml directly specify the object. default: `{"first_name": "givenName", "last_name": "sn", "email": "mail"}` |
|
||||
| `ldap.always_update_user` | `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | Always update the user on each login, default: `true` |
|
||||
| `ldap.cache_timeout` | `INVENTREE_LDAP_CACHE_TIMEOUT` | cache timeout to reduce traffic with LDAP server, default: `3600` (1h) |
|
||||
| `ldap.group_search` | `INVENTREE_LDAP_GROUP_SEARCH` | Base LDAP DN for group searching; required to enable group features |
|
||||
| `ldap.group_object_class` | `INVENTREE_LDAP_GROUP_OBJECT_CLASS` | The string to pass to the LDAP group search `(objectClass=<...>)`, default: `groupOfUniqueNames` |
|
||||
| `ldap.mirror_groups` | `INVENTREE_LDAP_MIRROR_GROUPS` | If `True`, mirror a user's LDAP group membership in the Django database, default: `False` |
|
||||
| `ldap.group_type_class` | `INVENTREE_LDAP_GROUP_TYPE_CLASS` | The group class to be imported from `django_auth_ldap.config` as a string, default: `'GroupOfUniqueNamesType'`|
|
||||
| `ldap.group_type_class_args` | `INVENTREE_LDAP_GROUP_TYPE_CLASS_ARGS` | A `list` of positional args to pass to the LDAP group type class, default `[]` |
|
||||
| `ldap.group_type_class_kwargs` | `INVENTREE_LDAP_GROUP_TYPE_CLASS_KWARGS` | A `dict` of keyword args to pass to the LDAP group type class, default `{'name_attr': 'cn'}` |
|
||||
| `ldap.require_group` | `INVENTREE_LDAP_REQUIRE_GROUP` | If set, users _must_ be in this group to log in to InvenTree |
|
||||
| `ldap.deny_group` | `INVENTREE_LDAP_DENY_GROUP` | If set, users _must not_ be in this group to log in to InvenTree |
|
||||
| `ldap.user_flags_by_group` | `INVENTREE_LDAP_USER_FLAGS_BY_GROUP` | LDAP group to InvenTree user flag map, can be json if used as env, in yml directly specify the object. See config template for example, default: `{}` |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_LDAP_ENABLED") }} Enable LDAP support |
|
||||
{{ configsetting("INVENTREE_LDAP_DEBUG") }} Set this to `True` to activate debug mode, useful for troubleshooting ldap configurations. |
|
||||
| `INVENTREE_LDAP_SERVER_URI` | `ldap.server_uri` | LDAP Server URI, e.g. `ldaps://example.org` |
|
||||
| `INVENTREE_LDAP_START_TLS` | `ldap.start_tls` | Enable TLS encryption over the standard LDAP port, [see](https://django-auth-ldap.readthedocs.io/en/latest/reference.html#auth-ldap-start-tls). (You can set TLS options via `ldap.global_options`) |
|
||||
| `INVENTREE_LDAP_BIND_DN` | `ldap.bind_dn` | LDAP bind dn, e.g. `cn=admin,dc=example,dc=org` |
|
||||
| `INVENTREE_LDAP_BIND_PASSWORD` | `ldap.bind_password` | LDAP bind password |
|
||||
| `INVENTREE_LDAP_SEARCH_BASE_DN` | `ldap.search_base_dn` | LDAP search base dn, e.g. `cn=Users,dc=example,dc=org` |
|
||||
| `INVENTREE_LDAP_USER_DN_TEMPLATE` | `ldap.user_dn_template` | use direct bind as auth user, `ldap.bind_dn` and `ldap.bin_password` is not necessary then, e.g. `uid=%(user)s,dc=example,dc=org` |
|
||||
| `INVENTREE_LDAP_GLOBAL_OPTIONS` | `ldap.global_options` | set advanced options as dict, e.g. TLS settings. For a list of all available options, see [python-ldap docs](https://www.python-ldap.org/en/latest/reference/ldap.html#ldap-options). (keys and values starting with OPT_ get automatically converted to `python-ldap` keys) |
|
||||
| `INVENTREE_LDAP_SEARCH_FILTER_STR`| `ldap.search_filter_str` | LDAP search filter str, default: `uid=%(user)s` |
|
||||
| `INVENTREE_LDAP_USER_ATTR_MAP` | `ldap.user_attr_map` | LDAP <-> InvenTree user attribute map, can be json if used as env, in yml directly specify the object. default: `{"first_name": "givenName", "last_name": "sn", "email": "mail"}` |
|
||||
| `INVENTREE_LDAP_ALWAYS_UPDATE_USER` | `ldap.always_update_user` | Always update the user on each login, default: `true` |
|
||||
| `INVENTREE_LDAP_CACHE_TIMEOUT` | `ldap.cache_timeout` | cache timeout to reduce traffic with LDAP server, default: `3600` (1h) |
|
||||
| `INVENTREE_LDAP_GROUP_SEARCH` | `ldap.group_search` | Base LDAP DN for group searching; required to enable group features |
|
||||
| `INVENTREE_LDAP_GROUP_OBJECT_CLASS` | `ldap.group_object_class` | The string to pass to the LDAP group search `(objectClass=<...>)`, default: `groupOfUniqueNames` |
|
||||
| `INVENTREE_LDAP_MIRROR_GROUPS` | `ldap.mirror_groups` | If `True`, mirror a user's LDAP group membership in the Django database, default: `False` |
|
||||
| `INVENTREE_LDAP_GROUP_TYPE_CLASS` | `ldap.group_type_class` | The group class to be imported from `django_auth_ldap.config` as a string, default: `'GroupOfUniqueNamesType'`|
|
||||
| `INVENTREE_LDAP_GROUP_TYPE_CLASS_ARGS` | `ldap.group_type_class_args` | A `list` of positional args to pass to the LDAP group type class, default `[]` |
|
||||
| `INVENTREE_LDAP_GROUP_TYPE_CLASS_KWARGS` | `ldap.group_type_class_kwargs` | A `dict` of keyword args to pass to the LDAP group type class, default `{'name_attr': 'cn'}` |
|
||||
| `INVENTREE_LDAP_REQUIRE_GROUP` | `ldap.require_group` | If set, users _must_ be in this group to log in to InvenTree |
|
||||
| `INVENTREE_LDAP_DENY_GROUP` | `ldap.deny_group` | If set, users _must not_ be in this group to log in to InvenTree |
|
||||
| `INVENTREE_LDAP_USER_FLAGS_BY_GROUP` | `ldap.user_flags_by_group` | LDAP group to InvenTree user flag map, can be json if used as env, in yml directly specify the object. See config template for example, default: `{}` |
|
||||
|
||||
## Tracing support
|
||||
|
||||
Starting with 0.14.0 InvenTree supports sending traces, logs and metrics to OpenTelemetry compatible endpoints (both HTTP and gRPC). A [list of vendors](https://opentelemetry.io/ecosystem/vendors) is available on the project site.
|
||||
This can be used to track usage and performance of the InvenTree backend and connected services like databases, caches and more.
|
||||
|
||||
| config key | ENV Variable | Description |
|
||||
| --- | --- | --- |
|
||||
| `tracing.enabled` | `INVENTREE_TRACING_ENABLED` | Set this to `True` to enable OpenTelemetry. |
|
||||
| `tracing.endpoint` | `INVENTREE_TRACING_ENDPOINT` | General endpoint for information (not specific trace/log url) |
|
||||
| `tracing.headers` | `INVENTREE_TRACING_HEADERS` | HTTP headers that should be send with every request (often used for authentication). Format as a dict. |
|
||||
| `tracing.auth.basic` | `INVENTREE_TRACING_AUTH_BASIC` | Auth headers that should be send with every requests (will be encoded to b64 and overwrite auth headers) |
|
||||
| `tracing.is_http` | `INVENTREE_TRACING_IS_HTTP` | Are the endpoints HTTP (True, default) or gRPC (False) |
|
||||
| `tracing.append_http` | `INVENTREE_TRACING_APPEND_HTTP` | Append default url routes (v1) to `tracing.endpoint` |
|
||||
| `tracing.console` | `INVENTREE_TRACING_CONSOLE` | Print out all exports (additionally) to the console for debugging. Do not use in production |
|
||||
| `tracing.resources` | `INVENTREE_TRACING_RESOURCES` | Add additional resources to all exports. This can be used to add custom tags to the traces. Format as a dict. |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_TRACING_ENABLED") }} Enable OpenTelemetry |
|
||||
{{ configsetting("INVENTREE_TRACING_ENDPOINT") }} General endpoint for information (not specific trace/log url) |
|
||||
{{ configsetting("INVENTREE_TRACING_HEADERS") }} HTTP headers that should be send with every request (often used for authentication). Format as a dict. |
|
||||
{{ configsetting("INVENTREE_TRACING_AUTH") }} Auth headers that should be send with every requests (will be encoded to b64 and overwrite auth headers) |
|
||||
{{ configsetting("INVENTREE_TRACING_IS_HTTP") }} Are the endpoints HTTP (True, default) or gRPC (False) |
|
||||
{{ configsetting("INVENTREE_TRACING_APPEND_HTTP") }} Append default url routes (v1) to `tracing.endpoint` |
|
||||
{{ configsetting("INVENTREE_TRACING_CONSOLE") }} Print out all exports (additionally) to the console for debugging. Do not use in production |
|
||||
{{ configsetting("INVENTREE_TRACING_RESOURCES") }} Add additional resources to all exports. This can be used to add custom tags to the traces. Format as a dict. |
|
||||
|
||||
## Multi Site Support
|
||||
|
||||
@@ -99,7 +97,6 @@ If your InvenTree instance is used in a multi-site environment, you can enable m
|
||||
!!! tip "Django Documentation"
|
||||
For more information on multi-site support, refer to the [Django documentation]({% include "django.html" %}/ref/contrib/sites/).
|
||||
|
||||
| Environment Variable | Config Key | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SITE_MULTI | site_multi | Enable multiple sites | False |
|
||||
| INVENTREE_SITE_ID | site_id | Specify a fixed site ID | _Not specified_ |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_SITE_MULTI") }} Enable multiple sites |
|
||||
{{ configsetting("INVENTREE_SITE_ID") }} Specify a fixed site ID |
|
||||
|
||||
@@ -17,19 +17,19 @@ The django-dbbackup library provides [multiple configuration options](https://ar
|
||||
|
||||
The following configuration options are available for backup:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_BACKUP_STORAGE | backup_storage | Backup storage backend. Refer to the [storage backend documentation](#storage-backend). | django.core.files.storage.FileSystemStorage |
|
||||
| INVENTREE_BACKUP_DIR | backup_dir | Backup storage directory. | *No default* |
|
||||
| INVENTREE_BACKUP_OPTIONS | backup_options | Specific options for the selected storage backend (dict) | *No default* |
|
||||
| INVENTREE_BACKUP_CONNECTOR_OPTIONS | backup_connector_options | Specific options for the database connector (dict). Refer to the [database connector options](#database-connector). | *No default* |
|
||||
| INVENTREE_BACKUP_SEND_EMAIL | backup_send_email | If True, an email is sent to the site admin when an error occurs during a backup or restore procedure. | False |
|
||||
| INVENTREE_BACKUP_EMAIL_PREFIX | backup_email_prefix | Prefix for the subject line of backup-related emails. | `[InvenTree Backup]` |
|
||||
| INVENTREE_BACKUP_GPG_RECIPIENT | backup_gpg_recipient | Specify GPG recipient if using encryption for backups. | *No default* |
|
||||
| INVENTREE_BACKUP_DATE_FORMAT | backup_date_format | Date format string used to format timestamps in backup filenames. | `%Y-%m-%d-%H%M%S` |
|
||||
| INVENTREE_BACKUP_DATABASE_FILENAME_TEMPLATE | backup_database_filename_template | Template string used to generate database backup filenames. | `InvenTree-db-{datetime}.{extension}` |
|
||||
| INVENTREE_BACKUP_MEDIA_FILENAME_TEMPLATE | backup_media_filename_template | Template string used to generate media backup filenames. | `InvenTree-media-{datetime}.{extension}` |
|
||||
| INVENTREE_BACKUP_RESTORE_ALLOW_NEWER_VERSION | backup_restore_allow_newer_version | If True, allows restoring a backup created with a newer version of InvenTree. This is dangerous as it can lead to hard-to-debug data loss. | False |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_BACKUP_STORAGE") }} Backup storage backend. Refer to the [storage backend documentation](#storage-backend). |
|
||||
{{ configsetting("INVENTREE_BACKUP_DIR") }} Backup storage directory. |
|
||||
{{ configsetting("INVENTREE_BACKUP_OPTIONS") }} Specific options for the selected storage backend (dict) |
|
||||
{{ configsetting("INVENTREE_BACKUP_CONNECTOR_OPTIONS") }} Specific options for the database connector (dict). Refer to the [database connector options](#database-connector) |
|
||||
{{ configsetting("INVENTREE_BACKUP_SEND_EMAIL") }} If True, an email is sent to the site admin when an error occurs during a backup or restore procedure. |
|
||||
{{ configsetting("INVENTREE_BACKUP_EMAIL_PREFIX") }} Prefix for the subject line of backup-related emails. |
|
||||
{{ configsetting("INVENTREE_BACKUP_GPG_RECIPIENT") }} Specify GPG recipient if using encryption for backups. |
|
||||
{{ configsetting("INVENTREE_BACKUP_DATE_FORMAT") }} Date format string used to format timestamps in backup filenames. |
|
||||
{{ configsetting("INVENTREE_BACKUP_DATABASE_FILENAME_TEMPLATE") }} Template string used to generate database backup filenames.
|
||||
{{ configsetting("INVENTREE_BACKUP_MEDIA_FILENAME_TEMPLATE") }} Template string used to generate media backup filenames. |
|
||||
{{ configsetting("INVENTREE_BACKUP_RESTORE_ALLOW_NEWER_VERSION") }} If True, allows restoring a backup created with a newer version of InvenTree. This is dangerous as it can lead to hard-to-debug data loss. |
|
||||
|
||||
|
||||
### Storage Backend
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ Environment variable settings generally use the `INVENTREE_` prefix, and are all
|
||||
!!! warning "Available Variables"
|
||||
Some configuration options cannot be set via environment variables. Refer to the documentation below.
|
||||
|
||||
!!! info "Environment Variable Priority"
|
||||
Note that a provided environment variable will override the value provided in the configuration file.
|
||||
|
||||
#### List Values
|
||||
|
||||
To specify a list value in an environment variable, use a comma-separated list. For example, to specify a list of trusted origins:
|
||||
@@ -58,14 +61,13 @@ INVENTREE_TRUSTED_ORIGINS='https://inventree.example.com:8443,https://stock.exam
|
||||
|
||||
The following basic options are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* |
|
||||
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
|
||||
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) | True |
|
||||
| INVENTREE_ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin |
|
||||
| INVENTREE_LANGUAGE | language | Default language | en-us |
|
||||
| INVENTREE_AUTO_UPDATE | auto_update | Database migrations will be run automatically | False |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_SITE_URL") }} Specify a fixed site URL |
|
||||
{{ configsetting("INVENTREE_TIMEZONE") }} Server timezone |
|
||||
{{ configsetting("INVENTREE_ADMIN_ENABLED") }} Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) |
|
||||
{{ configsetting("INVENTREE_ADMIN_URL") }} URL for accessing [admin interface](../settings/admin.md) |
|
||||
{{ configsetting("INVENTREE_LANGUAGE") }} Default language |
|
||||
{{ configsetting("INVENTREE_AUTO_UPDATE") }} Database migrations will be run automatically |
|
||||
|
||||
### Site URL
|
||||
|
||||
@@ -89,18 +91,17 @@ With "auto update" enabled, the InvenTree server will automatically apply databa
|
||||
|
||||
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 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 |
|
||||
| INVENTREE_WRITE_LOG | write_log | Enable writing of log messages to file at config base | False |
|
||||
| INVENTREE_CONSOLE_LOG | console_log | Enable logging to console | True |
|
||||
| INVENTREE_SCHEMA_LEVEL | schema.level | Set level of added schema extensions detail (0-3) 0 = including no additional detail | 0 |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_DEBUG") }} Enable [debug mode](./index.md#debug-mode) |
|
||||
{{ configsetting("INVENTREE_DB_LOGGING") }} Enable logging of database messages |
|
||||
{{ configsetting("INVENTREE_LOG_LEVEL") }} Set level of logging to terminal |
|
||||
{{ configsetting("INVENTREE_JSON_LOG") }} Log messages as json |
|
||||
{{ configsetting("INVENTREE_WRITE_LOG") }} Enable writing of log messages to file at config base |
|
||||
{{ configsetting("INVENTREE_CONSOLE_LOG") }} Enable logging to console |
|
||||
{{ configsetting("INVENTREE_SCHEMA_LEVEL") }} Set level of added schema extensions detail (0-3) 0 = including no additional detail |
|
||||
{{ configsetting("INVENTREE_DEBUG_QUERYCOUNT") }} Enable support for [django-querycount](../develop/index.md#django-querycount) middleware. |
|
||||
{{ configsetting("INVENTREE_DEBUG_SILK") }} Enable support for [django-silk](../develop/index.md#django-silk) profiling tool. |
|
||||
| `INVENTREE_DEBUG_SILK_PROFILING` | `debug_silk_profiling` | False | Enable detailed profiling in django-silk |
|
||||
|
||||
### Debug Mode
|
||||
|
||||
@@ -118,26 +119,24 @@ Depending on how your InvenTree installation is configured, you will need to pay
|
||||
!!! danger "Not Secure"
|
||||
Allowing access from any host is not secure, and should be adjusted for your installation.
|
||||
|
||||
!!! info "Environment Variables"
|
||||
Note that a provided environment variable will override the value provided in the configuration file.
|
||||
|
||||
!!! success "INVENTREE_SITE_URL"
|
||||
If you have specified the `INVENTREE_SITE_URL`, this will automatically be used as a trusted CSRF and CORS host (see below).
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_ALLOWED_HOSTS | allowed_hosts | List of allowed hosts | `*` |
|
||||
| INVENTREE_TRUSTED_ORIGINS | trusted_origins | List of trusted origins. Refer to the [django documentation]({% include "django.html" %}/ref/settings/#csrf-trusted-origins) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
|
||||
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | `False` |
|
||||
| INVENTREE_CORS_ORIGIN_WHITELIST | cors.whitelist | List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
|
||||
| INVENTREE_CORS_ORIGIN_REGEX | cors.regex | List of regular expressions for CORS whitelisted URL patterns | *Empty list* |
|
||||
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | `True` |
|
||||
| INVENTREE_SITE_LAX_PROTOCOL | site_lax_protocol | Ignore protocol mismatches on INVE-E7 site checks | `True` |
|
||||
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | `False` |
|
||||
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | `False` |
|
||||
| INVENTREE_USE_X_FORWARDED_PROTO | use_x_forwarded_proto | Use forwarded protocol header | `False` |
|
||||
| INVENTREE_SESSION_COOKIE_SECURE | cookie.secure | Enforce secure session cookies | `False` |
|
||||
| INVENTREE_COOKIE_SAMESITE | cookie.samesite | Session cookie mode. Must be one of `Strict | Lax | None | False`. Refer to the [mozilla developer docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) and the [django documentation]({% include "django.html" %}/ref/settings/#std-setting-SESSION_COOKIE_SAMESITE) for more information. | False |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_ALLOWED_HOSTS") }} List of allowed hosts |
|
||||
{{ configsetting("INVENTREE_TRUSTED_ORIGINS", default="Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list.") }} List of trusted origins. Refer to the [django documentation]({% include "django.html" %}/ref/settings/#csrf-trusted-origins) |
|
||||
{{ configsetting("INVENTREE_CORS_ORIGIN_ALLOW_ALL") }} Allow all remote URLS for CORS checks |
|
||||
{{ configsetting("INVENTREE_CORS_ORIGIN_WHITELIST", default="Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list.") }} List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) |
|
||||
{{ configsetting("INVENTREE_CORS_ORIGIN_REGEX") }} List of regular expressions for CORS whitelisted URL patterns |
|
||||
{{ configsetting("INVENTREE_CORS_ALLOW_CREDENTIALS") }} Allow cookies in cross-site requests |
|
||||
{{ configsetting("INVENTREE_SITE_LAX_PROTOCOL") }} Ignore protocol mismatches on INVE-E7 site checks |
|
||||
{{ configsetting("INVENTREE_USE_X_FORWARDED_HOST") }} Use forwarded host header |
|
||||
{{ configsetting("INVENTREE_USE_X_FORWARDED_PORT") }} Use forwarded port header |
|
||||
{{ configsetting("INVENTREE_USE_X_FORWARDED_PROTO") }} Use forwarded protocol header |
|
||||
| `INVENTREE_X_FORWARDED_PROTO_NAME` | `x_forwarded_proto_name` | `HTTP_X_FORWARDED_PROTO` | Name of the header to use for forwarded protocol information |
|
||||
{{ configsetting("INVENTREE_SESSION_COOKIE_SECURE") }} Enforce secure session cookies |
|
||||
{{ configsetting("INVENTREE_COOKIE_SAMESITE") }} Session cookie mode. Must be one of `Strict | Lax | None | False`. Refer to the [mozilla developer docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) and the [django documentation]({% include "django.html" %}/ref/settings/#std-setting-SESSION_COOKIE_SAMESITE) for more information. |
|
||||
|
||||
|
||||
### Debug Mode
|
||||
|
||||
@@ -185,17 +184,15 @@ Proxy configuration can be complex, and any configuration beyond the basic setup
|
||||
|
||||
Refer to the [proxy server documentation](./processes.md#proxy-server) for more information.
|
||||
|
||||
|
||||
## Admin Site
|
||||
|
||||
Django provides a powerful [administrator interface]({% include "django.html" %}/ref/contrib/admin/) which can be used to manage the InvenTree database. This interface is enabled by default, and available at the `/admin/` URL.
|
||||
|
||||
The following admin site configuration options are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the django administrator interface | True |
|
||||
| INVENTREE_ADMIN_URL | admin_url | URL for accessing the admin interface | admin |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_ADMIN_ENABLED") }} Enable the django administrator interface |
|
||||
{{ configsetting("INVENTREE_ADMIN_URL") }} URL for accessing the admin interface |
|
||||
|
||||
!!! warning "Security"
|
||||
Changing the admin URL is a simple way to improve security, but it is not a substitute for proper security practices.
|
||||
@@ -204,12 +201,11 @@ The following admin site configuration options are available:
|
||||
|
||||
An administrator account can be specified using the following environment variables:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_ADMIN_USER | admin_user | Admin account username | *Not specified* |
|
||||
| INVENTREE_ADMIN_PASSWORD | admin_password | Admin account password | *Not specified* |
|
||||
| INVENTREE_ADMIN_PASSWORD_FILE | admin_password_file | Admin account password file | *Not specified* |
|
||||
| INVENTREE_ADMIN_EMAIL | admin_email |Admin account email address | *Not specified* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_ADMIN_USER") }} Admin account username |
|
||||
{{ configsetting("INVENTREE_ADMIN_PASSWORD") }} Admin account password |
|
||||
{{ configsetting("INVENTREE_ADMIN_PASSWORD_FILE") }} Admin account password file |
|
||||
{{ configsetting("INVENTREE_ADMIN_EMAIL") }} Admin account email address |
|
||||
|
||||
You can either specify the password directly using `INVENTREE_ADMIN_PASSWORD`, or you can specify a file containing the password using `INVENTREE_ADMIN_PASSWORD_FILE` (this is useful for nix users).
|
||||
|
||||
@@ -239,12 +235,11 @@ A PEM-encoded file containing the oidc private key can be passed via the environ
|
||||
|
||||
If not specified via environment variables, the fallback files (automatically generated as part of InvenTree installation) will be used.
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SECRET_KEY | secret_key | Raw secret key value | *Not specified* |
|
||||
| INVENTREE_SECRET_KEY_FILE | secret_key_file | File containing secret key value | *Not specified* |
|
||||
| INVENTREE_OIDC_PRIVATE_KEY | oidc_private_key | Raw private key value | *Not specified* |
|
||||
| INVENTREE_OIDC_PRIVATE_KEY_FILE | oidc_private_key_file | File containing private key value in PEM format | *Not specified* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_SECRET_KEY") }} Raw secret key value |
|
||||
{{ configsetting("INVENTREE_SECRET_KEY_FILE") }} File containing secret key value |
|
||||
{{ configsetting("INVENTREE_OIDC_PRIVATE_KEY") }} Raw private key value |
|
||||
{{ configsetting("INVENTREE_OIDC_PRIVATE_KEY_FILE", default="oidc.pem") }} File containing private key value in PEM format |
|
||||
|
||||
## Database Options
|
||||
|
||||
@@ -254,14 +249,14 @@ Database options are specified under the *database* heading in the configuration
|
||||
|
||||
The following database options can be configured:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_DB_ENGINE | database.ENGINE | Database backend | *Not specified* |
|
||||
| INVENTREE_DB_NAME | database.NAME | Database name | *Not specified* |
|
||||
| INVENTREE_DB_USER | database.USER | Database username (if required) | *Not specified* |
|
||||
| INVENTREE_DB_PASSWORD | database.PASSWORD | Database password (if required) | *Not specified* |
|
||||
| INVENTREE_DB_HOST | database.HOST | Database host address (if required) | *Not specified* |
|
||||
| INVENTREE_DB_PORT | database.PORT | Database host port (if required) | *Not specified* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_DB_ENGINE") }} Database backend |
|
||||
{{ configsetting("INVENTREE_DB_NAME") }} Database name |
|
||||
{{ configsetting("INVENTREE_DB_USER") }} Database username (if required) |
|
||||
{{ configsetting("INVENTREE_DB_PASSWORD") }} Database password (if required) |
|
||||
{{ configsetting("INVENTREE_DB_HOST") }} Database host address (if required) |
|
||||
{{ configsetting("INVENTREE_DB_PORT") }} Database host port (if required) |
|
||||
{{ configsetting("INVENTREE_DB_OPTIONS") }} Additional database options (as a JSON object) |
|
||||
|
||||
!!! 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.
|
||||
@@ -270,22 +265,31 @@ The following database options can be configured:
|
||||
|
||||
If running with a PostgreSQL database backend, the following additional options are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_DB_TIMEOUT | database.timeout | Database connection timeout (s) | 2 |
|
||||
| INVENTREE_DB_TCP_KEEPALIVES | database.tcp_keepalives | TCP keepalive | 1 |
|
||||
| INVENTREE_DB_TCP_KEEPALIVES_IDLE | database.tcp_keepalives_idle | Idle TCP keepalive | 1 |
|
||||
| INVENTREE_DB_TCP_KEEPALIVES_INTERNAL | database.tcp_keepalives_internal | Internal TCP keepalive | 1|
|
||||
| INVENTREE_DB_TCP_KEEPALIVES_COUNT | database.tcp_keepalives_count | TCP keepalive count | 5 |
|
||||
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_DB_TIMEOUT", default="2") }} Database connection timeout (s) |
|
||||
| `INVENTREE_DB_TCP_KEEPALIVES` | database.tcp_keepalives | 1 | TCP keepalive |
|
||||
| `INVENTREE_DB_TCP_KEEPALIVES_IDLE` | database.tcp_keepalives_idle | 1 | Idle TCP keepalive |
|
||||
| `INVENTREE_DB_TCP_KEEPALIVES_INTERVAL` | database.tcp_keepalives_interval | 1| TCP keepalive interval |
|
||||
| `INVENTREE_DB_TCP_KEEPALIVES_COUNT` | database.tcp_keepalives_count | 5 | TCP keepalive count |
|
||||
| `INVENTREE_DB_ISOLATION_SERIALIZABLE` | database.serializable | False | Database isolation level configured to "serializable" |
|
||||
|
||||
### MySQL Settings
|
||||
|
||||
If running with a MySQL database backend, the following additional options are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
|
||||
{{ configtable() }}
|
||||
| `INVENTREE_DB_ISOLATION_SERIALIZABLE` | database.serializable | False | Database isolation level configured to "serializable" |
|
||||
|
||||
### SQLite Settings
|
||||
|
||||
!!! warning "SQLite Performance"
|
||||
SQLite is not recommended for production use, and should only be used for testing or development purposes. If you are using SQLite in production, you may want to adjust the following settings to improve performance.
|
||||
|
||||
If running with a SQLite database backend, the following additional options are available:
|
||||
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_DB_TIMEOUT", default="10") }} Database connection timeout (s) |
|
||||
| `INVENTREE_DB_WAL_MODE` | database.wal_mode | True | Enable Write-Ahead Logging (WAL) mode for SQLite databases |
|
||||
|
||||
## Caching
|
||||
|
||||
@@ -303,20 +307,21 @@ Enabling global caching requires connection to a redis server (which is separate
|
||||
|
||||
The following cache settings are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_CACHE_ENABLED | cache.enabled | Enable redis caching | False |
|
||||
| INVENTREE_CACHE_HOST | cache.host | Cache server host | *Not specified* |
|
||||
| INVENTREE_CACHE_PORT | cache.port | Cache server port | 6379 |
|
||||
| INVENTREE_CACHE_PASSWORD | cache.password | Cache server password | none |
|
||||
| INVENTREE_CACHE_USER | cache.user | Cache server username | none |
|
||||
| INVENTREE_CACHE_CONNECT_TIMEOUT | cache.connect_timeout | Cache connection timeout (seconds) | 3 |
|
||||
| INVENTREE_CACHE_TIMEOUT | cache.timeout | Cache timeout (seconds) | 3 |
|
||||
| INVENTREE_CACHE_TCP_KEEPALIVE | cache.tcp_keepalive | Cache TCP keepalive | True |
|
||||
| INVENTREE_CACHE_KEEPALIVE_COUNT | cache.keepalive_count | Cache keepalive count | 5 |
|
||||
| INVENTREE_CACHE_KEEPALIVE_IDLE | cache.keepalive_idle | Cache keepalive idle | 1 |
|
||||
| INVENTREE_CACHE_KEEPALIVE_INTERVAL | cache.keepalive_interval | Cache keepalive interval | 1 |
|
||||
| INVENTREE_CACHE_USER_TIMEOUT | cache.user_timeout | Cache user timeout | 1000 |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_CACHE_ENABLED") }} Enable redis caching |
|
||||
{{ configsetting("INVENTREE_CACHE_HOST") }} Cache server host |
|
||||
{{ configsetting("INVENTREE_CACHE_PORT") }} Cache server port |
|
||||
{{ configsetting("INVENTREE_CACHE_PASSWORD") }} Cache server password |
|
||||
{{ configsetting("INVENTREE_CACHE_USER") }} Cache server username |
|
||||
{{ configsetting("INVENTREE_CACHE_DB") }} Cache server database index |
|
||||
{{ configsetting("INVENTREE_CACHE_CONNECT_TIMEOUT") }} Cache connection timeout (seconds) |
|
||||
{{ configsetting("INVENTREE_CACHE_TIMEOUT") }} Cache timeout (seconds) |
|
||||
{{ configsetting("INVENTREE_CACHE_TCP_KEEPALIVE") }} Cache TCP keepalive |
|
||||
{{ configsetting("INVENTREE_CACHE_KEEPALIVE_COUNT") }} Cache keepalive count |
|
||||
{{ configsetting("INVENTREE_CACHE_KEEPALIVE_IDLE") }} Cache keepalive idle |
|
||||
{{ configsetting("INVENTREE_CACHE_KEEPALIVE_INTERVAL") }} Cache keepalive interval |
|
||||
{{ configsetting("INVENTREE_CACHE_USER_TIMEOUT") }} Cache user timeout |
|
||||
|
||||
|
||||
!!! 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.
|
||||
@@ -327,17 +332,16 @@ To enable [email functionality](../settings/email.md), email settings must be co
|
||||
|
||||
The following email settings are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_EMAIL_BACKEND | email.backend | Email backend module | django.core.mail.backends.smtp.EmailBackend |
|
||||
| INVENTREE_EMAIL_HOST | email.host | Email server host | *Not specified* |
|
||||
| INVENTREE_EMAIL_PORT | email.port | Email server port | 25 |
|
||||
| INVENTREE_EMAIL_USERNAME | email.username | Email account username | *Not specified* |
|
||||
| INVENTREE_EMAIL_PASSWORD | email.password | Email account password | *Not specified* |
|
||||
| INVENTREE_EMAIL_TLS | email.tls | Enable STARTTLS support (commonly port 567) | False |
|
||||
| INVENTREE_EMAIL_SSL | email.ssl | Enable legacy SSL/TLS support (commonly port 465) | False |
|
||||
| INVENTREE_EMAIL_SENDER | email.sender | Sending email address | *Not specified* |
|
||||
| INVENTREE_EMAIL_PREFIX | email.prefix | Prefix for subject text | [InvenTree] |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_EMAIL_BACKEND") }} Email backend module |
|
||||
{{ configsetting("INVENTREE_EMAIL_HOST") }} Email server host |
|
||||
{{ configsetting("INVENTREE_EMAIL_PORT") }} Email server port |
|
||||
{{ configsetting("INVENTREE_EMAIL_USERNAME") }} Email account username |
|
||||
{{ configsetting("INVENTREE_EMAIL_PASSWORD") }} Email account password |
|
||||
{{ configsetting("INVENTREE_EMAIL_TLS") }} Enable STARTTLS support (commonly port 567) |
|
||||
{{ configsetting("INVENTREE_EMAIL_SSL") }} Enable legacy SSL/TLS support (commonly port 465) |
|
||||
{{ configsetting("INVENTREE_EMAIL_SENDER") }} Sending email address |
|
||||
{{ configsetting("INVENTREE_EMAIL_PREFIX") }} Prefix for subject text |
|
||||
|
||||
### Email Backend
|
||||
|
||||
@@ -354,11 +358,10 @@ The "sender" email address is the address from which InvenTree emails are sent (
|
||||
|
||||
InvenTree requires some external directories for storing files:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_STATIC_ROOT | static_root | [Static files](./processes.md#static-files) directory | *Not specified* |
|
||||
| INVENTREE_MEDIA_ROOT | media_root | [Media files](./processes.md#media-files) directory | *Not specified* |
|
||||
| INVENTREE_BACKUP_DIR | backup_dir | Backup files directory | *Not specified* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_STATIC_ROOT") }} [Static files](./processes.md#static-files) directory |
|
||||
{{ configsetting("INVENTREE_MEDIA_ROOT") }} [Media files](./processes.md#media-files) directory |
|
||||
{{ configsetting("INVENTREE_BACKUP_DIR") }} Directory for backup files |
|
||||
|
||||
!!! tip "Serving Files"
|
||||
Read the [proxy server documentation](./processes.md#proxy-server) for more information on hosting *static* and *media* files
|
||||
@@ -392,47 +395,41 @@ Alternatively this location can be specified with the `INVENTREE_BACKUP_DIR` env
|
||||
|
||||
It is also possible to use alternative storage backends for static and media files, at the moment there is direct provide direct support bundled for S3 and SFTP. Google cloud storage and Azure blob storage would also be supported by the [used library](https://django-storages.readthedocs.io), but require additional packages to be installed.
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_STORAGE_TARGET | storage.target | Storage target to use for static and media files, valid options: local, s3, sftp | local |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_STORAGE_TARGET") }} Storage target to use for static and media files, valid options: local, s3, sftp |
|
||||
|
||||
|
||||
#### S3
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_S3_ACCESS_KEY | storage.s3.access_key | Access key | *Not specified* |
|
||||
| INVENTREE_S3_SECRET_KEY | storage.s3.secret_key | Secret key |
|
||||
| *Not specified* |
|
||||
| INVENTREE_S3_BUCKET_NAME | storage.s3.bucket_name | Bucket name, required by most providers |
|
||||
| *Not specified* |
|
||||
| INVENTREE_S3_REGION_NAME | storage.s3.region_name | S3 region name |
|
||||
| *Not specified* |
|
||||
| INVENTREE_S3_ENDPOINT_URL | storage.s3.endpoint_url | Custom S3 endpoint URL, defaults to AWS endpoints if not set |
|
||||
| *Not specified* |
|
||||
| INVENTREE_S3_LOCATION | storage.s3.location | Sub-Location that should be used | inventree-server |
|
||||
| INVENTREE_S3_DEFAULT_ACL | storage.s3.default_acl | Default ACL for uploaded files, defaults to provider default if not set | *Not specified* |
|
||||
| INVENTREE_S3_VERIFY_SSL | storage.s3.verify_ssl | Verify SSL certificate for S3 endpoint | True |
|
||||
| INVENTREE_S3_OVERWRITE | storage.s3.overwrite | Overwrite existing files in S3 bucket | False |
|
||||
| INVENTREE_S3_VIRTUAL | storage.s3.virtual | Use virtual addressing style - by default False -> `path` style, `virtual` style if True | False |
|
||||
{{ configtable() }}
|
||||
| `INVENTREE_S3_ACCESS_KEY` | storage.s3.access_key | *Not specified* | Access key |
|
||||
| `INVENTREE_S3_SECRET_KEY` | storage.s3.secret_key | *Not specified* | Secret key |
|
||||
| `INVENTREE_S3_BUCKET_NAME` | storage.s3.bucket_name | *Not specified* | Bucket name, required by most providers |
|
||||
| `INVENTREE_S3_REGION_NAME` | storage.s3.region_name | *Not specified* | S3 region name |
|
||||
| `INVENTREE_S3_ENDPOINT_URL` | storage.s3.endpoint_url | *Not specified* | Custom S3 endpoint URL, defaults to AWS endpoints if not set |
|
||||
| `INVENTREE_S3_LOCATION` | storage.s3.location | inventree-server | Sub-Location that should be used |
|
||||
| `INVENTREE_S3_DEFAULT_ACL` | storage.s3.default_acl | *Not specified* | Default ACL for uploaded files, defaults to provider default if not set |
|
||||
| `INVENTREE_S3_VERIFY_SSL` | storage.s3.verify_ssl | True | Verify SSL certificate for S3 endpoint |
|
||||
| `INVENTREE_S3_OVERWRITE` | storage.s3.overwrite | False | Overwrite existing files in S3 bucket |
|
||||
| `INVENTREE_S3_VIRTUAL` | storage.s3.virtual | False | Use virtual addressing style - by default False -> `path` style, `virtual` style if True |
|
||||
|
||||
#### SFTP
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SFTP_HOST | storage.sftp.host | SFTP host | *Not specified* |
|
||||
| INVENTREE_SFTP_PARAMS | storage.sftp.params | SFTP connection parameters, see https://docs.paramiko.org/en/latest/api/client.html#paramiko.client.SSHClient.connect; e.g. `{'port': 22, 'user': 'usr', 'password': 'pwd'}` | *Not specified* |
|
||||
| INVENTREE_SFTP_UID | storage.sftp.uid | SFTP user ID - not required | *Not specified* |
|
||||
| INVENTREE_SFTP_GID | storage.sftp.gid | SFTP group ID - not required | *Not specified* |
|
||||
| INVENTREE_SFTP_LOCATION | storage.sftp.location | Sub-Location that should be used | inventree-server |
|
||||
{{ configtable() }}
|
||||
| `INVENTREE_SFTP_HOST` | storage.sftp.host | *Not specified* | SFTP host |
|
||||
| `INVENTREE_SFTP_PARAMS` | storage.sftp.params | *Not specified* | SFTP connection parameters, see https://docs.paramiko.org/en/latest/api/client.html#paramiko.client.SSHClient.connect; e.g. `{'port': 22, 'user': 'usr', 'password': 'pwd'}` |
|
||||
| `INVENTREE_SFTP_UID` | storage.sftp.uid | *Not specified* | SFTP user ID - not required |
|
||||
| `INVENTREE_SFTP_GID` | storage.sftp.gid | *Not specified* | SFTP group ID - not required |
|
||||
| `INVENTREE_SFTP_LOCATION` | storage.sftp.location | inventree-server | Sub-Location that should be used |
|
||||
|
||||
## Authentication
|
||||
|
||||
InvenTree provides allowance for additional sign-in options. The following options are not enabled by default, and care must be taken by the system administrator when configuring these settings.
|
||||
|
||||
| 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,webauthn |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_MFA_ENABLED") }} Enable multi-factor authentication support for the InvenTree server |
|
||||
{{ configsetting("INVENTREE_MFA_SUPPORTED_TYPES") }} List of supported multi-factor authentication types |
|
||||
{{ configsetting("INVENTREE_USE_JWT") }} Enable support for JSON Web Tokens (JWT) for authentication |
|
||||
|
||||
### Single Sign On
|
||||
|
||||
@@ -447,11 +444,10 @@ There are multiple configuration parameters which must be specified (either in y
|
||||
|
||||
The login-experience can be altered with the following settings:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_LOGIN_CONFIRM_DAYS | login_confirm_days | Duration for which confirmation links are valid | 3 |
|
||||
| INVENTREE_LOGIN_ATTEMPTS | login_attempts | Count of allowed login attempts before blocking user | 5 |
|
||||
| INVENTREE_LOGIN_DEFAULT_HTTP_PROTOCOL | login_default_protocol | Default protocol to use for login callbacks (e.g. using [SSO](#single-sign-on)) | Uses the protocol specified in `INVENTREE_SITE_URL`, or defaults to *http* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_LOGIN_CONFIRM_DAYS") }} Duration for which confirmation links are valid |
|
||||
{{ configsetting("INVENTREE_LOGIN_ATTEMPTS") }} Count of allowed login attempts before blocking user |
|
||||
{{ configsetting("INVENTREE_LOGIN_DEFAULT_HTTP_PROTOCOL", default="Uses the protocol specified in `INVENTREE_SITE_URL`, or defaults to *http*") }} Default protocol to use for login callbacks (e.g. using [SSO](#single-sign-on)) |
|
||||
|
||||
!!! tip "Default Protocol"
|
||||
If you have specified `INVENTREE_SITE_URL`, the default protocol will be used from that setting. Otherwise, the default protocol will be *http*.
|
||||
@@ -460,15 +456,24 @@ The login-experience can be altered with the following settings:
|
||||
|
||||
Custom authentication backends can be used by specifying them here. These can for example be used to add [LDAP / AD login](https://django-auth-ldap.readthedocs.io/en/latest/) to InvenTree
|
||||
|
||||
## Background Worker Options
|
||||
|
||||
The following options are available for configuring the InvenTree [background worker process](./processes.md#background-worker):
|
||||
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_BACKGROUND_WORKERS") }} Number of background worker processes |
|
||||
{{ configsetting("INVENTREE_BACKGROUND_TIMEOUT") }} Timeout for background worker tasks (seconds) |
|
||||
{{ configsetting("INVENTREE_BACKGROUND_RETRY") }} Time to wait before retrying a background task (seconds) |
|
||||
{{ configsetting("INVENTREE_BACKGROUND_MAX_ATTEMPTS") }} Maximum number of attempts for a background task |
|
||||
|
||||
## Sentry Integration
|
||||
|
||||
The InvenTree server can be integrated with the [sentry.io](https://sentry.io) monitoring service, for error logging and performance tracking.
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SENTRY_ENABLED | sentry_enabled | Enable sentry.io integration | False |
|
||||
| INVENTREE_SENTRY_DSN | sentry_dsn | Sentry DSN (data source name) key | *Defaults to InvenTree developer key* |
|
||||
| INVENTREE_SENTRY_SAMPLE_RATE | sentry_sample_rate | How often to send data samples | 0.1 |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_SENTRY_ENABLED") }} Enable sentry.io integration |
|
||||
{{ configsetting("INVENTREE_SENTRY_DSN", default="Defaults to InvenTree developer key") }} Sentry DSN (data source name) key |
|
||||
{{ configsetting("INVENTREE_SENTRY_SAMPLE_RATE") }} How often to send data samples (seconds) |
|
||||
|
||||
!!! info "Default DSN"
|
||||
If enabled with the default DSN, server errors will be logged to a sentry.io account monitored by the InvenTree developers.
|
||||
@@ -477,13 +482,11 @@ The InvenTree server can be integrated with the [sentry.io](https://sentry.io) m
|
||||
|
||||
The logo and custom messages can be changed/set:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_CUSTOM_LOGO | customize.logo | Path to custom logo in the static files directory | *Not specified* |
|
||||
| INVENTREE_CUSTOM_SPLASH | customize.splash | Path to custom splash screen in the static files directory | *Not specified* |
|
||||
| INVENTREE_CUSTOMIZE | customize.site_header | Custom site header in the Django admin | InvenTree Admin |
|
||||
| INVENTREE_CUSTOMIZE | customize.login_message | Custom message for login page | *Not specified* |
|
||||
| INVENTREE_CUSTOMIZE | customize.navbar_message | Custom message for navbar | *Not specified* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_CUSTOM_LOGO") }} Path to custom logo in the static files directory |
|
||||
{{ configsetting("INVENTREE_CUSTOM_SPLASH") }} Path to custom splash screen in the static files directory |
|
||||
{{ configsetting("INVENTREE_SITE_HEADER") }} Custom header text for the django admin page |
|
||||
{{ configsetting("INVENTREE_CUSTOMIZE") }} JSON object containing custom messages for the login page, navbar, and Django admin site |
|
||||
|
||||
The INVENTREE_CUSTOMIZE environment variable must contain a json object with the keys from the table above and
|
||||
the wanted values. Example:
|
||||
@@ -527,15 +530,15 @@ INVENTREE_FRONTEND_SETTINGS='{"mobile_mode": "allow-ignore"}'
|
||||
|
||||
The following [plugin](../plugins/index.md) configuration options are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_PLUGINS_ENABLED | plugins_enabled | Enable plugin support | False |
|
||||
| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False |
|
||||
| INVENTREE_PLUGIN_FILE | plugins_plugin_file | Location of plugin installation file | *Not specified* |
|
||||
| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* |
|
||||
| INVENTREE_PLUGINS_MANDATORY | plugins_mandatory | List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) | *Not specified* |
|
||||
| INVENTREE_PLUGIN_DEV_SLUG | plugin_dev.slug | Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) | *Not specified* |
|
||||
| INVENTREE_PLUGIN_DEV_HOST | plugin_dev.host | Specify host for development mode plugin | http://localhost:5174 |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_PLUGINS_ENABLED") }} Enable plugin support |
|
||||
{{ configsetting("INVENTREE_PLUGIN_NOINSTALL") }} Disable Plugin installation via API |
|
||||
{{ configsetting("INVENTREE_PLUGIN_FILE") }} Location of plugin installation file |
|
||||
| `INVENTREE_PLUGIN_DIR` | `plugin_dir` | *Not specified* | Location of external plugin directory |
|
||||
{{ configsetting("INVENTREE_PLUGIN_RETRY") }} Number of tries to attempt loading a plugin before giving up |
|
||||
{{ configsetting("INVENTREE_PLUGINS_MANDATORY") }} List of [plugins which are considered mandatory](../plugins/index.md#mandatory-third-party-plugins) |
|
||||
{{ configsetting("INVENTREE_PLUGIN_DEV_SLUG") }} Specify plugin to run in [development mode](../plugins/creator.md#backend-configuration) |
|
||||
{{ configsetting("INVENTREE_PLUGIN_DEV_HOST") }} Specify host for development mode plugin |
|
||||
|
||||
## Override Global Settings
|
||||
|
||||
@@ -543,6 +546,12 @@ If required, [global settings values](../settings/global.md#override-global-sett
|
||||
|
||||
To override global settings, provide a "dictionary" of settings overrides in the configuration file, or via an environment variable.
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| GLOBAL_SETTINGS_OVERRIDES | global_settings_overrides | JSON object containing global settings overrides | *Not specified* |
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_GLOBAL_SETTINGS") }} JSON object containing global settings overrides |
|
||||
|
||||
## Other Settings
|
||||
|
||||
Other available settings, not categorized above, are detailed in the table below:
|
||||
|
||||
{{ configtable() }}
|
||||
{{ configsetting("INVENTREE_EXTRA_URL_SCHEMES") }} Allow additional URL schemes for URL validation |
|
||||
|
||||
@@ -87,13 +87,7 @@ If you are creating the initial database, you need to create an admin (superuser
|
||||
docker compose run inventree-server invoke superuser
|
||||
```
|
||||
|
||||
Alternatively, admin account details can be specified in the `.env` file, removing the need for this manual step:
|
||||
|
||||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| INVENTREE_ADMIN_USER | Admin account username |
|
||||
| INVENTREE_ADMIN_PASSWORD | Admin account password |
|
||||
| INVENTREE_ADMIN_EMAIL | Admin account email address |
|
||||
Alternatively, admin account details can be specified using environment variables, or in the `.env` file, removing the need for this manual step. Refer to the [configuration documentation](./config.md#administrator-account) for more information.
|
||||
|
||||
!!! warning "Scrub Account Data"
|
||||
Ensure that the admin account credentials are removed from the `.env` file after the first run, for security.
|
||||
@@ -241,9 +235,9 @@ By default, the Dockerized InvenTree web server binds to all available network i
|
||||
This can be adjusted using the following environment variables:
|
||||
|
||||
| Environment Variable | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_WEB_ADDR | 0.0.0.0 |
|
||||
| INVENTREE_WEB_PORT | 8000 |
|
||||
| --- | --- |
|
||||
| `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.
|
||||
|
||||
|
||||
@@ -3,9 +3,15 @@ title: InvenTree Installer
|
||||
---
|
||||
|
||||
## Installer
|
||||
|
||||
The InvenTree installer automates the installation procedure for a production InvenTree server.
|
||||
|
||||
Supported OSs are Debian 10, 11, 12 and Ubuntu 20.04 LTS, 22.04 LTS, 24.04 LTS.
|
||||
The installer supports the following Linux distributions:
|
||||
|
||||
- Debian 11, 12
|
||||
- Ubuntu 22.04 LTS, 24.04 LTS
|
||||
|
||||
Support for other OS versions is not currently planned. If you are using a different distribution, you can still follow the [docker](./docker.md) or [bare metal](./install.md) installation instructions.
|
||||
|
||||
### Quick Script
|
||||
|
||||
@@ -118,6 +124,9 @@ Extra python packages can be installed by setting the environment variable `SETU
|
||||
|
||||
The used database backend can be configured with environment variables (before the first setup) or in the config file after the installation. Check the [configuration section](./config.md#database-options) for more information.
|
||||
|
||||
!!! warning "SQLite Performance"
|
||||
SQLite is not recommended for production use, as it is not designed for high concurrency.
|
||||
|
||||
## Moving Data
|
||||
|
||||
To change the data storage location, link the new location to `/opt/inventree/data`. A rough outline of steps to achieve this could be:
|
||||
|
||||
@@ -67,6 +67,10 @@ invoke import-records -c -f data.json
|
||||
!!! warning "Character Encoding"
|
||||
If the character encoding of the data file does not exactly match the target database, the import operation may not succeed. In this case, some manual editing of the database JSON file may be required.
|
||||
|
||||
```
|
||||
{{ invoke_commands('import-records --help') }}
|
||||
```
|
||||
|
||||
### Copy Media Files
|
||||
|
||||
Any media files (images, documents, etc) that were stored in the original database must be copied to the new database. In a typical InvenTree installation, these files are stored in the `media` subdirectory of the InvenTree data location.
|
||||
@@ -197,3 +201,32 @@ 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.
|
||||
|
||||
## Migrating Plugin Data
|
||||
|
||||
Custom plugins may define their own database models, and thus have their own data records stored in the database. If a plugin is being migrated from one InvenTree installation to another, then the plugin data must also be migrated.
|
||||
|
||||
To account for this, the `export-records` and `import-records` commands have been designed to also export and import plugin data, in addition to the core InvenTree data.
|
||||
|
||||
### Exporting Plugin Data
|
||||
|
||||
When running the `export-records` command, any data records associated with plugins will also be exported, and included in the output JSON file.
|
||||
|
||||
### Importing Plugin Data
|
||||
|
||||
When running the `import-records` command, the import process will also attempt to import any plugin data records contained in the input JSON file. However, for the plugin data to be imported correctly, the following conditions must be met:
|
||||
|
||||
1. The plugin *code* must be present in the new InvenTree installation. Any plugins *not* installed will not have their tables created, and thus the import process will fail for those records.
|
||||
2. The plugin *version* must be the same in both installations. If the plugin version is different, then the database schema may be different, and thus the import process may fail.
|
||||
3. The InvenTree software version must be the same in both installations. If the InvenTree version is different, then the database schema may be different, and thus the import process may fail.
|
||||
|
||||
If all of the above conditions are met, then the plugin data *should* be imported correctly into the new database. To achieve this reliably, the following process steps are implemented in the `import-records` command:
|
||||
|
||||
1. The database is cleaned of all existing records (if the `-c` option is used).
|
||||
2. The core InvenTree database migrations are run to ensure that the core database schema is correct.
|
||||
3. User auth records are imported into the database
|
||||
4. Common configuration records (such as global settings) are imported into the database
|
||||
5. Plugin configuration records (defining which plugins are active) are imported into the database
|
||||
6. Database migrations are run once more, to ensure that any plugin database schema are correctly initialized
|
||||
7. The database is checked to ensure that all required apps are present (i.e. all plugins are installed and correctly activated)
|
||||
8. All remaining records (including plugin data) are imported into the database
|
||||
|
||||
@@ -16,7 +16,22 @@ InvenTree supports a [number of database backends]({% include "django.html" %}/r
|
||||
|
||||
Refer to the [database configuration guide](./config.md#database-options) for more information on selecting and configuring the database backend.
|
||||
|
||||
In running InvenTree via [docker compose](./docker_install.md), the database process is managed by the `inventree-db` service which provides a [Postgres docker container](https://hub.docker.com/_/postgres).
|
||||
If running InvenTree via [docker compose](./docker_install.md), the database process is managed by the `inventree-db` service which provides a [Postgres docker container](https://hub.docker.com/_/postgres).
|
||||
|
||||
!!! tip "Postgres Recommended"
|
||||
We recommend using Postgres as the database backend for InvenTree, as it is a robust and scalable database which is well-suited to production use.
|
||||
|
||||
#### SQLite Limitations
|
||||
|
||||
!!! warning "SQLite Performance"
|
||||
SQLite is not recommended for production use, as it is not designed for high concurrency.
|
||||
|
||||
While SQLite is supported, it is strongly *not* recommended for a production installation, especially where there may be multiple users accessing the system concurrently. SQLite is designed for low-concurrency applications, and can experience performance issues when multiple users are accessing the database at the same time.
|
||||
|
||||
In addition to concurrency issues, there are other structural limitations which exist in SQLite that can prevent operations on large querysets.
|
||||
|
||||
If you are using SQLite, you should be aware of these limitations. It is important to ensure that the database file is stored on a fast storage medium (such as an SSD), and that the database options are configured correctly to minimize locking issues. Refer to the [database configuration guide](./config.md#database-options) for more information on configuring SQLite options.
|
||||
|
||||
|
||||
### Web Server
|
||||
|
||||
@@ -112,6 +127,8 @@ If the background worker process is not running, InvenTree will not be able to p
|
||||
|
||||
If the [cache server](#cache-server) is not running, the background worker will be limited to running a single threaded worker. This is because the background worker uses the cache server to manage task locking, and without a global cache server to communicate between processes, concurrency issues can occur.
|
||||
|
||||
Additionally, if you are running SQLite as the database backend, the background worker will be limited to a single thread, due to database locking issues which can occur with SQLite when multiple threads are accessing the database concurrently.
|
||||
|
||||
### Cache Server
|
||||
|
||||
The InvenTree cache server is used to store temporary data which is shared between the InvenTree web server and the background worker processes. The cache server is also used to store task information, and to manage task locking between the background worker processes.
|
||||
@@ -126,3 +143,7 @@ InvenTree uses the [Redis](https://redis.io/) cache server to manage cache data.
|
||||
|
||||
!!! tip "Enable Cache"
|
||||
While a redis container is provided in the default configuration, by default it is not enabled in the InvenTree server. You can enable redis cache support by following the [caching configuration guide](./config.md#caching)
|
||||
|
||||
### Configuration
|
||||
|
||||
Refer to the [background worker configuration options](./config.md#background-worker-options) for more information on configuring the background worker process.
|
||||
|
||||
@@ -24,7 +24,7 @@ Each *Stock Item* is linked to the following information:
|
||||
|
||||
**Last Updated** - Date that the stock quantity was last updated
|
||||
|
||||
**Last Stocktake** - Date of most recent stocktake (count) of this item
|
||||
**Last Stocktake** - Date that this stock item was last counted
|
||||
|
||||
**Status** - Status of this stock item
|
||||
|
||||
|
||||
@@ -142,6 +142,8 @@ Note that any serial number adjustments are subject to the same validation rules
|
||||
|
||||
{{ image("stock/serial_edit_error.png", title="Error while editing a serial number") }}
|
||||
|
||||
!!! info "Disable Serial Number Editing"
|
||||
If you wish to prevent users from editing serial numbers, this can be achieved by disabling the `Allow Edit Serial Number` setting in the system settings view.
|
||||
|
||||
#### Plugin Support
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
|
||||
---
|
||||
title: Transfer Orders
|
||||
---
|
||||
|
||||
## Transfer Orders
|
||||
|
||||
Transfer orders provide a method for requesting stock to be moved from one location to another. It does not replace the existing on-demand stock transaction options, but lets you "document" many transactions from a single view.
|
||||
|
||||
### View Transfer Orders
|
||||
|
||||
To navigate to the Transfer Order display, select *Stock* from the main navigation menu, and *Transfer Orders* from the sidebar:
|
||||
|
||||
{{ image("stock/transfer_order_display.png", "Transfer Order display") }}
|
||||
|
||||
The following view modes are available:
|
||||
|
||||
#### Table View
|
||||
|
||||
*Table View* provides a list of Transfer Orders, which can be filtered to display a subset of orders according to user supplied parameters.
|
||||
|
||||
{{ image("stock/transfer_order_list.png", "Transfer Order list") }}
|
||||
|
||||
#### Calendar View
|
||||
|
||||
*Calendar View* shows a calendar display with outstanding transfer orders.
|
||||
|
||||
{{ image("stock/transfer_order_calendar.png", "Transfer Order calendar") }}
|
||||
|
||||
### Transfer Order Status Codes
|
||||
|
||||
Each Transfer Order has a specific status code, which represents the state of the order:
|
||||
|
||||
| Status | Description |
|
||||
| --- | --- |
|
||||
| Pending | The transfer order has been created, but has not been finalized or submitted |
|
||||
| Issued | The transfer order has been issued, and is in progress |
|
||||
| On Hold | The transfer order has been placed on hold, but is still active |
|
||||
| Complete | The transfer order is fully completed, and is now closed |
|
||||
| Cancelled | The transfer order was cancelled, and is now closed |
|
||||
|
||||
**Source Code**
|
||||
|
||||
Refer to the source code for the Transfer Order status codes:
|
||||
|
||||
::: order.status_codes.TransferOrderStatus
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
Transfer Order Status supports [custom states](../concepts/custom_states.md).
|
||||
|
||||
### Transfer Order Parameters
|
||||
|
||||
The following parameters are available for each Transfer Order, and can be edited by the user:
|
||||
|
||||
| Parameter | Description |
|
||||
| --- | --- |
|
||||
| Reference | Transfer Order reference e.g. '001' |
|
||||
| Description | Description of the Transfer Order |
|
||||
| Project Code | Project Code of the Transfer Order |
|
||||
| Source Location | Stock location to source stock items from (blank = all locations) |
|
||||
| Destination Location | Stock location where the stock will be transferred |
|
||||
| Consume Stock | Rather than transfer the stock to the destination, "consume" it by removing the specified quantity from the allocated stock item
|
||||
| Start Date | The scheduled start date for the transfer |
|
||||
| Target Date | Target date for transfer completion |
|
||||
| External Link | Link to external webpage |
|
||||
| Responsible | User (or group of users) who is responsible for the transfer |
|
||||
| Notes | Transfer notes, supports markdown |
|
||||
|
||||
## Create a Transfer Order
|
||||
|
||||
Once the transfer order page is loaded, click on <span class="badge inventree add">{{ icon("plus-circle") }} New Transfer Order</span> which opens the "Create Transfer Order" form.
|
||||
|
||||
Fill out the rest of the form with the transfer order information then click on <span class="badge inventree confirm">Submit</span> to create the order.
|
||||
|
||||
### Transfer Order Reference
|
||||
|
||||
Each Transfer Order is uniquely identified by its *Reference* field. Read more about [reference fields](../settings/reference.md).
|
||||
|
||||
### Add Line Items
|
||||
|
||||
On the transfer order detail page, user can link parts to the transfer order selecting the <span class="badge inventree nav side">{{ icon("list") }}</span> Line Items</span> tab then clicking on the <span class="badge inventree add">{{ icon("plus-circle") }} Add Line Item</span> button.
|
||||
|
||||
Once the "Add Line Item" form opens, select a part in the list.
|
||||
|
||||
!!! warning
|
||||
Only parts that have the "Virtual" attribute disabled will be shown and can be selected.
|
||||
|
||||
Fill out the rest of the form then click on <span class="badge inventree confirm">Submit</span>
|
||||
|
||||
### Allocate Stock Items
|
||||
|
||||
After line items were created, user can either:
|
||||
|
||||
* Allocate stock items for that part to the transfer order (click on {{ icon("arrow-right") }} button)
|
||||
* Create a build order for that part to cover the quantity of the transfer order (click on {{ icon("tools") }} button)
|
||||
|
||||
### Complete Order
|
||||
|
||||
Once all items in the transfer order have been allocated, click on <span class="badge inventree add">{{ icon("circle-check", color="green") }} Complete Order</span> to mark the transfer order as complete. Confirm then click on <span class="badge inventree confirm">Submit</span> to complete the order.
|
||||
|
||||
### Transferred Stock
|
||||
|
||||
After completing the transfer order, a <span class="badge inventree nav side">{{ icon("list") }}</span> Transferred Stock</span> tab will appear showing which stock was affected.
|
||||
|
||||
!!! warning
|
||||
Similar to received stock on purchase orders, this tab will only be accurate while the affected stock items still exist. Furthermore, if the stock item is depleted while using the "consume" parameter, it will not appear here unless "delete on deplete" is turned off for this stock item
|
||||
|
||||
### Cancel Order
|
||||
|
||||
To cancel the order, click on the {{ icon("tools") }} menu button next to the <span class="badge inventree add">{{ icon("circle-check", color="green") }} Complete Order</span> button, then click on the "{{ icon("tools") }} Cancel Order" menu option. Confirm then click on the <span class="badge inventree confirm">Submit</span> to cancel the order.
|
||||
|
||||
## Order Scheduling
|
||||
|
||||
Transfer orders can be scheduled for a future date, to allow for order scheduling.
|
||||
|
||||
### Start Date
|
||||
|
||||
The *Start Date* of the transfer order is the date on which the order is scheduled to be issued, allowing work to begin on the order.
|
||||
|
||||
### Target Date
|
||||
|
||||
The *Target Date* of the transfer order is the date on which the order is scheduled to be completed.
|
||||
|
||||
### Overdue Orders
|
||||
|
||||
If the *Target Date* of the transfer order has passed, the order will be marked as *overdue*.
|
||||
|
||||
## Calendar view
|
||||
|
||||
Using the button to the top right of the list of Transfer Orders, the view can be switched to a calendar view using the button {{ icon("calendar") }}. This view shows orders with a defined target date only.
|
||||
|
||||
This view can be accessed externally as an ICS calendar using a URL like the following:
|
||||
`http://inventree.example.org/api/order/calendar/transfer-order/calendar.ics`
|
||||
|
||||
By default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
|
||||
|
||||
## Transfer Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for transfer orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("TRANSFERORDER_ENABLED") }}
|
||||
{{ globalsetting("TRANSFERORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("TRANSFERORDER_REQUIRE_RESPONSIBLE") }}
|
||||
@@ -34,6 +34,7 @@ for key in [
|
||||
print(f' - {key}: {val}')
|
||||
|
||||
# Cached settings dict values
|
||||
global CONFIG_SETTINGS
|
||||
global GLOBAL_SETTINGS
|
||||
global USER_SETTINGS
|
||||
global TAGS
|
||||
@@ -64,6 +65,7 @@ with open(settings_file, encoding='utf-8') as sf:
|
||||
|
||||
GLOBAL_SETTINGS = settings['global']
|
||||
USER_SETTINGS = settings['user']
|
||||
CONFIG_SETTINGS = settings['config']
|
||||
|
||||
# Tags
|
||||
with open(gen_base.joinpath('inventree_tags.yml'), encoding='utf-8') as f:
|
||||
@@ -377,6 +379,34 @@ def define_env(env):
|
||||
|
||||
return rendersetting(key, setting, short=short)
|
||||
|
||||
@env.macro
|
||||
def configtable():
|
||||
"""Generate a header for the configuration settings table."""
|
||||
return '| Environment Variable | Configuration File | Default | Description |\n| --- | --- | --- | --- |'
|
||||
|
||||
@env.macro
|
||||
def configsetting(key: str, default: Optional[str] = None):
|
||||
"""Extract information on a particular configuration setting.
|
||||
|
||||
Arguments:
|
||||
key: The name of the configuration setting to extract information for.
|
||||
default: An optional default value to override the setting's default display value.
|
||||
"""
|
||||
global CONFIG_SETTINGS
|
||||
setting = CONFIG_SETTINGS[key]
|
||||
|
||||
observe_setting(key, 'config')
|
||||
|
||||
cfg_key = setting.get('config_key', None)
|
||||
cfg_key = f'`{cfg_key}`' if cfg_key else '-'
|
||||
|
||||
default = default or setting.get('default_value', None)
|
||||
|
||||
if default is None:
|
||||
default = '*Not Specified*'
|
||||
|
||||
return f'| <span title="{key}" style="white-space: nowrap;"><code>{key}</code></span> | {cfg_key} | {default} |'
|
||||
|
||||
@env.macro
|
||||
def tags_and_filters():
|
||||
"""Return a list of all tags and filters."""
|
||||
|
||||
@@ -96,6 +96,8 @@ nav:
|
||||
- Physical Units: concepts/units.md
|
||||
- Companies: concepts/company.md
|
||||
- Custom States: concepts/custom_states.md
|
||||
- Data Export: concepts/data_export.md
|
||||
- Data Import: concepts/data_import.md
|
||||
- Pricing: concepts/pricing.md
|
||||
- Project Codes: concepts/project_codes.md
|
||||
- Attachments: concepts/attachments.md
|
||||
@@ -145,11 +147,13 @@ nav:
|
||||
- Stock Expiry: stock/expiry.md
|
||||
- Stock Ownership: stock/owner.md
|
||||
- Test Results: stock/test.md
|
||||
- Transfer Orders: stock/transfer_order.md
|
||||
- Manufacturing:
|
||||
- Manufacturing: manufacturing/index.md
|
||||
- Bill of Materials: manufacturing/bom.md
|
||||
- Build Orders: manufacturing/build.md
|
||||
- Build Outputs: manufacturing/output.md
|
||||
- Required Quantity: manufacturing/required.md
|
||||
- Allocating Stock: manufacturing/allocate.md
|
||||
- External Manufacturing: manufacturing/external.md
|
||||
- Example Build Order: manufacturing/example.md
|
||||
@@ -184,8 +188,6 @@ nav:
|
||||
- Multi Factor Authentication: settings/MFA.md
|
||||
- Email Settings: settings/email.md
|
||||
- Experimental Features: settings/experimental.md
|
||||
- Export Data: settings/export.md
|
||||
- Import Data: settings/import.md
|
||||
- Operations:
|
||||
- Background Tasks: settings/tasks.md
|
||||
- Error Logs: settings/logs.md
|
||||
@@ -263,6 +265,7 @@ nav:
|
||||
- Stock: app/stock.md
|
||||
- Purchase Orders: app/po.md
|
||||
- Sales Orders: app/so.md
|
||||
- Build Orders: app/build.md
|
||||
- Settings: app/settings.md
|
||||
- Privacy: app/privacy.md
|
||||
- Translation: app/translation.md
|
||||
|
||||
@@ -6,5 +6,5 @@ mkdocs-redirects
|
||||
mkdocs-simple-hooks>=0.1,<1.0
|
||||
mkdocs-include-markdown-plugin
|
||||
neoteroi-mkdocs
|
||||
mkdocstrings[python]>=0.25.0,<=1.0.3
|
||||
mkdocstrings[python]<=1.0.4,>=1.0.4
|
||||
mkdocs-mermaid2-plugin
|
||||
|
||||
@@ -11,14 +11,14 @@ babel==2.18.0 \
|
||||
# -c src/backend/requirements.txt
|
||||
# mkdocs-git-revision-date-localized-plugin
|
||||
# mkdocs-material
|
||||
backrefs==6.1 \
|
||||
--hash=sha256:13eafbc9ccd5222e9c1f0bec563e6d2a6d21514962f11e7fc79872fd56cbc853 \
|
||||
--hash=sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1 \
|
||||
--hash=sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231 \
|
||||
--hash=sha256:4c9d3dc1e2e558965202c012304f33d4e0e477e1c103663fd2c3cc9bb18b0d05 \
|
||||
--hash=sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0 \
|
||||
--hash=sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a \
|
||||
--hash=sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7
|
||||
backrefs==6.2 \
|
||||
--hash=sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be \
|
||||
--hash=sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8 \
|
||||
--hash=sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b \
|
||||
--hash=sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7 \
|
||||
--hash=sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90 \
|
||||
--hash=sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7 \
|
||||
--hash=sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49
|
||||
# via mkdocs-material
|
||||
beautifulsoup4==4.14.3 \
|
||||
--hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb \
|
||||
@@ -28,143 +28,158 @@ bracex==2.6 \
|
||||
--hash=sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952 \
|
||||
--hash=sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7
|
||||
# via wcmatch
|
||||
certifi==2026.1.4 \
|
||||
--hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \
|
||||
--hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120
|
||||
certifi==2026.4.22 \
|
||||
--hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \
|
||||
--hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# httpcore
|
||||
# httpx
|
||||
# requests
|
||||
charset-normalizer==3.4.4 \
|
||||
--hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \
|
||||
--hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \
|
||||
--hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \
|
||||
--hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \
|
||||
--hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \
|
||||
--hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \
|
||||
--hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \
|
||||
--hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \
|
||||
--hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \
|
||||
--hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \
|
||||
--hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \
|
||||
--hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \
|
||||
--hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \
|
||||
--hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \
|
||||
--hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \
|
||||
--hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \
|
||||
--hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \
|
||||
--hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \
|
||||
--hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \
|
||||
--hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \
|
||||
--hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \
|
||||
--hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \
|
||||
--hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \
|
||||
--hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \
|
||||
--hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \
|
||||
--hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \
|
||||
--hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \
|
||||
--hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \
|
||||
--hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \
|
||||
--hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \
|
||||
--hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \
|
||||
--hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \
|
||||
--hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \
|
||||
--hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \
|
||||
--hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \
|
||||
--hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \
|
||||
--hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \
|
||||
--hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \
|
||||
--hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \
|
||||
--hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \
|
||||
--hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \
|
||||
--hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \
|
||||
--hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \
|
||||
--hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \
|
||||
--hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \
|
||||
--hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \
|
||||
--hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \
|
||||
--hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \
|
||||
--hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \
|
||||
--hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \
|
||||
--hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \
|
||||
--hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \
|
||||
--hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \
|
||||
--hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \
|
||||
--hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \
|
||||
--hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \
|
||||
--hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \
|
||||
--hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \
|
||||
--hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \
|
||||
--hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \
|
||||
--hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \
|
||||
--hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \
|
||||
--hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \
|
||||
--hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \
|
||||
--hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \
|
||||
--hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \
|
||||
--hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \
|
||||
--hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \
|
||||
--hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \
|
||||
--hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \
|
||||
--hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \
|
||||
--hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \
|
||||
--hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \
|
||||
--hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \
|
||||
--hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \
|
||||
--hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \
|
||||
--hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \
|
||||
--hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \
|
||||
--hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \
|
||||
--hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \
|
||||
--hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \
|
||||
--hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \
|
||||
--hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \
|
||||
--hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \
|
||||
--hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \
|
||||
--hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \
|
||||
--hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \
|
||||
--hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \
|
||||
--hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \
|
||||
--hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \
|
||||
--hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \
|
||||
--hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \
|
||||
--hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \
|
||||
--hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \
|
||||
--hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \
|
||||
--hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \
|
||||
--hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \
|
||||
--hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \
|
||||
--hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \
|
||||
--hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \
|
||||
--hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \
|
||||
--hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \
|
||||
--hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \
|
||||
--hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \
|
||||
--hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \
|
||||
--hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \
|
||||
--hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \
|
||||
--hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \
|
||||
--hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \
|
||||
--hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \
|
||||
--hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \
|
||||
--hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \
|
||||
--hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608
|
||||
charset-normalizer==3.4.7 \
|
||||
--hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \
|
||||
--hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \
|
||||
--hash=sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67 \
|
||||
--hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \
|
||||
--hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \
|
||||
--hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \
|
||||
--hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \
|
||||
--hash=sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444 \
|
||||
--hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \
|
||||
--hash=sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9 \
|
||||
--hash=sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01 \
|
||||
--hash=sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217 \
|
||||
--hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \
|
||||
--hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \
|
||||
--hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \
|
||||
--hash=sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83 \
|
||||
--hash=sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5 \
|
||||
--hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \
|
||||
--hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \
|
||||
--hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \
|
||||
--hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \
|
||||
--hash=sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42 \
|
||||
--hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \
|
||||
--hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \
|
||||
--hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \
|
||||
--hash=sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207 \
|
||||
--hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \
|
||||
--hash=sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734 \
|
||||
--hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \
|
||||
--hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \
|
||||
--hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \
|
||||
--hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \
|
||||
--hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \
|
||||
--hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \
|
||||
--hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \
|
||||
--hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \
|
||||
--hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \
|
||||
--hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \
|
||||
--hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \
|
||||
--hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \
|
||||
--hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \
|
||||
--hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \
|
||||
--hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \
|
||||
--hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \
|
||||
--hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \
|
||||
--hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \
|
||||
--hash=sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776 \
|
||||
--hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \
|
||||
--hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \
|
||||
--hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \
|
||||
--hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \
|
||||
--hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \
|
||||
--hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \
|
||||
--hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \
|
||||
--hash=sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5 \
|
||||
--hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \
|
||||
--hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \
|
||||
--hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \
|
||||
--hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \
|
||||
--hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \
|
||||
--hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \
|
||||
--hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \
|
||||
--hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \
|
||||
--hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \
|
||||
--hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \
|
||||
--hash=sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4 \
|
||||
--hash=sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545 \
|
||||
--hash=sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706 \
|
||||
--hash=sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366 \
|
||||
--hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \
|
||||
--hash=sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a \
|
||||
--hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \
|
||||
--hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \
|
||||
--hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \
|
||||
--hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \
|
||||
--hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \
|
||||
--hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \
|
||||
--hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \
|
||||
--hash=sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319 \
|
||||
--hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \
|
||||
--hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \
|
||||
--hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \
|
||||
--hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \
|
||||
--hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \
|
||||
--hash=sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0 \
|
||||
--hash=sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686 \
|
||||
--hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \
|
||||
--hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \
|
||||
--hash=sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c \
|
||||
--hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \
|
||||
--hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \
|
||||
--hash=sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60 \
|
||||
--hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \
|
||||
--hash=sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274 \
|
||||
--hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \
|
||||
--hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \
|
||||
--hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \
|
||||
--hash=sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f \
|
||||
--hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \
|
||||
--hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \
|
||||
--hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \
|
||||
--hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \
|
||||
--hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \
|
||||
--hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \
|
||||
--hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \
|
||||
--hash=sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00 \
|
||||
--hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \
|
||||
--hash=sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3 \
|
||||
--hash=sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7 \
|
||||
--hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \
|
||||
--hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \
|
||||
--hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \
|
||||
--hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \
|
||||
--hash=sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259 \
|
||||
--hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \
|
||||
--hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \
|
||||
--hash=sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30 \
|
||||
--hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \
|
||||
--hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \
|
||||
--hash=sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24 \
|
||||
--hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \
|
||||
--hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \
|
||||
--hash=sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc \
|
||||
--hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \
|
||||
--hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \
|
||||
--hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \
|
||||
--hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \
|
||||
--hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \
|
||||
--hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
click==8.3.1 \
|
||||
--hash=sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a \
|
||||
--hash=sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6
|
||||
click==8.3.3 \
|
||||
--hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \
|
||||
--hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613
|
||||
# via
|
||||
# mkdocs
|
||||
# neoteroi-mkdocs
|
||||
# properdocs
|
||||
colorama==0.4.6 \
|
||||
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
|
||||
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
||||
# via
|
||||
# griffe
|
||||
# mkdocs-material
|
||||
# via mkdocs-material
|
||||
editorconfig==0.17.1 \
|
||||
--hash=sha256:1eda9c2c0db8c16dbd50111b710572a5e6de934e39772de1959d41f64fc17c82 \
|
||||
--hash=sha256:23c08b00e8e08cc3adcddb825251c497478df1dada6aefeb01e626ad37303745
|
||||
@@ -173,25 +188,26 @@ essentials==1.1.9 \
|
||||
--hash=sha256:71ef161e0e27ef77cd6f5fc05e0b8688a575fcab870c01c95940f832e321dfbb \
|
||||
--hash=sha256:7fbea3a518cbeafe5374fb7e2ea2c15a109e8a7fd1eaab62ae87cbd1b3b1e8d0
|
||||
# via essentials-openapi
|
||||
essentials-openapi==1.3.0 \
|
||||
--hash=sha256:453327a0a847a431133f4472ced7e4a9180bf667437049b57381ddf88079e886 \
|
||||
--hash=sha256:9c2a88531e2c70c565d5b526d74043941e46f60c114f7a0e3ae91e9e6bef4dae
|
||||
essentials-openapi==1.4.0 \
|
||||
--hash=sha256:578c81501ccf6d18c0839d60636214fbd051f0ef37f1d207d4e3c92de2aac008 \
|
||||
--hash=sha256:86d879c32734248ad52482a90ee89a32883bce348b4edd323c01556b505b45b9
|
||||
# via neoteroi-mkdocs
|
||||
ghp-import==2.1.0 \
|
||||
--hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \
|
||||
--hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343
|
||||
# via mkdocs
|
||||
# via
|
||||
# mkdocs
|
||||
# properdocs
|
||||
gitdb==4.0.12 \
|
||||
--hash=sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571 \
|
||||
--hash=sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf
|
||||
# via gitpython
|
||||
gitpython==3.1.46 \
|
||||
--hash=sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f \
|
||||
--hash=sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058
|
||||
gitpython==3.1.50 \
|
||||
--hash=sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc \
|
||||
--hash=sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9
|
||||
# via mkdocs-git-revision-date-localized-plugin
|
||||
griffe==1.15.0 \
|
||||
--hash=sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3 \
|
||||
--hash=sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea
|
||||
griffelib==2.0.0 \
|
||||
--hash=sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f
|
||||
# via mkdocstrings-python
|
||||
h11==0.16.0 \
|
||||
--hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \
|
||||
@@ -211,9 +227,9 @@ httpx==0.28.1 \
|
||||
--hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
|
||||
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
|
||||
# via neoteroi-mkdocs
|
||||
idna==3.11 \
|
||||
--hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \
|
||||
--hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902
|
||||
idna==3.15 \
|
||||
--hash=sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8 \
|
||||
--hash=sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# anyio
|
||||
@@ -229,6 +245,7 @@ jinja2==3.1.6 \
|
||||
# mkdocs-material
|
||||
# mkdocstrings
|
||||
# neoteroi-mkdocs
|
||||
# properdocs
|
||||
jsbeautifier==1.15.4 \
|
||||
--hash=sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592 \
|
||||
--hash=sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528
|
||||
@@ -242,6 +259,7 @@ markdown==3.10.2 \
|
||||
# mkdocs-autorefs
|
||||
# mkdocs-material
|
||||
# mkdocstrings
|
||||
# properdocs
|
||||
# pymdown-extensions
|
||||
markdown-it-py==4.0.0 \
|
||||
--hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 \
|
||||
@@ -344,6 +362,7 @@ markupsafe==3.0.3 \
|
||||
# mkdocs
|
||||
# mkdocs-autorefs
|
||||
# mkdocstrings
|
||||
# properdocs
|
||||
mdurl==0.1.2 \
|
||||
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
|
||||
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
|
||||
@@ -369,31 +388,31 @@ mkdocs==1.6.1 \
|
||||
# mkdocs-simple-hooks
|
||||
# mkdocstrings
|
||||
# neoteroi-mkdocs
|
||||
mkdocs-autorefs==1.4.3 \
|
||||
--hash=sha256:469d85eb3114801d08e9cc55d102b3ba65917a869b893403b8987b601cf55dc9 \
|
||||
--hash=sha256:beee715b254455c4aa93b6ef3c67579c399ca092259cc41b7d9342573ff1fc75
|
||||
mkdocs-autorefs==1.4.4 \
|
||||
--hash=sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089 \
|
||||
--hash=sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197
|
||||
# via
|
||||
# mkdocstrings
|
||||
# mkdocstrings-python
|
||||
mkdocs-get-deps==0.2.0 \
|
||||
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
|
||||
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
|
||||
mkdocs-get-deps==0.2.2 \
|
||||
--hash=sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1 \
|
||||
--hash=sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650
|
||||
# via mkdocs
|
||||
mkdocs-git-revision-date-localized-plugin==1.5.1 \
|
||||
--hash=sha256:2b0239455cd84784dd87ac8dfc9253fe4b2dd35e102696f21b5d34e2175981c6 \
|
||||
--hash=sha256:b00fd36ed0f9b2326b1488fd8fa31bf2ce64e68c4aa60a9ce857f10719571903
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-include-markdown-plugin==7.2.1 \
|
||||
--hash=sha256:30da634c568ea5d5f9e5881d51f80ac30d8c5f891cec160344ad7a0fdaea6286 \
|
||||
--hash=sha256:5d94db87b06cd303619dbaebba5f7f43a3ded7fd7709451d26f08c176376ffec
|
||||
mkdocs-include-markdown-plugin==7.2.2 \
|
||||
--hash=sha256:f052ccb741eccf498116b826c1d78a2d761c56747372594709441cee0963fbc9 \
|
||||
--hash=sha256:f2ec4487cf32d3e33ca528f9366f20fb9280ded9c8d1630eb2bbda244962dcd1
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-macros-plugin==1.5.0 \
|
||||
--hash=sha256:12aa45ce7ecb7a445c66b9f649f3dd05e9b92e8af6bc65e4acd91d26f878c01f \
|
||||
--hash=sha256:c10fabd812bf50f9170609d0ed518e54f1f0e12c334ac29141723a83c881dd6f
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-material==9.7.1 \
|
||||
--hash=sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c \
|
||||
--hash=sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8
|
||||
mkdocs-material==9.7.6 \
|
||||
--hash=sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69 \
|
||||
--hash=sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-material-extensions==1.3.1 \
|
||||
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
|
||||
@@ -403,60 +422,67 @@ mkdocs-mermaid2-plugin==1.2.3 \
|
||||
--hash=sha256:33f60c582be623ed53829a96e19284fc7f1b74a1dbae78d4d2e47fe00c3e190d \
|
||||
--hash=sha256:fb6f901d53e5191e93db78f93f219cad926ccc4d51e176271ca5161b6cc5368c
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-redirects==1.2.2 \
|
||||
--hash=sha256:3094981b42ffab29313c2c1b8ac3969861109f58b2dd58c45fc81cd44bfa0095 \
|
||||
--hash=sha256:7dbfa5647b79a3589da4401403d69494bd1f4ad03b9c15136720367e1f340ed5
|
||||
mkdocs-redirects==1.2.3 \
|
||||
--hash=sha256:5e980330999299729a2d6a125347d1af78023d68a23681a4de3053ce7dfe2e51 \
|
||||
--hash=sha256:ec7312fff462d03ec16395d0c001006a418f8d0c21cdf2b47ff11cf839dc3ce0
|
||||
# via -r docs/requirements.in
|
||||
mkdocs-simple-hooks==0.1.5 \
|
||||
--hash=sha256:dddbdf151a18723c9302a133e5cf79538be8eb9d274e8e07d2ac3ac34890837c \
|
||||
--hash=sha256:efeabdbb98b0850a909adee285f3404535117159d5cb3a34f541d6eaa644d50a
|
||||
# via -r docs/requirements.in
|
||||
mkdocstrings[python]==1.0.3 \
|
||||
--hash=sha256:0d66d18430c2201dc7fe85134277382baaa15e6b30979f3f3bdbabd6dbdb6046 \
|
||||
--hash=sha256:ab670f55040722b49bb45865b2e93b824450fb4aef638b00d7acb493a9020434
|
||||
mkdocstrings[python]==1.0.4 \
|
||||
--hash=sha256:3969a6515b77db65fd097b53c1b7aa4ae840bd71a2ee62a6a3e89503446d7172 \
|
||||
--hash=sha256:63464b4b29053514f32a1dbbf604e52876d5e638111b0c295ab7ed3cac73ca9b
|
||||
# via
|
||||
# -r docs/requirements.in
|
||||
# mkdocstrings-python
|
||||
mkdocstrings-python==2.0.1 \
|
||||
--hash=sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90 \
|
||||
--hash=sha256:843a562221e6a471fefdd4b45cc6c22d2607ccbad632879234fa9692e9cf7732
|
||||
mkdocstrings-python==2.0.3 \
|
||||
--hash=sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12 \
|
||||
--hash=sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8
|
||||
# via mkdocstrings
|
||||
neoteroi-mkdocs==1.2.0 \
|
||||
--hash=sha256:58e25cb1b9db093ffa8d12bdb33264bf567cac30fb964b56e0a493efa749ad6e \
|
||||
--hash=sha256:91b6aa95a4e46c9bb93e00e021d2044cb0c7d80c0b4600434ff8f440d613a0a8
|
||||
# via -r docs/requirements.in
|
||||
packaging==26.0 \
|
||||
--hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \
|
||||
--hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529
|
||||
packaging==26.2 \
|
||||
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
|
||||
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# mkdocs
|
||||
# mkdocs-macros-plugin
|
||||
# properdocs
|
||||
paginate==0.5.7 \
|
||||
--hash=sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945 \
|
||||
--hash=sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591
|
||||
# via mkdocs-material
|
||||
pathspec==1.0.1 \
|
||||
--hash=sha256:8870061f22c58e6d83463cfce9a7dd6eca0512c772c1001fb09ac64091816721 \
|
||||
--hash=sha256:e2769b508d0dd47b09af6ee2c75b2744a2cb1f474ae4b1494fd6a1b7a841613c
|
||||
pathspec==1.0.4 \
|
||||
--hash=sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645 \
|
||||
--hash=sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723
|
||||
# via
|
||||
# mkdocs
|
||||
# mkdocs-macros-plugin
|
||||
platformdirs==4.9.2 \
|
||||
--hash=sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd \
|
||||
--hash=sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291
|
||||
# properdocs
|
||||
platformdirs==4.9.6 \
|
||||
--hash=sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a \
|
||||
--hash=sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# mkdocs-get-deps
|
||||
pygments==2.19.2 \
|
||||
--hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \
|
||||
--hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b
|
||||
# properdocs
|
||||
properdocs==1.6.7 \
|
||||
--hash=sha256:6fa0cfa2e01bf338f684892c8a506cf70ea88ae7f3479c933b6fa20168101cbd \
|
||||
--hash=sha256:adc7b16e562890af0e098a7e5b02e3a81c20894a87d6a28d345c9300de73c26e
|
||||
# via mkdocs-redirects
|
||||
pygments==2.20.0 \
|
||||
--hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
|
||||
--hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
|
||||
# via
|
||||
# mkdocs-material
|
||||
# rich
|
||||
pymdown-extensions==10.20 \
|
||||
--hash=sha256:5c73566ab0cf38c6ba084cb7c5ea64a119ae0500cce754ccb682761dfea13a52 \
|
||||
--hash=sha256:ea9e62add865da80a271d00bfa1c0fa085b20d133fb3fc97afdc88e682f60b2f
|
||||
pymdown-extensions==10.21.3 \
|
||||
--hash=sha256:72cfcf55f07aea0d4af2c4f11dd4e52466ddfb1bb819673146398e0bd3a77354 \
|
||||
--hash=sha256:d7a5d08014fc571e80ca21dd6f854e31f94c489800350564d55d15b3c41e76b6
|
||||
# via
|
||||
# mkdocs-material
|
||||
# mkdocs-mermaid2-plugin
|
||||
@@ -548,27 +574,30 @@ pyyaml==6.0.3 \
|
||||
# mkdocs
|
||||
# mkdocs-get-deps
|
||||
# mkdocs-macros-plugin
|
||||
# properdocs
|
||||
# pymdown-extensions
|
||||
# pyyaml-env-tag
|
||||
pyyaml-env-tag==1.1 \
|
||||
--hash=sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04 \
|
||||
--hash=sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff
|
||||
# via mkdocs
|
||||
requests==2.32.5 \
|
||||
--hash=sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6 \
|
||||
--hash=sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf
|
||||
# via
|
||||
# mkdocs
|
||||
# properdocs
|
||||
requests==2.33.1 \
|
||||
--hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \
|
||||
--hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# mkdocs-macros-plugin
|
||||
# mkdocs-material
|
||||
# mkdocs-mermaid2-plugin
|
||||
rich==14.3.2 \
|
||||
--hash=sha256:08e67c3e90884651da3239ea668222d19bea7b589149d8014a21c633420dbb69 \
|
||||
--hash=sha256:e712f11c1a562a11843306f5ed999475f09ac31ffb64281f73ab29ffdda8b3b8
|
||||
rich==15.0.0 \
|
||||
--hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \
|
||||
--hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36
|
||||
# via neoteroi-mkdocs
|
||||
setuptools==82.0.0 \
|
||||
--hash=sha256:22e0a2d69474c6ae4feb01951cb69d515ed23728cf96d05513d36e42b62b37cb \
|
||||
--hash=sha256:70b18734b607bd1da571d097d236cfcfacaf01de45717d59e6e04b96877532e0
|
||||
setuptools==82.0.1 \
|
||||
--hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \
|
||||
--hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# mkdocs-mermaid2-plugin
|
||||
@@ -579,13 +608,13 @@ six==1.17.0 \
|
||||
# -c src/backend/requirements.txt
|
||||
# jsbeautifier
|
||||
# python-dateutil
|
||||
smmap==5.0.2 \
|
||||
--hash=sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5 \
|
||||
--hash=sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e
|
||||
smmap==5.0.3 \
|
||||
--hash=sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c \
|
||||
--hash=sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f
|
||||
# via gitdb
|
||||
soupsieve==2.8.1 \
|
||||
--hash=sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350 \
|
||||
--hash=sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434
|
||||
soupsieve==2.8.3 \
|
||||
--hash=sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349 \
|
||||
--hash=sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95
|
||||
# via beautifulsoup4
|
||||
super-collections==0.6.2 \
|
||||
--hash=sha256:0c8d8abacd9fad2c7c1c715f036c29f5db213f8cac65f24d45ecba12b4da187a \
|
||||
@@ -602,9 +631,9 @@ typing-extensions==4.15.0 \
|
||||
# -c src/backend/requirements.txt
|
||||
# anyio
|
||||
# beautifulsoup4
|
||||
urllib3==2.6.3 \
|
||||
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
|
||||
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
|
||||
urllib3==2.7.0 \
|
||||
--hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \
|
||||
--hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897
|
||||
# via
|
||||
# -c src/backend/requirements.txt
|
||||
# requests
|
||||
@@ -639,7 +668,9 @@ watchdog==6.0.0 \
|
||||
--hash=sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8 \
|
||||
--hash=sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c \
|
||||
--hash=sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2
|
||||
# via mkdocs
|
||||
# via
|
||||
# mkdocs
|
||||
# properdocs
|
||||
wcmatch==10.1 \
|
||||
--hash=sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a \
|
||||
--hash=sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af
|
||||
|
||||
@@ -108,16 +108,19 @@ root = ["src/backend/InvenTree"]
|
||||
unresolved-reference="ignore" # 21 # see https://github.com/astral-sh/ty/issues/220
|
||||
unresolved-attribute="ignore" # 505 # need Plugin Mixin typing
|
||||
call-non-callable="ignore" # 8 ##
|
||||
invalid-assignment="ignore" # 17 # need to wait for better django field stubs
|
||||
# invalid-method-override="ignore" # 99
|
||||
invalid-return-type="ignore" # 22 ##
|
||||
# possibly-missing-attribute="ignore" # 25 # https://github.com/astral-sh/ty/issues/164
|
||||
unknown-argument="ignore" # 3 # need to wait for better django field stubs
|
||||
invalid-argument-type="ignore" # 49
|
||||
possibly-unbound-attribute="ignore" # 25 # https://github.com/astral-sh/ty/issues/164
|
||||
unknown-argument="ignore" # 3 # need to wait for betterdjango field stubs
|
||||
invalid-assignment="ignore" # 17 # need to wait for betterdjango field stubs
|
||||
no-matching-overload="ignore" # 3 # need to wait for betterdjango field stubs
|
||||
possibly-unbound-attribute="ignore" # 21
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["src/backend/InvenTree", "InvenTree"]
|
||||
dynamic_context = "test_function"
|
||||
omit = ["*/test_migrations.py"]
|
||||
|
||||
[tool.coverage.html]
|
||||
show_contexts = true
|
||||
@@ -139,3 +142,4 @@ django_find_project = false
|
||||
pythonpath = ["src/backend/InvenTree"]
|
||||
DJANGO_SETTINGS_MODULE = "InvenTree.settings"
|
||||
python_files = ["test*.py",]
|
||||
timeout = "120"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Files generated during unit testing
|
||||
_testfolder/
|
||||
_tests_report*.txt
|
||||
|
||||
# Playwright files for CI
|
||||
InvenTree/static/img/playwright*.png
|
||||
|
||||
@@ -15,7 +15,7 @@ from django.views.generic.base import RedirectView
|
||||
import structlog
|
||||
from django_q.models import OrmQ
|
||||
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
||||
from rest_framework import serializers
|
||||
from rest_framework import serializers, viewsets
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.request import clone_request
|
||||
from rest_framework.response import Response
|
||||
@@ -297,7 +297,7 @@ class InfoView(APIView):
|
||||
'instance': InvenTree.version.inventreeInstanceName(),
|
||||
'apiVersion': InvenTree.version.inventreeApiVersion(),
|
||||
'worker_running': is_worker_running(),
|
||||
'worker_count': settings.BACKGROUND_WORKER_COUNT,
|
||||
'worker_count': settings.Q_CLUSTER['workers'],
|
||||
'worker_pending_tasks': self.worker_pending_tasks(),
|
||||
'plugins_enabled': settings.PLUGINS_ENABLED,
|
||||
'plugins_install_disabled': settings.PLUGINS_INSTALL_DISABLED,
|
||||
@@ -423,23 +423,19 @@ class BulkOperationMixin:
|
||||
def get_bulk_queryset(self, request):
|
||||
"""Return a queryset based on the selection made in the request.
|
||||
|
||||
Selection can be made by providing either:
|
||||
|
||||
- items: A list of primary key values
|
||||
- filters: A dictionary of filter values
|
||||
Selection can be made by providing a list of primary key values,
|
||||
which will be used to filter the queryset.
|
||||
"""
|
||||
model = self.serializer_class.Meta.model
|
||||
|
||||
items = request.data.pop('items', None)
|
||||
filters = request.data.pop('filters', None)
|
||||
all_filter = request.GET.get('all', None)
|
||||
|
||||
queryset = model.objects.all()
|
||||
# Return the base queryset for this model
|
||||
queryset = self.get_queryset()
|
||||
|
||||
if not items and not filters and all_filter is None:
|
||||
if not items and all_filter is None:
|
||||
raise ValidationError({
|
||||
'non_field_errors': _(
|
||||
'List of items or filters must be provided for bulk operation'
|
||||
'List of items must be provided for bulk operation'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -457,19 +453,6 @@ class BulkOperationMixin:
|
||||
'non_field_errors': _('Invalid items list provided')
|
||||
})
|
||||
|
||||
if filters:
|
||||
if type(filters) is not dict:
|
||||
raise ValidationError({
|
||||
'non_field_errors': _('Filters must be provided as a dict')
|
||||
})
|
||||
|
||||
try:
|
||||
queryset = queryset.filter(**filters)
|
||||
except Exception:
|
||||
raise ValidationError({
|
||||
'non_field_errors': _('Invalid filters provided')
|
||||
})
|
||||
|
||||
if all_filter and not helpers.str2bool(all_filter):
|
||||
raise ValidationError({
|
||||
'non_field_errors': _('All filter must only be used with true')
|
||||
@@ -507,7 +490,7 @@ class BulkCreateMixin:
|
||||
if unique_create_fields := getattr(self, 'unique_create_fields', None):
|
||||
existing = collections.defaultdict(list)
|
||||
for idx, item in enumerate(data):
|
||||
key = tuple(item[v] for v in unique_create_fields)
|
||||
key = tuple(item[v] for v in list(unique_create_fields)) # type: ignore[not-subscriptable]
|
||||
existing[key].append(idx)
|
||||
|
||||
unique_errors = [[] for _ in range(len(data))]
|
||||
@@ -594,17 +577,24 @@ class BulkUpdateMixin(BulkOperationMixin):
|
||||
|
||||
n = queryset.count()
|
||||
|
||||
instance_data = []
|
||||
|
||||
with transaction.atomic():
|
||||
# Perform object update
|
||||
# Note that we do not perform a bulk-update operation here,
|
||||
# as we want to trigger any custom post_save methods on the model
|
||||
|
||||
# Run validation first
|
||||
for instance in queryset:
|
||||
serializer = self.get_serializer(instance, data=data, partial=True)
|
||||
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
|
||||
return Response({'success': f'Updated {n} items'}, status=200)
|
||||
instance_data.append(serializer.data)
|
||||
|
||||
return Response(
|
||||
{'success': f'Updated {n} items', 'items': instance_data}, status=200
|
||||
)
|
||||
|
||||
|
||||
class ParameterListMixin:
|
||||
@@ -635,12 +625,8 @@ class ParameterListMixin:
|
||||
return queryset
|
||||
|
||||
|
||||
class BulkDeleteMixin(BulkOperationMixin):
|
||||
"""Mixin class for enabling 'bulk delete' operations for various models.
|
||||
|
||||
Bulk delete allows for multiple items to be deleted in a single API query,
|
||||
rather than using multiple API calls to the various detail endpoints.
|
||||
"""
|
||||
class CommonBulkDeleteMixin(BulkOperationMixin):
|
||||
"""Helper for creating bulk delete operation on classic cbv and viewsets."""
|
||||
|
||||
def validate_delete(self, queryset, request) -> None:
|
||||
"""Perform validation right before deletion.
|
||||
@@ -665,7 +651,7 @@ class BulkDeleteMixin(BulkOperationMixin):
|
||||
return queryset
|
||||
|
||||
@extend_schema(request=BulkRequestSerializer)
|
||||
def delete(self, request, *args, **kwargs):
|
||||
def _delete(self, request, *args, **kwargs):
|
||||
"""Perform a DELETE operation against this list endpoint.
|
||||
|
||||
Note that the typical DRF list endpoint does not support DELETE,
|
||||
@@ -689,6 +675,37 @@ class BulkDeleteMixin(BulkOperationMixin):
|
||||
return Response({'success': f'Deleted {n_deleted} items'}, status=200)
|
||||
|
||||
|
||||
class BulkDeleteMixin(CommonBulkDeleteMixin):
|
||||
"""Mixin class for enabling 'bulk delete' operations for various models.
|
||||
|
||||
Bulk delete allows for multiple items to be deleted in a single API query,
|
||||
rather than using multiple API calls to the various detail endpoints.
|
||||
"""
|
||||
|
||||
@extend_schema(request=BulkRequestSerializer)
|
||||
def delete(self, request, *args, **kwargs):
|
||||
"""Perform a DELETE operation against this list endpoint.
|
||||
|
||||
Note that the typical DRF list endpoint does not support DELETE,
|
||||
so this method is provided as a custom implementation.
|
||||
"""
|
||||
return self._delete(request, *args, **kwargs)
|
||||
|
||||
|
||||
class BulkDeleteViewsetMixin(CommonBulkDeleteMixin, viewsets.GenericViewSet):
|
||||
"""Mixin class for enabling 'bulk delete' operations for viewsets."""
|
||||
|
||||
@extend_schema(request=BulkRequestSerializer)
|
||||
def bulk_delete(self, request, *args, **kwargs):
|
||||
"""Perform a bulk delete operation.
|
||||
|
||||
Provide either a list of ids (via `items`) or a filter (via `filters`) to select the items to be deleted.
|
||||
|
||||
This action is performed attomically, so either all items will be deleted, or none will be deleted.
|
||||
"""
|
||||
return self._delete(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ListCreateDestroyAPIView(BulkDeleteMixin, ListCreateAPI):
|
||||
"""Custom API endpoint which provides BulkDelete functionality in addition to List and Create."""
|
||||
|
||||
|
||||
@@ -1,11 +1,127 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 459
|
||||
INVENTREE_API_VERSION = 494
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v494 -> 2026-05-23 : https://github.com/inventree/InvenTree/pull/11990
|
||||
- Offload build output operations to a background task, and return a task ID which can be used to monitor the progress of the task
|
||||
|
||||
v493 -> 2026-05-22 : https://github.com/inventree/InvenTree/pull/11961
|
||||
- Adds "thumbnail" field to the Attachment API endpoint, which provides a URL to a thumbnail image for image attachments (if available)
|
||||
|
||||
v492 -> 2026-05-22 : https://github.com/inventree/InvenTree/pull/11281
|
||||
- Add Transfer Order model and associated API endpoint
|
||||
|
||||
v491 -> 2026-05-21 : https://github.com/inventree/InvenTree/pull/11979
|
||||
- Add API serializer for deleting a part category
|
||||
- Add API serializer for deleting a stock location
|
||||
|
||||
v490 -> 2026-05-19 : https://github.com/inventree/InvenTree/pull/11963
|
||||
- Moves user-self-filtered endpoints to /user/me/ to make their security boundaries clearer
|
||||
|
||||
v489 -> 2026-05-18 : https://github.com/inventree/InvenTree/pull/11962
|
||||
- Removes the "remote_image" field from the Part API endpoint
|
||||
- Removes the "remote_image" field from the Company API endpoint
|
||||
|
||||
v488 -> 2026-05-17 : https://github.com/inventree/InvenTree/pull/11920
|
||||
- Allow renaming of attachments after upload via the API
|
||||
|
||||
v487 -> 2026-05-15 : https://github.com/inventree/InvenTree/pull/11948
|
||||
- Make SelectionList default nullable
|
||||
- Add icon to TreePath schema
|
||||
|
||||
v486 -> 2026-05-10 : https://github.com/inventree/InvenTree/pull/11914
|
||||
- Adds "maximum_stock" field to the Part model and associated API endpoints
|
||||
- Adds "high_stock" filter to the Part API endpoint to filter parts which are above their maximum stock level
|
||||
|
||||
v485 -> 2026-05-10 : https://github.com/inventree/InvenTree/pull/11631
|
||||
- Adds "raw_amount" field to the BomItem API endpoint
|
||||
|
||||
v484 -> 2026-05-10 : https://github.com/inventree/InvenTree/pull/11910
|
||||
- Adds more docstrings to (scheduled) tasks
|
||||
|
||||
v483 -> 2026-05-04 : https://github.com/inventree/InvenTree/pull/11861
|
||||
- Enable bulk-update operations on the BomItem API endpoint, allowing multiple BOM items to be updated in a single API call
|
||||
|
||||
v482 -> 2026-03-15 : https://github.com/inventree/InvenTree/pull/11540
|
||||
- Add id to the ordering fields of the Parts model
|
||||
|
||||
v481 -> 2026-04-28 : https://github.com/inventree/InvenTree/pull/11825
|
||||
- Adds new "bom" ruleset and associated permissions for BOM management, separate from the "part" ruleset which remains focused on part management
|
||||
|
||||
v480 -> 2026-04-27 : https://github.com/inventree/InvenTree/pull/11816
|
||||
- The "issued_by" field on the Build API endpoint is now read-only, and is automatically set to the current user when a build is created
|
||||
|
||||
v479 -> 2026-04-11 : https://github.com/inventree/InvenTree/pull/11723
|
||||
- POST /api/notifications/readall/ now requires a POST action
|
||||
- POST /api/admin/email/test/ - now returns a 200 on. a successful test
|
||||
- GET /api/notifications/ - now uses user-centric permissions, not a general read
|
||||
|
||||
v478 -> 2026-04-11 : https://github.com/inventree/InvenTree/pull/11073
|
||||
- Add OptionalField class for cleaner handling of optional fields in serializers
|
||||
|
||||
v477 -> 2026-04-11 : https://github.com/inventree/InvenTree/pull/11617
|
||||
- Non-functional refactor, adaptations of descriptions
|
||||
|
||||
v476 -> 2026-04-09 : https://github.com/inventree/InvenTree/pull/11705
|
||||
- Adds sorting / filtering / searching functionality to the SelectionListEntry API endpoint
|
||||
|
||||
v475 -> 2026-04-09 : https://github.com/inventree/InvenTree/pull/11702
|
||||
- Adds "updated" and "updated_by" fields to the LabelTemplate and ReportTemplate API endpoints
|
||||
|
||||
v474 -> 2026-04-08 : https://github.com/inventree/InvenTree/pull/11693
|
||||
- Adds DataImportMixin to the ManufacturerPartList API endpoint
|
||||
|
||||
v473 -> 2026-04-08 : https://github.com/inventree/InvenTree/pull/11692
|
||||
- Adds "line" field to PurchaseOrderLineItem and PurchaseOrderExtraLineItem API endpoints
|
||||
- Adds "line" field to SalesOrderLineItem and SalesOrderExtraLineItem API endpoints
|
||||
- Adds "line" field to ReturnOrderLineItem and ReturnOrderExtraLineItem API endpoints
|
||||
|
||||
v472 -> 2026-04-01 : https://github.com/inventree/InvenTree/pull/xxxx
|
||||
- Fixes writable fields on the user detail endpoint
|
||||
|
||||
v471 -> 2026-04-07 : https://github.com/inventree/InvenTree/pull/11685
|
||||
- Adds data importer support for the "SalesOrderShipment" model
|
||||
|
||||
v470 -> 2026-04-01 : https://github.com/inventree/InvenTree/pull/11659
|
||||
- Renames "is_staff" field to "is_admin" and updates help texts accordingly to highlight current security boundaries
|
||||
|
||||
v469 -> 2026-03-31 : https://github.com/inventree/InvenTree/pull/11641
|
||||
- Adds parameter support to the SalesOrderShipment model and API endpoints
|
||||
|
||||
v468 -> 2026-03-31 : https://github.com/inventree/InvenTree/pull/11649
|
||||
- Add ordering to contentype related fields - no functional changes
|
||||
|
||||
v467 -> 2026-03-20 : https://github.com/inventree/InvenTree/pull/11573
|
||||
- Fix definition for the "parent" field on the StockItemSerializer
|
||||
|
||||
v466 -> 2026-03-17 : https://github.com/inventree/InvenTree/pull/11525
|
||||
- SalesOrderShipmentComplete endpoint now returns a task ID which can be used to track the progress of the shipment completion process
|
||||
|
||||
v465 -> 2026-03-16 : https://github.com/inventree/InvenTree/pull/11529/
|
||||
- BuildOrderAutoAllocate endpoint now returns a task ID which can be used to track the progress of the auto-allocation process
|
||||
- BuildOrderConsume endpoint now returns a task ID which can be used to track the progress of the stock consumption process
|
||||
|
||||
v464 -> 2026-03-15 : https://github.com/inventree/InvenTree/pull/11527
|
||||
- Add API endpoint for monitoring the progress of a particular background task
|
||||
|
||||
v463 -> 2026-03-12 : https://github.com/inventree/InvenTree/pull/11499
|
||||
- Allow "bulk update" actions against StockItem endpoint
|
||||
|
||||
v462 -> 2026-03-12 : https://github.com/inventree/InvenTree/pull/11497
|
||||
- Allow "ScheduledTask" API endpoint to be filtered by "name" field
|
||||
|
||||
v461 -> 2026-03-10 : https://github.com/inventree/InvenTree/pull/11479
|
||||
- Adds option to copy parameters when duplicating an order via the API
|
||||
|
||||
v460 -> 2026-02-25 : https://github.com/inventree/InvenTree/pull/11374
|
||||
- Adds "updated_at" field to PurchaseOrder, SalesOrder and ReturnOrder API endpoints
|
||||
- Adds "updated_before" and "updated_after" date filters to all three order list endpoints
|
||||
- Adds "updated_at" ordering option to all three order list endpoints
|
||||
|
||||
v459 -> 2026-02-23 : https://github.com/inventree/InvenTree/pull/11411
|
||||
- Changed PurchaseOrderLine "auto_pricing" default value from true to false
|
||||
|
||||
@@ -296,12 +412,6 @@ v381 -> 2025-08-06 : https://github.com/inventree/InvenTree/pull/10132
|
||||
v380 -> 2025-08-06 : https://github.com/inventree/InvenTree/pull/10135
|
||||
- Fixes "issued_by" filter for the BuildOrder list API endpoint
|
||||
|
||||
v380 -> 2025-08-06 : https://github.com/inventree/InvenTree/pull/10132
|
||||
- Refactor the "return stock item" API endpoint to align with other stock adjustment actions
|
||||
|
||||
v380 -> 2025-08-06 : https://github.com/inventree/InvenTree/pull/10135
|
||||
- Fixes "issued_by" filter for the BuildOrder list API endpoint
|
||||
|
||||
v379 -> 2025-08-04 : https://github.com/inventree/InvenTree/pull/10124
|
||||
- Removes "PartStocktakeReport" model and associated API endpoints
|
||||
- Remove "last_stocktake" field from the Part model
|
||||
|
||||
@@ -65,10 +65,12 @@ class InvenTreeConfig(AppConfig):
|
||||
self.start_background_tasks()
|
||||
|
||||
if not InvenTree.ready.isInTestMode(): # pragma: no cover
|
||||
# Update exchange rates
|
||||
InvenTree.tasks.offload_task(InvenTree.tasks.update_exchange_rates)
|
||||
# Let the background worker check for migrations
|
||||
InvenTree.tasks.offload_task(InvenTree.tasks.check_for_migrations)
|
||||
# Update exchange rates
|
||||
InvenTree.tasks.offload_task(
|
||||
InvenTree.tasks.update_exchange_rates, force_async=True
|
||||
)
|
||||
|
||||
self.update_site_url()
|
||||
self.load_unit_registry()
|
||||
|
||||
@@ -50,6 +50,11 @@ def cache_user():
|
||||
return cache_setting('user', None)
|
||||
|
||||
|
||||
def cache_db():
|
||||
"""Return the cache database index."""
|
||||
return cache_setting('db', 0, typecast=int)
|
||||
|
||||
|
||||
def is_global_cache_enabled() -> bool:
|
||||
"""Check if the global cache is enabled.
|
||||
|
||||
@@ -89,15 +94,17 @@ def get_cache_config(global_cache: bool) -> dict:
|
||||
Returns:
|
||||
A dictionary containing the cache configuration options.
|
||||
"""
|
||||
if global_cache:
|
||||
# Build Redis URL with optional password
|
||||
password = cache_password()
|
||||
user = cache_user() or ''
|
||||
host = cache_host()
|
||||
port = cache_port()
|
||||
db = cache_db()
|
||||
|
||||
if password:
|
||||
redis_url = f'redis://{user}:{password}@{cache_host()}:{cache_port()}/0'
|
||||
redis_url = f'redis://{user}:{password}@{host}:{port}/{db}'
|
||||
else:
|
||||
redis_url = f'redis://{cache_host()}:{cache_port()}/0'
|
||||
redis_url = f'redis://{host}:{port}/{db}'
|
||||
|
||||
keepalive_options = {
|
||||
'TCP_KEEPCNT': cache_setting('keepalive_count', 5, typecast=int),
|
||||
@@ -106,19 +113,15 @@ def get_cache_config(global_cache: bool) -> dict:
|
||||
'TCP_USER_TIMEOUT': cache_setting('user_timeout', 1000, typecast=int),
|
||||
}
|
||||
|
||||
return {
|
||||
global_cache_config = {
|
||||
'BACKEND': 'django_redis.cache.RedisCache',
|
||||
'LOCATION': redis_url,
|
||||
'OPTIONS': {
|
||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||
'SOCKET_CONNECT_TIMEOUT': cache_setting(
|
||||
'connect_timeout', 5, typecast=int
|
||||
),
|
||||
'SOCKET_CONNECT_TIMEOUT': cache_setting('connect_timeout', 5, typecast=int),
|
||||
'SOCKET_TIMEOUT': cache_setting('timeout', 3, typecast=int),
|
||||
'CONNECTION_POOL_KWARGS': {
|
||||
'socket_keepalive': cache_setting(
|
||||
'tcp_keepalive', True, typecast=bool
|
||||
),
|
||||
'socket_keepalive': cache_setting('tcp_keepalive', True, typecast=bool),
|
||||
'socket_keepalive_options': {
|
||||
# Only include options which are available on this platform
|
||||
# e.g. MacOS does not have TCP_KEEPIDLE and TCP_USER_TIMEOUT
|
||||
@@ -130,6 +133,10 @@ def get_cache_config(global_cache: bool) -> dict:
|
||||
},
|
||||
}
|
||||
|
||||
if global_cache:
|
||||
# Only return the global cache configuration if the global cache is enabled
|
||||
return global_cache_config
|
||||
else:
|
||||
# Default: Use django local memory cache
|
||||
return {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import shutil
|
||||
import string
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
CONFIG_DATA = None
|
||||
@@ -188,7 +188,7 @@ def load_config_data(set_cache: bool = False) -> map | None:
|
||||
if CONFIG_DATA is not None and not set_cache:
|
||||
return CONFIG_DATA
|
||||
|
||||
import yaml
|
||||
import yaml.parser
|
||||
|
||||
cfg_file = get_config_file()
|
||||
|
||||
@@ -251,6 +251,24 @@ def do_typecast(value, type, var_name=None):
|
||||
return value
|
||||
|
||||
|
||||
def get_config_value(config_key: str) -> Optional[Any]:
|
||||
"""Helper function to retrieve a configuration value from the config file."""
|
||||
cfg_data = load_config_data()
|
||||
|
||||
result = None
|
||||
|
||||
# Hack to allow 'path traversal' in configuration file
|
||||
for key in config_key.strip().split('.'):
|
||||
if type(cfg_data) is not dict or key not in cfg_data:
|
||||
result = None
|
||||
break
|
||||
|
||||
result = cfg_data[key]
|
||||
cfg_data = cfg_data[key]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_setting(env_var=None, config_key=None, default_value=None, typecast=None):
|
||||
"""Helper function for retrieving a configuration setting value.
|
||||
|
||||
@@ -267,10 +285,13 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None
|
||||
|
||||
def set_metadata(source: str):
|
||||
"""Set lookup metadata for the setting."""
|
||||
global CONFIG_LOOKUPS
|
||||
|
||||
key = env_var or config_key
|
||||
CONFIG_LOOKUPS[key] = {
|
||||
'env_var': env_var,
|
||||
'config_key': config_key,
|
||||
'default_value': default_value,
|
||||
'source': source,
|
||||
'accessed': datetime.datetime.now(),
|
||||
}
|
||||
@@ -285,18 +306,7 @@ def get_setting(env_var=None, config_key=None, default_value=None, typecast=None
|
||||
|
||||
# Next, try to load from configuration file
|
||||
if config_key is not None:
|
||||
cfg_data = load_config_data()
|
||||
|
||||
result = None
|
||||
|
||||
# Hack to allow 'path traversal' in configuration file
|
||||
for key in config_key.strip().split('.'):
|
||||
if type(cfg_data) is not dict or key not in cfg_data:
|
||||
result = None
|
||||
break
|
||||
|
||||
result = cfg_data[key]
|
||||
cfg_data = cfg_data[key]
|
||||
result = get_config_value(config_key)
|
||||
|
||||
if result is not None:
|
||||
set_metadata('yaml')
|
||||
@@ -533,13 +543,14 @@ def get_frontend_settings(debug=True):
|
||||
'INVENTREE_FRONTEND_SETTINGS', 'frontend_settings', {}, typecast=dict
|
||||
)
|
||||
|
||||
base_url = get_setting(
|
||||
'INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web', typecast=str
|
||||
)
|
||||
|
||||
# Set the base URL for the user interface
|
||||
# This is the UI path e.g. '/web/'
|
||||
if 'base_url' not in frontend_settings:
|
||||
frontend_settings['base_url'] = (
|
||||
get_setting('INVENTREE_FRONTEND_URL_BASE', 'frontend_url_base', 'web')
|
||||
or 'web'
|
||||
)
|
||||
frontend_settings['base_url'] = base_url
|
||||
|
||||
# If provided, specify the API host
|
||||
api_host = frontend_settings.get('api_host', None) or get_setting(
|
||||
|
||||
@@ -184,7 +184,7 @@ def from_engineering_notation(value):
|
||||
return value
|
||||
|
||||
|
||||
def convert_value(value, unit):
|
||||
def convert_value(value, unit=None):
|
||||
"""Attempt to convert a value to a specified unit.
|
||||
|
||||
Arguments:
|
||||
@@ -221,11 +221,11 @@ def convert_physical_value(value: str, unit: Optional[str] = None, strip_units=T
|
||||
unit: Optional unit to convert to, and validate against
|
||||
strip_units: If True, strip units from the returned value, and return only the dimension
|
||||
|
||||
Raises:
|
||||
ValidationError: If the value is invalid or cannot be converted to the specified unit
|
||||
|
||||
Returns:
|
||||
The converted quantity, in the specified units
|
||||
|
||||
Raises:
|
||||
ValidationError: If the value is invalid or cannot be converted to the specified unit
|
||||
"""
|
||||
ureg = get_unit_registry()
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ def log_error(
|
||||
data = error_data
|
||||
else:
|
||||
try:
|
||||
formatted_exception = traceback.format_exception(kind, info, data) # type: ignore[no-matching-overload]
|
||||
formatted_exception = traceback.format_exception(kind, info, data)
|
||||
data = '\n'.join(formatted_exception)
|
||||
except AttributeError:
|
||||
data = 'No traceback information available'
|
||||
|
||||
@@ -111,9 +111,11 @@ class InvenTreeOrderingFilter(filters.OrderingFilter):
|
||||
|
||||
def get_ordering(self, request, queryset, view):
|
||||
"""Override ordering for supporting aliases."""
|
||||
ordering = super().get_ordering(request, queryset, view)
|
||||
ordering = list(super().get_ordering(request, queryset, view) or [])
|
||||
|
||||
aliases = getattr(view, 'ordering_field_aliases', None)
|
||||
lookup_field = getattr(view, 'lookup_field', 'pk')
|
||||
lookup_reversed = len(ordering) > 0 and ordering[-1].startswith('-')
|
||||
|
||||
# Attempt to map ordering fields based on provided aliases
|
||||
if ordering is not None and aliases is not None:
|
||||
@@ -123,9 +125,8 @@ class InvenTreeOrderingFilter(filters.OrderingFilter):
|
||||
ordering = []
|
||||
|
||||
for field in ordering_initial:
|
||||
reverse = field.startswith('-')
|
||||
|
||||
if reverse:
|
||||
field_reversed = field.startswith('-')
|
||||
if field_reversed:
|
||||
field = field[1:]
|
||||
|
||||
# Are aliases defined for this field?
|
||||
@@ -153,11 +154,22 @@ class InvenTreeOrderingFilter(filters.OrderingFilter):
|
||||
continue
|
||||
|
||||
for a in alias:
|
||||
if reverse:
|
||||
if field_reversed:
|
||||
a = '-' + a
|
||||
|
||||
ordering.append(a)
|
||||
|
||||
# Ensure that any API filtering appends the primary-key field
|
||||
# This is to prevent "ambiguous ordering" errors across pagination boundaries
|
||||
# Ref: https://github.com/inventree/InvenTree/issues/11442
|
||||
if lookup_field and not any(
|
||||
field in ordering for field in [lookup_field, f'-{lookup_field}']
|
||||
):
|
||||
if lookup_reversed:
|
||||
ordering.append(f'-{lookup_field}')
|
||||
else:
|
||||
ordering.append(lookup_field)
|
||||
|
||||
return ordering
|
||||
|
||||
|
||||
@@ -220,18 +232,10 @@ class NumericInFilter(rest_filters.BaseInFilter):
|
||||
return super().filter(qs, numeric_values)
|
||||
|
||||
|
||||
SEARCH_ORDER_FILTER = [
|
||||
drf_backend.DjangoFilterBackend,
|
||||
InvenTreeSearchFilter,
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
ORDER_FILTER = [drf_backend.DjangoFilterBackend, InvenTreeOrderingFilter]
|
||||
|
||||
SEARCH_ORDER_FILTER_ALIAS = [
|
||||
SEARCH_ORDER_FILTER = [
|
||||
drf_backend.DjangoFilterBackend,
|
||||
InvenTreeSearchFilter,
|
||||
InvenTreeOrderingFilter,
|
||||
]
|
||||
|
||||
ORDER_FILTER = [drf_backend.DjangoFilterBackend, filters.OrderingFilter]
|
||||
|
||||
ORDER_FILTER_ALIAS = [drf_backend.DjangoFilterBackend, InvenTreeOrderingFilter]
|
||||
|
||||
@@ -8,6 +8,7 @@ import json
|
||||
import os.path
|
||||
import re
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from pathlib import Path
|
||||
from typing import Optional, TypeVar
|
||||
from wsgiref.util import FileWrapper
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
@@ -21,19 +22,19 @@ from django.http import StreamingHttpResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import bleach
|
||||
import bleach.css_sanitizer
|
||||
import bleach.sanitizer
|
||||
import nh3
|
||||
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
|
||||
|
||||
from .setting.storages import StorageBackends
|
||||
from .settings import MEDIA_URL, STATIC_URL
|
||||
from InvenTree.sanitizer import (
|
||||
DEAFAULT_ATTRS,
|
||||
DEFAULT_CSS,
|
||||
DEFAULT_PROTOCOLS,
|
||||
DEFAULT_TAGS,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger('inventree')
|
||||
|
||||
@@ -186,9 +187,8 @@ def getMediaUrl(
|
||||
)
|
||||
if name is not None:
|
||||
file = regenerate_imagefile(file, name)
|
||||
if settings.STORAGE_TARGET == StorageBackends.S3:
|
||||
return str(file.url)
|
||||
return os.path.join(MEDIA_URL, str(file.url))
|
||||
|
||||
return default_storage.url(file.name)
|
||||
|
||||
|
||||
def regenerate_imagefile(_file, _name: str):
|
||||
@@ -199,7 +199,7 @@ def regenerate_imagefile(_file, _name: str):
|
||||
_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
|
||||
return ImageFieldFile(_file.instance, _file, name) # ty:ignore[too-many-positional-arguments]
|
||||
|
||||
|
||||
def image2name(img_obj: StdImageField, do_preview: bool, do_thumbnail: bool):
|
||||
@@ -226,11 +226,18 @@ def image2name(img_obj: StdImageField, do_preview: bool, do_thumbnail: bool):
|
||||
|
||||
def getStaticUrl(filename):
|
||||
"""Return the qualified access path for the given file, under the static media directory."""
|
||||
return os.path.join(STATIC_URL, str(filename))
|
||||
return StaticFilesStorage().url(filename)
|
||||
|
||||
|
||||
def TestIfImage(img):
|
||||
"""Test if an image file is indeed an image."""
|
||||
def TestIfImage(img) -> bool:
|
||||
"""Test if an image file is indeed an image.
|
||||
|
||||
Arguments:
|
||||
img: A file-like object
|
||||
|
||||
Returns:
|
||||
True if the file is a valid image, False otherwise
|
||||
"""
|
||||
try:
|
||||
Image.open(img).verify()
|
||||
return True
|
||||
@@ -248,6 +255,13 @@ def getBlankThumbnail():
|
||||
return getStaticUrl('img/blank_image.thumbnail.png')
|
||||
|
||||
|
||||
def checkStaticFile(*args) -> bool:
|
||||
"""Check if a file exists in the static storage."""
|
||||
static_storage = StaticFilesStorage()
|
||||
fn = Path(*args)
|
||||
return static_storage.exists(str(fn))
|
||||
|
||||
|
||||
def getLogoImage(as_file=False, custom=True):
|
||||
"""Return the InvenTree logo image, or a custom logo if available."""
|
||||
if custom and settings.CUSTOM_LOGO:
|
||||
@@ -311,7 +325,7 @@ def TestIfImageURL(url):
|
||||
]
|
||||
|
||||
|
||||
def str2bool(text, test=True):
|
||||
def str2bool(text, test=True) -> bool:
|
||||
"""Test if a string 'looks' like a boolean value.
|
||||
|
||||
Args:
|
||||
@@ -881,13 +895,13 @@ def clean_decimal(number):
|
||||
|
||||
|
||||
def strip_html_tags(value: str, raise_error=True, field_name=None):
|
||||
"""Strip HTML tags from an input string using the bleach library.
|
||||
"""Strip HTML tags from an input string using the nh3 library.
|
||||
|
||||
If raise_error is True, a ValidationError will be thrown if HTML tags are detected
|
||||
"""
|
||||
value = str(value).strip()
|
||||
|
||||
cleaned = clean(value, strip=True, tags=[], attributes=[])
|
||||
cleaned = nh3.clean(value, tags=frozenset())
|
||||
|
||||
# Add escaped characters back in
|
||||
replacements = {'>': '>', '<': '<', '&': '&'}
|
||||
@@ -947,34 +961,32 @@ def clean_markdown(value: str) -> str:
|
||||
output_format='html',
|
||||
)
|
||||
|
||||
# Bleach settings
|
||||
whitelist_tags = markdownify_settings.get(
|
||||
'WHITELIST_TAGS', bleach.sanitizer.ALLOWED_TAGS
|
||||
)
|
||||
whitelist_attrs = markdownify_settings.get(
|
||||
'WHITELIST_ATTRS', bleach.sanitizer.ALLOWED_ATTRIBUTES
|
||||
)
|
||||
whitelist_styles = markdownify_settings.get(
|
||||
'WHITELIST_STYLES', bleach.css_sanitizer.ALLOWED_CSS_PROPERTIES
|
||||
)
|
||||
# nh3 sanitizer settings
|
||||
whitelist_tags = markdownify_settings.get('WHITELIST_TAGS', DEFAULT_TAGS)
|
||||
whitelist_attrs = markdownify_settings.get('WHITELIST_ATTRS', DEAFAULT_ATTRS)
|
||||
whitelist_styles = markdownify_settings.get('WHITELIST_STYLES', DEFAULT_CSS)
|
||||
whitelist_protocols = markdownify_settings.get(
|
||||
'WHITELIST_PROTOCOLS', bleach.sanitizer.ALLOWED_PROTOCOLS
|
||||
'WHITELIST_PROTOCOLS', DEFAULT_PROTOCOLS
|
||||
)
|
||||
strip = markdownify_settings.get('STRIP', True)
|
||||
|
||||
css_sanitizer = bleach.css_sanitizer.CSSSanitizer(
|
||||
allowed_css_properties=whitelist_styles
|
||||
)
|
||||
cleaner = bleach.Cleaner(
|
||||
tags=whitelist_tags,
|
||||
attributes=whitelist_attrs,
|
||||
css_sanitizer=css_sanitizer,
|
||||
protocols=whitelist_protocols,
|
||||
strip=strip,
|
||||
)
|
||||
# Convert bleach-style attributes (list or dict) to nh3-compatible dict format
|
||||
if isinstance(whitelist_attrs, (list, tuple, set, frozenset)):
|
||||
attrs_dict = {'*': set(whitelist_attrs)}
|
||||
elif isinstance(whitelist_attrs, dict):
|
||||
attrs_dict = {tag: set(allowed) for tag, allowed in whitelist_attrs.items()}
|
||||
else:
|
||||
attrs_dict = None
|
||||
|
||||
# Clean the HTML content (for comparison). This must be the same as the original content
|
||||
clean_html = cleaner.clean(html)
|
||||
clean_html = nh3.clean(
|
||||
html,
|
||||
tags=set(whitelist_tags),
|
||||
attributes=attrs_dict,
|
||||
url_schemes=set(whitelist_protocols),
|
||||
filter_style_properties=set(whitelist_styles),
|
||||
link_rel=None,
|
||||
strip_comments=True,
|
||||
)
|
||||
|
||||
if html != clean_html:
|
||||
raise ValidationError(_('Data contains prohibited markdown content'))
|
||||
@@ -1169,3 +1181,18 @@ def plugins_info(*args, **kwargs):
|
||||
return [
|
||||
{'name': plg.name, 'slug': plg.slug, 'version': plg.version} for plg in plugins
|
||||
]
|
||||
|
||||
|
||||
def sanitize_token(token_value: str, front=8, back=12) -> str:
|
||||
"""Sanitize a token by replacing the middle characters with asterisks.
|
||||
|
||||
Args:
|
||||
token_value: The token string to sanitize
|
||||
front: Number of characters to show at the start of the token (default = 8)
|
||||
back: Number of characters to show at the end of the token (default = 12)
|
||||
|
||||
Returns:
|
||||
The sanitized token string
|
||||
"""
|
||||
middle = len(token_value) - (front + back)
|
||||
return token_value[:front] + '*' * middle + token_value[-back:]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Helpers for InvenTrees way of using drf viewset."""
|
||||
|
||||
from rest_framework import mixins, routers, viewsets
|
||||
|
||||
from InvenTree.api import BulkDeleteViewsetMixin
|
||||
|
||||
|
||||
class RetrieveUpdateDestroyModelViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.UpdateModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
viewsets.GenericViewSet,
|
||||
):
|
||||
"""Viewset which provides 'retrieve', 'update', 'destroy' and 'list' actions."""
|
||||
|
||||
|
||||
class RetrieveDestroyModelViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.DestroyModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
viewsets.GenericViewSet,
|
||||
):
|
||||
"""Viewset which provides 'retrieve', 'destroy' and 'list' actions."""
|
||||
|
||||
|
||||
class InvenTreeApiRouter(routers.SimpleRouter):
|
||||
"""Custom router which adds various specific functions.
|
||||
|
||||
Currently adds the following features:
|
||||
- support for bulk delete operations
|
||||
"""
|
||||
|
||||
def get_routes(self, viewset):
|
||||
"""Override the default get_routes method to add bulk delete support."""
|
||||
routes = super().get_routes(viewset)
|
||||
|
||||
if issubclass(viewset, BulkDeleteViewsetMixin):
|
||||
list_route = next(
|
||||
(route for route in routes if route.mapping.get('get') == 'list'), None
|
||||
)
|
||||
list_route.mapping['delete'] = 'bulk_delete'
|
||||
|
||||
return routes
|
||||
|
||||
def get_default_basename(self, viewset):
|
||||
"""Extract the default base name from the viewset."""
|
||||
basename = super().get_default_basename(viewset)
|
||||
return 'api-' + basename
|
||||
@@ -8,6 +8,7 @@ import structlog
|
||||
from allauth.account.models import EmailAddress
|
||||
|
||||
import InvenTree.ready
|
||||
import InvenTree.tasks as tasks
|
||||
from common.models import Priority, issue_mail
|
||||
|
||||
logger = structlog.get_logger('inventree')
|
||||
@@ -98,7 +99,7 @@ def send_email(
|
||||
)
|
||||
return False, 'INVE-W7: no from_email or DEFAULT_FROM_EMAIL specified'
|
||||
|
||||
InvenTree.tasks.offload_task(
|
||||
tasks.offload_task(
|
||||
issue_mail,
|
||||
subject=subject,
|
||||
body=body,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Provides helper functions used throughout the InvenTree project that access the database."""
|
||||
|
||||
import io
|
||||
import ipaddress
|
||||
import socket
|
||||
from decimal import Decimal
|
||||
from typing import Optional, cast
|
||||
from urllib.parse import urljoin
|
||||
from urllib.parse import urljoin, urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -88,7 +90,42 @@ def construct_absolute_url(*arg, base_url=None, request=None):
|
||||
return urljoin(base_url, relative_url)
|
||||
|
||||
|
||||
def download_image_from_url(remote_url, timeout=2.5):
|
||||
def validate_url_no_ssrf(url):
|
||||
"""Validate that a URL does not point to a private/internal network address.
|
||||
|
||||
Resolves the hostname to an IP address and checks it against private,
|
||||
loopback, link-local, and reserved IP ranges to prevent SSRF attacks.
|
||||
|
||||
Arguments:
|
||||
url: The URL to validate
|
||||
|
||||
Raises:
|
||||
ValueError: If the URL resolves to a private or reserved IP address
|
||||
"""
|
||||
parsed = urlparse(url)
|
||||
hostname = parsed.hostname
|
||||
|
||||
if not hostname:
|
||||
raise ValueError(_('Invalid URL: no hostname'))
|
||||
|
||||
try:
|
||||
addrinfo = socket.getaddrinfo(hostname, None)
|
||||
except socket.gaierror:
|
||||
raise ValueError(_('Invalid URL: hostname could not be resolved'))
|
||||
|
||||
for _family, _type, _proto, _canonname, sockaddr in addrinfo:
|
||||
ip = ipaddress.ip_address(sockaddr[0])
|
||||
|
||||
if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved:
|
||||
raise ValueError(_('URL points to a private or reserved IP address'))
|
||||
|
||||
|
||||
def download_image_from_url(
|
||||
remote_url: str,
|
||||
timeout: float = 2.5,
|
||||
user_agent: str = '',
|
||||
max_size: Optional[int] = None,
|
||||
):
|
||||
"""Download an image file from a remote URL.
|
||||
|
||||
This is a potentially dangerous operation, so we must perform some checks:
|
||||
@@ -98,8 +135,9 @@ def download_image_from_url(remote_url, timeout=2.5):
|
||||
|
||||
Arguments:
|
||||
remote_url: The remote URL to retrieve image
|
||||
max_size: Maximum allowed image size (default = 1MB)
|
||||
timeout: Connection timeout in seconds (default = 5)
|
||||
user_agent: User-Agent string to use for the request (optional)
|
||||
max_size: Maximum allowed image size (in bytes) (default = 1MB)
|
||||
|
||||
Returns:
|
||||
An in-memory PIL image file, if the download was successful
|
||||
@@ -115,24 +153,49 @@ def download_image_from_url(remote_url, timeout=2.5):
|
||||
validator = URLValidator()
|
||||
validator(remote_url)
|
||||
|
||||
# SSRF protection: validate the resolved IP is not private/internal
|
||||
validate_url_no_ssrf(remote_url)
|
||||
|
||||
# Calculate maximum allowable image size (in bytes)
|
||||
max_size = (
|
||||
int(get_global_setting('INVENTREE_DOWNLOAD_IMAGE_MAX_SIZE')) * 1024 * 1024
|
||||
)
|
||||
max_size = max_size or 1 * 1024 * 1024 # Default to 1MB if not provided
|
||||
|
||||
# Add user specified user-agent to request (if specified)
|
||||
user_agent = get_global_setting('INVENTREE_DOWNLOAD_FROM_URL_USER_AGENT')
|
||||
|
||||
headers = {'User-Agent': user_agent} if user_agent else None
|
||||
|
||||
try:
|
||||
response = requests.get(
|
||||
remote_url,
|
||||
timeout=timeout,
|
||||
allow_redirects=True,
|
||||
allow_redirects=False,
|
||||
stream=True,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
# Handle redirects manually to validate each destination
|
||||
max_redirects = 5
|
||||
redirect_count = 0
|
||||
|
||||
while response.is_redirect and redirect_count < max_redirects:
|
||||
redirect_url = response.headers.get('Location')
|
||||
if not redirect_url:
|
||||
break
|
||||
|
||||
# Validate the redirect destination against SSRF
|
||||
validator(redirect_url)
|
||||
validate_url_no_ssrf(redirect_url)
|
||||
|
||||
redirect_count += 1
|
||||
response = requests.get(
|
||||
redirect_url,
|
||||
timeout=timeout,
|
||||
allow_redirects=False,
|
||||
stream=True,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if redirect_count >= max_redirects:
|
||||
raise ValueError(_('Too many redirects'))
|
||||
|
||||
# Throw an error if anything goes wrong
|
||||
response.raise_for_status()
|
||||
except requests.exceptions.ConnectionError as exc:
|
||||
@@ -143,6 +206,8 @@ def download_image_from_url(remote_url, timeout=2.5):
|
||||
raise requests.exceptions.HTTPError(
|
||||
_('Server responded with invalid status code') + f': {response.status_code}'
|
||||
)
|
||||
except ValueError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise Exception(_('Exception occurred') + f': {exc!s}')
|
||||
|
||||
@@ -288,6 +353,8 @@ def getModelsWithMixin(mixin_class) -> list:
|
||||
models_with_mixin = [
|
||||
x for x in db_models if x is not None and issubclass(x, mixin_class)
|
||||
]
|
||||
# sort to make resulting list deterministic (and easier to test)
|
||||
models_with_mixin.sort(key=lambda x: x._meta.label_lower)
|
||||
|
||||
# Store the result in the session cache
|
||||
set_session_cache(cache_key, models_with_mixin)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Helpers for logging integrations."""
|
||||
|
||||
from django.dispatch import receiver
|
||||
|
||||
import structlog
|
||||
from django_structlog import signals
|
||||
|
||||
|
||||
@receiver(signals.update_failure_response)
|
||||
@receiver(signals.bind_extra_request_finished_metadata)
|
||||
def add_request_id_to_response(response, logger, **kwargs):
|
||||
"""Add the request ID to the response header, so that it can be traced through logs.
|
||||
|
||||
source: https://django-structlog.readthedocs.io/en/latest/how_tos.html#bind-request-id-to-response-s-header
|
||||
"""
|
||||
context = structlog.contextvars.get_merged_contextvars(logger)
|
||||
response['X-InvenTree-ReqId'] = context['request_id']
|
||||