Compare commits
47 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
01bf600004 | |
|
|
6bb6bcca2e | |
|
|
d737a7d7a2 | |
|
|
50ac52f09a | |
|
|
da1be76352 | |
|
|
b19528b341 | |
|
|
c5ff2f514f | |
|
|
badd6e431b | |
|
|
add122faf1 | |
|
|
b810b47a4a | |
|
|
651e1c0dac | |
|
|
2bc9e0f0d2 | |
|
|
604f5fdb45 | |
|
|
ee3d01cf73 | |
|
|
3e447066c6 | |
|
|
17fd3174fc | |
|
|
c92f1c3413 | |
|
|
65f1a4bde5 | |
|
|
8077849fc5 | |
|
|
4789a202ee | |
|
|
60f2d9d314 | |
|
|
c173c4e051 | |
|
|
73cab199f1 | |
|
|
e749ece58e | |
|
|
e5ace7d761 | |
|
|
5bf41f1463 | |
|
|
59f3b9f2f6 | |
|
|
f2fa65ff1d | |
|
|
9cfec02a69 | |
|
|
c35ab145a4 | |
|
|
b611960d9f | |
|
|
12da3b01fc | |
|
|
2df9eb80ca | |
|
|
44387f3bb4 | |
|
|
ed8331e26a | |
|
|
228577c871 | |
|
|
fd7d0bda0f | |
|
|
90e5169283 | |
|
|
198594d7d3 | |
|
|
dededb0aa1 | |
|
|
eb413bcc57 | |
|
|
9b11cd294e | |
|
|
ccf5b39bf1 | |
|
|
eceaa68c32 | |
|
|
ccb4ea248b | |
|
|
9b3b68c8d8 | |
|
|
3752b610c8 |
|
|
@ -1,7 +1,9 @@
|
||||||
# Dockerfile for the InvenTree devcontainer
|
# Dockerfile for the InvenTree devcontainer
|
||||||
# This container is used for development of the InvenTree project, and includes all necessary dependencies for both backend and frontend development.
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/devcontainers/python:3.12-trixie@sha256:5440cb68898d190ad6c6e8a4634ce89d0645bea47f9c8beb75612bb8e3983711
|
# In contrast with the "production" image (which is based on an Alpine image)
|
||||||
|
# we use a Debian-based image for the devcontainer
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/devcontainers/python:3.11-bookworm@sha256:e754c29c4e3ffcf6c794c1020e36a0812341d88ec9569a34704b975fa89e8848
|
||||||
|
|
||||||
# InvenTree paths
|
# InvenTree paths
|
||||||
ENV INVENTREE_HOME="/home/inventree"
|
ENV INVENTREE_HOME="/home/inventree"
|
||||||
|
|
@ -23,10 +25,10 @@ RUN chmod +x init.sh
|
||||||
|
|
||||||
# Install required base packages
|
# Install required base packages
|
||||||
RUN apt update && apt install -y \
|
RUN apt update && apt install -y \
|
||||||
python3-dev python3-venv \
|
python3.11-dev python3.11-venv \
|
||||||
postgresql-client \
|
postgresql-client \
|
||||||
libldap2-dev libsasl2-dev \
|
libldap2-dev libsasl2-dev \
|
||||||
libpango-1.0-0 libcairo2 \
|
libpango1.0-0 libcairo2 \
|
||||||
poppler-utils weasyprint
|
poppler-utils weasyprint
|
||||||
|
|
||||||
# Install packages required for frontend development
|
# Install packages required for frontend development
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
trigger:
|
||||||
|
batch: true
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- master
|
||||||
|
- stable
|
||||||
|
- refs/tags/*
|
||||||
|
paths:
|
||||||
|
include:
|
||||||
|
- src/backend
|
||||||
|
|
||||||
|
pool:
|
||||||
|
vmImage: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
Python39:
|
||||||
|
PYTHON_VERSION: '3.11'
|
||||||
|
maxParallel: 3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- task: UsePythonVersion@0
|
||||||
|
inputs:
|
||||||
|
versionSpec: '$(PYTHON_VERSION)'
|
||||||
|
architecture: 'x64'
|
||||||
|
|
||||||
|
- task: PythonScript@0
|
||||||
|
displayName: 'Export project path'
|
||||||
|
inputs:
|
||||||
|
scriptSource: 'inline'
|
||||||
|
script: |
|
||||||
|
"""Search all subdirectories for `manage.py`."""
|
||||||
|
from glob import iglob
|
||||||
|
from os import path
|
||||||
|
# Python >= 3.5
|
||||||
|
manage_py = next(iglob(path.join('**', 'manage.py'), recursive=True), None)
|
||||||
|
if not manage_py:
|
||||||
|
raise SystemExit('Could not find a Django project')
|
||||||
|
project_location = path.dirname(path.abspath(manage_py))
|
||||||
|
print('Found Django project in', project_location)
|
||||||
|
print('##vso[task.setvariable variable=projectRoot]{}'.format(project_location))
|
||||||
|
|
||||||
|
- script: |
|
||||||
|
python -m pip install --upgrade pip setuptools wheel uv
|
||||||
|
uv pip install --require-hashes -r src/backend/requirements.txt
|
||||||
|
uv pip install --require-hashes -r src/backend/requirements-dev.txt
|
||||||
|
sudo apt-get install poppler-utils
|
||||||
|
sudo apt-get install libpoppler-dev
|
||||||
|
uv pip install unittest-xml-reporting coverage invoke
|
||||||
|
displayName: 'Install prerequisites'
|
||||||
|
env:
|
||||||
|
UV_SYSTEM_PYTHON: 1
|
||||||
|
|
||||||
|
- script: |
|
||||||
|
pushd '$(projectRoot)'
|
||||||
|
invoke update --uv
|
||||||
|
coverage run manage.py test --testrunner xmlrunner.extra.djangotestrunner.XMLTestRunner --no-input
|
||||||
|
coverage xml -i
|
||||||
|
displayName: 'Run tests'
|
||||||
|
env:
|
||||||
|
INVENTREE_DB_ENGINE: sqlite3
|
||||||
|
INVENTREE_DB_NAME: inventree
|
||||||
|
INVENTREE_MEDIA_ROOT: ./media
|
||||||
|
INVENTREE_STATIC_ROOT: ./static
|
||||||
|
INVENTREE_BACKUP_DIR: ./backup
|
||||||
|
INVENTREE_SITE_URL: http://localhost:8000
|
||||||
|
INVENTREE_PLUGINS_ENABLED: true
|
||||||
|
UV_SYSTEM_PYTHON: 1
|
||||||
|
INVENTREE_DEBUG: true
|
||||||
|
INVENTREE_LOG_LEVEL: INFO
|
||||||
|
|
||||||
|
- task: PublishTestResults@2
|
||||||
|
inputs:
|
||||||
|
testResultsFiles: "**/TEST-*.xml"
|
||||||
|
testRunTitle: 'Python $(PYTHON_VERSION)'
|
||||||
|
condition: succeededOrFailed()
|
||||||
|
|
||||||
|
- task: PublishCodeCoverageResults@2
|
||||||
|
inputs:
|
||||||
|
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
polar: inventree
|
||||||
|
github: inventree
|
||||||
|
custom: [paypal.me/inventree]
|
||||||
|
|
@ -13,5 +13,5 @@ runs:
|
||||||
invoke export-records -f data.json
|
invoke export-records -f data.json
|
||||||
python3 ./src/backend/InvenTree/manage.py flush --noinput
|
python3 ./src/backend/InvenTree/manage.py flush --noinput
|
||||||
invoke migrate
|
invoke migrate
|
||||||
invoke import-records -c -f data.json --strict
|
invoke import-records -c -f data.json
|
||||||
invoke import-records -c -f data.json --strict
|
invoke import-records -c -f data.json
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,14 @@ runs:
|
||||||
using: 'composite'
|
using: 'composite'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
# Python installs
|
# Python installs
|
||||||
- name: Set up Python ${{ env.python_version }}
|
- name: Set up Python ${{ env.python_version }}
|
||||||
if: ${{ inputs.python == 'true' && env.python_version != '3.14' }}
|
if: ${{ inputs.python == 'true' && env.python_version != '3.14' }}
|
||||||
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
|
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # pin@v5.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.python_version }}
|
python-version: ${{ env.python_version }}
|
||||||
cache: pip
|
cache: pip
|
||||||
|
|
@ -57,7 +57,7 @@ runs:
|
||||||
contrib/dev_reqs/requirements.txt
|
contrib/dev_reqs/requirements.txt
|
||||||
- name: Setup Python 3.14
|
- name: Setup Python 3.14
|
||||||
if: ${{ inputs.python == 'true' && env.python_version == '3.14' }}
|
if: ${{ inputs.python == 'true' && env.python_version == '3.14' }}
|
||||||
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
|
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # pin@v5.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.python_version }}
|
python-version: ${{ env.python_version }}
|
||||||
- name: Install Base Python Dependencies
|
- name: Install Base Python Dependencies
|
||||||
|
|
@ -106,7 +106,7 @@ runs:
|
||||||
- name: Run invoke update
|
- name: Run invoke update
|
||||||
if: ${{ inputs.update == 'true' }}
|
if: ${{ inputs.update == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: invoke update --skip-backup --skip-static --no-frontend
|
run: invoke update --skip-backup --skip-static
|
||||||
- name: Collect static files
|
- name: Collect static files
|
||||||
if: ${{ inputs.static == 'true' }}
|
if: ${{ inputs.static == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
"""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!')
|
|
||||||
|
|
@ -264,7 +264,6 @@ def main() -> bool:
|
||||||
|
|
||||||
# Determine which docker tag we are going to use
|
# Determine which docker tag we are going to use
|
||||||
docker_tags: Optional[list[str]] = None
|
docker_tags: Optional[list[str]] = None
|
||||||
pkg_channel = None
|
|
||||||
|
|
||||||
if GITHUB_REF_TYPE == 'tag':
|
if GITHUB_REF_TYPE == 'tag':
|
||||||
# GITHUB_REF should be of the form /refs/heads/<tag>
|
# GITHUB_REF should be of the form /refs/heads/<tag>
|
||||||
|
|
@ -279,14 +278,10 @@ def main() -> bool:
|
||||||
|
|
||||||
docker_tags = [version_tag, 'stable'] if highest_release else [version_tag]
|
docker_tags = [version_tag, 'stable'] if highest_release else [version_tag]
|
||||||
|
|
||||||
# Add release-line tag
|
|
||||||
pkg_channel = '.'.join(version_tag.split('.')[:2]) + '.x'
|
|
||||||
|
|
||||||
elif GITHUB_REF_TYPE == 'branch':
|
elif GITHUB_REF_TYPE == 'branch':
|
||||||
# Otherwise we know we are targeting the 'master' branch
|
# Otherwise we know we are targeting the 'master' branch
|
||||||
docker_tags = ['latest']
|
docker_tags = ['latest']
|
||||||
highest_release = False
|
highest_release = False
|
||||||
pkg_channel = GITHUB_BASE_REF
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print('Unsupported branch / version combination:')
|
print('Unsupported branch / version combination:')
|
||||||
|
|
@ -315,8 +310,6 @@ def main() -> bool:
|
||||||
|
|
||||||
if GITHUB_REF_TYPE == 'tag' and highest_release:
|
if GITHUB_REF_TYPE == 'tag' and highest_release:
|
||||||
env_file.write('stable_release=true\n')
|
env_file.write('stable_release=true\n')
|
||||||
|
|
||||||
env_file.write(f'pkg_channel={pkg_channel}\n')
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
)
|
)
|
||||||
steps:
|
steps:
|
||||||
- name: Backport Action
|
- name: Backport Action
|
||||||
uses: sorenlouv/backport-github-action@8a6c0381851f43f9f1fddc7303f0e9015eb57b62 # v12.0.4
|
uses: sqren/backport-github-action@ad888e978060bc1b2798690dd9d03c4036560947 # pin@v9.2.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
auto_backport_label_prefix: backport-to-
|
auto_backport_label_prefix: backport-to-
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ on:
|
||||||
- l10
|
- l10
|
||||||
|
|
||||||
env:
|
env:
|
||||||
python_version: 3.12
|
python_version: 3.11
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
@ -31,7 +31,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,10 @@ jobs:
|
||||||
docker: ${{ steps.filter.outputs.docker }}
|
docker: ${{ steps.filter.outputs.docker }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
|
@ -62,12 +62,12 @@ jobs:
|
||||||
contents: read
|
contents: read
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
python_version: 3.12
|
python_version: "3.11"
|
||||||
runs-on: ubuntu-latest # in the future we can try to use alternative runners here
|
runs-on: ubuntu-latest # in the future we can try to use alternative runners here
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Test Docker Image
|
- name: Test Docker Image
|
||||||
|
|
@ -84,16 +84,6 @@ jobs:
|
||||||
docker run --rm inventree-test test -f /home/inventree/gunicorn.conf.py
|
docker run --rm inventree-test test -f /home/inventree/gunicorn.conf.py
|
||||||
docker run --rm inventree-test test -f /home/inventree/src/backend/requirements.txt
|
docker run --rm inventree-test test -f /home/inventree/src/backend/requirements.txt
|
||||||
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/manage.py
|
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/manage.py
|
||||||
# Check that backend translations were compiled into the image (a few representative languages)
|
|
||||||
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/de/LC_MESSAGES/django.mo
|
|
||||||
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/fr/LC_MESSAGES/django.mo
|
|
||||||
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/ru/LC_MESSAGES/django.mo
|
|
||||||
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/zh_Hans/LC_MESSAGES/django.mo
|
|
||||||
# Check that frontend translations (lingui) were compiled into the static build output (a few representative languages)
|
|
||||||
docker run --rm inventree-test grep -q '"src/locales/de/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
|
|
||||||
docker run --rm inventree-test grep -q '"src/locales/fr/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
|
|
||||||
docker run --rm inventree-test grep -q '"src/locales/ru/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
|
|
||||||
docker run --rm inventree-test grep -q '"src/locales/zh_Hans/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
|
|
||||||
- name: Build Docker Image
|
- name: Build Docker Image
|
||||||
# Build the development docker image (using docker-compose.yml)
|
# Build the development docker image (using docker-compose.yml)
|
||||||
run: docker compose --project-directory . -f contrib/container/dev-docker-compose.yml build --no-cache
|
run: docker compose --project-directory . -f contrib/container/dev-docker-compose.yml build --no-cache
|
||||||
|
|
@ -134,12 +124,12 @@ jobs:
|
||||||
contents: read
|
contents: read
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
python_version: 3.12
|
python_version: "3.11"
|
||||||
runs-on: ubuntu-latest # in the future we can try to use alternative runners here
|
runs-on: ubuntu-latest # in the future we can try to use alternative runners here
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run Migration Tests
|
- name: Run Migration Tests
|
||||||
|
|
@ -158,16 +148,16 @@ jobs:
|
||||||
id-token: write
|
id-token: write
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
python_version: 3.12
|
python_version: "3.11"
|
||||||
runs-on: ubuntu-latest # in the future we can try to use alternative runners here
|
runs-on: ubuntu-latest # in the future we can try to use alternative runners here
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set Up Python ${{ env.python_version }}
|
- name: Set Up Python ${{ env.python_version }}
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # pin@v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.python_version }}
|
python-version: ${{ env.python_version }}
|
||||||
- name: Version Check
|
- name: Version Check
|
||||||
|
|
@ -178,13 +168,13 @@ jobs:
|
||||||
echo "git_commit_date=$(git show -s --format=%ci)" >> $GITHUB_ENV
|
echo "git_commit_date=$(git show -s --format=%ci)" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # pin@v3.7.0
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # pin@v3.12.0
|
||||||
- name: Set up cosign
|
- name: Set up cosign
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # pin@v4.0.0
|
||||||
- name: Check if Dockerhub login is required
|
- name: Check if Dockerhub login is required
|
||||||
id: docker_login
|
id: docker_login
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -195,14 +185,14 @@ jobs:
|
||||||
fi
|
fi
|
||||||
- name: Login to Dockerhub
|
- name: Login to Dockerhub
|
||||||
if: github.event_name != 'pull_request' && steps.docker_login.outputs.skip_dockerhub_login != 'true'
|
if: github.event_name != 'pull_request' && steps.docker_login.outputs.skip_dockerhub_login != 'true'
|
||||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # pin@v3.7.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
- name: Log into registry ghcr.io
|
- name: Log into registry ghcr.io
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # pin@v3.7.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
|
@ -211,16 +201,16 @@ jobs:
|
||||||
- name: Extract Docker metadata
|
- name: Extract Docker metadata
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
|
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # pin@v5.10.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
inventree/inventree
|
inventree/inventree
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
- uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1
|
- uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # pin@v1
|
||||||
- name: Push Docker Images
|
- name: Push Docker Images
|
||||||
id: push-docker
|
id: push-docker
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1
|
uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # pin@v1
|
||||||
with:
|
with:
|
||||||
project: jczzbjkk68
|
project: jczzbjkk68
|
||||||
context: .
|
context: .
|
||||||
|
|
|
||||||
|
|
@ -1,312 +0,0 @@
|
||||||
# Playwright testing for frontend code
|
|
||||||
# Runs the following tests:
|
|
||||||
# - Playwright tests in Firefox (against compiled frontend code)
|
|
||||||
# - Playwright tests in Chromium (coverage enabled, against vite frontend code)
|
|
||||||
# - Build frontend code and upload as artifact
|
|
||||||
|
|
||||||
name: Frontend
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore: ["l10*", "dependabot/**", "backport/**"]
|
|
||||||
pull_request:
|
|
||||||
branches-ignore: ["l10*"]
|
|
||||||
|
|
||||||
env:
|
|
||||||
python_version: 3.12
|
|
||||||
node_version: 24
|
|
||||||
plugin_creator_version: 1.20.0
|
|
||||||
# The OS version must be set per job
|
|
||||||
server_start_sleep: 60
|
|
||||||
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
INVENTREE_DB_ENGINE: postgresql
|
|
||||||
INVENTREE_DB_NAME: inventree
|
|
||||||
INVENTREE_DB_HOST: "127.0.0.1"
|
|
||||||
INVENTREE_DB_PORT: 5432
|
|
||||||
INVENTREE_DB_USER: inventree_user
|
|
||||||
INVENTREE_DB_PASSWORD: inventree_password
|
|
||||||
INVENTREE_DEBUG: true
|
|
||||||
INVENTREE_PLUGINS_ENABLED: false
|
|
||||||
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
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
paths-filter:
|
|
||||||
name: Filter
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
frontend: ${{ steps.filter.outputs.frontend }}
|
|
||||||
force: ${{ steps.force.outputs.force }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
|
||||||
id: filter
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
frontend:
|
|
||||||
- 'src/frontend/**'
|
|
||||||
- name: Is CI being forced?
|
|
||||||
run: echo "force=true" >> $GITHUB_OUTPUT
|
|
||||||
id: force
|
|
||||||
if: |
|
|
||||||
contains(github.event.pull_request.labels.*.name, 'dependency') ||
|
|
||||||
contains(github.event.pull_request.labels.*.name, 'full-run')
|
|
||||||
|
|
||||||
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
timeout-minutes: 60
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Environment Setup
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
with:
|
|
||||||
npm: true
|
|
||||||
- name: Install dependencies
|
|
||||||
run: cd src/frontend && yarn install
|
|
||||||
- name: Build frontend
|
|
||||||
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
with:
|
|
||||||
name: frontend-build
|
|
||||||
path: src/backend/InvenTree/web/static/web
|
|
||||||
include-hidden-files: true
|
|
||||||
|
|
||||||
firefox:
|
|
||||||
name: Tests [Firefox ${{ matrix.shard }} / 2]
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
timeout-minutes: 60
|
|
||||||
needs: ["build", "paths-filter"]
|
|
||||||
if: github.ref == 'refs/heads/master' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shard: [1, 2]
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:17
|
|
||||||
env:
|
|
||||||
POSTGRES_DB: inventree
|
|
||||||
POSTGRES_USER: inventree_user
|
|
||||||
POSTGRES_PASSWORD: inventree_password
|
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
|
||||||
--health-cmd "pg_isready -U testuser"
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
permissions:
|
|
||||||
contents: read # Required for actions/checkout
|
|
||||||
id-token: write # Required for GitHub OIDC
|
|
||||||
env:
|
|
||||||
VITE_COVERAGE: false
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Environment Setup
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
with:
|
|
||||||
npm: true
|
|
||||||
install: true
|
|
||||||
update: true
|
|
||||||
apt-dependency: gettext postgresql-client libpq-dev
|
|
||||||
pip-dependency: psycopg2
|
|
||||||
- name: Set up test data
|
|
||||||
run: |
|
|
||||||
invoke dev.setup-test -iv
|
|
||||||
invoke int.rebuild-thumbnails
|
|
||||||
- name: Install dependencies
|
|
||||||
run: invoke int.frontend-compile --extract
|
|
||||||
- name: Cache Playwright browsers
|
|
||||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: ~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('src/frontend/yarn.lock') }}
|
|
||||||
- name: Install Playwright browsers
|
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
||||||
run: cd src/frontend && npx playwright install --with-deps
|
|
||||||
- name: Install Playwright OS dependencies
|
|
||||||
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
|
||||||
run: cd src/frontend && npx playwright install-deps
|
|
||||||
- name: Install Sample Plugin
|
|
||||||
run: |
|
|
||||||
pip install -U inventree-plugin-creator==${{ env.plugin_creator_version }}
|
|
||||||
create-inventree-plugin --default
|
|
||||||
cd MyCustomPlugin && pip install -e . && cd frontend && npm install && npm run translate && npm run build
|
|
||||||
- name: Run Playwright tests
|
|
||||||
id: tests
|
|
||||||
run: |
|
|
||||||
cd src/frontend
|
|
||||||
cp ./tests/fixtures/playwright_custom_logo.png ../backend/InvenTree/InvenTree/static/img/playwright_custom_logo.png
|
|
||||||
cp ./tests/fixtures/playwright_custom_splash.png ../backend/InvenTree/InvenTree/static/img/playwright_custom_splash.png
|
|
||||||
invoke static
|
|
||||||
env INVENTREE_CUSTOM_SPLASH="img/playwright_custom_splash.png" INVENTREE_CUSTOM_LOGO="img/playwright_custom_logo.png" PLAYWRIGHT_BASE_URL=http://localhost:8000 npx playwright test --project=firefox --shard=${{ matrix.shard }}/2
|
|
||||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
|
|
||||||
with:
|
|
||||||
name: playwright-report-firefox-${{ matrix.shard }}
|
|
||||||
path: src/frontend/playwright-report/
|
|
||||||
retention-days: 14
|
|
||||||
|
|
||||||
chromium:
|
|
||||||
name: Tests [Chromium ${{ matrix.shard }} / 4]
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
timeout-minutes: 60
|
|
||||||
needs: ["build", "paths-filter"]
|
|
||||||
if: github.ref == 'refs/heads/master' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
shard: [1, 2, 3, 4]
|
|
||||||
services:
|
|
||||||
postgres:
|
|
||||||
image: postgres:17
|
|
||||||
env:
|
|
||||||
POSTGRES_DB: inventree
|
|
||||||
POSTGRES_USER: inventree_user
|
|
||||||
POSTGRES_PASSWORD: inventree_password
|
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
options: >-
|
|
||||||
--health-cmd "pg_isready -U testuser"
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
permissions:
|
|
||||||
contents: read # Required for actions/checkout
|
|
||||||
id-token: write # Required for GitHub OIDC
|
|
||||||
|
|
||||||
env:
|
|
||||||
COVERAGE: true
|
|
||||||
VITE_COVERAGE: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- name: Environment Setup
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
with:
|
|
||||||
npm: true
|
|
||||||
install: true
|
|
||||||
update: true
|
|
||||||
apt-dependency: gettext postgresql-client libpq-dev
|
|
||||||
pip-dependency: psycopg2
|
|
||||||
- name: Set up test data
|
|
||||||
run: |
|
|
||||||
invoke dev.setup-test -iv
|
|
||||||
invoke int.rebuild-thumbnails
|
|
||||||
- name: Install dependencies
|
|
||||||
run: invoke int.frontend-compile --extract
|
|
||||||
- name: Cache Playwright browsers
|
|
||||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
|
||||||
id: playwright-cache
|
|
||||||
with:
|
|
||||||
path: ~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('src/frontend/yarn.lock') }}
|
|
||||||
- name: Install Playwright browsers
|
|
||||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
||||||
run: cd src/frontend && npx playwright install --with-deps
|
|
||||||
- name: Install Playwright OS dependencies
|
|
||||||
if: steps.playwright-cache.outputs.cache-hit == 'true'
|
|
||||||
run: cd src/frontend && npx playwright install-deps
|
|
||||||
- name: Install Sample Plugin
|
|
||||||
run: |
|
|
||||||
pip install -U inventree-plugin-creator==${{ env.plugin_creator_version }}
|
|
||||||
create-inventree-plugin --default
|
|
||||||
cd MyCustomPlugin && pip install -e . && cd frontend && npm install && npm run translate && npm run build
|
|
||||||
- name: Playwright [${{ matrix.shard }} / 4]
|
|
||||||
id: tests
|
|
||||||
run: |
|
|
||||||
cd src/frontend
|
|
||||||
npx nyc playwright test --project=chromium --shard=${{ matrix.shard }}/4
|
|
||||||
- name: Playwright Report [${{ matrix.shard }} / 4]
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
|
|
||||||
with:
|
|
||||||
name: playwright-report-chromium-${{ matrix.shard }}
|
|
||||||
path: src/frontend/playwright-report/
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 7
|
|
||||||
- name: Upload Coverage Artifact [${{ matrix.shard }} / 4]
|
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
||||||
id: coverage-upload
|
|
||||||
if: ${{ !cancelled() && steps.tests.outcome != 'failure' }}
|
|
||||||
with:
|
|
||||||
name: coverage-${{ matrix.shard }}
|
|
||||||
path: src/frontend/.nyc_output/
|
|
||||||
if-no-files-found: error
|
|
||||||
include-hidden-files: true
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
# Recombine the coverage reports from the 4 shards, and upload to Codecov
|
|
||||||
coverage:
|
|
||||||
name: Merge coverage reports and upload to Codecov
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: chromium
|
|
||||||
timeout-minutes: 30
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
|
|
||||||
- name: Environment Setup
|
|
||||||
uses: ./.github/actions/setup
|
|
||||||
with:
|
|
||||||
npm: true
|
|
||||||
install: false
|
|
||||||
update: false
|
|
||||||
|
|
||||||
- name: Download Coverage Artifacts
|
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
||||||
with:
|
|
||||||
pattern: coverage-*
|
|
||||||
path: all-coverage/
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Merge Coverage Reports
|
|
||||||
run: |
|
|
||||||
mkdir -p .nyc_output
|
|
||||||
cp all-coverage/*.json .nyc_output/ 2>/dev/null || true
|
|
||||||
npx nyc merge .nyc_output merged-coverage.json
|
|
||||||
npx nyc report \
|
|
||||||
--tempdir .nyc_output \
|
|
||||||
--reporter=lcov \
|
|
||||||
--reporter=text-summary \
|
|
||||||
--report-dir ./coverage \
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
if: ${{ !cancelled() && github.ref == 'refs/heads/master' }}
|
|
||||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
slug: inventree/InvenTree
|
|
||||||
flags: web
|
|
||||||
files: ./coverage/lcov.info
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
# 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*", "dependabot/**", "backport/**"]
|
|
||||||
pull_request:
|
|
||||||
branches-ignore: ["l10*"]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
python_version: 3.12
|
|
||||||
|
|
||||||
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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
with:
|
|
||||||
persist-credentials: false
|
|
||||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # 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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
|
||||||
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 }}
|
|
||||||
|
|
@ -4,13 +4,13 @@ name: QC
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore: ["l10*", "dependabot/**", "backport/**"]
|
branches-ignore: ["l10*"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches-ignore: ["l10*"]
|
branches-ignore: ["l10*"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
python_version: 3.12
|
python_version: 3.11
|
||||||
node_version: 24
|
node_version: 20
|
||||||
# The OS version must be set per job
|
# The OS version must be set per job
|
||||||
server_start_sleep: 60
|
server_start_sleep: 60
|
||||||
|
|
||||||
|
|
@ -23,6 +23,8 @@ env:
|
||||||
INVENTREE_SITE_URL: http://localhost:8000
|
INVENTREE_SITE_URL: http://localhost:8000
|
||||||
INVENTREE_DEBUG: true
|
INVENTREE_DEBUG: true
|
||||||
|
|
||||||
|
use_performance: false
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
|
@ -40,14 +42,12 @@ jobs:
|
||||||
cicd: ${{ steps.filter.outputs.cicd }}
|
cicd: ${{ steps.filter.outputs.cicd }}
|
||||||
requirements: ${{ steps.filter.outputs.requirements }}
|
requirements: ${{ steps.filter.outputs.requirements }}
|
||||||
runner-perf: ${{ steps.runner-perf.outputs.runner }}
|
runner-perf: ${{ steps.runner-perf.outputs.runner }}
|
||||||
performance: ${{ steps.performance.outputs.force-performance }}
|
|
||||||
submit-performance: ${{ steps.runner-perf.outputs.submit-performance }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
|
|
@ -77,47 +77,28 @@ jobs:
|
||||||
if: |
|
if: |
|
||||||
contains(github.event.pull_request.labels.*.name, 'dependency') ||
|
contains(github.event.pull_request.labels.*.name, 'dependency') ||
|
||||||
contains(github.event.pull_request.labels.*.name, 'full-run')
|
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?
|
- name: Which runner to use?
|
||||||
env:
|
|
||||||
GITHUB_REF: ${{ github.ref }}
|
|
||||||
PERFORMANCE: ${{ steps.performance.outputs.force-performance }}
|
|
||||||
id: runner-perf
|
id: runner-perf
|
||||||
# decide if we are running in inventree/inventree -> use codspeed-macro runner else ubuntu-24.04
|
# decide if we are running in inventree/inventree -> use codspeed-macro runner else ubuntu-24.04
|
||||||
run: |
|
run: echo "runner=$([[ '${{ github.repository }}' == 'inventree/InvenTree' ]] && '${{ env.use_performance }}' == 'true' ]] && echo 'codspeed-macro' || echo 'ubuntu-24.04')" >> $GITHUB_OUTPUT
|
||||||
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
|
|
||||||
|
|
||||||
code-style:
|
pre-commit:
|
||||||
name: Style [prek]
|
name: Style [pre-commit]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: paths-filter
|
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'
|
if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Python ${{ env.python_version }}
|
- name: Set up Python ${{ env.python_version }}
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # pin@v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.python_version }}
|
python-version: ${{ env.python_version }}
|
||||||
cache: "pip"
|
cache: "pip"
|
||||||
- name: Run pre commit hook Checks
|
- name: Run pre-commit Checks
|
||||||
uses: j178/prek-action@e98a699c41eb69ab013a45817a0406469a748f8d # v2.0.5
|
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # pin@v3.0.1
|
||||||
- name: Check Version
|
- name: Check Version
|
||||||
run: |
|
run: |
|
||||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
||||||
|
|
@ -126,11 +107,11 @@ jobs:
|
||||||
typecheck:
|
typecheck:
|
||||||
name: Style [Typecheck]
|
name: Style [Typecheck]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: [code-style, paths-filter]
|
needs: [paths-filter, pre-commit]
|
||||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'
|
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.requirements == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -152,11 +133,11 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Python ${{ env.python_version }}
|
- name: Set up Python ${{ env.python_version }}
|
||||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # pin@v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.python_version }}
|
python-version: ${{ env.python_version }}
|
||||||
- name: Check Config
|
- name: Check Config
|
||||||
|
|
@ -165,7 +146,7 @@ jobs:
|
||||||
pip install --require-hashes -r docs/requirements.txt
|
pip install --require-hashes -r docs/requirements.txt
|
||||||
python docs/ci/check_mkdocs_config.py
|
python docs/ci/check_mkdocs_config.py
|
||||||
- name: Check Links
|
- name: Check Links
|
||||||
uses: tcort/github-action-markdown-link-check@e7c7a18363c842693fadde5d41a3bd3573a7a225 # v1
|
uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # pin@v1
|
||||||
with:
|
with:
|
||||||
folder-path: docs
|
folder-path: docs
|
||||||
config-file: docs/mlc_config.json
|
config-file: docs/mlc_config.json
|
||||||
|
|
@ -190,7 +171,7 @@ jobs:
|
||||||
version: ${{ steps.version.outputs.version }}
|
version: ${{ steps.version.outputs.version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -202,7 +183,7 @@ jobs:
|
||||||
- name: Export API Documentation
|
- name: Export API Documentation
|
||||||
run: invoke dev.schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
|
run: invoke dev.schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
|
||||||
- name: Upload schema
|
- name: Upload schema
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: schema.yml
|
name: schema.yml
|
||||||
path: src/backend/InvenTree/schema.yml
|
path: src/backend/InvenTree/schema.yml
|
||||||
|
|
@ -222,7 +203,7 @@ jobs:
|
||||||
echo "Downloaded api.yaml"
|
echo "Downloaded api.yaml"
|
||||||
- name: Running OpenAPI Spec diff action
|
- name: Running OpenAPI Spec diff action
|
||||||
id: breaking_changes
|
id: breaking_changes
|
||||||
uses: oasdiff/oasdiff-action/diff@3e9d440d37f468355457604348009f50e0cddbf3 # v0.1.4
|
uses: oasdiff/oasdiff-action/diff@1c611ffb1253a72924624aa4fb662e302b3565d3 # pin@main
|
||||||
with:
|
with:
|
||||||
base: "api.yaml"
|
base: "api.yaml"
|
||||||
revision: "src/backend/InvenTree/schema.yml"
|
revision: "src/backend/InvenTree/schema.yml"
|
||||||
|
|
@ -251,17 +232,17 @@ jobs:
|
||||||
- name: Extract settings / tags
|
- name: Extract settings / tags
|
||||||
run: invoke int.export-definitions --basedir docs
|
run: invoke int.export-definitions --basedir docs
|
||||||
- name: Upload settings
|
- name: Upload settings
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: inventree_settings.json
|
name: inventree_settings.json
|
||||||
path: docs/generated/inventree_settings.json
|
path: docs/generated/inventree_settings.json
|
||||||
- name: Upload tags
|
- name: Upload tags
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: inventree_tags.yml
|
name: inventree_tags.yml
|
||||||
path: docs/generated/inventree_tags.yml
|
path: docs/generated/inventree_tags.yml
|
||||||
- name: Upload filters
|
- name: Upload filters
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: inventree_filters.yml
|
name: inventree_filters.yml
|
||||||
path: docs/generated/inventree_filters.yml
|
path: docs/generated/inventree_filters.yml
|
||||||
|
|
@ -275,7 +256,7 @@ jobs:
|
||||||
version: ${{ needs.schema.outputs.version }}
|
version: ${{ needs.schema.outputs.version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
name: Checkout Code
|
name: Checkout Code
|
||||||
with:
|
with:
|
||||||
repository: inventree/schema
|
repository: inventree/schema
|
||||||
|
|
@ -284,7 +265,7 @@ jobs:
|
||||||
- name: Create artifact directory
|
- name: Create artifact directory
|
||||||
run: mkdir -p artifact
|
run: mkdir -p artifact
|
||||||
- name: Download schema artifact
|
- name: Download schema artifact
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # pin@v7.0.0
|
||||||
with:
|
with:
|
||||||
path: artifact
|
path: artifact
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
@ -301,7 +282,7 @@ jobs:
|
||||||
echo "after move"
|
echo "after move"
|
||||||
ls -la artifact
|
ls -la artifact
|
||||||
rm -rf artifact
|
rm -rf artifact
|
||||||
- uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # v7.1.0
|
- uses: stefanzweifel/git-auto-commit-action@04702edda442b2e678b25b537cec683a1493fcb9 # pin@v7.1.0
|
||||||
name: Commit schema changes
|
name: Commit schema changes
|
||||||
with:
|
with:
|
||||||
commit_message: "Update API schema for ${{ env.version }} / ${{ github.sha }}"
|
commit_message: "Update API schema for ${{ env.version }} / ${{ github.sha }}"
|
||||||
|
|
@ -310,8 +291,8 @@ jobs:
|
||||||
name: Tests - inventree-python
|
name: Tests - inventree-python
|
||||||
runs-on: ${{ needs.paths-filter.outputs.runner-perf }}
|
runs-on: ${{ needs.paths-filter.outputs.runner-perf }}
|
||||||
|
|
||||||
needs: ["code-style", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true' || needs.paths-filter.outputs.performance == 'true'
|
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
@ -329,10 +310,10 @@ jobs:
|
||||||
INVENTREE_SITE_URL: http://127.0.0.1:12345
|
INVENTREE_SITE_URL: http://127.0.0.1:12345
|
||||||
INVENTREE_DEBUG: true
|
INVENTREE_DEBUG: true
|
||||||
INVENTREE_LOG_LEVEL: WARNING
|
INVENTREE_LOG_LEVEL: WARNING
|
||||||
node_version: '>=24'
|
node_version: '>=20.19.6'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -361,11 +342,10 @@ jobs:
|
||||||
pip uninstall pytest-django -y
|
pip uninstall pytest-django -y
|
||||||
cd ${WRAPPER_NAME}
|
cd ${WRAPPER_NAME}
|
||||||
pip install .
|
pip install .
|
||||||
if: needs.paths-filter.outputs.submit-performance == 'true'
|
|
||||||
- name: Performance Reporting
|
- name: Performance Reporting
|
||||||
uses: CodSpeedHQ/action@63f3e98b61959fe67f146a3ff022e4136fe9bb9c # v4.17.6
|
uses: CodSpeedHQ/action@dbda7111f8ac363564b0c51b992d4ce76bb89f2f # pin@v4
|
||||||
# check if we are in inventree/inventree - reporting only works in that OIDC context
|
# check if we are in inventree/inventree - reporting only works in that OIDC context
|
||||||
if: github.repository == 'inventree/InvenTree' && needs.paths-filter.outputs.submit-performance == 'true'
|
if: github.repository == 'inventree/InvenTree'
|
||||||
with:
|
with:
|
||||||
mode: walltime
|
mode: walltime
|
||||||
run: pytest ./src/performance --codspeed
|
run: pytest ./src/performance --codspeed
|
||||||
|
|
@ -374,12 +354,12 @@ jobs:
|
||||||
name: Tests - DB [SQLite] + Coverage ${{ matrix.python_version }}
|
name: Tests - DB [SQLite] + Coverage ${{ matrix.python_version }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
needs: ["code-style", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
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
|
continue-on-error: true # continue if a step fails so that coverage gets pushed
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python_version: [3.12, 3.14]
|
python_version: [3.11, 3.14]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
INVENTREE_DB_NAME: ./inventree.sqlite
|
INVENTREE_DB_NAME: ./inventree.sqlite
|
||||||
|
|
@ -390,7 +370,7 @@ jobs:
|
||||||
python_version: ${{ matrix.python_version }}
|
python_version: ${{ matrix.python_version }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -409,13 +389,13 @@ jobs:
|
||||||
- name: Coverage Tests
|
- name: Coverage Tests
|
||||||
run: invoke dev.test --check --coverage --translations
|
run: invoke dev.test --check --coverage --translations
|
||||||
- name: Upload raw coverage to artifacts
|
- name: Upload raw coverage to artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: coverage
|
name: coverage
|
||||||
path: .coverage
|
path: .coverage
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # pin@v5.5.2
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
@ -426,9 +406,9 @@ jobs:
|
||||||
name: Tests - Performance
|
name: Tests - Performance
|
||||||
runs-on: ${{ needs.paths-filter.outputs.runner-perf }}
|
runs-on: ${{ needs.paths-filter.outputs.runner-perf }}
|
||||||
|
|
||||||
needs: ["code-style", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
# check if we are in inventree/inventree - reporting only works in that OIDC context
|
# 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' && needs.paths-filter.outputs.submit-performance == 'true'
|
if: (needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true') && github.repository == 'inventree/InvenTree'
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
@ -441,7 +421,7 @@ jobs:
|
||||||
INVENTREE_AUTO_UPDATE: true
|
INVENTREE_AUTO_UPDATE: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -452,9 +432,9 @@ jobs:
|
||||||
update: true
|
update: true
|
||||||
npm: true
|
npm: true
|
||||||
env:
|
env:
|
||||||
node_version: '>=24'
|
node_version: '>=20.19.0'
|
||||||
- name: Performance Reporting
|
- name: Performance Reporting
|
||||||
uses: CodSpeedHQ/action@63f3e98b61959fe67f146a3ff022e4136fe9bb9c # v4.17.6
|
uses: CodSpeedHQ/action@dbda7111f8ac363564b0c51b992d4ce76bb89f2f # pin@v4
|
||||||
with:
|
with:
|
||||||
mode: walltime
|
mode: walltime
|
||||||
run: inv dev.test --pytest
|
run: inv dev.test --pytest
|
||||||
|
|
@ -462,7 +442,7 @@ jobs:
|
||||||
postgres:
|
postgres:
|
||||||
name: Tests - DB [PostgreSQL]
|
name: Tests - DB [PostgreSQL]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: ["code-style", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
@ -492,7 +472,7 @@ jobs:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -512,7 +492,7 @@ jobs:
|
||||||
name: Tests - DB [MySQL]
|
name: Tests - DB [MySQL]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
needs: ["code-style", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
@ -541,7 +521,7 @@ jobs:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -584,7 +564,7 @@ jobs:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -597,7 +577,7 @@ jobs:
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: invoke dev.test --check --migrations --report --coverage --translations
|
run: invoke dev.test --check --migrations --report --coverage --translations
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # pin@v5.5.2
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
@ -618,7 +598,7 @@ jobs:
|
||||||
INVENTREE_PLUGINS_ENABLED: false
|
INVENTREE_PLUGINS_ENABLED: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
name: Checkout Code
|
name: Checkout Code
|
||||||
|
|
@ -664,18 +644,138 @@ jobs:
|
||||||
chmod +rw /home/runner/work/InvenTree/db.sqlite3
|
chmod +rw /home/runner/work/InvenTree/db.sqlite3
|
||||||
invoke migrate
|
invoke migrate
|
||||||
|
|
||||||
|
web_ui:
|
||||||
|
name: Tests - Web UI
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 60
|
||||||
|
needs: ["pre-commit", "paths-filter"]
|
||||||
|
if: needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:17
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: inventree
|
||||||
|
POSTGRES_USER: inventree_user
|
||||||
|
POSTGRES_PASSWORD: inventree_password
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd "pg_isready -U testuser"
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
|
||||||
|
env:
|
||||||
|
INVENTREE_DB_ENGINE: postgresql
|
||||||
|
INVENTREE_DB_NAME: inventree
|
||||||
|
INVENTREE_DB_HOST: "127.0.0.1"
|
||||||
|
INVENTREE_DB_PORT: 5432
|
||||||
|
INVENTREE_DB_USER: inventree_user
|
||||||
|
INVENTREE_DB_PASSWORD: inventree_password
|
||||||
|
INVENTREE_DEBUG: true
|
||||||
|
INVENTREE_PLUGINS_ENABLED: false
|
||||||
|
VITE_COVERAGE_BUILD: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Environment Setup
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
npm: true
|
||||||
|
install: true
|
||||||
|
update: true
|
||||||
|
apt-dependency: postgresql-client libpq-dev
|
||||||
|
pip-dependency: psycopg2
|
||||||
|
- name: Set up test data
|
||||||
|
run: |
|
||||||
|
invoke dev.setup-test -iv
|
||||||
|
invoke int.rebuild-thumbnails
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
invoke int.frontend-compile --extract
|
||||||
|
cd src/frontend && npx playwright install --with-deps
|
||||||
|
- name: Run Playwright tests
|
||||||
|
id: tests
|
||||||
|
run: |
|
||||||
|
cd src/frontend
|
||||||
|
cp ./tests/fixtures/playwright_custom_logo.png ../backend/InvenTree/InvenTree/static/img/playwright_custom_logo.png
|
||||||
|
cp ./tests/fixtures/playwright_custom_splash.png ../backend/InvenTree/InvenTree/static/img/playwright_custom_splash.png
|
||||||
|
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
|
||||||
|
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: src/frontend/playwright-report/
|
||||||
|
retention-days: 14
|
||||||
|
- 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
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
slug: inventree/InvenTree
|
||||||
|
flags: web
|
||||||
|
- name: Upload bundler info
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
run: |
|
||||||
|
cd src/frontend
|
||||||
|
yarn install
|
||||||
|
yarn run build
|
||||||
|
|
||||||
|
web_ui_build:
|
||||||
|
name: Build - Web UI
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Environment Setup
|
||||||
|
uses: ./.github/actions/setup
|
||||||
|
with:
|
||||||
|
npm: true
|
||||||
|
- name: Install dependencies
|
||||||
|
run: cd src/frontend && yarn install
|
||||||
|
- name: Build frontend
|
||||||
|
run: cd src/frontend && yarn run compile && 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
|
||||||
|
with:
|
||||||
|
name: frontend-build
|
||||||
|
path: src/backend/InvenTree/web/static/web
|
||||||
|
include-hidden-files: true
|
||||||
|
|
||||||
zizmor:
|
zizmor:
|
||||||
name: Security [Zizmor]
|
name: Security [Zizmor]
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: ["code-style", "paths-filter"]
|
needs: ["pre-commit", "paths-filter"]
|
||||||
if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.force == 'true'
|
if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
security-events: write
|
security-events: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Run zizmor 🌈
|
- uses: hynek/setup-cached-uv@757bedc3f972eb7227a1aa657651f15a8527c817 # pin@v2
|
||||||
uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7
|
- 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
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ on:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
env:
|
env:
|
||||||
python_version: 3.12
|
python_version: 3.11
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stable:
|
stable:
|
||||||
|
|
@ -20,7 +20,7 @@ jobs:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Version Check
|
- name: Version Check
|
||||||
|
|
@ -28,7 +28,7 @@ jobs:
|
||||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
||||||
python3 .github/scripts/version_check.py
|
python3 .github/scripts/version_check.py
|
||||||
- name: Push to Stable Branch
|
- name: Push to Stable Branch
|
||||||
uses: ad-m/github-push-action@881a6320fdb16eb5318c5054f31c218aec2b324c # v1.3.0
|
uses: ad-m/github-push-action@77c5b412c50b723d2a4fbc6d71fb5723bcd439aa # pin@v1.0.0
|
||||||
if: env.stable_release == 'true'
|
if: env.stable_release == 'true'
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
@ -42,10 +42,8 @@ jobs:
|
||||||
id-token: write
|
id-token: write
|
||||||
contents: write
|
contents: write
|
||||||
attestations: write
|
attestations: write
|
||||||
artifact-metadata: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -57,7 +55,7 @@ jobs:
|
||||||
- name: Build frontend
|
- name: Build frontend
|
||||||
run: cd src/frontend && npm run compile && npm run build
|
run: cd src/frontend && npm run compile && npm run build
|
||||||
- name: Create SBOM for frontend
|
- name: Create SBOM for frontend
|
||||||
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
|
uses: anchore/sbom-action@0b82b0b1a22399a1c542d4d656f70cd903571b5c # pin@v0
|
||||||
with:
|
with:
|
||||||
artifact-name: frontend-build.spdx
|
artifact-name: frontend-build.spdx
|
||||||
path: src/frontend
|
path: src/frontend
|
||||||
|
|
@ -75,30 +73,31 @@ jobs:
|
||||||
zip -r ../frontend-build.zip * .vite
|
zip -r ../frontend-build.zip * .vite
|
||||||
- name: Attest Build Provenance
|
- name: Attest Build Provenance
|
||||||
id: attest
|
id: attest
|
||||||
uses: actions/attest@a1948c3f048ba23858d222213b7c278aabede763 # v4.1.1
|
uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # pin@v1
|
||||||
with:
|
with:
|
||||||
subject-path: "${{ github.workspace }}/src/backend/InvenTree/web/static/frontend-build.zip"
|
subject-path: "${{ github.workspace }}/src/backend/InvenTree/web/static/frontend-build.zip"
|
||||||
|
|
||||||
- name: Upload frontend
|
- name: Upload frontend
|
||||||
run: gh release upload ${REF} src/backend/InvenTree/web/static/frontend-build.zip#frontend-build.zip
|
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||||
env:
|
with:
|
||||||
REF: ${{ github.ref_name }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
file: src/backend/InvenTree/web/static/frontend-build.zip
|
||||||
|
asset_name: frontend-build.zip
|
||||||
|
tag: ${{ github.ref }}
|
||||||
|
overwrite: true
|
||||||
- name: Upload frontend to artifacts
|
- name: Upload frontend to artifacts
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: frontend-build
|
name: frontend-build
|
||||||
path: src/backend/InvenTree/web/static/frontend-build.zip
|
path: src/backend/InvenTree/web/static/frontend-build.zip
|
||||||
- name: Rename Attestation Bundle
|
|
||||||
run: |
|
|
||||||
mv ${BUNDLE_PATH} src/backend/InvenTree/web/static/frontend-build.intoto.jsonl
|
|
||||||
env:
|
|
||||||
BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path}}
|
|
||||||
- name: Upload Attestation
|
- name: Upload Attestation
|
||||||
run: gh release upload ${REF} src/backend/InvenTree/web/static/frontend-build.intoto.jsonl#frontend-build.intoto.jsonl
|
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||||
env:
|
with:
|
||||||
REF: ${{ github.ref_name }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
asset_name: frontend-build.intoto.jsonl
|
||||||
|
file: ${{ steps.attest.outputs.bundle-path}}
|
||||||
|
tag: ${{ github.ref }}
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
@ -115,7 +114,7 @@ jobs:
|
||||||
INVENTREE_DEBUG: true
|
INVENTREE_DEBUG: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -135,33 +134,34 @@ jobs:
|
||||||
cd docs/site
|
cd docs/site
|
||||||
zip -r docs-html.zip *
|
zip -r docs-html.zip *
|
||||||
- name: Publish documentation
|
- name: Publish documentation
|
||||||
run: gh release upload ${REF} docs/site/docs-html.zip#docs-html.zip
|
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||||
env:
|
with:
|
||||||
REF: ${{ github.ref_name }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
file: docs/site/docs-html.zip
|
||||||
|
asset_name: docs-html.zip
|
||||||
|
tag: ${{ github.ref }}
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
build-pkgr:
|
build-pkgr:
|
||||||
if: github.repository == 'inventree/InvenTree'
|
if: github.repository == 'inventree/InvenTree'
|
||||||
name: ${{ matrix.target }}
|
name: ${{ matrix.target }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
needs: [build, docs]
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target:
|
target:
|
||||||
|
- ubuntu:22.04
|
||||||
- ubuntu:24.04
|
- ubuntu:24.04
|
||||||
- ubuntu:26.04
|
- debian:12
|
||||||
- debian:13
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Get frontend artifact
|
- name: Get frontend artifact
|
||||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # pin@v7.0.0
|
||||||
with:
|
with:
|
||||||
name: frontend-build
|
name: frontend-build
|
||||||
- name: Setup
|
- name: Setup
|
||||||
|
|
@ -171,7 +171,6 @@ jobs:
|
||||||
BRANCH: ${{ github.event.release.target_commitish }}
|
BRANCH: ${{ github.event.release.target_commitish }}
|
||||||
TARGET: ${{github.event.release.target_commitish}}
|
TARGET: ${{github.event.release.target_commitish}}
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
VERSION_REF: ${{ github.ref_name }}
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -204,19 +203,8 @@ jobs:
|
||||||
# Move frontend build into place
|
# Move frontend build into place
|
||||||
mkdir -p src/backend/InvenTree/web/static
|
mkdir -p src/backend/InvenTree/web/static
|
||||||
unzip -qq frontend-build.zip -d src/backend/InvenTree/web/static/web
|
unzip -qq frontend-build.zip -d src/backend/InvenTree/web/static/web
|
||||||
|
- name: Package
|
||||||
echo "INFO write release_version"
|
uses: pkgr/action/package@c5666febcd31750da6428042193fc5b2fb765435 # pin@main
|
||||||
echo "VERSION=$VERSION_REF" >> $GITHUB_OUTPUT
|
|
||||||
echo "REF=$GITHUB_REF" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Disable before.sh for now - will need to be removed once we switch to go.packager.io fully
|
|
||||||
echo "#!/bin/bash" > contrib/packager.io/before.sh
|
|
||||||
|
|
||||||
echo "calculate release channel"
|
|
||||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
|
||||||
python3 .github/scripts/version_check.py
|
|
||||||
- name: Package - current release channel
|
|
||||||
uses: pkgr/action/package@c5666febcd31750da6428042193fc5b2fb765435 # main
|
|
||||||
id: package
|
id: package
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
|
@ -233,44 +221,19 @@ jobs:
|
||||||
INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
|
INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
|
||||||
INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
|
INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
|
||||||
APP_REPO=inventree/InvenTree
|
APP_REPO=inventree/InvenTree
|
||||||
- name: Publish to go.packager.io - current release channel
|
- name: Publish to go.packager.io
|
||||||
uses: pkgr/action/publish@c5666febcd31750da6428042193fc5b2fb765435 # main
|
uses: pkgr/action/publish@3bce081ae512c5020856e237d37b3f5479d4aa71 # pin@main
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
token: ${{ secrets.PACKAGER_RELEASE_TOKEN }}
|
token: ${{ secrets.PACKAGER_RELEASE_TOKEN }}
|
||||||
repository: inventree/InvenTree
|
repository: inventree/inventree
|
||||||
channel: ${{ env.pkg_channel }}
|
channel: ${{ github.ref_name }}
|
||||||
file: ${{ steps.package.outputs.package_path }}
|
file: ${{ steps.package.outputs.package_path }}
|
||||||
- name: Publish to artifact
|
- name: Publish to artifact
|
||||||
run: gh release upload ${REF} ${PACKAGE_PATH}#${PACKAGE_NAME}
|
uses: svenstaro/upload-release-action@6b7fa9f267e90b50a19fef07b3596790bb941741 # pin@2.11.3
|
||||||
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 }}
|
|
||||||
- name: Package - stable release channel
|
|
||||||
uses: pkgr/action/package@c5666febcd31750da6428042193fc5b2fb765435 # main
|
|
||||||
id: package-stable
|
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
version: ${{ steps.setup.outputs.version }}
|
file: ${{ steps.package.outputs.package_path }}
|
||||||
debug: true
|
asset_name: ${{ matrix.target }}-{{ steps.setup.outputs.version }}.tar.gz
|
||||||
cache_prefix: ${{ github.ref_name }}
|
tag: ${{ github.ref }}
|
||||||
env: |
|
overwrite: true
|
||||||
INVENTREE_DB_ENGINE=sqlite3
|
|
||||||
INVENTREE_DB_NAME=database.sqlite3
|
|
||||||
INVENTREE_PLUGINS_ENABLED=true
|
|
||||||
INVENTREE_MEDIA_ROOT=/opt/inventree/media
|
|
||||||
INVENTREE_STATIC_ROOT=/opt/inventree/static
|
|
||||||
INVENTREE_BACKUP_DIR=/opt/inventree/backup
|
|
||||||
INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
|
|
||||||
INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
|
|
||||||
APP_REPO=inventree/InvenTree
|
|
||||||
- name: Publish to go.packager.io - stable release channel
|
|
||||||
uses: pkgr/action/publish@c5666febcd31750da6428042193fc5b2fb765435 # main
|
|
||||||
with:
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
token: ${{ secrets.PACKAGER_RELEASE_TOKEN }}
|
|
||||||
repository: inventree/InvenTree
|
|
||||||
channel: stable
|
|
||||||
file: ${{ steps.package-stable.outputs.package_path }}
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ jobs:
|
||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
|
|
@ -67,6 +67,6 @@ jobs:
|
||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
|
uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # pin@v10.1.1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
stale-issue-message: "This issue seems stale. Please react to show this is still important."
|
stale-issue-message: "This issue seems stale. Please react to show this is still important."
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ on:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
env:
|
env:
|
||||||
python_version: 3.12
|
python_version: 3.11
|
||||||
node_version: 24
|
node_version: 20
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
@ -32,7 +32,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # pin@v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Environment Setup
|
- name: Environment Setup
|
||||||
|
|
@ -56,7 +56,7 @@ jobs:
|
||||||
echo "Resetting to HEAD~"
|
echo "Resetting to HEAD~"
|
||||||
git reset HEAD~ || true
|
git reset HEAD~ || true
|
||||||
- name: crowdin action
|
- name: crowdin action
|
||||||
uses: crowdin/github-action@52aa776766211d83d975df51f3b9c53c2f8ba35f # v2
|
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # pin@v2
|
||||||
with:
|
with:
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: false
|
upload_translations: false
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup
|
- name: Setup
|
||||||
|
|
@ -18,7 +18,7 @@ jobs:
|
||||||
run: pip-compile --output-file=requirements.txt requirements.in -U
|
run: pip-compile --output-file=requirements.txt requirements.in -U
|
||||||
- name: Update requirements-dev.txt
|
- name: Update requirements-dev.txt
|
||||||
run: pip-compile --generate-hashes --output-file=requirements-dev.txt requirements-dev.in -U
|
run: pip-compile --generate-hashes --output-file=requirements-dev.txt requirements-dev.in -U
|
||||||
- uses: stefanzweifel/git-auto-commit-action@fd157da78fa13d9383e5580d1fd1184d89554b51 # v4.15.1
|
- uses: stefanzweifel/git-auto-commit-action@fd157da78fa13d9383e5580d1fd1184d89554b51 # pin@v4.15.1
|
||||||
with:
|
with:
|
||||||
commit_message: "[Bot] Updated dependency"
|
commit_message: "[Bot] Updated dependency"
|
||||||
branch: dep-update
|
branch: dep-update
|
||||||
|
|
|
||||||
10
.pkgr.yml
|
|
@ -21,9 +21,9 @@ before:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
- poppler-utils
|
- poppler-utils
|
||||||
- "python3.12 | python3.13 | python3.14"
|
- "python3.11 | python3.12 | python3.13 | python3.14"
|
||||||
- "python3.12-venv | python3.13-venv | python3.14-venv"
|
- "python3.11-venv | python3.12-venv | python3.13-venv | python3.14-venv"
|
||||||
- "python3.12-dev | python3.13-dev | python3.14-dev"
|
- "python3.11-dev | python3.12-dev | python3.13-dev | python3.14-dev"
|
||||||
- python3-pip
|
- python3-pip
|
||||||
- python3-cffi
|
- python3-cffi
|
||||||
- python3-brotli
|
- python3-brotli
|
||||||
|
|
@ -36,6 +36,6 @@ dependencies:
|
||||||
- jq
|
- jq
|
||||||
- "libffi7 | libffi8"
|
- "libffi7 | libffi8"
|
||||||
targets:
|
targets:
|
||||||
|
ubuntu-22.04: true
|
||||||
ubuntu-24.04: true
|
ubuntu-24.04: true
|
||||||
ubuntu-26.04: true
|
debian-12: true
|
||||||
debian-13: true
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ repos:
|
||||||
exclude: mkdocs.yml
|
exclude: mkdocs.yml
|
||||||
- id: mixed-line-ending
|
- id: mixed-line-ending
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.15.12
|
rev: v0.15.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
args: [--preview]
|
args: [--preview]
|
||||||
|
|
@ -29,7 +29,7 @@ repos:
|
||||||
--preview
|
--preview
|
||||||
]
|
]
|
||||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||||
rev: 0.11.12
|
rev: 0.10.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: pip-compile
|
- id: pip-compile
|
||||||
name: pip-compile requirements-dev.in
|
name: pip-compile requirements-dev.in
|
||||||
|
|
@ -37,7 +37,7 @@ repos:
|
||||||
files: src/backend/requirements-dev\.(in|txt)$
|
files: src/backend/requirements-dev\.(in|txt)$
|
||||||
- id: pip-compile
|
- id: pip-compile
|
||||||
name: pip-compile requirements-dev.in 3.14
|
name: pip-compile requirements-dev.in 3.14
|
||||||
args: [src/backend/requirements-dev.in, -o, src/backend/requirements-dev-3.14.txt, -c, src/backend/requirements-3.14.txt, --python-version=3.14, -c, src/backend/requirements.txt, -c, src/backend/requirements-dev.txt]
|
args: [src/backend/requirements-dev.in, -o, src/backend/requirements-dev-3.14.txt, -c, src/backend/requirements-3.14.txt, --python-version=3.14, -c, src/backend/requirements.txt]
|
||||||
files: src/backend/requirements-dev\.(in|txt)$
|
files: src/backend/requirements-dev\.(in|txt)$
|
||||||
- id: pip-compile
|
- id: pip-compile
|
||||||
name: pip-compile requirements.txt
|
name: pip-compile requirements.txt
|
||||||
|
|
@ -48,23 +48,23 @@ repos:
|
||||||
args: [src/backend/requirements.in, -o, src/backend/requirements-3.14.txt, --python-version=3.14, -c, src/backend/requirements.txt]
|
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)$
|
files: src/backend/requirements\.(in|txt)$
|
||||||
- id: pip-compile
|
- id: pip-compile
|
||||||
name: pip-compile contrib/dev_reqs/requirements.txt
|
name: pip-compile requirements.txt
|
||||||
args: [contrib/dev_reqs/requirements.in, -o, contrib/dev_reqs/requirements.txt, -c, src/backend/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)$
|
files: contrib/dev_reqs/requirements\.(in|txt)$
|
||||||
- id: pip-compile
|
- id: pip-compile
|
||||||
name: pip-compile docs/requirements.txt
|
name: pip-compile requirements.txt
|
||||||
args: [docs/requirements.in, -o, docs/requirements.txt, -c, src/backend/requirements.txt]
|
args: [docs/requirements.in, -o, docs/requirements.txt, -c, src/backend/requirements.txt]
|
||||||
files: docs/requirements\.(in|txt)$
|
files: docs/requirements\.(in|txt)$
|
||||||
- id: pip-compile
|
- id: pip-compile
|
||||||
name: pip-compile contrib/container/requirements.txt
|
name: pip-compile requirements.txt
|
||||||
args: [contrib/container/requirements.in, -o, contrib/container/requirements.txt, --python-version=3.14, -c, src/backend/requirements.txt]
|
args: [contrib/container/requirements.in, -o, contrib/container/requirements.txt, --python-version=3.11, -c, src/backend/requirements.txt]
|
||||||
files: contrib/container/requirements\.(in|txt)$
|
files: contrib/container/requirements\.(in|txt)$
|
||||||
- repo: https://github.com/Riverside-Healthcare/djLint
|
- repo: https://github.com/Riverside-Healthcare/djLint
|
||||||
rev: v1.36.4
|
rev: v1.36.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: djlint-django
|
- id: djlint-django
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.4.2
|
rev: v2.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
|
|
@ -79,13 +79,13 @@ repos:
|
||||||
src/frontend/vite.config.ts |
|
src/frontend/vite.config.ts |
|
||||||
)$
|
)$
|
||||||
- repo: https://github.com/biomejs/pre-commit
|
- repo: https://github.com/biomejs/pre-commit
|
||||||
rev: v2.4.14
|
rev: v2.3.15
|
||||||
hooks:
|
hooks:
|
||||||
- id: biome-check
|
- id: biome-check
|
||||||
additional_dependencies: ["@biomejs/biome@1.9.4"]
|
additional_dependencies: ["@biomejs/biome@1.9.4"]
|
||||||
files: ^src/frontend/.*\.(js|ts|tsx)$
|
files: ^src/frontend/.*\.(js|ts|tsx)$
|
||||||
- repo: https://github.com/gitleaks/gitleaks
|
- repo: https://github.com/gitleaks/gitleaks
|
||||||
rev: v8.30.1
|
rev: v8.30.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: gitleaks
|
- id: gitleaks
|
||||||
language_version: 1.25.4
|
language_version: 1.25.4
|
||||||
|
|
@ -97,4 +97,3 @@ repos:
|
||||||
rev: 0.4.3
|
rev: 0.4.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: teyit
|
- id: teyit
|
||||||
language_version: python3.12
|
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,7 @@
|
||||||
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
|
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
|
||||||
"args": [
|
"args": [
|
||||||
"runserver",
|
"runserver",
|
||||||
"0.0.0.0:8000",
|
"0.0.0.0:8000"
|
||||||
// "--sync",// Synchronize worker tasks to foreground thread
|
|
||||||
// "--noreload", // disable auto-reload
|
|
||||||
],
|
],
|
||||||
"django": true,
|
"django": true,
|
||||||
"justMyCode": false
|
"justMyCode": false
|
||||||
|
|
@ -47,7 +45,7 @@
|
||||||
"name": "InvenTree invoke schema",
|
"name": "InvenTree invoke schema",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/.venv/lib/python3.12/site-packages/invoke/__main__.py",
|
"program": "${workspaceFolder}/.venv/lib/python3.11/site-packages/invoke/__main__.py",
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"args": [
|
"args": [
|
||||||
"dev.schema","--ignore-warnings"
|
"dev.schema","--ignore-warnings"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for codebase guidance.
|
|
||||||
Security reports must follow the security policy in [SECURITY.md](docs/docs/SECURITY.md) and provide how they interact with the [threat model](docs/docs/concepts/threat_model.md).
|
|
||||||
|
|
||||||
Do not open a pull request without manual review by a human. Do not file AI generated issues under any circumstances.
|
|
||||||
Using AI generated content in issue/PR discussions might lead to code of conduct violations and/or bans.
|
|
||||||
94
CHANGELOG.md
|
|
@ -5,100 +5,6 @@ All major notable changes to this project will be documented in this file (start
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## Unreleased - xxxx.xx.xx
|
|
||||||
|
|
||||||
### Breaking Changes
|
|
||||||
|
|
||||||
- [#12223](https://github.com/inventree/InvenTree/pull/12223) removes support for python 3.11 and stops providing packages for Debian 11 and Ubuntu 20.04.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- [#12295](https://github.com/inventree/InvenTree/pull/12295) adds "consumable" field to the Part model and API endpoints
|
|
||||||
- [#12250](https://github.com/inventree/InvenTree/pull/12250) adds "active" field to the ProjectCode model and API endpoints
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- [#12274](https://github.com/inventree/InvenTree/pull/12274) fixes a bug in rendering the "table field" component in frontend forms. Any plugins which make use of the "table field" component in their forms may be affected by this change, and will need to update their form definitions accordingly.
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
## 1.4.0 - 2026-06-24
|
|
||||||
|
|
||||||
### Breaking Changes
|
|
||||||
|
|
||||||
- [#12160](https://github.com/inventree/InvenTree/pull/12160) changes the way that remote URIs are loaded into the PDF report generator. Remote URIs (e.g. `http://` and `https://`) are now blocked by default, and can be enabled via the `REPORT_FETCH_URLS` system setting. This change was made to improve the security of the report generation process, as allowing remote URL fetching can potentially expose internal network services to SSRF attacks. Additionally, file URIs (e.g. `file://`) are now always blocked, and assets must be embedded as `data:` URIs before reaching the PDF generator.
|
|
||||||
- [#12107](https://github.com/inventree/InvenTree/pull/12107) makes a breaking change to the `SalesOrderStatusGroups` enum, fixing a bug where the "shipped" status was not included in the "active" group. This change may affect any external client applications which make use of the `SalesOrderStatusGroups` enum, as the "shipped" status will now be included in the "active" group instead of the "complete" group. If you are using this enum in an external client application, you will need to update your application to account for this change.
|
|
||||||
- [#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
|
|
||||||
|
|
||||||
- [#12208](https://github.com/inventree/InvenTree/pull/12208) adds custom locale support for rendering currencies, dates and numbers within reports. This allows users to specify a custom locale for report rendering, which can be used to control the formatting of dates, numbers and currency values in the generated reports.
|
|
||||||
- [#12204](https://github.com/inventree/InvenTree/pull/12204) adds new filtering options to PartCategoryTree and StockLocationTree API endpoints, allowing tree data to be fetched dynamically
|
|
||||||
- [#12165](https://github.com/inventree/InvenTree/pull/12165) adds support for parameters against the PartCategory model
|
|
||||||
- [#12103](https://github.com/inventree/InvenTree/pull/12103) adds column-based filtering to table views in the user interface. This extends the existing table filtering functionality by allowing users to apply filters directly to individual columns.
|
|
||||||
- [#12093](https://github.com/inventree/InvenTree/pull/12093) adds "read_only" attribute to PluginSetting API endpoint, which indicates whether a particular plugin setting is read-only (i.e. cannot be modified via the API)
|
|
||||||
- [#12079](https://github.com/inventree/InvenTree/pull/12079) adds the ability to save filter groups for table and calendar views in the user interface. This allows users to save and reuse commonly used filter configurations, improving the usability and efficiency of the interface.
|
|
||||||
- [#12077](https://github.com/inventree/InvenTree/pull/12077) adds "tags" fields to multiple new model types and a /api/tag/ endpoint for fetching tags. Also adds the ability to filter various model types by tags.
|
|
||||||
- [#12019](https://github.com/inventree/InvenTree/pull/12019) adds a "location" field to the StockCount API endpoint, allowing users to specify a location when performing a stock count. This field is optional, and if not provided, the stock count will be performed without changing the location of the stock item. If a location is provided, the stock item(s) will be moved to the specified location as part of the stock count operation.
|
|
||||||
- [#12011](https://github.com/inventree/InvenTree/pull/12011) adds a "creation_date" field to the StockItem API endpoint, allowing users to track when each stock item was created. This field is read-only and is automatically set to the current date and time when a new stock item is created.
|
|
||||||
- [#12000](https://github.com/inventree/InvenTree/pull/12000) adds support for auto-allocation of stock items against sales orders. This includes both backend and frontend changes, allowing users to trigger auto-allocation via the API or through the UI. The auto-allocation process will attempt to allocate available stock items to the sales order line items, based on the specified stock sorting and allocation rules.
|
|
||||||
- [#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
|
|
||||||
|
|
||||||
- [#12197](https://github.com/inventree/InvenTree/pull/12197) requires staff permissions to restart machines via the API.
|
|
||||||
- [#12142](https://github.com/inventree/InvenTree/pull/12142) prevents users from printing reports or labels against models for which they do not have adequate permissions. This change improves the security of the system by ensuring that users cannot access or print reports or labels for models they do not have permission to view.
|
|
||||||
- [#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
|
|
||||||
|
|
||||||
- [#12071](https://github.com/inventree/InvenTree/pull/12071) removes the "review_needed" field on the StockItem model. Note that this field was never exposed to the API or any business logic, and is essentially a no-op field which is not used for anything. This field was removed to simplify the StockItem model and reduce confusion around its purpose.
|
|
||||||
- [#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
|
## 1.2.0 - 2026-02-12
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for codebase guidance.
|
|
||||||
Security reports must follow the security policy in [SECURITY.md](docs/docs/SECURITY.md) and provide how they interact with the [threat model](docs/docs/concepts/threat_model.md).
|
|
||||||
|
|
||||||
Do not open a pull request without manual review by a human. Do not file AI generated issues under any circumstances.
|
|
||||||
Using AI generated content in issue/PR discussions might lead to code of conduct violations and/or bans.
|
|
||||||
220
CONTRIBUTING.md
|
|
@ -1,28 +1,15 @@
|
||||||
# Contributing to InvenTree
|
### Contributing to InvenTree
|
||||||
|
|
||||||
Hi there, thank you for your interest in contributing!
|
Hi there, thank you for your interest in contributing!
|
||||||
Please read our contribution guidelines, before submitting your first pull request to the InvenTree codebase.
|
Please read our contribution guidelines, before submitting your first pull request to the InvenTree codebase.
|
||||||
|
|
||||||
## About InvenTree
|
### Project File Structure
|
||||||
|
|
||||||
InvenTree is an open-source inventory management system designed for makers, engineers, and small manufacturers.
|
|
||||||
|
|
||||||
The codebase is split into two independently deployable layers:
|
|
||||||
|
|
||||||
**Backend — Django (Python)**
|
|
||||||
A REST API built with Django and Django REST Framework. It owns all business logic, the database schema, background task processing (Django-Q2), plugin execution, and report generation. The backend can be used standalone via the API without the frontend.
|
|
||||||
|
|
||||||
**Frontend — React (TypeScript)**
|
|
||||||
A single-page application built with React 19, Mantine 9, and Vite. It communicates exclusively through the backend REST API. It has its own build pipeline, test suite (Playwright), and i18n system (Lingui). The frontend lives entirely in `src/frontend/` and has no Django awareness.
|
|
||||||
|
|
||||||
When making changes, be clear about which layer you are working in — they have separate toolchains, test runners, and linting rules.
|
|
||||||
|
|
||||||
## Project File Structure
|
|
||||||
|
|
||||||
The InvenTree project is split into two main components: frontend and backend. This source is located in the `src` directory. All other files are used for project management, documentation, and testing.
|
The InvenTree project is split into two main components: frontend and backend. This source is located in the `src` directory. All other files are used for project management, documentation, and testing.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
InvenTree/
|
InvenTree/
|
||||||
|
├─ .devops/ # Files for Azure DevOps
|
||||||
├─ .github/ # Files for GitHub
|
├─ .github/ # Files for GitHub
|
||||||
│ ├─ actions/ # Reused actions
|
│ ├─ actions/ # Reused actions
|
||||||
│ ├─ ISSUE_TEMPLATE/ # Templates for issues and pull requests
|
│ ├─ ISSUE_TEMPLATE/ # Templates for issues and pull requests
|
||||||
|
|
@ -52,209 +39,12 @@ InvenTree/
|
||||||
│ │ ├─ tsconfig.json # Settings for frontend compilation
|
│ │ ├─ tsconfig.json # Settings for frontend compilation
|
||||||
├─ .pkgr.yml # Build definition for Debian/Ubuntu packages
|
├─ .pkgr.yml # Build definition for Debian/Ubuntu packages
|
||||||
├─ .pre-commit-config.yaml # Code formatter/linter configuration
|
├─ .pre-commit-config.yaml # Code formatter/linter configuration
|
||||||
├─ biome.json # JS/TS linting and formatting config
|
|
||||||
├─ CONTRIBUTING.md # Contribution guidelines and overview
|
├─ CONTRIBUTING.md # Contribution guidelines and overview
|
||||||
├─ Procfile # Process definition for Debian/Ubuntu packages
|
├─ Procfile # Process definition for Debian/Ubuntu packages
|
||||||
├─ pyproject.toml # Python tooling config (Ruff, pytest, coverage, djLint, ty)
|
|
||||||
├─ README.md # General project information and overview
|
├─ README.md # General project information and overview
|
||||||
|
├─ runtime.txt # Python runtime settings for Debian/Ubuntu packages build
|
||||||
├─ SECURITY.md # Project security policy
|
├─ SECURITY.md # Project security policy
|
||||||
├─ tasks.py # Action definitions for development, testing and deployment
|
├─ tasks.py # Action definitions for development, testing and deployment
|
||||||
```
|
```
|
||||||
|
|
||||||
### Backend app domains
|
|
||||||
|
|
||||||
The following Django apps are defined in `src/backend/InvenTree/`:
|
|
||||||
|
|
||||||
| Directory | Domain |
|
|
||||||
|-----------|--------|
|
|
||||||
| `build/` | Build (manufacturing/assembly) orders |
|
|
||||||
| `common/` | Shared models, settings, utilities |
|
|
||||||
| `company/` | Suppliers, customers, manufacturers |
|
|
||||||
| `data_exporter/` | Data export functionality |
|
|
||||||
| `generic/` | Shared enums, events, and state machine utilities |
|
|
||||||
| `importer/` | Data import functionality |
|
|
||||||
| `InvenTree/` | Core configuration (settings, URLs, middleware) |
|
|
||||||
| `machine/` | Support for external machines and devices |
|
|
||||||
| `order/` | Purchase orders and sales orders |
|
|
||||||
| `part/` | Parts catalogue and categories |
|
|
||||||
| `stock/` | Stock items and locations |
|
|
||||||
| `report/` | Report templates and generation |
|
|
||||||
| `plugin/` | Plugin system |
|
|
||||||
| `users/` | User accounts and permissions |
|
|
||||||
| `web/` | Serves the React frontend static build to Django |
|
|
||||||
|
|
||||||
### Frontend layout
|
|
||||||
|
|
||||||
```
|
|
||||||
src/frontend/src/
|
|
||||||
components/ # Reusable React components
|
|
||||||
pages/ # Page-level route components
|
|
||||||
tables/ # Data table components
|
|
||||||
forms/ # Form components
|
|
||||||
hooks/ # Custom React hooks
|
|
||||||
states/ # Zustand global state
|
|
||||||
locales/ # i18n translation catalogues (Lingui)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Setup
|
|
||||||
|
|
||||||
The project uses [Invoke](https://www.pyinvoketasks.com/) (`tasks.py`) as the task runner.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# One-time setup: creates venv at dev/venv/, installs deps, sets up pre-commit hooks
|
|
||||||
invoke dev.setup-dev
|
|
||||||
|
|
||||||
# Apply database migrations
|
|
||||||
invoke migrate
|
|
||||||
|
|
||||||
# Create an admin account (required to log in)
|
|
||||||
invoke superuser
|
|
||||||
|
|
||||||
# Terminal 1 — Django dev server (port 8000)
|
|
||||||
invoke dev.server
|
|
||||||
|
|
||||||
# Terminal 2 — Vite dev server with HMR (port 5173)
|
|
||||||
invoke dev.frontend-server
|
|
||||||
|
|
||||||
# Terminal 3 (optional) — Background task worker
|
|
||||||
invoke worker
|
|
||||||
```
|
|
||||||
|
|
||||||
A VS Code Dev Container configuration is available at `.devcontainer/` and includes PostgreSQL 15 and Redis 7 sidecar services.
|
|
||||||
|
|
||||||
To see all available tasks, run `invoke --list`.
|
|
||||||
|
|
||||||
## Running Backend Tests
|
|
||||||
|
|
||||||
Always pass `--keepdb` to backend test commands. It reuses the existing test database instead of rebuilding it from scratch on every run, which is significantly faster.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# All backend tests
|
|
||||||
invoke dev.test --keepdb
|
|
||||||
|
|
||||||
# Specific test module
|
|
||||||
invoke dev.test --keepdb --runtest=company.test_api
|
|
||||||
|
|
||||||
# With coverage
|
|
||||||
invoke dev.test --keepdb --coverage
|
|
||||||
|
|
||||||
# Migration tests only (skip --keepdb here — migrations must run against a fresh DB)
|
|
||||||
invoke dev.test --migrations
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running Frontend Tests
|
|
||||||
|
|
||||||
Frontend tests use [Playwright](https://playwright.dev/) and target Chromium and Firefox. The test runner automatically starts the Vite dev server (port 5173), the Django backend (port 8000), and the background worker — no manual server startup is needed.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Open Playwright's interactive UI (recommended for local development)
|
|
||||||
invoke dev.frontend-test
|
|
||||||
|
|
||||||
# Run tests in headless mode from the frontend directory
|
|
||||||
cd src/frontend && npx playwright test
|
|
||||||
|
|
||||||
# Run a specific test file
|
|
||||||
cd src/frontend && npx playwright test tests/pui_login.spec.ts
|
|
||||||
|
|
||||||
# Run tests in a specific browser only
|
|
||||||
cd src/frontend && npx playwright test --project=chromium
|
|
||||||
```
|
|
||||||
|
|
||||||
Test files live in `src/frontend/tests/` and follow the `pui_*.spec.ts` naming convention.
|
|
||||||
|
|
||||||
Locally, tests run with a single worker (required for Vite HMR compatibility). In CI they run with multiple workers against a production build for speed.
|
|
||||||
|
|
||||||
## Code Style and Linting
|
|
||||||
|
|
||||||
All formatting and linting runs automatically on commit via pre-commit hooks. Never skip them.
|
|
||||||
|
|
||||||
### Python (Backend)
|
|
||||||
|
|
||||||
Tool: **Ruff** (replaces Black, isort, flake8, and others).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ruff check src/backend/ # Lint
|
|
||||||
ruff format src/backend/ # Format
|
|
||||||
```
|
|
||||||
|
|
||||||
- Google-style docstrings enforced
|
|
||||||
- Enabled rule sets: A, B, C, D, F, I, N, SIM, PIE, PLE, PLW, RUF, UP, W
|
|
||||||
- Type checking: `ty` (configured in `pyproject.toml`)
|
|
||||||
|
|
||||||
### Django Templates
|
|
||||||
|
|
||||||
Tool: **djLint**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
djlint src/backend/ --check
|
|
||||||
```
|
|
||||||
|
|
||||||
### JavaScript / TypeScript (Frontend)
|
|
||||||
|
|
||||||
Tool: **Biome** (replaces ESLint + Prettier).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd src/frontend
|
|
||||||
yarn biome check . # Lint + format check
|
|
||||||
yarn biome format . # Format only
|
|
||||||
```
|
|
||||||
|
|
||||||
- Single quotes, space indentation
|
|
||||||
- `noUnusedImports` is an error
|
|
||||||
|
|
||||||
## Making Changes
|
|
||||||
|
|
||||||
### Backend
|
|
||||||
|
|
||||||
- Each Django app has its own `models.py`, `serializers.py`, `views.py`, `urls.py`, and `filters.py`.
|
|
||||||
- API endpoints use Django REST Framework; document them with drf-spectacular decorators.
|
|
||||||
- After changing models, create a migration: `invoke dev.makemigrations`.
|
|
||||||
- Tests live in `test_*.py` files within each app directory.
|
|
||||||
- Background tasks go through Django-Q2; define them in the app's `tasks.py`.
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
|
|
||||||
- Pages register themselves in `src/frontend/src/router.tsx`.
|
|
||||||
- Server state fetching uses TanStack Query (React Query); avoid raw `useEffect` for data fetching.
|
|
||||||
- Global UI state uses Zustand stores in `states/`.
|
|
||||||
- UI components come from Mantine 9.x; use the Mantine component library before writing custom CSS.
|
|
||||||
- Add i18n strings with Lingui (`t` macro / `Trans` component); run `invoke int.frontend-trans` to extract from source and compile. (`invoke dev.translate` is for the full translation pipeline and is not intended for local use.)
|
|
||||||
- Playwright tests live in `src/frontend/tests/`.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
**Python:** Add packages to `src/backend/requirements.in` (or `requirements-dev.in` for dev-only tools). The pre-commit hook runs `pip-compile` automatically on commit to regenerate `requirements.txt`. Never edit `requirements.txt` directly.
|
|
||||||
|
|
||||||
**Frontend:** Add packages with `yarn add <package>` from `src/frontend/`.
|
|
||||||
|
|
||||||
### Migrations
|
|
||||||
|
|
||||||
- Never edit existing migration files; always generate new ones.
|
|
||||||
- Keep migrations reversible where possible.
|
|
||||||
- Migration tests run in CI under the tag `migration_test`.
|
|
||||||
|
|
||||||
## CI / CD
|
|
||||||
|
|
||||||
GitHub Actions workflows live in `.github/workflows/`. Key workflows:
|
|
||||||
|
|
||||||
| Workflow | Purpose |
|
|
||||||
|----------|---------|
|
|
||||||
| `qc_checks.yaml` | Lint, type-check, backend tests, API schema |
|
|
||||||
| `frontend.yaml` | Playwright E2E tests, frontend build |
|
|
||||||
| `docker.yaml` | Docker image builds |
|
|
||||||
| `translations.yaml` | Crowdin i18n sync |
|
|
||||||
|
|
||||||
CI uses path-based filtering — only affected jobs run per PR. Coverage tracked via Codecov; quality analysis via SonarCloud.
|
|
||||||
|
|
||||||
## Key Conventions
|
|
||||||
|
|
||||||
- **REST API versioning:** Endpoint changes must not break the published API schema. Run `invoke dev.schema` and check the diff before opening a PR.
|
|
||||||
- **Plugin safety:** The plugin system (`plugin/`) is a public extension point; avoid breaking its interfaces.
|
|
||||||
- **No hardcoded secrets:** gitleaks runs in pre-commit and CI — any credential-shaped string will block merges.
|
|
||||||
- **Database portability:** Code must work on PostgreSQL, MySQL/MariaDB, and SQLite. Avoid database-specific SQL or ORM features.
|
|
||||||
- **Frontend translations:** Every user-visible string must be wrapped in a Lingui `t` macro or `<Trans>` component.
|
|
||||||
- **Test tagging:** Tag slow or special tests (`@tag('migration_test')`, `@tag('performance_test')`) so CI can filter them selectively.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Refer to our [contribution guidelines](https://docs.inventree.org/en/latest/develop/contributing/) for more information!
|
Refer to our [contribution guidelines](https://docs.inventree.org/en/latest/develop/contributing/) for more information!
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
[](https://inventree.readthedocs.io/en/latest/?badge=latest)
|
[](https://inventree.readthedocs.io/en/latest/?badge=latest)
|
||||||

|

|
||||||
[](https://app.netlify.com/sites/inventree/deploys)
|
[](https://app.netlify.com/sites/inventree/deploys)
|
||||||
|
[](https://dev.azure.com/InvenTree/InvenTree%20test%20statistics/_build/latest?definitionId=3&branchName=testing)
|
||||||
|
|
||||||
[](https://bestpractices.coreinfrastructure.org/projects/7179)
|
[](https://bestpractices.coreinfrastructure.org/projects/7179)
|
||||||
[](https://securityscorecards.dev/viewer/?uri=github.com/inventree/InvenTree)
|
[](https://securityscorecards.dev/viewer/?uri=github.com/inventree/InvenTree)
|
||||||
|
|
@ -76,7 +77,6 @@ InvenTree is designed to be **extensible**, and provides multiple options for **
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://www.postgresql.org/">PostgreSQL</a></li>
|
<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.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://www.sqlite.org/">SQLite</a></li>
|
||||||
<li><a href="https://redis.io/">Redis</a></li>
|
<li><a href="https://redis.io/">Redis</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -201,7 +201,6 @@ Find a full list of used third-party libraries in the license information dialog
|
||||||
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-bg.svg" alt="Deploys by Netlify" /> </a>
|
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-bg.svg" alt="Deploys by Netlify" /> </a>
|
||||||
<a href="https://crowdin.com"> <img src="https://crowdin.com/images/crowdin-logo.svg" alt="Translation by Crowdin" /> </a> <br>
|
<a href="https://crowdin.com"> <img src="https://crowdin.com/images/crowdin-logo.svg" alt="Translation by Crowdin" /> </a> <br>
|
||||||
<a href="https://codspeed.io/inventree/InvenTree?utm_source=badge"><img src="https://img.shields.io/endpoint?url=https://codspeed.io/badge.json" alt="CodSpeed Badge"/></a>
|
<a href="https://codspeed.io/inventree/InvenTree?utm_source=badge"><img src="https://img.shields.io/endpoint?url=https://codspeed.io/badge.json" alt="CodSpeed Badge"/></a>
|
||||||
<a href="https://flakiness.io/InvenTree/InvenTree"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fflakiness.io%2Fapi%2Fbadge%3Finput%3D%257B%2522badgeToken%2522%253A%2522badge-35mqq5Ht4uL3vGF8lR9P2D%2522%257D" alt="Flakiness Badge"/></a>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
10
codecov.yml
|
|
@ -1,6 +1,3 @@
|
||||||
ignore:
|
|
||||||
- "src/backend/InvenTree/**/test_migrations.py"
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
status:
|
status:
|
||||||
project:
|
project:
|
||||||
|
|
@ -24,7 +21,7 @@ flag_management:
|
||||||
carryforward: true
|
carryforward: true
|
||||||
statuses:
|
statuses:
|
||||||
- type: project
|
- type: project
|
||||||
target: 38%
|
target: 40%
|
||||||
- name: web
|
- name: web
|
||||||
carryforward: true
|
carryforward: true
|
||||||
statuses:
|
statuses:
|
||||||
|
|
@ -43,10 +40,8 @@ component_management:
|
||||||
name: Backend Apps
|
name: Backend Apps
|
||||||
paths:
|
paths:
|
||||||
- src/backend/InvenTree/build/**
|
- src/backend/InvenTree/build/**
|
||||||
- src/backend/InvenTree/common/**
|
|
||||||
- src/backend/InvenTree/company/**
|
- src/backend/InvenTree/company/**
|
||||||
- src/backend/InvenTree/data_exporter/**
|
- src/backend/InvenTree/data_exporter/**
|
||||||
- src/backend/InvenTree/generic/**
|
|
||||||
- src/backend/InvenTree/importer/**
|
- src/backend/InvenTree/importer/**
|
||||||
- src/backend/InvenTree/machine/**
|
- src/backend/InvenTree/machine/**
|
||||||
- src/backend/InvenTree/order/**
|
- src/backend/InvenTree/order/**
|
||||||
|
|
@ -72,8 +67,7 @@ component_management:
|
||||||
- component_id: web
|
- component_id: web
|
||||||
name: Frontend
|
name: Frontend
|
||||||
paths:
|
paths:
|
||||||
- src/frontend/lib/**
|
- src/frontend/**
|
||||||
- src/frontend/src/**
|
|
||||||
statuses:
|
statuses:
|
||||||
- type: project
|
- type: project
|
||||||
target: 68% # 90%
|
target: 68% # 90%
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ INVENTREE_TAG=stable
|
||||||
INVENTREE_SITE_URL="http://inventree.localhost"
|
INVENTREE_SITE_URL="http://inventree.localhost"
|
||||||
#INVENTREE_SITE_URL="http://192.168.1.2" # You can specify a local IP address here
|
#INVENTREE_SITE_URL="http://192.168.1.2" # You can specify a local IP address here
|
||||||
#INVENTREE_SITE_URL="https://inventree.my-domain.com" # Or a public domain name (which you control)
|
#INVENTREE_SITE_URL="https://inventree.my-domain.com" # Or a public domain name (which you control)
|
||||||
INVENTREE_WEB_PORT=8000
|
|
||||||
|
|
||||||
# InvenTree proxy forwarding settings
|
# InvenTree proxy forwarding settings
|
||||||
INVENTREE_USE_X_FORWARDED_HOST=True
|
INVENTREE_USE_X_FORWARDED_HOST=True
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
# The following environment variables may be used:
|
# The following environment variables may be used:
|
||||||
# - INVENTREE_SITE_URL: The upstream URL of the InvenTree site (default: inventree.localhost)
|
# - INVENTREE_SITE_URL: The upstream URL of the InvenTree site (default: inventree.localhost)
|
||||||
# - INVENTREE_SERVER: The internal URL of the InvenTree container (default: http://inventree-server:8000)
|
# - INVENTREE_SERVER: The internal URL of the InvenTree container (default: http://inventree-server:8000)
|
||||||
# - INVENTREE_WEB_PORT: The port on which the InvenTree web server listens (default: 8000)
|
|
||||||
#
|
#
|
||||||
# Note that while this file is a good starting point, it may need to be modified to suit your specific requirements
|
# Note that while this file is a good starting point, it may need to be modified to suit your specific requirements
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
# - Runs InvenTree web server under django development server
|
# - Runs InvenTree web server under django development server
|
||||||
# - Monitors source files for any changes, and live-reloads server
|
# - Monitors source files for any changes, and live-reloads server
|
||||||
|
|
||||||
# Base image last bumped 2026-06-16
|
# Base image last bumped 2026-02-12
|
||||||
FROM python:3.14.6-slim-trixie@sha256:44dd04494ee8f3b538294360e7c4b3acb87c8268e4d0a4828a6500b1eff50061 AS inventree_base
|
FROM python:3.11-slim-trixie@sha256:0b23cfb7425d065008b778022a17b1551c82f8b4866ee5a7a200084b7e2eafbf AS inventree_base
|
||||||
|
|
||||||
# Build arguments for this image
|
# Build arguments for this image
|
||||||
ARG commit_tag=""
|
ARG commit_tag=""
|
||||||
|
|
@ -18,7 +18,6 @@ ARG commit_hash=""
|
||||||
ARG commit_date=""
|
ARG commit_date=""
|
||||||
|
|
||||||
ARG data_dir="data"
|
ARG data_dir="data"
|
||||||
ARG NODE_VERSION=24
|
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
|
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||||
|
|
@ -119,11 +118,15 @@ RUN pip install --user --require-hashes -r base_requirements.txt --no-cache-dir
|
||||||
rm -rf /root/.cache/pip
|
rm -rf /root/.cache/pip
|
||||||
|
|
||||||
# Install frontend build dependencies
|
# Install frontend build dependencies
|
||||||
# pinned to v0.40.5
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/1889911f0841e669de0be5bd02c737a3f1fd20fa/install.sh | bash && \
|
nodejs npm \
|
||||||
bash -c "export NVM_DIR="$HOME/.nvm" && source $HOME/.nvm/nvm.sh && \
|
&& apt-get clean \
|
||||||
nvm install $NODE_VERSION && corepack enable yarn && yarn config set network-timeout 600000 -g"
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
RUN bash -c "source $HOME/.nvm/nvm.sh && cd '${INVENTREE_HOME}' && invoke int.frontend-compile --extract"
|
|
||||||
|
RUN npm install -g n yarn --ignore-scripts && \
|
||||||
|
yarn config set network-timeout 600000 -g
|
||||||
|
RUN bash -c "n lts"
|
||||||
|
RUN cd "${INVENTREE_HOME}" && invoke int.frontend-compile --extract
|
||||||
|
|
||||||
# InvenTree production image:
|
# InvenTree production image:
|
||||||
# - Copies required files from local directory
|
# - Copies required files from local directory
|
||||||
|
|
@ -146,9 +149,6 @@ ENV PATH=/root/.local/bin:$PATH
|
||||||
COPY --from=builder_stage ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web
|
COPY --from=builder_stage ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web
|
||||||
COPY --from=builder_stage /root/.local /root/.local
|
COPY --from=builder_stage /root/.local /root/.local
|
||||||
|
|
||||||
# Compile Django backend translations during image build
|
|
||||||
RUN bash -c "cd '${INVENTREE_HOME}' && invoke int.backend-compilemessages"
|
|
||||||
|
|
||||||
# Launch the production server
|
# Launch the production server
|
||||||
CMD ["sh", "-c", "exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} --chdir ${INVENTREE_BACKEND_DIR}/InvenTree"]
|
CMD ["sh", "-c", "exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} --chdir ${INVENTREE_BACKEND_DIR}/InvenTree"]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,12 @@ services:
|
||||||
container_name: inventree-server
|
container_name: inventree-server
|
||||||
# Only change this port if you understand the stack.
|
# Only change this port if you understand the stack.
|
||||||
expose:
|
expose:
|
||||||
- ${INVENTREE_WEB_PORT:-8000}
|
- 8000
|
||||||
depends_on:
|
depends_on:
|
||||||
- inventree-db
|
- inventree-db
|
||||||
- inventree-cache
|
- inventree-cache
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
|
||||||
INVENTREE_SERVER: http://inventree-server:${INVENTREE_WEB_PORT}
|
|
||||||
volumes:
|
volumes:
|
||||||
# Data volume must map to /home/inventree/data
|
# Data volume must map to /home/inventree/data
|
||||||
- ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z
|
- ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ threads = 4
|
||||||
|
|
||||||
|
|
||||||
# Worker timeout (default = 90 seconds)
|
# Worker timeout (default = 90 seconds)
|
||||||
timeout = int(os.environ.get('INVENTREE_GUNICORN_TIMEOUT', '90'))
|
timeout = os.environ.get('INVENTREE_GUNICORN_TIMEOUT', '90')
|
||||||
|
|
||||||
# Number of worker processes
|
# Number of worker processes
|
||||||
workers = os.environ.get('INVENTREE_GUNICORN_WORKERS', None)
|
workers = os.environ.get('INVENTREE_GUNICORN_WORKERS', None)
|
||||||
|
|
@ -43,13 +43,7 @@ preload_app = True
|
||||||
|
|
||||||
|
|
||||||
def post_fork(server, worker):
|
def post_fork(server, worker):
|
||||||
"""Post-fork hook called after each worker process is forked."""
|
"""Post-fork hook to set up logging for each worker."""
|
||||||
from django.db import connections
|
|
||||||
|
|
||||||
# Close any DB connections inherited from the master process — PostgreSQL
|
|
||||||
# connections are not fork-safe, so each worker must open its own.
|
|
||||||
connections.close_all()
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
if not settings.TRACING_ENABLED:
|
if not settings.TRACING_ENABLED:
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
# Base python requirements for docker containers
|
# Base python requirements for docker containers
|
||||||
|
|
||||||
# Basic package requirements
|
# Basic package requirements
|
||||||
invoke # Invoke build tool
|
invoke>=2.2.0 # Invoke build tool
|
||||||
pyyaml
|
pyyaml>=6.0.1
|
||||||
setuptools
|
setuptools>=69.0.0
|
||||||
wheel
|
wheel>=0.41.0
|
||||||
|
|
||||||
# Database links
|
# Database links
|
||||||
psycopg[binary, pool]
|
psycopg[binary, pool]
|
||||||
mysqlclient
|
mysqlclient>=2.2.0
|
||||||
mariadb
|
mariadb>=1.1.8
|
||||||
|
|
||||||
# gunicorn web server
|
# gunicorn web server
|
||||||
gunicorn
|
gunicorn>=22.0.0
|
||||||
|
|
||||||
# LDAP required packages
|
# LDAP required packages
|
||||||
django-auth-ldap # Django integration for ldap auth
|
django-auth-ldap # Django integration for ldap auth
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
# This file was autogenerated by uv via the following command:
|
# This file was autogenerated by uv via the following command:
|
||||||
# uv pip compile contrib/container/requirements.in -o contrib/container/requirements.txt --python-version=3.14 -c src/backend/requirements.txt
|
# uv pip compile contrib/container/requirements.in -o contrib/container/requirements.txt --python-version=3.11 -c src/backend/requirements.txt
|
||||||
asgiref==3.11.1 \
|
asgiref==3.11.0 \
|
||||||
--hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \
|
--hash=sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4 \
|
||||||
--hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133
|
--hash=sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# django
|
# django
|
||||||
django==5.2.15 \
|
django==5.2.11 \
|
||||||
--hash=sha256:0eb4a9bb1853a35b0286dbc6d916bd352c8c2687195a7f2d6f80cefd840e4970 \
|
--hash=sha256:7f2d292ad8b9ee35e405d965fbbad293758b858c34bbf7f3df551aeeac6f02d3 \
|
||||||
--hash=sha256:5154a9bf84ac01dde011e367f355c07dbb329532e06810dcf3ef2af269e236e7
|
--hash=sha256:e7130df33ada9ab5e5e929bc19346a20fe383f5454acb2cc004508f242ee92c0
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/container/requirements.in
|
# -r contrib/container/requirements.in
|
||||||
|
|
@ -17,15 +17,15 @@ django-auth-ldap==5.3.0 \
|
||||||
--hash=sha256:743d8107b146240b46f7e97207dc06cb11facc0cd70dce490b7ca09dd5643d19 \
|
--hash=sha256:743d8107b146240b46f7e97207dc06cb11facc0cd70dce490b7ca09dd5643d19 \
|
||||||
--hash=sha256:aa880415983149b072f876d976ef8ec755a438090e176817998263a6ed9e1038
|
--hash=sha256:aa880415983149b072f876d976ef8ec755a438090e176817998263a6ed9e1038
|
||||||
# via -r contrib/container/requirements.in
|
# via -r contrib/container/requirements.in
|
||||||
gunicorn==26.0.0 \
|
gunicorn==25.0.3 \
|
||||||
--hash=sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc \
|
--hash=sha256:aca364c096c81ca11acd4cede0aaeea91ba76ca74e2c0d7f879154db9d890f35 \
|
||||||
--hash=sha256:ca9346f85e3a4aeeb64d491045c16b9a35647abd37ea15efe53080eb8b090baf
|
--hash=sha256:b53a7fff1a07b825b962af320554de44ae77a26abfa373711ff3f83d57d3506d
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/container/requirements.in
|
# -r contrib/container/requirements.in
|
||||||
invoke==3.0.3 \
|
invoke==2.2.1 \
|
||||||
--hash=sha256:437b6a622223824380bfb4e64f612711a6b648c795f565efc8625af66fb57f0c \
|
--hash=sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8 \
|
||||||
--hash=sha256:f11327165e5cbb89b2ad1d88d3292b5113332c43b8553b494da435d6ec6f5053
|
--hash=sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/container/requirements.in
|
# -r contrib/container/requirements.in
|
||||||
|
|
@ -44,91 +44,92 @@ mariadb==1.1.14 \
|
||||||
--hash=sha256:98d552a8bb599eceaa88f65002ad00bd88aeed160592c273a7e5c1d79ab733dd \
|
--hash=sha256:98d552a8bb599eceaa88f65002ad00bd88aeed160592c273a7e5c1d79ab733dd \
|
||||||
--hash=sha256:e6d702a53eccf20922e47f2f45cfb5c7a0c2c6c0a46e4ee2d8a80d0ff4a52f34
|
--hash=sha256:e6d702a53eccf20922e47f2f45cfb5c7a0c2c6c0a46e4ee2d8a80d0ff4a52f34
|
||||||
# via -r contrib/container/requirements.in
|
# via -r contrib/container/requirements.in
|
||||||
mysqlclient==2.2.8 \
|
mysqlclient==2.2.7 \
|
||||||
--hash=sha256:000c7ec3d11e7c411db832e4cfcd7f05db47464326381f5d5ae991b4bb572f93 \
|
--hash=sha256:199dab53a224357dd0cb4d78ca0e54018f9cee9bf9ec68d72db50e0a23569076 \
|
||||||
--hash=sha256:260cce0e81446c83bf0a389e0fae38d68547d9f8fc0833bc733014e10ce28a99 \
|
--hash=sha256:201a6faa301011dd07bca6b651fe5aaa546d7c9a5426835a06c3172e1056a3c5 \
|
||||||
--hash=sha256:60c9ed339dc09e3d5380cc2a9f42e86754fee25a661ced77a02df77990664c92 \
|
--hash=sha256:24ae22b59416d5fcce7e99c9d37548350b4565baac82f95e149cac6ce4163845 \
|
||||||
--hash=sha256:86db31bba7b3480fec2751350e9790e24f016f89af33a87bab7e79f7196474e8 \
|
--hash=sha256:2e3c11f7625029d7276ca506f8960a7fd3c5a0a0122c9e7404e6a8fe961b3d22 \
|
||||||
--hash=sha256:8ed20c5615a915da451bb308c7d0306648a4fd9a2809ba95c992690006306199 \
|
--hash=sha256:4b4c0200890837fc64014cc938ef2273252ab544c1b12a6c1d674c23943f3f2e \
|
||||||
--hash=sha256:9bed7c8d3b629bdc09e17fb628d5b3b0a5fd1f12b09432b464b9126c727bedc0 \
|
--hash=sha256:92af368ed9c9144737af569c86d3b6c74a012a6f6b792eb868384787b52bb585 \
|
||||||
--hash=sha256:a81f5e12f8d05439709cb02fba97f9f76d1a6c528164f2260d8798fec969e300
|
--hash=sha256:977e35244fe6ef44124e9a1c2d1554728a7b76695598e4b92b37dc2130503069 \
|
||||||
|
--hash=sha256:a22d99d26baf4af68ebef430e3131bb5a9b722b79a9fcfac6d9bbf8a88800687
|
||||||
# via -r contrib/container/requirements.in
|
# via -r contrib/container/requirements.in
|
||||||
packaging==26.2 \
|
packaging==26.0 \
|
||||||
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
|
--hash=sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4 \
|
||||||
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
|
--hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# gunicorn
|
# gunicorn
|
||||||
# mariadb
|
# mariadb
|
||||||
# wheel
|
# wheel
|
||||||
psycopg[binary, pool]==3.3.4 \
|
psycopg[binary, pool]==3.3.2 \
|
||||||
--hash=sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a \
|
--hash=sha256:3e94bc5f4690247d734599af56e51bae8e0db8e4311ea413f801fef82b14a99b \
|
||||||
--hash=sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc
|
--hash=sha256:707a67975ee214d200511177a6a80e56e654754c9afca06a7194ea6bbfde9ca7
|
||||||
# via -r contrib/container/requirements.in
|
# via -r contrib/container/requirements.in
|
||||||
psycopg-binary==3.3.4 \
|
psycopg-binary==3.3.2 \
|
||||||
--hash=sha256:018fbed325936da502feb546642c982dcc4b9ffdea32dfef78dbf3b7f7ad4070 \
|
--hash=sha256:03b7cd73fb8c45d272a34ae7249713e32492891492681e3cf11dff9531cf37e9 \
|
||||||
--hash=sha256:0579252a1202cd73e4da137a1426e2dae993ae44e757605344282af3a082848c \
|
--hash=sha256:04bb2de4ba69d6f8395b446ede795e8884c040ec71d01dd07ac2b2d18d4153d1 \
|
||||||
--hash=sha256:136f199a407b5348b9b857c504aff60c77622a28482e7195839ce1b51238c4cc \
|
--hash=sha256:0611f4822674f3269e507a307236efb62ae5a828fcfc923ac85fe22ca19fd7c8 \
|
||||||
--hash=sha256:13a7f380824c35896dcac7fe0f61440f7ca49d6dc73f3c13a9a4471e6a3b302e \
|
--hash=sha256:0768c5f32934bb52a5df098317eca9bdcf411de627c5dca2ee57662b64b54b41 \
|
||||||
--hash=sha256:17a21953a9e5ff3a16dab692625a3676e2f101db5e40072f39dbee2250194d68 \
|
--hash=sha256:07a5f030e0902ec3e27d0506ceb01238c0aecbc73ecd7fa0ee55f86134600b5b \
|
||||||
--hash=sha256:1dc1f79fd16bb1f3f4421417a514607539f17804d95c7ed617265369d1981cae \
|
--hash=sha256:083c2e182be433f290dc2c516fd72b9b47054fcd305cce791e0a50d9e93e06f2 \
|
||||||
--hash=sha256:1fbaa292a3c8bb61b45df1ad3da1908ccee7cb889db9425e3557d9e34e2a4829 \
|
--hash=sha256:09b3014013f05cd89828640d3a1db5f829cc24ad8fa81b6e42b2c04685a0c9d4 \
|
||||||
--hash=sha256:22cdbf5f91ef7bb91fe0c5757e1962d3127a8010256eefd9c61fcaf441802097 \
|
--hash=sha256:0ae60e910531cfcc364a8f615a7941cac89efeb3f0fffe0c4824a6d11461eef7 \
|
||||||
--hash=sha256:26df2717e59c0473e4465a97dfb1b7afebaa479277870fd5784d1436470db47c \
|
--hash=sha256:136c43f185244893a527540307167f5d3ef4e08786508afe45d6f146228f5aa9 \
|
||||||
--hash=sha256:276904e3452d6a23d474ef9a21eee19f20eed3d53ddd2576af033827e0ba0992 \
|
--hash=sha256:1586e220be05547c77afc326741dd41cc7fba38a81f9931f616ae98865439678 \
|
||||||
--hash=sha256:28b7398fdd19db3232c884fb24550bdfe951221f510e195e233299e4c9b78f97 \
|
--hash=sha256:1e09d0d93d35c134704a2cb2b15f81ffc8174fd602f3e08f7b1a3d8896156cf0 \
|
||||||
--hash=sha256:2c09aad7051326e7603c14e50636db9c01f78272dc54b3accff03d46370461e6 \
|
--hash=sha256:1ea41c0229f3f5a3844ad0857a83a9f869aa7b840448fa0c200e6bcf85d33d19 \
|
||||||
--hash=sha256:32a6fbf8481e3a370d0d72b860d35948a693cb01281da217f7b2f307636e591a \
|
--hash=sha256:23d2594af848c1fd3d874a9364bef50730124e72df7bb145a20cb45e728c50ed \
|
||||||
--hash=sha256:41f2ec0fea529832982bcb6c9415de3c86264ebe562b77a467c0fbcd7efbba8d \
|
--hash=sha256:3789d452a9d17a841c7f4f97bbcba51a21f957ea35641a4c98507520e6b6a068 \
|
||||||
--hash=sha256:46893c26858be12cc49ca4226ed6a60b4bfccadd946b3bebb783a60b38788228 \
|
--hash=sha256:3ff7489df5e06c12d1829544eaec64970fe27fe300f7cf04c8495fe682064688 \
|
||||||
--hash=sha256:47c656a8a7ba6eb0cff1801a4caaa9c8bdc12d03080e273aff1c8ac39971a77e \
|
--hash=sha256:43b130e3b6edcb5ee856c7167ccb8561b473308c870ed83978ae478613764f1c \
|
||||||
--hash=sha256:494ca54901be8cf9eb7e02c25b731f2317c378efa44f43e8f9bd0e1184ae7be4 \
|
--hash=sha256:44e89938d36acc4495735af70a886d206a5bfdc80258f95b69b52f68b2968d9e \
|
||||||
--hash=sha256:514404ed543efd620c85602b747df2a23cf1241b4067199e1a66f2d2757aaa41 \
|
--hash=sha256:458696a5fa5dad5b6fb5d5862c22454434ce4fe1cf66ca6c0de5f904cbc1ae3e \
|
||||||
--hash=sha256:574ea21a9651958f1535c5a1c649c7409e9168bcbffa29a3f2f961f58b322949 \
|
--hash=sha256:50ff10ab8c0abdb5a5451b9315538865b50ba64c907742a1385fdf5f5772b73e \
|
||||||
--hash=sha256:580ae30a5f95ccd90008ec697d3ed6a4a2047a516407ad904283fa42086936e9 \
|
--hash=sha256:522b79c7db547767ca923e441c19b97a2157f2f494272a119c854bba4804e186 \
|
||||||
--hash=sha256:5ab28a2a7649df3b72e6b674b4c190e448e8e77cf496a65bd846472048de2089 \
|
--hash=sha256:59d0163c4617a2c577cb34afbed93d7a45b8c8364e54b2bd2020ff25d5f5f860 \
|
||||||
--hash=sha256:5c4ab71be17bdca30cb34c34c4e1496e2f5d6f20c199c12bad226070b22ef9bf \
|
--hash=sha256:5a327327f1188b3fbecac41bf1973a60b86b2eb237db10dc945bd3dc97ec39e4 \
|
||||||
--hash=sha256:612a627d733f695b1de1f9b4bd511c15f999a5d8b915d444bbd7dd71cf3370da \
|
--hash=sha256:649c1d33bedda431e0c1df646985fbbeb9274afa964e1aef4be053c0f23a2924 \
|
||||||
--hash=sha256:6402a9d8146cf4b3974ded3fd28a971e83dc6a0333eb7822524a3aa20b546578 \
|
--hash=sha256:716a586f99bbe4f710dc58b40069fcb33c7627e95cc6fc936f73c9235e07f9cf \
|
||||||
--hash=sha256:6b9016b1714da4dd5ecaaa75b82098aa5a0b87854ce9b092e21c27c4ae23e014 \
|
--hash=sha256:742ce48cde825b8e52fb1a658253d6d1ff66d152081cbc76aa45e2986534858d \
|
||||||
--hash=sha256:71e55ccbdfae79a2ed9c6369c3008a3025817ff9d7e27b32a2d84e2a4267e66e \
|
--hash=sha256:74bc306c4b4df35b09bc8cecf806b271e1c5d708f7900145e4e54a2e5dedfed0 \
|
||||||
--hash=sha256:7465bfe6087d2d5b42d4c53b9b11ca9f218e477317a4a162a10e3c19e984ba8e \
|
--hash=sha256:7c1feba5a8c617922321aef945865334e468337b8fc5c73074f5e63143013b5a \
|
||||||
--hash=sha256:75a9067e236f9b9ae3535b66fe99bddb33d39c0de10112e49b9ab11eee53dc31 \
|
--hash=sha256:7c43a773dd1a481dbb2fe64576aa303d80f328cce0eae5e3e4894947c41d1da7 \
|
||||||
--hash=sha256:773d573e11f437ce0bdb95b7c18dc58390494f96d43f8b45b9760436114f7652 \
|
--hash=sha256:8309ee4569dced5e81df5aa2dcd48c7340c8dee603a66430f042dfbd2878edca \
|
||||||
--hash=sha256:77df19583501ea288eaf15ac0fe7ad01e6d8091a91d5c41df5c718f307d8e31b \
|
--hash=sha256:8db9034cde3bcdafc66980f0130813f5c5d19e74b3f2a19fb3cfbc25ad113121 \
|
||||||
--hash=sha256:7f7668f30b9dd5163197e5cbf4e0efd54e00f0a859cc566ce56cfc31f4054839 \
|
--hash=sha256:8ea05b499278790a8fa0ff9854ab0de2542aca02d661ddff94e830df971ff640 \
|
||||||
--hash=sha256:8c0056529e68dbe9184cd4019a1f3d8f3a4ead2f6fc7a5afcf27d3314edd1277 \
|
--hash=sha256:90ed9da805e52985b0202aed4f352842c907c6b4fc6c7c109c6e646c32e2f43b \
|
||||||
--hash=sha256:94596f9e7633ee3f6440711d43bb70aa31cc0a46a900ab8b4201a366ace5c9e7 \
|
--hash=sha256:94503b79f7da0b65c80d0dbb2f81dd78b300319ec2435d5e6dcf9622160bc2fa \
|
||||||
--hash=sha256:ab8cca8ef8fb1ccf5b048ae5bd78ba55b9e4b5d472e3ce5ca39ff4d2a9c249e4 \
|
--hash=sha256:9742580ecc8e1ac45164e98d32ca6df90da509c2d3ff26be245d94c430f92db4 \
|
||||||
--hash=sha256:ad3bc94054876155549fdaedf4a46d1ec69d39a5bcee377148afe498e84c4b8e \
|
--hash=sha256:9ca24062cd9b2270e4d77576042e9cc2b1d543f09da5aba1f1a3d016cea28390 \
|
||||||
--hash=sha256:b56b603ebcea8aa10b46228b8410ba7f13e7c2ee54389d4d9be0927fd8ce2a70 \
|
--hash=sha256:a9387ab615f929e71ef0f4a8a51e986fa06236ccfa9f3ec98a88f60fbf230634 \
|
||||||
--hash=sha256:b6f5a29e9c775b9f12a1a717aa7a2c80f9e1db6f27ba44a5b59c80ac61d2ffcf \
|
--hash=sha256:ac230e3643d1c436a2dfb59ca84357dfc6862c9f372fc5dbd96bafecae581f9f \
|
||||||
--hash=sha256:b7bfff1ca23732b488cbca3076fc11bc98d520ee122514fdb17a8e20d3338f5a \
|
--hash=sha256:c3a9ccdfee4ae59cf9bf1822777e763bc097ed208f4901e21537fca1070e1391 \
|
||||||
--hash=sha256:bdef84570ebbce1d42b4e7ea952d21c414c5f118ad02fee00c5625f35e134429 \
|
--hash=sha256:c5774272f754605059521ff037a86e680342e3847498b0aa86b0f3560c70963c \
|
||||||
--hash=sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8 \
|
--hash=sha256:c6464150e25b68ae3cb04c4e57496ea11ebfaae4d98126aea2f4702dd43e3c12 \
|
||||||
--hash=sha256:c677c4ad433cb7150c8cd304a0769ae3bcfbe5ea0676eb53faa7b1443b16d0d3 \
|
--hash=sha256:c749770da0947bc972e512f35366dd4950c0e34afad89e60b9787a37e97cb443 \
|
||||||
--hash=sha256:cf7f73a4a792bc5db58a4b385d8a1467e8d468f7548702fb0ed1e9b7501b1c13 \
|
--hash=sha256:cabb2a554d9a0a6bf84037d86ca91782f087dfff2a61298d0b00c19c0bc43f6d \
|
||||||
--hash=sha256:cffc3408d77a27973f33e5d909b624cce683db5fc25964b02fe0aae7886c1007 \
|
--hash=sha256:d391b70c9cc23f6e1142729772a011f364199d2c5ddc0d596f5f43316fbf982d \
|
||||||
--hash=sha256:d7b4d40c153fa352ab3cca530f3a0baedf7621b2ebcbd7f084009522c21788fc \
|
--hash=sha256:d45acedcaa58619355f18e0f42af542fcad3fd84ace4b8355d3a5dea23318578 \
|
||||||
--hash=sha256:dbfdb9b6cc79f31104a7b162a2b921b765fcc62af6c00540a167a8de47e4ed38 \
|
--hash=sha256:d79b0093f0fbf7a962d6a46ae292dc056c65d16a8ee9361f3cfbafd4c197ab14 \
|
||||||
--hash=sha256:df1d567fc430f6df15c9fcf67d87685fc49bdb325adc0db5af1adfb2f44eb5c9 \
|
--hash=sha256:d88f32ff8c47cb7f4e7e7a9d1747dcee6f3baa19ed9afa9e5694fd2fb32b61ed \
|
||||||
--hash=sha256:e2631da29253a98bd496e6c4813b24e09a4fe3fb2a9e88513305d6f8747cce95 \
|
--hash=sha256:d8c899a540f6c7585cee53cddc929dd4d2db90fd828e37f5d4017b63acbc1a5d \
|
||||||
--hash=sha256:e7510c37550f91a187e3660a8cc50d4b760f8c3b8b2f89ebc5698cd2c7f2c85d \
|
--hash=sha256:de9173f8cc0efd88ac2a89b3b6c287a9a0011cdc2f53b2a12c28d6fd55f9f81c \
|
||||||
--hash=sha256:eb05ee1c2b817d27c537333224c9e83c7afb86fe7296ba970990068baf819b16 \
|
--hash=sha256:df65174c7cf6b05ea273ce955927d3270b3a6e27b0b12762b009ce6082b8d3fc \
|
||||||
--hash=sha256:eb4eed2079c01a4850bf467deacfab56d356d4225040170af03dc9958321242d \
|
--hash=sha256:e22bf6b54df994aff37ab52695d635f1ef73155e781eee1f5fa75bc08b58c8da \
|
||||||
--hash=sha256:ee17a2cf4943cde261adfad1bbc5bf38d6b3776d7afff74c7cabcbeaeb08c260 \
|
--hash=sha256:e750afe74e6c17b2c7046d2c3e3173b5a3f6080084671c8aa327215323df155b \
|
||||||
--hash=sha256:f80e3f2b5331dbbf0901bcb658056c03eeb2c1ef31d774afb0d61598b242e744 \
|
--hash=sha256:ea4fe6b4ead3bbbe27244ea224fcd1f53cb119afc38b71a2f3ce570149a03e30 \
|
||||||
--hash=sha256:f9b1c2533af01cd7648378599f82b0b8ae32f293296e6eec5753a625bc97ef28 \
|
--hash=sha256:f26f113013c4dcfbfe9ced57b5bad2035dda1a7349f64bf726021968f9bccad3 \
|
||||||
--hash=sha256:fa1cbc10768a796c96d3243656016bf4e337c81c71097270bb7b0ad6210d9765 \
|
--hash=sha256:f3f601f32244a677c7b029ec39412db2772ad04a28bc2cbb4b1f0931ed0ffad7 \
|
||||||
--hash=sha256:fbd1d4ed566895ad2d3bf4ddfd8bae90026930ddf29df3b9d91d32c8c47866a7
|
--hash=sha256:fc5a189e89cbfff174588665bb18d28d2d0428366cc9dae5864afcaa2e57380b
|
||||||
# via psycopg
|
# via psycopg
|
||||||
psycopg-pool==3.3.1 \
|
psycopg-pool==3.3.0 \
|
||||||
--hash=sha256:2af5b432941c4c9ad5c87b3fa410aec910ec8f7c122855897983a06c45f2e4b5 \
|
--hash=sha256:2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 \
|
||||||
--hash=sha256:b10b10b7a175d5cc1592147dc5b7eec8a9e0834eb3ed2c4a92c858e2f51eb63c
|
--hash=sha256:fa115eb2860bd88fce1717d75611f41490dec6135efb619611142b24da3f6db5
|
||||||
# via psycopg
|
# via psycopg
|
||||||
pyasn1==0.6.3 \
|
pyasn1==0.6.2 \
|
||||||
--hash=sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf \
|
--hash=sha256:1eb26d860996a18e9b6ed05e7aae0e9fc21619fcee6af91cca9bad4fbea224bf \
|
||||||
--hash=sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde
|
--hash=sha256:9b59a2b25ba7e4f8197db7686c09fb33e658b98339fadb826e9512629017833b
|
||||||
# via
|
# via
|
||||||
# pyasn1-modules
|
# pyasn1-modules
|
||||||
# python-ldap
|
# python-ldap
|
||||||
|
|
@ -136,8 +137,8 @@ pyasn1-modules==0.4.2 \
|
||||||
--hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \
|
--hash=sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a \
|
||||||
--hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6
|
--hash=sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6
|
||||||
# via python-ldap
|
# via python-ldap
|
||||||
python-ldap==3.4.7 \
|
python-ldap==3.4.5 \
|
||||||
--hash=sha256:bacd9fb680d20263d8570ade1cf234d90d281149a8beb4f079dd8f33f7613dc8
|
--hash=sha256:b2f6ef1c37fe2c6a5a85212efe71311ee21847766a7d45fcb711f3b270a5f79a
|
||||||
# via
|
# via
|
||||||
# -r contrib/container/requirements.in
|
# -r contrib/container/requirements.in
|
||||||
# django-auth-ldap
|
# django-auth-ldap
|
||||||
|
|
@ -218,9 +219,9 @@ pyyaml==6.0.3 \
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/container/requirements.in
|
# -r contrib/container/requirements.in
|
||||||
setuptools==82.0.1 \
|
setuptools==80.10.2 \
|
||||||
--hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \
|
--hash=sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70 \
|
||||||
--hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb
|
--hash=sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/container/requirements.in
|
# -r contrib/container/requirements.in
|
||||||
|
|
@ -235,29 +236,30 @@ typing-extensions==4.15.0 \
|
||||||
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
|
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
|
# psycopg
|
||||||
# psycopg-pool
|
# psycopg-pool
|
||||||
uv==0.11.24 \
|
uv==0.9.22 \
|
||||||
--hash=sha256:047d763d20d71968c00f4afec40b0e75d9da7e3693f725b9f502d84a25256893 \
|
--hash=sha256:012bdc5285a9cdb091ac514b7eb8a707e3b649af5355fe4afb4920bfe1958c00 \
|
||||||
--hash=sha256:0e100b9cbc59beff2730840ac989b1f100cc03c90514e7cab6992a1f3f13784e \
|
--hash=sha256:0cdc653fb601aa7f273242823fa93024f5fd319c66cdf22f36d784858493564c \
|
||||||
--hash=sha256:3176668ea8a2318d775c0d9188661c61ccc36790220724b1d360fcc8945d520e \
|
--hash=sha256:0d8f007616cac5962620252b56a1d8224e9b2de566e78558efe04cc18526d507 \
|
||||||
--hash=sha256:356435577fae11fafe7a067ee3269cccefd35b031b83a3a36c87fe9d192bffba \
|
--hash=sha256:1f45e1e0f26dd47fa01eb421c54cfd39de10fd52ac0a9d7ae45b92fce7d92b0b \
|
||||||
--hash=sha256:38f38c9449aafd71dc0fa35e66a8a547ee48947b2f2f94084893c2afe6058cfa \
|
--hash=sha256:1f979c9d313b4616d9865859ef520bea5df0d4f15c57214589f5676fafa440c1 \
|
||||||
--hash=sha256:438f8291fb9daea30a4bd333299b82e6ef755578302745a021162f328cfcfc68 \
|
--hash=sha256:2a4155cf7d0231d0adae94257ee10d70c57c2f592207536ddd55d924590a8c15 \
|
||||||
--hash=sha256:48a6123f71b801e0e0b8a38520b011632ad81e0a043445044ce5b1a7b1cec7b6 \
|
--hash=sha256:3422b093b8e6e8de31261133b420c34dbef81f3fd1d82f787ac771b00b54adf8 \
|
||||||
--hash=sha256:6ecdad43e870f88d3772d9d37e877259ae35ec374d51589805cdcf6196205829 \
|
--hash=sha256:369b55341c6236f42d8fc335876308e5c57c921850975b3019cc9f7ebbe31567 \
|
||||||
--hash=sha256:79757f90b7765996366b80e77cad13555c7f7e1282ca8d8b686ee21773ab0b77 \
|
--hash=sha256:3b2bcce464186f8fafa3bf2aa5d82db4e3229366345399cc3f5bcafd616b8fe0 \
|
||||||
--hash=sha256:8346b0086d213b80430f3bb4477379a8a4fa550b03447d23c84eee85c2e52bc4 \
|
--hash=sha256:41c73a4938818ede30e601cd0be87953e5c6a83dc4762e04e626f2eb9b240ebe \
|
||||||
--hash=sha256:8602a1b6300a3a948afacc62e1cb933c8394c27966db85ed7e29483300b69dc4 \
|
--hash=sha256:59c4f6b3659a68c26c50865432a7134386f607432160aad51e2247f862902697 \
|
||||||
--hash=sha256:9312b6fd44361674e9c847a828ded792493766697816261e05848a024fe34129 \
|
--hash=sha256:77ec4c101d41d7738226466191a7d62f9fa4de06ea580e0801da2f5cd5fa08aa \
|
||||||
--hash=sha256:bf568c62dddd55ad9c85a16ffdfbcc7746be9c3159ed644e4f9e6f5e44905cbb \
|
--hash=sha256:8f73043ade8ff6335e19fe1f4e7425d5e28aec9cafd72d13d5b40bb1cbb85690 \
|
||||||
--hash=sha256:c4ab221c0a949f8006a7582786dae384706b2f82016a0db60baa7cc696042705 \
|
--hash=sha256:9c238525272506845fe07c0b9088c5e33fcd738e1f49ef49dc3c8112096d2e3a \
|
||||||
--hash=sha256:c8ec3caf656645f58b53cb9aee9aa95cfc65c82ba2d7f1362bfd2660d1484307 \
|
--hash=sha256:b1985559b38663642658069e8d09fa6c30ed1c67654b7e5765240d9e4e9cdd57 \
|
||||||
--hash=sha256:d6a49543c659c0cf1ff4c944955d97e69dbce4b76e3d284f5a92cea19133ebb6 \
|
--hash=sha256:b78f2605d65c4925631d891dec99b677b05f50c774dedc6ef8968039a5bcfdb0 \
|
||||||
--hash=sha256:e499579f557abf17b8ffd1490d3e827748aea096f6eaa66737e729e046449f08 \
|
--hash=sha256:b807bafe6b65fc1fe9c65ffd0d4228db894872de96e7200c44943f24beb68931 \
|
||||||
--hash=sha256:e7e78c18686202c8b8715bebb83bfaf58f82d7fb848b6a5ae4e925a9fac3de4c \
|
--hash=sha256:d9d4be990bb92a68781f7c98d2321b528667b61d565c02ba978488c0210aa768 \
|
||||||
--hash=sha256:ed0c9a9d7909f0e48a9dafe666ca9ebefe2a1534e51ed05c0a7de7406465f868
|
--hash=sha256:e4b61a9c8b8dcbf64e642d2052342d36a46886b8bc3ccc407282962b970101af
|
||||||
# via -r contrib/container/requirements.in
|
# via -r contrib/container/requirements.in
|
||||||
wheel==0.47.0 \
|
wheel==0.46.3 \
|
||||||
--hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \
|
--hash=sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d \
|
||||||
--hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3
|
--hash=sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803
|
||||||
# via -r contrib/container/requirements.in
|
# via -r contrib/container/requirements.in
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Packages needed for CI/packages
|
# Packages needed for CI/packages
|
||||||
requests==2.34.2
|
requests==2.33.0
|
||||||
pyyaml==6.0.3
|
pyyaml==6.0.3
|
||||||
jc
|
jc==1.25.6
|
||||||
|
|
|
||||||
|
|
@ -1,157 +1,141 @@
|
||||||
# This file was autogenerated by uv via the following command:
|
# 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
|
# uv pip compile contrib/dev_reqs/requirements.in -o contrib/dev_reqs/requirements.txt -c src/backend/requirements.txt
|
||||||
certifi==2026.6.17 \
|
certifi==2026.1.4 \
|
||||||
--hash=sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432 \
|
--hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \
|
||||||
--hash=sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db
|
--hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# requests
|
# requests
|
||||||
charset-normalizer==3.4.7 \
|
charset-normalizer==3.4.4 \
|
||||||
--hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \
|
--hash=sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad \
|
||||||
--hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \
|
--hash=sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93 \
|
||||||
--hash=sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67 \
|
--hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \
|
||||||
--hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \
|
--hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \
|
||||||
--hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \
|
--hash=sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc \
|
||||||
--hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \
|
--hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \
|
||||||
--hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \
|
--hash=sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63 \
|
||||||
--hash=sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444 \
|
--hash=sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d \
|
||||||
--hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \
|
--hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \
|
||||||
--hash=sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9 \
|
--hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \
|
||||||
--hash=sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01 \
|
--hash=sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0 \
|
||||||
--hash=sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217 \
|
--hash=sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505 \
|
||||||
--hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \
|
--hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \
|
||||||
--hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \
|
--hash=sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af \
|
||||||
--hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \
|
--hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \
|
||||||
--hash=sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83 \
|
--hash=sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318 \
|
||||||
--hash=sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5 \
|
--hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \
|
||||||
--hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \
|
--hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \
|
||||||
--hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \
|
--hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \
|
||||||
--hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \
|
--hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \
|
||||||
--hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \
|
--hash=sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576 \
|
||||||
--hash=sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42 \
|
--hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \
|
||||||
--hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \
|
--hash=sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1 \
|
||||||
--hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \
|
--hash=sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8 \
|
||||||
--hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \
|
--hash=sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1 \
|
||||||
--hash=sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207 \
|
--hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \
|
||||||
--hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \
|
--hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \
|
||||||
--hash=sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734 \
|
--hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \
|
||||||
--hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \
|
--hash=sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88 \
|
||||||
--hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \
|
--hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \
|
||||||
--hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \
|
--hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \
|
||||||
--hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \
|
--hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \
|
||||||
--hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \
|
--hash=sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a \
|
||||||
--hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \
|
--hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \
|
||||||
--hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \
|
--hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \
|
||||||
--hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \
|
--hash=sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84 \
|
||||||
--hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \
|
--hash=sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db \
|
||||||
--hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \
|
--hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \
|
||||||
--hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \
|
--hash=sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7 \
|
||||||
--hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \
|
--hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \
|
||||||
--hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \
|
--hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \
|
||||||
--hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \
|
--hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \
|
||||||
--hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \
|
--hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \
|
||||||
--hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \
|
--hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \
|
||||||
--hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \
|
--hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \
|
||||||
--hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \
|
--hash=sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2 \
|
||||||
--hash=sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776 \
|
--hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \
|
||||||
--hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \
|
--hash=sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d \
|
||||||
--hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \
|
--hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \
|
||||||
--hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \
|
--hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \
|
||||||
--hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \
|
--hash=sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf \
|
||||||
--hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \
|
--hash=sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6 \
|
||||||
--hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \
|
--hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \
|
||||||
--hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \
|
--hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \
|
||||||
--hash=sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5 \
|
--hash=sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa \
|
||||||
--hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \
|
--hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \
|
||||||
--hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \
|
--hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \
|
||||||
--hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \
|
--hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \
|
||||||
--hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \
|
--hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \
|
||||||
--hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \
|
--hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \
|
||||||
--hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \
|
--hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \
|
||||||
--hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \
|
--hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \
|
||||||
--hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \
|
--hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \
|
||||||
--hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \
|
--hash=sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e \
|
||||||
--hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \
|
--hash=sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313 \
|
||||||
--hash=sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4 \
|
--hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \
|
||||||
--hash=sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545 \
|
--hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \
|
||||||
--hash=sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706 \
|
--hash=sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d \
|
||||||
--hash=sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366 \
|
--hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \
|
||||||
--hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \
|
--hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \
|
||||||
--hash=sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a \
|
--hash=sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3 \
|
||||||
--hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \
|
--hash=sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9 \
|
||||||
--hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \
|
--hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \
|
||||||
--hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \
|
--hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \
|
||||||
--hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \
|
--hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \
|
||||||
--hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \
|
--hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \
|
||||||
--hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \
|
--hash=sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50 \
|
||||||
--hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \
|
--hash=sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf \
|
||||||
--hash=sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319 \
|
--hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \
|
||||||
--hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \
|
--hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \
|
||||||
--hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \
|
--hash=sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac \
|
||||||
--hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \
|
--hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \
|
||||||
--hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \
|
--hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \
|
||||||
--hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \
|
--hash=sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c \
|
||||||
--hash=sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0 \
|
--hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \
|
||||||
--hash=sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686 \
|
--hash=sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6 \
|
||||||
--hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \
|
--hash=sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e \
|
||||||
--hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \
|
--hash=sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4 \
|
||||||
--hash=sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c \
|
--hash=sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84 \
|
||||||
--hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \
|
--hash=sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69 \
|
||||||
--hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \
|
--hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \
|
||||||
--hash=sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60 \
|
--hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \
|
||||||
--hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \
|
--hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \
|
||||||
--hash=sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274 \
|
--hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \
|
||||||
--hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \
|
--hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \
|
||||||
--hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \
|
--hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \
|
||||||
--hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \
|
--hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \
|
||||||
--hash=sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f \
|
--hash=sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d \
|
||||||
--hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \
|
--hash=sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074 \
|
||||||
--hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \
|
--hash=sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3 \
|
||||||
--hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \
|
--hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \
|
||||||
--hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \
|
--hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \
|
||||||
--hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \
|
--hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \
|
||||||
--hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \
|
--hash=sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d \
|
||||||
--hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \
|
--hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \
|
||||||
--hash=sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00 \
|
--hash=sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f \
|
||||||
--hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \
|
--hash=sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8 \
|
||||||
--hash=sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3 \
|
--hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \
|
||||||
--hash=sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7 \
|
--hash=sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966 \
|
||||||
--hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \
|
--hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9 \
|
||||||
--hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \
|
--hash=sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3 \
|
||||||
--hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \
|
--hash=sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e \
|
||||||
--hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \
|
--hash=sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608
|
||||||
--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
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# requests
|
# requests
|
||||||
idna==3.18 \
|
idna==3.11 \
|
||||||
--hash=sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2 \
|
--hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \
|
||||||
--hash=sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848
|
--hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# requests
|
# requests
|
||||||
jc==1.25.7 \
|
jc==1.25.6 \
|
||||||
--hash=sha256:5ed6a0da915c931c04693cab806d5c31d9ef14ca998c6c32308460d37cb30baf \
|
--hash=sha256:27f58befc7ae0a4c63322926c5f1ec892e3eac4a065eff3b07cfe420a6924a07 \
|
||||||
--hash=sha256:63481e34d92715781e1b094eca7d76d122a14489463d45b7f3e8180f44ea3a10
|
--hash=sha256:7367b59e6e0da8babeede1e5b0da083f3c5aa6b6e585b4aed28dd7c4b2d76162
|
||||||
# via -r contrib/dev_reqs/requirements.in
|
# via -r contrib/dev_reqs/requirements.in
|
||||||
pygments==2.20.0 \
|
pygments==2.19.2 \
|
||||||
--hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
|
--hash=sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887 \
|
||||||
--hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
|
--hash=sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b
|
||||||
# via jc
|
# via jc
|
||||||
pyyaml==6.0.3 \
|
pyyaml==6.0.3 \
|
||||||
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
|
--hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
|
||||||
|
|
@ -230,9 +214,9 @@ pyyaml==6.0.3 \
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/dev_reqs/requirements.in
|
# -r contrib/dev_reqs/requirements.in
|
||||||
requests==2.34.2 \
|
requests==2.33.0 \
|
||||||
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
|
--hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \
|
||||||
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
|
--hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# -r contrib/dev_reqs/requirements.in
|
# -r contrib/dev_reqs/requirements.in
|
||||||
|
|
@ -240,13 +224,13 @@ ruamel-yaml==0.19.1 \
|
||||||
--hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93 \
|
--hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93 \
|
||||||
--hash=sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993
|
--hash=sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993
|
||||||
# via jc
|
# via jc
|
||||||
urllib3==2.7.0 \
|
urllib3==2.6.3 \
|
||||||
--hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \
|
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
|
||||||
--hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897
|
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
|
||||||
# via
|
# via
|
||||||
# -c src/backend/requirements.txt
|
# -c src/backend/requirements.txt
|
||||||
# requests
|
# requests
|
||||||
xmltodict==1.0.4 \
|
xmltodict==1.0.2 \
|
||||||
--hash=sha256:6d94c9f834dd9e44514162799d344d815a3a4faec913717a9ecbfa5be1bb8e61 \
|
--hash=sha256:54306780b7c2175a3967cad1db92f218207e5bc1aba697d887807c0fb68b7649 \
|
||||||
--hash=sha256:a4a00d300b0e1c59fc2bfccb53d7b2e88c32f200df138a0dd2229f842497026a
|
--hash=sha256:62d0fddb0dcbc9f642745d8bbf4d81fd17d6dfaec5a15b5c1876300aad92af0d
|
||||||
# via jc
|
# via jc
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ function detect_python() {
|
||||||
echo "${On_Red}"
|
echo "${On_Red}"
|
||||||
echo "# POI07| Python ${SETUP_PYTHON} not found - aborting!"
|
echo "# POI07| Python ${SETUP_PYTHON} not found - aborting!"
|
||||||
echo "# POI07| Please ensure python can be executed with the command '$SETUP_PYTHON' by the current user '$USER'."
|
echo "# POI07| Please ensure python can be executed with the command '$SETUP_PYTHON' by the current user '$USER'."
|
||||||
echo "# POI07| If you are using a different python version, please set the environment variable SETUP_PYTHON to the correct command - eg. 'python3.12'."
|
echo "# POI07| If you are using a different python version, please set the environment variable SETUP_PYTHON to the correct command - eg. 'python3.11'."
|
||||||
echo "${Color_Off}"
|
echo "${Color_Off}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ export DATA_DIR=${APP_HOME}/data
|
||||||
export SETUP_NGINX_FILE=${SETUP_NGINX_FILE:-/etc/nginx/sites-enabled/inventree.conf}
|
export SETUP_NGINX_FILE=${SETUP_NGINX_FILE:-/etc/nginx/sites-enabled/inventree.conf}
|
||||||
export SETUP_ADMIN_PASSWORD_FILE=${CONF_DIR}/admin_password.txt
|
export SETUP_ADMIN_PASSWORD_FILE=${CONF_DIR}/admin_password.txt
|
||||||
export SETUP_NO_CALLS=${SETUP_NO_CALLS:-false}
|
export SETUP_NO_CALLS=${SETUP_NO_CALLS:-false}
|
||||||
export SETUP_PYTHON=${SETUP_PYTHON:-python3.12}
|
export SETUP_PYTHON=${SETUP_PYTHON:-python3.11}
|
||||||
export SETUP_ADMIN_NOCREATION=${SETUP_ADMIN_NOCREATION:-false}
|
export SETUP_ADMIN_NOCREATION=${SETUP_ADMIN_NOCREATION:-false}
|
||||||
# SETUP_DEBUG can be set to get debug info
|
# SETUP_DEBUG can be set to get debug info
|
||||||
# SETUP_EXTRA_PIP can be set to install extra pip packages
|
# SETUP_EXTRA_PIP can be set to install extra pip packages
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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/me/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/token/`. No data are required, but a valid username / password combination must be supplied in the authentication headers.
|
||||||
|
|
||||||
!!! info "Credentials"
|
!!! info "Credentials"
|
||||||
Ensure that a valid username:password combination are supplied as basic authorization headers.
|
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).
|
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:
|
Once a user has *authenticated* via the API, a list of the available roles can be retrieved from:
|
||||||
|
|
||||||
`/api/user/me/roles/`
|
`/api/user/roles/`
|
||||||
|
|
||||||
For example, when accessing the API from a *superuser* account:
|
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
|
## API Version
|
||||||
|
|
||||||
This documentation is for API version: `459`
|
This documentation is for API version: `449`
|
||||||
|
|
||||||
!!! tip "API Schema History"
|
!!! 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/).
|
We track API schema changes, and provide a snapshot of each API schema version in the [API schema repository](https://github.com/inventree/schema/).
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,6 @@ Transfer the currently selected stock location into another location. Scanning a
|
||||||
|
|
||||||
Receive incoming purchase order items into the selected location. Scanning a *new* barcode which is associated with an item in an incoming purchase order will receive the item into the selected location.
|
Receive incoming purchase order items into the selected location. Scanning a *new* barcode which is associated with an item in an incoming purchase order will receive the item into the selected location.
|
||||||
|
|
||||||
*Note: Both purchase order number and supplier SKU's are required to be found on the barcode for this function to find the associated line item. Missing one will lead to an error.*
|
|
||||||
|
|
||||||
#### Scan Items Into Location
|
#### Scan Items Into Location
|
||||||
|
|
||||||
the *Scan Items Into Location* action allows you to scan items into the selected location. Scanning a valid barcode associated with a stock item (already in the database) will result in that item being transferred to the selected location.
|
the *Scan Items Into Location* action allows you to scan items into the selected location. Scanning a valid barcode associated with a stock item (already in the database) will result in that item being transferred to the selected location.
|
||||||
|
|
@ -107,8 +105,6 @@ From the [Purchase Order detail page](./po.md#purchase-order-detail) page, the f
|
||||||
|
|
||||||
Receive incoming purchase order items against the selected purchase order. Scanning a *new* barcode which is associated with an item in an incoming purchase order will receive the item into stock.
|
Receive incoming purchase order items against the selected purchase order. Scanning a *new* barcode which is associated with an item in an incoming purchase order will receive the item into stock.
|
||||||
|
|
||||||
*Note: supplier SKU's are required to be found on the barcode for this function to find the associated line item.*
|
|
||||||
|
|
||||||
### Sales Order Actions
|
### Sales Order Actions
|
||||||
|
|
||||||
The following barcode actions are available for [Sales Orders](./so.md):
|
The following barcode actions are available for [Sales Orders](./so.md):
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
---
|
|
||||||
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") }}
|
{{ image("app/po_lines.png", "Purchase order line items") }}
|
||||||
|
|
||||||
Select a particular line item to view the details for that line, and receive the line item into stock.
|
Long press on a particular line item to receive the item into stock.
|
||||||
|
|
||||||
### Stock Items
|
### Stock Items
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ The *Part Settings* view allows you to configure various options governing what
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Parameters | Enable display of parameters in the part detail view |
|
| Parameters | Enable display of part parameters in the part detail view |
|
||||||
| BOM | Enable bill of materials display 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 |
|
| Stock History | Enable display of stock history in the stock detail view |
|
||||||
| Test Results | Enable display of test results in the stock detail view |
|
| Test Results | Enable display of test results in the stock detail view |
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
|
@ -16,36 +16,3 @@ 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:
|
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") }}
|
{{ 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,70 +4,16 @@ title: Custom States
|
||||||
|
|
||||||
## Custom States
|
## Custom States
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
!!! info "Display Only"
|
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.
|
||||||
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.
|
|
||||||
|
|
||||||
### Example
|
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.
|
||||||
|
|
||||||
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.
|
Custom states can be defined for the following models:
|
||||||
|
|
||||||
You would create a custom state:
|
- [Stock Item](../stock/index.md)
|
||||||
|
- [Build Order](../manufacturing/build.md)
|
||||||
- **Logical key**: `OK` — the system treats the item as available stock
|
- [Purchase Order](../purchasing/purchase_order.md)
|
||||||
- **Label**: `Awaiting Inspection` — shown in the interface instead of "OK"
|
- [Sales Order](../sales/sales_order.md)
|
||||||
- **Colour**: `warning` — displayed in amber to draw attention
|
- [Return Order](../sales/return_order.md)
|
||||||
|
|
||||||
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 |
|
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
---
|
|
||||||
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.
|
|
||||||
|
|
@ -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:
|
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", "Parameters Example") }}
|
{{ image("concepts/parameter-tab.png", "Part Parameters Example") }}
|
||||||
|
|
||||||
## Parameter Templates
|
## Parameter Templates
|
||||||
|
|
||||||
|
|
@ -40,9 +40,9 @@ Parameter templates are created and edited via the [admin interface](../settings
|
||||||
To create a template:
|
To create a template:
|
||||||
|
|
||||||
- Navigate to the "Settings" page
|
- Navigate to the "Settings" page
|
||||||
- Click on the "Parameters" tab
|
- Click on the "Part Parameters" tab
|
||||||
- Click on the "New Parameter" button
|
- Click on the "New Parameter" button
|
||||||
- Fill out the `Create Parameter Template` form: `Name` (required) and `Units` (optional) fields
|
- Fill out the `Create Part Parameter Template` form: `Name` (required) and `Units` (optional) fields
|
||||||
- Click on the "Submit" button.
|
- Click on the "Submit" button.
|
||||||
|
|
||||||
An existing template can be edited by clicking on the "Edit" button associated with that template:
|
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.
|
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 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 Part Parameter` form will be displayed:
|
||||||
|
|
||||||
{{ image("part/create_part_parameter.png", "Create Parameter Form") }}
|
{{ image("part/create_part_parameter.png", "Create Part 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.
|
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
|
### Incompatible Units
|
||||||
|
|
||||||
If a parameter is created with a value which is incompatible with the units specified for the template, it will be rejected:
|
If a part 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") }}
|
{{ 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.
|
It is possible that plugins lock selection lists to ensure a known state.
|
||||||
|
|
||||||
|
|
||||||
Administration of lists can be done through the `Parameter` section in the [Admin Center](../settings/admin.md#admin-center) or via the API.
|
Administration of lists can be done through the Part Parameter section in the [Admin Center](../settings/admin.md#admin-center) or via the API.
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,3 @@ Currency exchange rates are updated periodically, using the configured currency
|
||||||
## Pricing Settings
|
## Pricing Settings
|
||||||
|
|
||||||
Refer to the [global settings](../settings/global.md#pricing-and-currency) documentation for more information on available currency settings.
|
Refer to the [global settings](../settings/global.md#pricing-and-currency) documentation for more information on available currency settings.
|
||||||
|
|
||||||
## Rendering Currencies in Reports
|
|
||||||
|
|
||||||
Currency values can be rendered in report templates using the [`render_currency`](../report/helpers.md#render_currency) helper function. This function formats a currency amount according to a locale, and supports currency conversion within the template.
|
|
||||||
|
|
||||||
See the [report helpers documentation](../report/helpers.md#currency-formatting) for full details and examples.
|
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
---
|
|
||||||
title: Tags
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tags
|
|
||||||
|
|
||||||
*Tags* are short, arbitrary labels that can be attached to InvenTree objects to group or classify them in flexible ways that don't require changes to the underlying data model. Unlike [parameters](./parameters.md), tags carry no typed value — they are simply names. A tag can be applied to objects of any supported model type, and tags are shared across the entire InvenTree instance.
|
|
||||||
|
|
||||||
!!! note "Shared Tag Namespace"
|
|
||||||
Tags are global: a tag named `prototype` applied to a Part and the same tag applied to a Build Order refer to the same underlying tag record. Renaming or deleting a tag affects every object to which it is attached.
|
|
||||||
|
|
||||||
### Supported Models
|
|
||||||
|
|
||||||
Tags can be attached to the following InvenTree objects:
|
|
||||||
|
|
||||||
- [Parts](../part/index.md)
|
|
||||||
- [Supplier Parts](../purchasing/supplier.md#supplier-parts)
|
|
||||||
- [Manufacturer Parts](../purchasing/manufacturer.md#manufacturer-parts)
|
|
||||||
- [Companies](./company.md)
|
|
||||||
- [Stock Items](../stock/index.md#stock-item)
|
|
||||||
- [Stock Locations](../stock/index.md#stock-location)
|
|
||||||
- [Build Orders](../manufacturing/build.md)
|
|
||||||
- [Purchase Orders](../purchasing/purchase_order.md)
|
|
||||||
- [Sales Orders](../sales/sales_order.md)
|
|
||||||
- [Return Orders](../sales/return_order.md)
|
|
||||||
- [Sales Order Shipments](../sales/sales_order.md#shipments)
|
|
||||||
|
|
||||||
## Managing Tags
|
|
||||||
|
|
||||||
### Adding and Removing Tags
|
|
||||||
|
|
||||||
Any object that supports tags will expose a *Tags* field in its detail and edit forms. Tags are entered as a comma-separated list of names and can be freely added or removed at any time. Tag names are case-insensitive — `Prototype`, `prototype`, and `PROTOTYPE` all refer to the same tag.
|
|
||||||
|
|
||||||
### Tag Names
|
|
||||||
|
|
||||||
Tag names must be unique within the InvenTree instance (case-insensitively). If you type a name that already exists under a different capitalisation, the existing tag is assigned rather than a new one created. Tag names may contain spaces, but leading and trailing whitespace is stripped automatically.
|
|
||||||
|
|
||||||
## Filtering by Tags
|
|
||||||
|
|
||||||
Tables that support tags can be filtered by one or more tag names. When multiple tags are specified, only objects that carry **all** of the specified tags are returned (AND logic).
|
|
||||||
|
|
||||||
For example, filtering a Parts table by the tags `approved` and `prototype` returns only parts tagged with both.
|
|
||||||
|
|
||||||
## API Access
|
|
||||||
|
|
||||||
### Tag Endpoints
|
|
||||||
|
|
||||||
The tag list is available at `/api/tag/`. Individual tags can be retrieved, updated, or deleted at `/api/tag/<id>/`.
|
|
||||||
|
|
||||||
The `model_type` query parameter narrows the tag list to tags currently applied to a specific model type:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/tag/?model_type=part
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tags on Model Endpoints
|
|
||||||
|
|
||||||
For models that support tags, the `tags` field is returned in the detail endpoint response as a list of tag name strings:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"pk": 42,
|
|
||||||
"name": "Widget",
|
|
||||||
"tags": ["approved", "prototype"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Tags can be updated via a `PATCH` or `POST` request by supplying a JSON-encoded list of tag name strings. The full list of tags replaces the previous set — omitting a tag removes it:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"tags": ["approved", "production"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Tags can also be used as a filter parameter on list endpoints. Supply a comma-separated list of tag names to the `tags` query parameter:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/part/?tags=approved,prototype
|
|
||||||
```
|
|
||||||
|
|
||||||
This returns only parts tagged with **both** `approved` and `prototype`.
|
|
||||||
|
|
@ -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
|
- Ensures consistent use of real units for your inventory management
|
||||||
- Convert between compatible units of measure from suppliers
|
- Convert between compatible units of measure from suppliers
|
||||||
- Enforce use of compatible units when creating parameters
|
- Enforce use of compatible units when creating part parameters
|
||||||
- Enable custom units as required
|
- Enable custom units as required
|
||||||
|
|
||||||
### Unit Conversion
|
### Unit Conversion
|
||||||
|
|
@ -61,7 +61,7 @@ The [supplier part](../part/index.md/#supplier-parts) model uses real-world unit
|
||||||
|
|
||||||
### Parameter
|
### Parameter
|
||||||
|
|
||||||
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
|
The [parameter template](../concepts/parameters.md#parameter-templates) model can specify units of measure, and part parameters can be specified against these templates with compatible units
|
||||||
|
|
||||||
## Custom Units
|
## Custom Units
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,18 +91,6 @@ Click on the navigation tree icon to expand the tree and view the available navi
|
||||||
|
|
||||||
{{ image("concepts/ui_navigation_tree.png", "Navigation Tree") }}
|
{{ image("concepts/ui_navigation_tree.png", "Navigation Tree") }}
|
||||||
|
|
||||||
#### Searching
|
|
||||||
|
|
||||||
The navigation tree includes a search bar at the top of the panel. Typing into the search bar filters the tree to show only entries that match the search query. When a search is active, all matching results are expanded and displayed in a flat list. Clearing the search field returns the tree to its normal browsing mode.
|
|
||||||
|
|
||||||
#### Highlight Selected Entry
|
|
||||||
|
|
||||||
The currently selected entry in the navigation tree is highlighted with a distinct background color, making it easy to identify the active page or section within the hierarchy.
|
|
||||||
|
|
||||||
#### Auto-Expand to Selected Entry
|
|
||||||
|
|
||||||
When the navigation tree is opened, it automatically expands to reveal the currently selected entry. All ancestor nodes in the hierarchy are expanded so the active entry is immediately visible, without requiring manual navigation through the tree.
|
|
||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
|
|
||||||
The dashboard provides a customizable landing page for users when they log in to the system. The dashboard can be configured to display a variety of widgets and information panels, providing users with quick access to important data and actions.
|
The dashboard provides a customizable landing page for users when they log in to the system. The dashboard can be configured to display a variety of widgets and information panels, providing users with quick access to important data and actions.
|
||||||
|
|
@ -170,41 +158,6 @@ Select the "table filters" button to open the filter selection menu
|
||||||
|
|
||||||
Table filters are saved across browser sessions, allowing users to maintain their preferred filter settings when returning to the particular table view.
|
Table filters are saved across browser sessions, allowing users to maintain their preferred filter settings when returning to the particular table view.
|
||||||
|
|
||||||
#### Column Filters
|
|
||||||
|
|
||||||
Many table columns expose an inline filter icon directly in the column header, providing a quick way to filter by that column without opening the full filter drawer. Columns that support filtering display a small filter icon alongside the column title. The icon is highlighted when a filter for that column is currently active, giving an at-a-glance indication of which columns have active filters.
|
|
||||||
|
|
||||||
Clicking the icon opens a compact popover anchored to the column header:
|
|
||||||
|
|
||||||
{{ image("concepts/ui_table_column_filter_popover.png", "Column Filter Popover") }}
|
|
||||||
|
|
||||||
**Single-filter columns** — for columns linked to one filter (e.g. *Active*, *Has IPN*, *Status*), selecting a value immediately applies the filter and the popover closes automatically.
|
|
||||||
|
|
||||||
**Range columns** — for columns that represent a range concept (e.g. *Start Date*, *Target Date*, *Creation Date*), the popover stays open and presents multiple controls — for example *before* and *after* date pickers — so both bounds can be set in a single interaction.
|
|
||||||
|
|
||||||
Once a filter is active, the popover shows a badge with the current value and a remove button (red ×) instead of the value picker. Clicking the × clears only that column's filter.
|
|
||||||
|
|
||||||
!!! info "Column filters and the filter drawer share the same state"
|
|
||||||
Filters applied via a column popover appear immediately in the filter drawer's active-filter list, and filters added through the drawer are reflected in the column icons. Clearing all filters from the drawer also removes any filters set via column popovers.
|
|
||||||
|
|
||||||
#### Saved Filter Groups
|
|
||||||
|
|
||||||
Frequently used combinations of filters can be saved as a named *filter group*, allowing them to be quickly recalled later without having to re-add each filter individually.
|
|
||||||
|
|
||||||
The **Saved Filter Groups** panel is displayed at the bottom of the filter drawer. When one or more filters are active, a **Save current filters** button is available. Clicking it opens an inline name input — enter a name and press Enter (or click the confirm icon) to save the group. Press Escape or click the cancel icon to discard.
|
|
||||||
|
|
||||||
{{ image("concepts/ui_table_filter_group.png", "Filter Groups") }}
|
|
||||||
|
|
||||||
Previously saved filter groups are listed in the panel. Each entry shows the group name alongside two actions:
|
|
||||||
|
|
||||||
- **Load** (green reload icon): Replaces the current active filters with the filters stored in that group. The table immediately re-fetches data using the restored filters.
|
|
||||||
- **Delete** (red × icon): Permanently removes the saved filter group.
|
|
||||||
|
|
||||||
Saved filter groups are stored in the browser's local storage and are specific to each table or calendar view, so groups saved for one view are not available in another. They persist across local browser sessions until explicitly deleted. Filter groups are not shared to other devices.
|
|
||||||
|
|
||||||
!!! info "Loading a filter group replaces active filters"
|
|
||||||
Loading a saved filter group replaces all currently active filters with those stored in the group. Any unsaved active filters will be overwritten.
|
|
||||||
|
|
||||||
### Data Sorting
|
### Data Sorting
|
||||||
|
|
||||||
Some table columns support data sorting, allowing the dataset to be sorted in ascending or descending order based on the values in that column. To sort a column, click on the column header. Clicking the column header again will toggle the sort order between ascending and descending. The current sort order is indicated by an arrow icon in the column header.
|
Some table columns support data sorting, allowing the dataset to be sorted in ascending or descending order based on the values in that column. To sort a column, click on the column header. Clicking the column header again will toggle the sort order between ascending and descending. The current sort order is indicated by an arrow icon in the column header.
|
||||||
|
|
@ -237,7 +190,7 @@ For tables which reference other objects within the system, clicking on a row wi
|
||||||
|
|
||||||
## Calendar Views
|
## Calendar Views
|
||||||
|
|
||||||
Some [table views](#table-views) associated with various order types can be switched to a calendar view, which provides a visual representation of data based on date fields. The calendar view allows users to easily see and interact with data that is organized by date, such as scheduled tasks, events, or deadlines.
|
Some [table views](#table-views) can be switched to a calendar view, which provides a visual representation of data based on date fields. The calendar view allows users to easily see and interact with data that is organized by date, such as scheduled tasks, events, or deadlines.
|
||||||
|
|
||||||
To switch to the "calendar view" (for a table which supports it), click on the "calendar view" button located above and to the right of the table view:
|
To switch to the "calendar view" (for a table which supports it), click on the "calendar view" button located above and to the right of the table view:
|
||||||
|
|
||||||
|
|
@ -247,10 +200,6 @@ This will display the data in a calendar format:
|
||||||
|
|
||||||
{{ image("concepts/ui_calendar_view.png", "Calendar View") }}
|
{{ image("concepts/ui_calendar_view.png", "Calendar View") }}
|
||||||
|
|
||||||
### Calendar Horizon
|
|
||||||
|
|
||||||
The calendar view provides a configurable "horizon" setting, which allows users to adjust the number of months displayed in the calendar view.
|
|
||||||
|
|
||||||
## Parametric Views
|
## Parametric Views
|
||||||
|
|
||||||
Some [table views](#table-views) can be switched to a parametric view, which provides a visual representation of data based on specific parameters or attributes. The parametric view allows users to easily see and interact with data that is organized by certain characteristics, such as categories, types, or other relevant attributes.
|
Some [table views](#table-views) can be switched to a parametric view, which provides a visual representation of data based on specific parameters or attributes. The parametric view allows users to easily see and interact with data that is organized by certain characteristics, such as categories, types, or other relevant attributes.
|
||||||
|
|
@ -275,8 +224,6 @@ Example: Creating a new part via the "Add Part" form:
|
||||||
|
|
||||||
{{ image("concepts/ui_form_add_part.png", "Add Part Button") }}
|
{{ 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
|
### Data Editing
|
||||||
|
|
||||||
Example: Editing an existing purchase order via the "Edit Purchase Order" form:
|
Example: Editing an existing purchase order via the "Edit Purchase Order" form:
|
||||||
|
|
@ -319,37 +266,10 @@ To remove a particular category of search results from the global search menu, c
|
||||||
|
|
||||||
## Spotlight
|
## Spotlight
|
||||||
|
|
||||||
The user interface features a "spotlight" search functionality, which provides a quick and efficient way to access common actions or navigate to specific pages within the InvenTree system. The spotlight search is designed to enhance user productivity by allowing users to quickly find and execute actions without needing to navigate through menus or remember specific page locations.
|
## Barcode Scanning
|
||||||
|
|
||||||
{{ image("concepts/ui_spotlight.png", "Spotlight Search") }}
|
## Notifications
|
||||||
|
|
||||||
### Open Spotlight
|
## Customization
|
||||||
|
|
||||||
To open the "spotlight" search, click on the "spotlight" icon located in the main menu at the top of the interface. This will open the spotlight search menu, allowing you to enter search queries and view available actions.
|
|
||||||
|
|
||||||
Alternatively, the spotlight search can be opened using the keyboard shortcut `Ctrl + K` (or `Cmd + K` on macOS), providing a quick and convenient way to access the spotlight functionality without needing to click on the menu icon.
|
|
||||||
|
|
||||||
### Disable Spotlight
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Copy Button
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
!!! 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
|
## User Permissions
|
||||||
|
|
||||||
Many aspects of the user interface are controlled by user permissions, which determine what actions and features are available to each user based on their assigned roles and permissions within the system. This allows for a highly customizable user experience, where different users can have access to different features and functionality based on their specific needs and responsibilities within the organization.
|
|
||||||
|
|
||||||
If a user does not have permission to access a particular feature or section of the system, that feature will be hidden from their view in the user interface. This helps to ensure that users only see the features and information that are relevant to their role, reducing clutter and improving usability.
|
|
||||||
|
|
||||||
## Language Support
|
|
||||||
|
|
||||||
The InvenTree user interface supports multiple languages, allowing users to interact with the system in their preferred language.
|
|
||||||
|
|
||||||
The default system language can be configured by the system administrator in the [server configuration options](../start/config.md#basic-options).
|
|
||||||
|
|
||||||
Additionally, users can select their preferred language in their [user settings](../settings/user.md), allowing them to override the system default language with their own choice. This provides a personalized experience for each user, ensuring that they can interact with the system in the language they are most comfortable with.
|
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,14 @@ invoke dev.setup-dev
|
||||||
InvenTree roughly follow the [GitLab flow](https://about.gitlab.com/topics/version-control/what-are-gitlab-flow-best-practices/) branching style, to allow simple management of multiple tagged releases, short-lived branches, and development on the main branch.
|
InvenTree roughly follow the [GitLab flow](https://about.gitlab.com/topics/version-control/what-are-gitlab-flow-best-practices/) branching style, to allow simple management of multiple tagged releases, short-lived branches, and development on the main branch.
|
||||||
|
|
||||||
There are nominally 5 active branches:
|
There are nominally 5 active branches:
|
||||||
|
- `master` - The main development branch
|
||||||
|
- `stable` - The latest stable release
|
||||||
|
- `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`)
|
||||||
|
|
||||||
| Branch | Description |
|
All other branches are removed periodically by maintainers or core team members. This includes old release branches.
|
||||||
| --- | --- |
|
Do not use them as base for feature development or forks as patches from them might not be accepted without rebasing.
|
||||||
| `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`) |
|
|
||||||
|
|
||||||
All other branches are removed periodically by maintainers or core team members. This includes old release branches. Do not use them as base for feature development or forks as patches from them might not be accepted without rebasing.
|
|
||||||
|
|
||||||
### Version Numbering
|
### Version Numbering
|
||||||
|
|
||||||
|
|
@ -118,23 +115,6 @@ 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
|
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
|
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
|
## API versioning
|
||||||
|
|
||||||
The [API version]({{ sourcefile("src/backend/InvenTree/InvenTree/api_version.py") }}) needs to be bumped every time when the API is changed.
|
The [API version]({{ sourcefile("src/backend/InvenTree/InvenTree/api_version.py") }}) needs to be bumped every time when the API is changed.
|
||||||
|
|
@ -164,7 +144,7 @@ The core software modules are targeting the following versions:
|
||||||
| Python | {{ config.extra.min_python_version }} | Minimum required version |
|
| Python | {{ config.extra.min_python_version }} | Minimum required version |
|
||||||
| Invoke | {{ config.extra.min_invoke_version }} | Minimum required version |
|
| Invoke | {{ config.extra.min_invoke_version }} | Minimum required version |
|
||||||
| Django | {{ config.extra.django_version }} | Pinned version |
|
| Django | {{ config.extra.django_version }} | Pinned version |
|
||||||
| Node | 24 | Only needed for frontend development |
|
| Node | 20 | Only needed for frontend development |
|
||||||
|
|
||||||
Any other software dependencies are handled by the project package config.
|
Any other software dependencies are handled by the project package config.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,7 @@ If you only need a superuser, run the `superuser` task. It should prompt you for
|
||||||
|
|
||||||
#### Run background workers
|
#### Run background workers
|
||||||
|
|
||||||
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 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 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
|
### Running InvenTree
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ If the installed version of invoke is too old, users may see error messages duri
|
||||||
Make sure you are running a stable or production release of InvenTree. The frontend panel is not included in development releases.
|
Make sure you are running a stable or production release of InvenTree. The frontend panel is not included in development releases.
|
||||||
More Information: [Error Codes - INVE-E1](./settings/error_codes.md#inve-e1)
|
More Information: [Error Codes - INVE-E1](./settings/error_codes.md#inve-e1)
|
||||||
|
|
||||||
### No module named xyz
|
### No module named <xxx>
|
||||||
|
|
||||||
During the install or update process, you may be presented with an error like:
|
During the install or update process, you may be presented with an error like:
|
||||||
|
|
||||||
|
|
@ -195,24 +195,3 @@ This means that either:
|
||||||
- The docker user does not have write permission to the specified directory
|
- 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.
|
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 screenshot 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.
|
|
||||||
|
|
||||||
## Expression tree is too large
|
|
||||||
|
|
||||||
If you are running a large InvenTree deployment on an SQLite database, you may encounter an error similar to:
|
|
||||||
|
|
||||||
```
|
|
||||||
Expression tree is too large (maximum depth 1000)
|
|
||||||
```
|
|
||||||
|
|
||||||
This is a [known limitation of SQLite](https://www.sqlite.org/limits.html) which can occur when performing complex queries on a large database. Due to [structural limitations](./start/processes.md#sqlite-limitations) of SQLite, it is recommended to use a more robust database backend such as PostgreSQL for larger deployments.
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from distutils.version import StrictVersion # type: ignore[import]
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from packaging.version import Version
|
|
||||||
|
|
||||||
here = Path(__file__).parent
|
here = Path(__file__).parent
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ def fetch_rtd_versions():
|
||||||
print('No RTD token found - skipping RTD version fetch')
|
print('No RTD token found - skipping RTD version fetch')
|
||||||
|
|
||||||
# Sort versions by version number
|
# Sort versions by version number
|
||||||
versions = sorted(versions, key=lambda x: Version(x['version']), reverse=True)
|
versions = sorted(versions, key=lambda x: StrictVersion(x['version']), reverse=True)
|
||||||
|
|
||||||
# Add "latest" version first
|
# Add "latest" version first
|
||||||
if not any(x['title'] == 'latest' for x in versions):
|
if not any(x['title'] == 'latest' for x in versions):
|
||||||
|
|
@ -280,27 +280,9 @@ def on_post_build(*args, **kwargs):
|
||||||
ignored_settings = {
|
ignored_settings = {
|
||||||
'global': ['SERVER_RESTART_REQUIRED'],
|
'global': ['SERVER_RESTART_REQUIRED'],
|
||||||
'user': ['LAST_USED_PRINTING_MACHINES'],
|
'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', 'config']:
|
for group in ['global', 'user']:
|
||||||
expected = expected_settings.get(group, {})
|
expected = expected_settings.get(group, {})
|
||||||
observed = observed_settings.get(group, {})
|
observed = observed_settings.get(group, {})
|
||||||
ignored = ignored_settings.get(group, [])
|
ignored = ignored_settings.get(group, [])
|
||||||
|
|
|
||||||
|
|
@ -80,16 +80,7 @@ The *Deallocate Stock* button can be used to remove all allocations of untracked
|
||||||
|
|
||||||
## Automatic Stock Allocation
|
## Automatic Stock Allocation
|
||||||
|
|
||||||
To speed up the allocation process, the *Auto Allocate* button can be used to automatically allocate stock items to the build.
|
To speed up the allocation process, the *Auto Allocate* button can be used to allocate untracked stock items to the build. Automatic allocation of stock items does not work in every situation, as a number of criteria must be met.
|
||||||
|
|
||||||
!!! info "Background Task"
|
|
||||||
Auto-allocation runs as a background task. The UI will display a progress indicator while the task is running.
|
|
||||||
|
|
||||||
#### Selecting Lines to Allocate
|
|
||||||
|
|
||||||
By default, auto-allocation processes **all eligible BOM line items** in the build order. To restrict allocation to a subset of lines, select the desired rows in the allocation table before pressing the button — the dialog will indicate how many lines are selected.
|
|
||||||
|
|
||||||
#### Auto Allocation Options
|
|
||||||
|
|
||||||
The *Automatic Allocation* dialog is presented as shown below:
|
The *Automatic Allocation* dialog is presented as shown below:
|
||||||
|
|
||||||
|
|
@ -99,16 +90,12 @@ The *Automatic Allocation* dialog is presented as shown below:
|
||||||
|
|
||||||
Select the master location where stock items are to be allocated from. Leave this input blank to allocate stock items from any available location.
|
Select the master location where stock items are to be allocated from. Leave this input blank to allocate stock items from any available location.
|
||||||
|
|
||||||
**Exclude Location**
|
|
||||||
|
|
||||||
Exclude stock from a specific location (and all of its sub-locations). Useful for reserving stock in a particular area.
|
|
||||||
|
|
||||||
**Interchangeable Stock**
|
**Interchangeable Stock**
|
||||||
|
|
||||||
Set this option to *True* to signal that stock items can be used interchangeably. This means that in the case where multiple stock items are available, the auto-allocation routine does not care which stock item it uses.
|
Set this option to *True* to signal that stock items can be used interchangeably. This means that in the case where multiple stock items are available, the auto-allocation routine does not care which stock item it uses.
|
||||||
|
|
||||||
!!! warning "Take Care"
|
!!! warning "Take Care"
|
||||||
If the *Interchangeable Stock* option is enabled, and there are multiple stock items available, the results of the automatic allocation algorithm may be somewhat unexpected.
|
If the *Interchangeable Stock* option is enabled, and there are multiple stock items available, the results of the automatic allocation algorithm may somewhat unexpected.
|
||||||
|
|
||||||
!!! info "Example"
|
!!! info "Example"
|
||||||
Let's say that we have 5 reels of our *C_100nF_0603* capacitor, each with 4,000 parts available. If we do not mind which of these reels the stock should be taken from, we enable the *Interchangeable Stock* option in the dialog above. In this case, the stock will be allocated from one of these reels, and eventually subtracted from stock when the build is completed.
|
Let's say that we have 5 reels of our *C_100nF_0603* capacitor, each with 4,000 parts available. If we do not mind which of these reels the stock should be taken from, we enable the *Interchangeable Stock* option in the dialog above. In this case, the stock will be allocated from one of these reels, and eventually subtracted from stock when the build is completed.
|
||||||
|
|
@ -117,39 +104,13 @@ Set this option to *True* to signal that stock items can be used interchangeably
|
||||||
|
|
||||||
Set this option to *True* to allow substitute parts (as specified by the BOM) to be allocated, if the primary parts are not available.
|
Set this option to *True* to allow substitute parts (as specified by the BOM) to be allocated, if the primary parts are not available.
|
||||||
|
|
||||||
**Optional Items**
|
|
||||||
|
|
||||||
Set this option to *True* to include optional BOM line items in the auto-allocation. By default, optional items are not automatically allocated.
|
|
||||||
|
|
||||||
**Item Type**
|
|
||||||
|
|
||||||
Controls which category of BOM line items is considered for auto-allocation:
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| Untracked Items | Only untracked (non-serialized) BOM lines are allocated *(default)* |
|
|
||||||
| Tracked Items | Only tracked BOM lines are allocated |
|
|
||||||
| All Items | Both tracked and untracked BOM lines are allocated |
|
|
||||||
|
|
||||||
**Stock Priority**
|
|
||||||
|
|
||||||
Controls the order in which matching stock items are consumed:
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| Oldest stock first (FIFO) | Stock items updated least recently are consumed first *(default)* |
|
|
||||||
| Newest stock first (LIFO) | Stock items updated most recently are consumed first |
|
|
||||||
| Smallest quantity first | Stock items with the lowest available quantity are consumed first |
|
|
||||||
| Largest quantity first | Stock items with the highest available quantity are consumed first |
|
|
||||||
| Soonest expiry date first | Stock items expiring earliest are consumed first; items with no expiry date are used last |
|
|
||||||
|
|
||||||
## Allocating Tracked Stock
|
## Allocating Tracked Stock
|
||||||
|
|
||||||
Allocation of tracked stock items is slightly more complex. Instead of being allocated against the *Build Order*, tracked stock items must be allocated against an individual *Build Output*.
|
Allocation of tracked stock items is slightly more complex. Instead of being allocated against the *Build Order*, tracked stock items must be allocated against an individual *Build Output*.
|
||||||
|
|
||||||
Allocating tracked stock items to particular build outputs is performed in the *Incomplete Outputs* tab:
|
Allocating tracked stock items to particular build outputs is performed in the *Pending Items* tab:
|
||||||
|
|
||||||
In the *Incomplete Outputs* tab, we can see that each build output has a stock allocation requirement which must be met before that build output can be completed:
|
In the *Pending Items* tab, we can see that each build output has a stock allocation requirement which must be met before that build output can be completed:
|
||||||
|
|
||||||
{{ image("build/build_allocate_tracked_parts.png", "Allocate tracked parts") }}
|
{{ image("build/build_allocate_tracked_parts.png", "Allocate tracked parts") }}
|
||||||
|
|
||||||
|
|
@ -165,12 +126,6 @@ Here we can see that the incomplete build outputs (serial numbers 15 and 14) now
|
||||||
!!! note "Example: Tracked Stock"
|
!!! note "Example: Tracked Stock"
|
||||||
Let's say we have 5 units of "Tracked Part" in stock - with 1 unit allocated to the build output. Once we complete the build output, there will be 4 units of "Tracked Part" in stock, with 1 unit being marked as "installed" within the assembled part
|
Let's say we have 5 units of "Tracked Part" in stock - with 1 unit allocated to the build output. Once we complete the build output, there will be 4 units of "Tracked Part" in stock, with 1 unit being marked as "installed" within the assembled part
|
||||||
|
|
||||||
### Automatic Stock Allocation
|
|
||||||
|
|
||||||
Tracked stock items can be automatically allocated to build outputs using the *Auto Allocate* button in the *Incomplete Outputs* tab. This will attempt to allocate tracked stock items to build outputs based on matching serial numbers.
|
|
||||||
|
|
||||||
For each build output, the auto-allocation routine will attempt to find a matching component item with the same serial number. If such a stock item is found, and it is available for use, it will be allocated to that build output.
|
|
||||||
|
|
||||||
## Consuming Stock
|
## Consuming Stock
|
||||||
|
|
||||||
Allocating stock items to a build order does not immediately remove them from stock. Instead, the stock items are marked as "allocated" against the build order, and are only removed from stock when they are "consumed" by the build order.
|
Allocating stock items to a build order does not immediately remove them from stock. Instead, the stock items are marked as "allocated" against the build order, and are only removed from stock when they are "consumed" by the build order.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ title: Bill of Materials
|
||||||
|
|
||||||
## Bill of Materials
|
## Bill of Materials
|
||||||
|
|
||||||
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 Bill of Materials (BOM) defines the list of component parts required to make an assembly, [create builds](./build.md) and allocate inventory.
|
||||||
|
|
||||||
A part which can be built from other sub components is called an *Assembly*.
|
A part which can be built from other sub components is called an *Assembly*.
|
||||||
|
|
||||||
|
|
@ -18,8 +18,7 @@ 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 |
|
| 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 |
|
| Reference | Optional reference field to describe the BOM Line Item, e.g. part designator |
|
||||||
| 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 |
|
||||||
| 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%) |
|
| 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 |
|
| 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. |
|
| Rounding Multiple | A value which indicates that the required quantity should be rounded up to the nearest multiple of this value. |
|
||||||
|
|
@ -28,18 +27,6 @@ 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" |
|
| Optional | A boolean field which indicates if this BOM Line Item is "optional" |
|
||||||
| Note | Optional note field for additional information
|
| 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
|
### 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.
|
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.
|
||||||
|
|
@ -50,14 +37,6 @@ 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*.
|
Further, in the [Build Order](./build.md) stock allocation table, we see that this line item cannot be allocated, as it is *consumable*.
|
||||||
|
|
||||||
Marking a BOM line item as consumable is a per-assembly override - it only affects how the part is treated within *this* particular BOM, and does not change the underlying part definition. A part can also be marked as consumable directly - refer to the [consumable parts documentation](../part/consumable.md) for more information on the distinction between the two, and reasons why a part may be marked as 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
|
### 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.
|
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.
|
||||||
|
|
@ -107,20 +86,13 @@ Note that inherited BOM Line Items only flow "downwards" in the variant inherita
|
||||||
!!! info "Editing Inherited Items"
|
!!! 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.
|
When editing an inherited BOM Line Item for a template part, the changes are automatically reflected in the BOM of any variant parts.
|
||||||
|
|
||||||
## BOM Editing
|
## BOM Creation
|
||||||
|
|
||||||
Bills of Material (BOMs) can be created manually, by adjusting individual line items, or by uploading (importing) an existing BOM file.
|
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
|
### 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](../concepts/data_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](../settings/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.
|
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.
|
||||||
|
|
||||||
|
|
@ -184,40 +156,89 @@ If the BOM requires revalidation, the status will be displayed as "Not Validated
|
||||||
|
|
||||||
{{ image("build/bom_invalid.png", "BOM Not Validated") }}
|
{{ image("build/bom_invalid.png", "BOM Not Validated") }}
|
||||||
|
|
||||||
## BOM Comparison
|
## Required Quantity Calculation
|
||||||
|
|
||||||
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.
|
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:
|
||||||
|
|
||||||
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:
|
### Base Quantity
|
||||||
|
|
||||||
{{ image("build/bom_compare_icon.png", "BOM Compare") }}
|
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.
|
||||||
|
|
||||||
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:
|
```
|
||||||
|
Required Quantity = Base Quantity * Number of Assemblies
|
||||||
|
```
|
||||||
|
|
||||||
{{ image("build/bom_compare.png", "BOM Compare") }}
|
### Attrition
|
||||||
|
|
||||||
### Display Mode
|
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%).
|
||||||
|
|
||||||
When comparing BOMs from two different assemblies, the user can select from the following view modes:
|
If a non-zero attrition percentage is specified, it is applied to the calculated `Required Quantity` value.
|
||||||
|
|
||||||
| View Mode | Description |
|
```
|
||||||
| --- | --- |
|
Required Quantity = Required Quantity * (1 + Attrition Percentage)
|
||||||
| *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. |
|
|
||||||
|
|
||||||
In each case, any differences between the BOM line items are highlighted in red.
|
!!! info "Optional"
|
||||||
|
The attrition percentage is optional. If not specified, it defaults to 0%.
|
||||||
|
|
||||||
## Replacing Components
|
### Setup 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.
|
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`.
|
||||||
|
|
||||||
To replace a component part within multiple assemblies:
|
```
|
||||||
|
Required Quantity = Required Quantity + Setup Quantity
|
||||||
|
```
|
||||||
|
|
||||||
- Navigate to the [Used In](../part/views.md#used-in) tab of the component part detail page
|
!!! info "Optional"
|
||||||
- Select the assemblies you wish to update by ticking the checkbox next to each assembly
|
The setup quantity is optional. If not specified, it defaults to 0.
|
||||||
- Click on the {{ icon("replace", color="blue", title="Replace Component") }} icon to open the "Replace Component" dialog
|
|
||||||
|
|
||||||
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:
|
### Rounding Multiple
|
||||||
|
|
||||||
{{ image("build/replace_component.png", "Replace Component") }}
|
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.
|
||||||
|
|
|
||||||
|
|
@ -304,11 +304,10 @@ The following [global settings](../settings/global.md) are available for adjusti
|
||||||
| Name | Description | Default | Units |
|
| Name | Description | Default | Units |
|
||||||
| ---- | ----------- | ------- | ----- |
|
| ---- | ----------- | ------- | ----- |
|
||||||
{{ globalsetting("BUILDORDER_REFERENCE_PATTERN") }}
|
{{ globalsetting("BUILDORDER_REFERENCE_PATTERN") }}
|
||||||
|
{{ globalsetting("BUILDORDER_EXTERNAL_BUILDS") }}
|
||||||
{{ globalsetting("BUILDORDER_REQUIRE_RESPONSIBLE") }}
|
{{ globalsetting("BUILDORDER_REQUIRE_RESPONSIBLE") }}
|
||||||
{{ globalsetting("BUILDORDER_REQUIRE_ACTIVE_PART") }}
|
{{ globalsetting("BUILDORDER_REQUIRE_ACTIVE_PART") }}
|
||||||
{{ globalsetting("BUILDORDER_REQUIRE_LOCKED_PART") }}
|
{{ globalsetting("BUILDORDER_REQUIRE_LOCKED_PART") }}
|
||||||
{{ globalsetting("BUILDORDER_REQUIRE_VALID_BOM") }}
|
{{ globalsetting("BUILDORDER_REQUIRE_VALID_BOM") }}
|
||||||
{{ globalsetting("BUILDORDER_REQUIRE_CLOSED_CHILDS") }}
|
{{ globalsetting("BUILDORDER_REQUIRE_CLOSED_CHILDS") }}
|
||||||
{{ globalsetting("PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS") }}
|
{{ globalsetting("PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS") }}
|
||||||
{{ globalsetting("BUILDORDER_EXTERNAL_BUILDS") }}
|
|
||||||
{{ globalsetting("BUILDORDER_EXTERNAL_REQUIRED") }}
|
|
||||||
|
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
---
|
|
||||||
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.
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
---
|
|
||||||
title: Consumable Parts
|
|
||||||
---
|
|
||||||
|
|
||||||
## Consumable Parts
|
|
||||||
|
|
||||||
A *Consumable* part is one which is generally used up, or expended, during a build or other process, rather than being tracked and allocated as a discrete item.
|
|
||||||
|
|
||||||
Consumable parts can be used to represent things such as:
|
|
||||||
|
|
||||||
- Glue or adhesive
|
|
||||||
- Solder
|
|
||||||
- Cleaning fluid
|
|
||||||
- Fasteners or other low-value hardware
|
|
||||||
- Other general workshop supplies
|
|
||||||
|
|
||||||
Marking a part as consumable is intended to help distinguish these types of supplies from other parts in the inventory, making it easier to filter and report on "real" stock items versus general consumables.
|
|
||||||
|
|
||||||
### Why Mark a Part as Consumable?
|
|
||||||
|
|
||||||
Not every item required to complete a build needs to be tracked and allocated with the same rigor as a high-value component. A part might be marked as consumable because it is:
|
|
||||||
|
|
||||||
- Low value, making the overhead of precise allocation not worth the effort
|
|
||||||
- Kept in abundant stock, such that running out is unlikely to be a concern
|
|
||||||
- Difficult to track in discrete units, such as a fluid or adhesive
|
|
||||||
- Not something the business needs (or wants) visibility into at the build order level
|
|
||||||
|
|
||||||
Marking a part as consumable communicates this intent clearly wherever the part is used, without needing to remember to configure each individual BOM line item.
|
|
||||||
|
|
||||||
### Stock Items
|
|
||||||
|
|
||||||
Consumable parts can have stock items associated with them, the same as any other part. Stock levels for consumable parts can be tracked and managed as normal.
|
|
||||||
|
|
||||||
### Bills of Material
|
|
||||||
|
|
||||||
Consumable parts can be added as a subcomponent to the [Bills of Material](../manufacturing/bom.md) for an assembled part, in the same way as any other component.
|
|
||||||
|
|
||||||
### Build Orders
|
|
||||||
|
|
||||||
Unlike a regular component, a consumable part is not allocated to (or consumed by) a [Build Order](../manufacturing/build.md). When a build order is completed, stock quantities for consumable parts are not adjusted.
|
|
||||||
|
|
||||||
If stock levels for a consumable part need to be updated to reflect usage during a build, this must be done manually.
|
|
||||||
|
|
||||||
### Consumable BOM Line Items
|
|
||||||
|
|
||||||
Separately from the *Consumable* flag on the part itself, an individual [BOM line item](../manufacturing/bom.md#consumable-bom-line-items) can also be marked as *consumable*.
|
|
||||||
|
|
||||||
This allows a part which is **not** normally marked as consumable to be treated as consumable *for the purposes of a specific BOM*. For example, a fastener which is usually tracked precisely in one assembly might be treated as consumable in another assembly, without needing to change the underlying part definition.
|
|
||||||
|
|
||||||
In other words:
|
|
||||||
|
|
||||||
- The part-level *Consumable* flag is a permanent property of the part, and applies everywhere that part is used.
|
|
||||||
- The BOM line item *Consumable* flag is a per-assembly override, and only affects how that part is treated within that particular BOM.
|
|
||||||
|
|
||||||
If a part is already marked as consumable, marking the corresponding BOM line item as consumable as well has no additional effect.
|
|
||||||
|
|
@ -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") }}
|
{{ image("part/part_create_form.png", "New part form") }}
|
||||||
|
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
Once the form is completed, the browser window is redirected to the new part detail page.
|
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.
|
Parts can be imported from an external file, by selecting the *Import from File* option.
|
||||||
|
|
||||||
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.
|
This action opens the [data import wizard](../settings/import.md), which steps the user through the process of importing parts from the selected file.
|
||||||
|
|
||||||
## Import from Supplier
|
## Import from Supplier
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,6 @@ 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.
|
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 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.
|
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.
|
||||||
|
|
@ -39,7 +27,7 @@ Clicking on the part name links to the [*Part Detail*](./views.md) view.
|
||||||
|
|
||||||
## Part Attributes
|
## 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
|
### Virtual
|
||||||
|
|
||||||
|
|
@ -87,19 +75,13 @@ 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.
|
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.
|
||||||
|
|
||||||
### Consumable
|
|
||||||
|
|
||||||
A *Consumable* part is one which is generally used up during a build or other process, rather than being tracked and allocated as a discrete item. Refer to the [consumable parts documentation](./consumable.md) for more information.
|
|
||||||
|
|
||||||
## Locked Parts
|
## 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:
|
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
|
- Locked parts cannot be deleted
|
||||||
- BOM items cannot be created, edited, or deleted when they are part of a locked assembly
|
- BOM items cannot be created, edited, or deleted when they are part of a locked assembly
|
||||||
- Parameters linked to a locked part cannot be created, edited or deleted
|
- Part 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
|
## Active Parts
|
||||||
|
|
||||||
|
|
@ -163,4 +145,4 @@ The [InvenTree mobile app](../app/part.md#part-image-view) allows part images to
|
||||||
|
|
||||||
## Part Import
|
## Part Import
|
||||||
|
|
||||||
*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.
|
*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.
|
||||||
|
|
|
||||||
|
|
@ -27,22 +27,24 @@ 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.
|
* **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.
|
* **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 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.
|
* **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
|
## Revision Settings
|
||||||
|
|
||||||
The following [global settings](../settings/global.md) are available to control the behavior of part revisions:
|
The following options are available to control the behavior of part revisions.
|
||||||
|
|
||||||
| Name | Description | Default | Units |
|
Note that these options can be changed in the InvenTree settings:
|
||||||
| ---- | ----------- | ------- | ----- |
|
|
||||||
{{ 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
|
## Create a Revision
|
||||||
|
|
||||||
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).
|
To create a new revision for a given part, navigate to the part detail page, and click on the "Revisions" tab.
|
||||||
|
|
||||||
Select the "Duplicate Part" action, to create a new copy of the selected part. This will open the "Duplicate Part" form:
|
Select the "Duplicate Part" action, to create a new copy of the selected part. This will open the "Duplicate Part" form:
|
||||||
|
|
||||||
|
|
@ -65,5 +67,4 @@ When multiple revisions exist for a particular part, you can navigate between re
|
||||||
|
|
||||||
{{ image("part/part_revision_select.png", "Select part revision") }}
|
{{ image("part/part_revision_select.png", "Select part revision") }}
|
||||||
|
|
||||||
!!! info "Revision Selector Visibility"
|
Note that this revision selector is only visible when multiple revisions exist for the part.
|
||||||
Note that this revision selector is only visible when multiple revisions exist for the part.
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ This plugin is a *mandatory* plugin, and is always enabled.
|
||||||
|
|
||||||
This plugin provides selection of the barcode format to use when generating labels. The format can be selected from:
|
This plugin provides selection of the barcode format to use when generating labels. The format can be selected from:
|
||||||
|
|
||||||
|
- **JSON Barcodes**: This format is used for generating barcodes in JSON format, which is a 'human readable' format.
|
||||||
- **Short Barcodes**: This format is used for generating barcodes in a short format, which is a more compact representation of the barcode data.
|
- **Short Barcodes**: This format is used for generating barcodes in a short format, which is a more compact representation of the barcode data.
|
||||||
- **JSON Barcodes**: This format is used for generating barcodes in JSON format, which is a more verbose format. This format is not recommended for use in production environments, as it can be more difficult to parse and may not be supported by all barcode scanners. It is supported for legacy purposes, and is not recommended for use in new deployments.
|
|
||||||
|
|
||||||
Additionally, if the "Short Barcodes" format is selected, the user can specify the prefix used for the barcode. This prefix is used to identify the barcode format, and can be set to any value. The default value is `INV-` - although can be changed.
|
Additionally, if the "Short Barcodes" format is selected, the user can specify the prefix used for the barcode. This prefix is used to identify the barcode format, and can be set to any value. The default value is `INV-` - although can be changed.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,10 +214,10 @@ The frontend code for your plugin is located in the `frontend/src` directory. Yo
|
||||||
|
|
||||||
Refer to the `./frontend/src/Panel.tsx` file as a starting point. This is where the custom panel for the part detail page is implemented. You can modify this file to change the content and behavior of the panel.
|
Refer to the `./frontend/src/Panel.tsx` file as a starting point. This is where the custom panel for the part detail page is implemented. You can modify this file to change the content and behavior of the panel.
|
||||||
|
|
||||||
While the `npm dev` server is running, any changes to the frontend are reflected in the browser using React Fast Refresh, allowing for rapid development without rebuilding the frontend with every change.
|
While the `npm dev` server is running, any changes you make to the frontend code will be automatically reloaded allowing for rapid development and testing of your plugin's frontend features. This avoids the need to rebuild the frontend code every time you make a change.
|
||||||
|
|
||||||
!!! info "Page Reload"
|
!!! info "Page Reload"
|
||||||
All exports in plugin modules that export React components must start with a capital letter. Otherwise, React Fast Refresh will fall back to a full page reload instead of performing a component-level update. Additionally, any render functions referenced from Python must also be capitalized.
|
Due to the way the InvenTree frontend is structured, you will need to manually refresh the page in your browser to see changes to the frontend code. The development server will automatically reload the frontend code, but the InvenTree server needs to be aware of the changes.
|
||||||
|
|
||||||
## Build Plugin
|
## Build Plugin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 import InvenTreePlugin, registry
|
||||||
from plugin.mixins import APICallMixin, SettingsMixin, ScheduleMixin, BarcodeMixin
|
from plugin.mixins import APICallMixin, SettingsMixin, ScheduleMixin, BarcodeMixin
|
||||||
```
|
```
|
||||||
- Deliver as a package (see [below](#packaging))
|
- Feliver 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
|
- 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.
|
- 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
|
- 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,9 +10,6 @@ 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.
|
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
|
## Frontend Architecture
|
||||||
|
|
||||||
When designing a frontend plugin component, it is important to have at least a basic understanding of the InvenTree frontend architecture.
|
When designing a frontend plugin component, it is important to have at least a basic understanding of the InvenTree frontend architecture.
|
||||||
|
|
@ -79,28 +76,20 @@ The following properties are available in the `context` object:
|
||||||
| Property | Description |
|
| Property | Description |
|
||||||
| -------- | ----------- |
|
| -------- | ----------- |
|
||||||
| `version` | An object containing the current InvenTree version information. |
|
| `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. |
|
| `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. |
|
| `host` | An object containing information about the current host (server) configuration. |
|
||||||
| `i18n` | An object containing internationalization (i18n) functions for translating text. |
|
| `i18n` | An object containing internationalization (i18n) functions for translating text. |
|
||||||
| `locale` | The current locale being used for the user interface. |
|
| `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. |
|
| `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. |
|
| `theme` | The current Mantine theme being used in the InvenTree web interface. |
|
||||||
| `colorScheme` | The current color scheme 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. |
|
| `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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,27 +8,3 @@ If this mixin is added to a plugin the directory the plugin class is defined in
|
||||||
|
|
||||||
!!! warning "Danger Zone"
|
!!! 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.
|
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.
|
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,14 @@ class InvenTreeBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||||
VERSION = "0.0.1"
|
VERSION = "0.0.1"
|
||||||
AUTHOR = "Michael"
|
AUTHOR = "Michael"
|
||||||
|
|
||||||
def scan(self, barcode_data, user, **kwargs):
|
def scan(self, barcode_data):
|
||||||
if barcode_data.startswith("PART-"):
|
if barcode_data.startswith("PART-"):
|
||||||
try:
|
try:
|
||||||
pk = int(barcode_data.split("PART-")[1])
|
pk = int(barcode_data.split("PART-")[1])
|
||||||
instance = Part.objects.get(pk=pk)
|
instance = Part.objects.get(pk=pk)
|
||||||
label = Part.barcode_model_type()
|
label = Part.barcode_model_type()
|
||||||
|
|
||||||
return {label: instance.format_matched_response(user=user)}
|
return {label: instance.format_matched_response()}
|
||||||
except Part.DoesNotExist:
|
except Part.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,6 @@ When a certain (server-side) event occurs, the background worker passes the even
|
||||||
|
|
||||||
{{ image("plugin/enable_events.png", "Enable event integration") }}
|
{{ 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
|
||||||
|
|
||||||
Events are passed through using a string identifier, e.g. `build.completed`
|
Events are passed through using a string identifier, e.g. `build.completed`
|
||||||
|
|
|
||||||
|
|
@ -4,57 +4,15 @@ title: Report Mixin
|
||||||
|
|
||||||
## ReportMixin
|
## ReportMixin
|
||||||
|
|
||||||
The `ReportMixin` class provides a plugin with the ability to extend the functionality of custom [report templates](../../report/report.md). A plugin which implements the ReportMixin mixin class can add custom context data to a report template for rendering, and can also receive a callback when a report is generated.
|
The `ReportMixin` class provides a plugin with the ability to extend the functionality of custom [report templates](../../report/report.md). A plugin which implements the ReportMixin mixin class can add custom context data to a report template for rendering.
|
||||||
|
|
||||||
### Add Report Context
|
### Add Report Context
|
||||||
|
|
||||||
A plugin which implements the ReportMixin mixin can define the `add_report_context` method, allowing custom context data to be added to a report template at time of printing.
|
A plugin which implements the ReportMixin mixin can define the `add_report_context` method, allowing custom context data to be added to a report template at time of printing.
|
||||||
|
|
||||||
This method is called each time a report is generated, and is passed the following arguments:
|
|
||||||
|
|
||||||
| Argument | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| `report_instance` | The report template instance which is being rendered |
|
|
||||||
| `model_instance` | The model instance against which the report is being generated |
|
|
||||||
| `user` | The user who initiated the report generation |
|
|
||||||
| `context` | The context dictionary, which can be modified in-place |
|
|
||||||
|
|
||||||
Any data added to the provided `context` dictionary is made available to the report template, and can be rendered using standard django template syntax:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def add_report_context(self, report_instance, model_instance, user, context):
|
|
||||||
"""Add extra context data to the report template."""
|
|
||||||
context['my_custom_data'] = self.calculate_custom_data(model_instance)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add Label Context
|
### Add Label Context
|
||||||
|
|
||||||
Similarly, the `add_label_context` method allows custom context data to be added to a label template at time of printing:
|
Additionally the `add_label_context` method, allowing custom context data to be added to a label template at time of printing.
|
||||||
|
|
||||||
| Argument | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| `label_instance` | The label template instance which is being rendered |
|
|
||||||
| `model_instance` | The model instance against which the label is being generated |
|
|
||||||
| `user` | The user who initiated the label generation |
|
|
||||||
| `context` | The context dictionary, which can be modified in-place |
|
|
||||||
|
|
||||||
### Report Callback
|
|
||||||
|
|
||||||
The `report_callback` method is called after a report has been generated, and allows the plugin to perform custom actions with the generated report - for example, forwarding the report to an external system, or performing custom post-processing.
|
|
||||||
|
|
||||||
| Argument | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| `template` | The report template instance which was used to generate the report |
|
|
||||||
| `instance` | The model instance against which the report was generated |
|
|
||||||
| `report` | The generated report (PDF file data) |
|
|
||||||
| `user` | The user who initiated the report generation |
|
|
||||||
|
|
||||||
```python
|
|
||||||
def report_callback(self, template, instance, report, user, **kwargs):
|
|
||||||
"""Custom callback function - called after a report is generated."""
|
|
||||||
# For example, forward the generated report to an external service
|
|
||||||
self.upload_to_external_service(report)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sample Plugin
|
### Sample Plugin
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,20 +183,6 @@ The `get_ui_template_previews` feature type can be used to provide custom templa
|
||||||
summary: False
|
summary: False
|
||||||
members: []
|
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
|
## 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:
|
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,10 +9,11 @@ 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:
|
For plugin testing the following environment variables must be set to True:
|
||||||
|
|
||||||
{{ configtable() }}
|
| Name | Function | Value |
|
||||||
{{ 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 |
|
| INVENTREE_PLUGINS_ENABLED | Enables the use of 3rd party plugins | True |
|
||||||
{{ configsetting("INVENTREE_PLUGIN_TESTING_SETUP") }} Enables the url mixin |
|
| 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 |
|
||||||
|
|
||||||
### Test Program
|
### Test Program
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ function AttachmentCarouselPanel({context}: {context: InvenTreePluginContext;})
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the function which is called by InvenTree to render the actual panel component
|
// This is the function which is called by InvenTree to render the actual panel component
|
||||||
export function RenderAttachmentCarouselPanel(context: InvenTreePluginContext) {
|
export function renderAttachmentCarouselPanel(context: InvenTreePluginContext) {
|
||||||
checkPluginVersion(context);
|
checkPluginVersion(context);
|
||||||
return <AttachmentCarouselPanel context={context} />;
|
return <AttachmentCarouselPanel context={context} />;
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +300,7 @@ Back to the walkthrough, open `core.py` in the `attachment_carousel` folder and
|
||||||
'title': 'Attachment Carousel',
|
'title': 'Attachment Carousel',
|
||||||
'description': 'Custom panel description',
|
'description': 'Custom panel description',
|
||||||
'icon': 'ti:carousel-horizontal:outline',
|
'icon': 'ti:carousel-horizontal:outline',
|
||||||
'source': self.plugin_static_file('Panel.js:RenderAttachmentCarouselPanel'),
|
'source': self.plugin_static_file('Panel.js:renderAttachmentCarouselPanel'),
|
||||||
'context': {
|
'context': {
|
||||||
# Provide additional context data to the panel
|
# Provide additional context data to the panel
|
||||||
'settings': self.get_settings_dict(),
|
'settings': self.get_settings_dict(),
|
||||||
|
|
@ -385,7 +385,7 @@ panels.append({
|
||||||
'description': 'Custom panel description',
|
'description': 'Custom panel description',
|
||||||
- 'icon': 'ti:mood-smile:outline',
|
- 'icon': 'ti:mood-smile:outline',
|
||||||
+ 'icon': 'ti:carousel-horizontal:outline',
|
+ 'icon': 'ti:carousel-horizontal:outline',
|
||||||
'source': self.plugin_static_file('Panel.js:RenderAttachmentCarouselPanel'),
|
'source': self.plugin_static_file('Panel.js:renderAttachmentCarouselPanel'),
|
||||||
'context': {
|
'context': {
|
||||||
# Provide additional context data to the panel
|
# Provide additional context data to the panel
|
||||||
'settings': self.get_settings_dict(),
|
'settings': self.get_settings_dict(),
|
||||||
|
|
@ -519,7 +519,7 @@ panels.append({
|
||||||
'title': 'Attachment Carousel',
|
'title': 'Attachment Carousel',
|
||||||
'description': 'Custom panel description',
|
'description': 'Custom panel description',
|
||||||
'icon': 'ti:carousel-horizontal:outline',
|
'icon': 'ti:carousel-horizontal:outline',
|
||||||
'source': self.plugin_static_file('Panel.js:RenderAttachmentCarouselPanel'),
|
'source': self.plugin_static_file('Panel.js:renderAttachmentCarouselPanel'),
|
||||||
'context': {
|
'context': {
|
||||||
# Provide additional context data to the panel
|
# Provide additional context data to the panel
|
||||||
'settings': self.get_settings_dict(),
|
'settings': self.get_settings_dict(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
title: Sponsored Resources
|
title: Resources InvenTree receives
|
||||||
---
|
---
|
||||||
|
|
||||||
The InvenTree project is grateful to receive resources from various vendors free of charge.
|
The InvenTree project is grateful to receive resources from various vendors free of charge.
|
||||||
|
|
@ -14,12 +14,9 @@ Individuals and companies can also support via [GitHub sponsors](https://github.
|
||||||
- [Codecov](https://codecov.io) - Code coverage as a service, used to track the code coverage of the various components
|
- [Codecov](https://codecov.io) - Code coverage as a service, used to track the code coverage of the various components
|
||||||
- [Netlify](https://www.netlify.com/) - Static site hosting provider, used to test deploy the frontend and website
|
- [Netlify](https://www.netlify.com/) - Static site hosting provider, used to test deploy the frontend and website
|
||||||
- [Depot](https://depot.dev/?utm_source=inventree) - Docker build accelerator, used to build the multi-arch images for the InvenTree docker image
|
- [Depot](https://depot.dev/?utm_source=inventree) - Docker build accelerator, used to build the multi-arch images for the InvenTree docker image
|
||||||
- [CodSpeed](https://codspeed.io/) - Performance testing platform, used to track the performance of a few performance benchmarks
|
|
||||||
- [Flakiness](https://flakiness.io/) - Frontend test flakiness detection, used to track the flakiness of the various test harnesses
|
|
||||||
|
|
||||||
## Past Supporters
|
## Past Supporters
|
||||||
|
|
||||||
Non comprehensive list of past supporters. The project stops consuming resources for various reasons, this does not mean they are not good resources.
|
Non comprehensive list of past supporters. The project stops consuming resources for various reasons, this does not mean they are not good resources.
|
||||||
|
|
||||||
- [Coveralls](https://coveralls.io/) - Code coverage as a service
|
- [Coveralls](https://coveralls.io/) - Code coverage as a service
|
||||||
- [Deepsource](https://deepsource.io/) - Code quality and security analysis
|
- [Deepsource](https://deepsource.io/) - Code quality and security analysis
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
---
|
|
||||||
title: Report Assets
|
|
||||||
---
|
|
||||||
|
|
||||||
## Report Assets
|
|
||||||
|
|
||||||
Users can upload asset files (e.g. images) which can be used when generating reports. For example, you may wish to generate a report with your company logo in the header.
|
|
||||||
|
|
||||||
Asset files are managed from the [Admin Center](../settings/admin.md#admin-center), via the *Report Assets* panel. Staff users can upload new asset files, and remove assets which are no longer required.
|
|
||||||
|
|
||||||
Asset files can be rendered directly into the template as follows
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
<!-- Need to include the report template tags at the start of the template file -->
|
|
||||||
{% load report %}
|
|
||||||
|
|
||||||
<!-- Simple stylesheet -->
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
.company-logo {
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<!-- Report template code here -->
|
|
||||||
|
|
||||||
<!-- Render an uploaded asset image -->
|
|
||||||
<img src="{% asset 'company_image.png' %}" class="company-logo">
|
|
||||||
|
|
||||||
<!-- ... -->
|
|
||||||
</body>
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! warning "Asset Naming"
|
|
||||||
If the requested asset name does not match the name of an uploaded asset, the template will continue without loading the image.
|
|
||||||
|
|
||||||
!!! info "Assets location"
|
|
||||||
Upload new assets via the *Report Assets* panel in the [Admin Center](../settings/admin.md#admin-center) to ensure they are uploaded to the correct location on the server.
|
|
||||||
|
|
||||||
There are various [helper functions](./helpers.md#report-assets) available to assist with embedding assets into templates.
|
|
||||||
|
|
@ -69,14 +69,12 @@ Templates (whether for generating [reports](./report.md) or [labels](./labels.md
|
||||||
|
|
||||||
| Model Type | Description |
|
| Model Type | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [company](#company) | A Company instance |
|
| company | A Company instance |
|
||||||
| [build](#build-order) | A [Build Order](../manufacturing/build.md) instance |
|
| [build](#build-order) | A [Build Order](../manufacturing/build.md) instance |
|
||||||
| [buildline](#build-line) | A [Build Order Line Item](../manufacturing/build.md) instance |
|
| [buildline](#build-line) | A [Build Order Line Item](../manufacturing/build.md) instance |
|
||||||
| [salesorder](#sales-order) | A [Sales Order](../sales/sales_order.md) instance |
|
| [salesorder](#sales-order) | A [Sales Order](../sales/sales_order.md) instance |
|
||||||
| [salesordershipment](#sales-order-shipment) | A [Sales Order Shipment](../sales/sales_order.md#sales-order-shipments) instance |
|
|
||||||
| [returnorder](#return-order) | A [Return Order](../sales/return_order.md) instance |
|
| [returnorder](#return-order) | A [Return Order](../sales/return_order.md) instance |
|
||||||
| [purchaseorder](#purchase-order) | A [Purchase Order](../purchasing/purchase_order.md) instance |
|
| [purchaseorder](#purchase-order) | A [Purchase Order](../purchasing/purchase_order.md) instance |
|
||||||
| [transferorder](#transfer-order) | A [Transfer Order](../stock/transfer_order.md) instance |
|
|
||||||
| [stockitem](#stock-item) | A [StockItem](../stock/index.md#stock-item) instance |
|
| [stockitem](#stock-item) | A [StockItem](../stock/index.md#stock-item) instance |
|
||||||
| [stocklocation](#stock-location) | A [StockLocation](../stock/index.md#stock-location) instance |
|
| [stocklocation](#stock-location) | A [StockLocation](../stock/index.md#stock-location) instance |
|
||||||
| [part](#part) | A [Part](../part/index.md) instance |
|
| [part](#part) | A [Part](../part/index.md) instance |
|
||||||
|
|
@ -143,16 +141,6 @@ When printing a report or label against a [PurchaseOrder](../purchasing/purchase
|
||||||
|
|
||||||
{{ report_context("models", "purchaseorder") }}
|
{{ report_context("models", "purchaseorder") }}
|
||||||
|
|
||||||
### Transfer Order
|
|
||||||
|
|
||||||
When printing a report or label against a [TransferOrder](../stock/transfer_order.md) object, the following context variables are available:
|
|
||||||
|
|
||||||
{{ report_context("models", "transferorder") }}
|
|
||||||
|
|
||||||
::: order.models.TransferOrder.report_context
|
|
||||||
options:
|
|
||||||
show_source: True
|
|
||||||
|
|
||||||
### Stock Item
|
### Stock Item
|
||||||
|
|
||||||
When printing a report or label against a [StockItem](../stock/index.md#stock-item) object, the following context variables are available:
|
When printing a report or label against a [StockItem](../stock/index.md#stock-item) object, the following context variables are available:
|
||||||
|
|
@ -185,7 +173,7 @@ When printing a report or label against a [Part](../part/index.md) object, the f
|
||||||
|
|
||||||
## Model Variables
|
## Model Variables
|
||||||
|
|
||||||
Additional to the context variables provided directly to each template, each model type has a number of attributes and methods which can be accessed via the template.
|
Additional to the context variables provided directly to each template, each model type has a number of attributes and methods which can be accessedd via the template.
|
||||||
|
|
||||||
For each model type, a subset of the most commonly used attributes are listed below. For a full list of attributes and methods, refer to the source code for the particular model type.
|
For each model type, a subset of the most commonly used attributes are listed below. For a full list of attributes and methods, refer to the source code for the particular model type.
|
||||||
|
|
||||||
|
|
@ -199,6 +187,7 @@ Each part object has access to a lot of context variables about the part. The fo
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| name | Brief name for this part |
|
| name | Brief name for this part |
|
||||||
| full_name | Full name for this part (including IPN, if not null and including variant, if not null) |
|
| full_name | Full name for this part (including IPN, if not null and including variant, if not null) |
|
||||||
|
| variant | Optional variant number for this part - Must be unique for the part name
|
||||||
| category | The [PartCategory](#part-category) object to which this part belongs
|
| category | The [PartCategory](#part-category) object to which this part belongs
|
||||||
| description | Longer form description of the part
|
| description | Longer form description of the part
|
||||||
| keywords | Optional keywords for improving part search results
|
| keywords | Optional keywords for improving part search results
|
||||||
|
|
@ -255,6 +244,7 @@ Each part object has access to a lot of context variables about the part. The fo
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| parent | Link to another [StockItem](#stock-item) from which this StockItem was created |
|
| parent | Link to another [StockItem](#stock-item) from which this StockItem was created |
|
||||||
|
| uid | Field containing a unique-id which is mapped to a third-party identifier (e.g. a barcode) |
|
||||||
| part | Link to the master abstract [Part](#part) that this [StockItem](#stock-item) is an instance of |
|
| part | Link to the master abstract [Part](#part) that this [StockItem](#stock-item) is an instance of |
|
||||||
| supplier_part | Link to a specific [SupplierPart](#supplierpart) (optional) |
|
| supplier_part | Link to a specific [SupplierPart](#supplierpart) (optional) |
|
||||||
| location | The [StockLocation](#stock-location) Where this [StockItem](#stock-item) is located |
|
| location | The [StockLocation](#stock-location) Where this [StockItem](#stock-item) is located |
|
||||||
|
|
@ -266,6 +256,7 @@ Each part object has access to a lot of context variables about the part. The fo
|
||||||
| expiry_date | Expiry date of the [StockItem](#stock-item) (optional) |
|
| expiry_date | Expiry date of the [StockItem](#stock-item) (optional) |
|
||||||
| stocktake_date | Date of last stocktake for this item |
|
| stocktake_date | Date of last stocktake for this item |
|
||||||
| stocktake_user | User that performed the most recent stocktake |
|
| stocktake_user | User that performed the most recent stocktake |
|
||||||
|
| review_needed | Flag if [StockItem](#stock-item) needs review |
|
||||||
| delete_on_deplete | If True, [StockItem](#stock-item) will be deleted when the stock level gets to zero |
|
| delete_on_deplete | If True, [StockItem](#stock-item) will be deleted when the stock level gets to zero |
|
||||||
| status | Status of this [StockItem](#stock-item) (ref: InvenTree.status_codes.StockStatus) |
|
| status | Status of this [StockItem](#stock-item) (ref: InvenTree.status_codes.StockStatus) |
|
||||||
| status_label | Textual representation of the status e.g. "OK" |
|
| status_label | Textual representation of the status e.g. "OK" |
|
||||||
|
|
@ -273,6 +264,7 @@ Each part object has access to a lot of context variables about the part. The fo
|
||||||
| build | Link to a Build (if this stock item was created from a build) |
|
| build | Link to a Build (if this stock item was created from a build) |
|
||||||
| is_building | Boolean field indicating if this stock item is currently being built (or is "in production") |
|
| is_building | Boolean field indicating if this stock item is currently being built (or is "in production") |
|
||||||
| purchase_order | Link to a [PurchaseOrder](#purchase-order) (if this stock item was created from a PurchaseOrder) |
|
| purchase_order | Link to a [PurchaseOrder](#purchase-order) (if this stock item was created from a PurchaseOrder) |
|
||||||
|
| infinite | If True this [StockItem](#stock-item) can never be exhausted |
|
||||||
| sales_order | Link to a [SalesOrder](#sales-order) object (if the StockItem has been assigned to a SalesOrder) |
|
| sales_order | Link to a [SalesOrder](#sales-order) object (if the StockItem has been assigned to a SalesOrder) |
|
||||||
| purchase_price | The unit purchase price for this [StockItem](#stock-item) - this is the unit price at time of purchase (if this item was purchased from an external supplier) |
|
| purchase_price | The unit purchase price for this [StockItem](#stock-item) - this is the unit price at time of purchase (if this item was purchased from an external supplier) |
|
||||||
| packaging | Description of how the StockItem is packaged (e.g. "reel", "loose", "tape" etc) |
|
| packaging | Description of how the StockItem is packaged (e.g. "reel", "loose", "tape" etc) |
|
||||||
|
|
@ -350,6 +342,7 @@ Each part object has access to a lot of context variables about the part. The fo
|
||||||
| note | Longer form note field |
|
| note | Longer form note field |
|
||||||
| base_cost | Base charge added to order independent of quantity e.g. "Reeling Fee" |
|
| base_cost | Base charge added to order independent of quantity e.g. "Reeling Fee" |
|
||||||
| multiple | Multiple that the part is provided in |
|
| multiple | Multiple that the part is provided in |
|
||||||
|
| lead_time | Supplier lead time |
|
||||||
| packaging | packaging that the part is supplied in, e.g. "Reel" |
|
| packaging | packaging that the part is supplied in, e.g. "Reel" |
|
||||||
| pretty_name | The IPN, supplier name, supplier SKU and (if not null) manufacturer string joined by `|`. Ex. `P00037 | Company | 000021` |
|
| pretty_name | The IPN, supplier name, supplier SKU and (if not null) manufacturer string joined by `|`. Ex. `P00037 | Company | 000021` |
|
||||||
| unit_pricing | The price for one unit. |
|
| unit_pricing | The price for one unit. |
|
||||||
|
|
@ -362,7 +355,7 @@ Each part object has access to a lot of context variables about the part. The fo
|
||||||
| Variable | Description |
|
| Variable | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| username | the username of the user |
|
| username | the username of the user |
|
||||||
| first_name | The first name of the user |
|
| fist_name | The first name of the user |
|
||||||
| last_name | The last name of the user |
|
| last_name | The last name of the user |
|
||||||
| email | The email address of the user |
|
| email | The email address of the user |
|
||||||
| pk | The primary key of the user |
|
| pk | The primary key of the user |
|
||||||
|
|
|
||||||
|
|
@ -173,144 +173,8 @@ 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.
|
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
|
## 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
|
### 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:
|
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:
|
||||||
|
|
@ -320,86 +184,15 @@ The helper function `format_number` allows for some common number formatting opt
|
||||||
show_docstring_description: false
|
show_docstring_description: false
|
||||||
show_source: False
|
show_source: False
|
||||||
|
|
||||||
#### Examples
|
#### Example
|
||||||
|
|
||||||
```html
|
```html
|
||||||
{% raw %}
|
{% raw %}
|
||||||
{% load report %}
|
{% load report %}
|
||||||
|
{% format_number 3.14159265359 decimal_places=5, leading=3 %}
|
||||||
<!-- Basic usage: strip trailing zeros -->
|
<!-- output: 0003.14159 -->
|
||||||
{% format_number 3.14159265359 decimal_places=5 %}
|
|
||||||
<!-- output: 3.14159 -->
|
|
||||||
|
|
||||||
<!-- Leading zeros with 'leading' option -->
|
|
||||||
{% format_number 3.14159265359 decimal_places=5 leading=3 %}
|
|
||||||
<!-- output: 003.14159 -->
|
|
||||||
|
|
||||||
<!-- Round to integer -->
|
|
||||||
{% format_number 3.14159265359 integer=True %}
|
{% format_number 3.14159265359 integer=True %}
|
||||||
<!-- output: 3 -->
|
<!-- output: 3 -->
|
||||||
|
|
||||||
<!-- Thousands separator -->
|
|
||||||
{% format_number 9988776.5 decimal_places=2 separator=True %}
|
|
||||||
<!-- output: 9,988,776.50 -->
|
|
||||||
|
|
||||||
<!-- Locale-aware formatting: decimal comma, dot thousands separator -->
|
|
||||||
{% format_number 9988776.5 decimal_places=2 separator=True locale='de-de' %}
|
|
||||||
<!-- output: 9.988.776,50 -->
|
|
||||||
|
|
||||||
<!-- Scale a value with a multiplier before formatting -->
|
|
||||||
{% format_number 0.175 multiplier=100 decimal_places=1 %}
|
|
||||||
<!-- output: 17.5 -->
|
|
||||||
|
|
||||||
<!-- Allow up to N significant decimal places, but suppress trailing zeros -->
|
|
||||||
{% format_number 1234.5 decimal_places=2 max_decimal_places=6 %}
|
|
||||||
<!-- output: 1234.5 -->
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom Format Strings
|
|
||||||
|
|
||||||
The `fmt` argument accepts a [Unicode number pattern](https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns) string (the same syntax used by [Babel](https://babel.pocoo.org/en/latest/numbers.html)). When `fmt` is provided it takes complete priority over the `decimal_places`, `max_decimal_places`, `leading`, and `separator` arguments — those arguments are silently ignored.
|
|
||||||
|
|
||||||
The `integer` and `multiplier` arguments **are** still applied to the number before the format string is used.
|
|
||||||
|
|
||||||
| Symbol | Meaning |
|
|
||||||
| --- | --- |
|
|
||||||
| `0` | Required digit — always rendered, even if zero |
|
|
||||||
| `#` | Optional digit — suppressed when not significant |
|
|
||||||
| `,` | Grouping separator (position defines group size) |
|
|
||||||
| `.` | Decimal separator |
|
|
||||||
|
|
||||||
Common patterns:
|
|
||||||
|
|
||||||
| Pattern | Example output |
|
|
||||||
| --- | --- |
|
|
||||||
| `0` | `1235` |
|
|
||||||
| `#,##0` | `1,235` |
|
|
||||||
| `0.00` | `1234.57` |
|
|
||||||
| `#,##0.00` | `1,234.57` |
|
|
||||||
| `000` | `007` |
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
{% load report %}
|
|
||||||
|
|
||||||
<!-- Two decimal places, no grouping -->
|
|
||||||
{% format_number 1234.5678 fmt='0.00' %}
|
|
||||||
<!-- output: 1234.57 -->
|
|
||||||
|
|
||||||
<!-- Two decimal places with thousands separator -->
|
|
||||||
{% format_number 1234.5678 fmt='#,##0.00' %}
|
|
||||||
<!-- output: 1,234.57 -->
|
|
||||||
|
|
||||||
<!-- Same pattern, German locale: dot thousands, comma decimal -->
|
|
||||||
{% format_number 1234.5678 fmt='#,##0.00' locale='de-de' %}
|
|
||||||
<!-- output: 1.234,57 -->
|
|
||||||
|
|
||||||
<!-- Integer with thousands separator, large number -->
|
|
||||||
{% format_number 9988776655.4321 fmt='#,##0' integer=True %}
|
|
||||||
<!-- output: 9,988,776,655 -->
|
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -407,25 +200,6 @@ Common patterns:
|
||||||
|
|
||||||
For rendering date and datetime information, the following helper functions are available:
|
For rendering date and datetime information, the following helper functions are available:
|
||||||
|
|
||||||
Both functions resolve their output using the following priority order:
|
|
||||||
|
|
||||||
1. **`fmt=` argument** — a [strftime format string](https://docs.python.org/3/library/datetime.html#format-codes). When provided, this takes full priority; `locale` and `date_format` are ignored.
|
|
||||||
2. **`locale=` argument** — when no `fmt` is given, Babel formats the value using the style set by `date_format` (default `medium`).
|
|
||||||
3. **Server `LANGUAGE_CODE`** — used as the locale when no `locale=` argument is supplied.
|
|
||||||
|
|
||||||
#### Date Format Styles
|
|
||||||
|
|
||||||
The `date_format` argument controls how Babel renders the date when locale-aware formatting is used. The four named styles are:
|
|
||||||
|
|
||||||
| Style | `format_date` example (en-us, 2025-01-12) | `format_datetime` example (en-us, 2025-01-12 14:30) |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `full` | `Sunday, January 12, 2025` | `Sunday, January 12, 2025 at 2:30:00 PM UTC` |
|
|
||||||
| `long` | `January 12, 2025` | `January 12, 2025 at 2:30:00 PM UTC` |
|
|
||||||
| `medium` *(default)* | `Jan 12, 2025` | `Jan 12, 2025, 2:30:00 PM` |
|
|
||||||
| `short` | `1/12/25` | `1/12/25, 2:30 PM` |
|
|
||||||
|
|
||||||
The exact output varies by locale — the table above uses `en-us`.
|
|
||||||
|
|
||||||
### format_date
|
### format_date
|
||||||
|
|
||||||
::: report.templatetags.report.format_date
|
::: report.templatetags.report.format_date
|
||||||
|
|
@ -433,35 +207,6 @@ The exact output varies by locale — the table above uses `en-us`.
|
||||||
show_docstring_description: false
|
show_docstring_description: false
|
||||||
show_source: False
|
show_source: False
|
||||||
|
|
||||||
#### Examples
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
{% load report %}
|
|
||||||
|
|
||||||
<!-- Default: medium style, locale from LANGUAGE_CODE -->
|
|
||||||
{% format_date my_date %}
|
|
||||||
<!-- output (en-us): Jan 12, 2025 -->
|
|
||||||
|
|
||||||
<!-- Explicit strftime format string — locale and date_format are ignored -->
|
|
||||||
{% format_date my_date fmt="%d/%m/%Y" %}
|
|
||||||
<!-- output: 12/01/2025 -->
|
|
||||||
|
|
||||||
<!-- Locale-aware, default medium style -->
|
|
||||||
{% format_date my_date locale='en-us' %}
|
|
||||||
<!-- output: Jan 12, 2025 -->
|
|
||||||
|
|
||||||
<!-- Short style -->
|
|
||||||
{% format_date my_date locale='en-us' date_format='short' %}
|
|
||||||
<!-- output: 1/12/25 -->
|
|
||||||
|
|
||||||
<!-- Full style -->
|
|
||||||
{% format_date my_date locale='en-us' date_format='full' %}
|
|
||||||
<!-- output: Sunday, January 12, 2025 -->
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
### format_datetime
|
### format_datetime
|
||||||
|
|
||||||
::: report.templatetags.report.format_datetime
|
::: report.templatetags.report.format_datetime
|
||||||
|
|
@ -469,31 +214,20 @@ The exact output varies by locale — the table above uses `en-us`.
|
||||||
show_docstring_description: false
|
show_docstring_description: false
|
||||||
show_source: False
|
show_source: False
|
||||||
|
|
||||||
#### Examples
|
### Date Formatting
|
||||||
|
|
||||||
|
If not specified, these methods return a result which uses ISO formatting. Refer to the [datetime format codes](https://docs.python.org/3/library/datetime.html#format-codes) for more information! |
|
||||||
|
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
A simple example of using the date formatting helper functions:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
{% raw %}
|
{% raw %}
|
||||||
{% load report %}
|
{% load report %}
|
||||||
|
Date: {% format_date my_date timezone="Australia/Sydney" %}
|
||||||
<!-- Default: medium style, locale from LANGUAGE_CODE -->
|
Datetime: {% format_datetime my_datetime format="%d-%m-%Y %H:%M%S" %}
|
||||||
{% format_datetime my_datetime %}
|
|
||||||
<!-- output (en-us): Jan 12, 2025, 2:30:00 PM -->
|
|
||||||
|
|
||||||
<!-- Explicit strftime format — locale and date_format are ignored -->
|
|
||||||
{% format_datetime my_datetime fmt="%d-%m-%Y %H:%M" %}
|
|
||||||
<!-- output: 12-01-2025 14:30 -->
|
|
||||||
|
|
||||||
<!-- Locale-aware, default medium style -->
|
|
||||||
{% format_datetime my_datetime locale='en-us' %}
|
|
||||||
<!-- output: Jan 12, 2025, 2:30:00 PM -->
|
|
||||||
|
|
||||||
<!-- Short style -->
|
|
||||||
{% format_datetime my_datetime locale='de-de' date_format='short' %}
|
|
||||||
<!-- output: 12.01.25, 14:30 -->
|
|
||||||
|
|
||||||
<!-- Convert to a specific timezone before formatting -->
|
|
||||||
{% format_datetime my_datetime timezone="Australia/Sydney" locale='en-au' %}
|
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -503,115 +237,11 @@ The exact output varies by locale — the table above uses `en-us`.
|
||||||
|
|
||||||
The helper function `render_currency` allows for simple rendering of currency data. This function can also convert the specified amount of currency into a different target currency:
|
The helper function `render_currency` allows for simple rendering of currency data. This function can also convert the specified amount of currency into a different target currency:
|
||||||
|
|
||||||
::: report.templatetags.report.render_currency
|
::: InvenTree.helpers_model.render_currency
|
||||||
options:
|
options:
|
||||||
show_docstring_description: false
|
show_docstring_description: false
|
||||||
show_source: False
|
show_source: False
|
||||||
|
|
||||||
#### Decimal Places
|
|
||||||
|
|
||||||
When no decimal place arguments are provided, the locale/currency standard is used (e.g. 2 places for USD, 0 for JPY).
|
|
||||||
|
|
||||||
`decimal_places` and `max_decimal_places` work the same way as in [`format_number`](#format_number):
|
|
||||||
|
|
||||||
| Argument | Effect |
|
|
||||||
| --- | --- |
|
|
||||||
| `decimal_places=N` | Forces exactly N decimal digits (zero-padded) |
|
|
||||||
| `max_decimal_places=M` | Allows up to M decimal digits, suppressing trailing zeros beyond `decimal_places` |
|
|
||||||
| Both set | Forced minimum of `decimal_places`, optional up to `max_decimal_places` |
|
|
||||||
| Neither set | Locale/currency default (e.g. 2 for USD) |
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
{% load report %}
|
|
||||||
|
|
||||||
<!-- locale default for USD: 2 decimal places -->
|
|
||||||
{% render_currency order.total_price currency='USD' %}
|
|
||||||
<!-- output: $1,234.56 -->
|
|
||||||
|
|
||||||
<!-- force 3 decimal places -->
|
|
||||||
{% render_currency order.total_price currency='USD' decimal_places=3 %}
|
|
||||||
<!-- output: $1,234.560 -->
|
|
||||||
|
|
||||||
<!-- at least 2, up to 4 — trailing zeros beyond the value are suppressed -->
|
|
||||||
{% render_currency order.total_price currency='USD' decimal_places=2 max_decimal_places=4 %}
|
|
||||||
<!-- output: $1,234.5600 or $1,234.56 depending on the value -->
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Locale and Symbol Rendering
|
|
||||||
|
|
||||||
The locale controls how the currency symbol and separators are rendered. For example, `USD 1234.56` with various locales:
|
|
||||||
|
|
||||||
| Locale | Output |
|
|
||||||
| --- | --- |
|
|
||||||
| `en-us` | `$1,234.56` |
|
|
||||||
| `en-gb` | `US$1,234.56` |
|
|
||||||
| `en-au` | `USD1,234.56` |
|
|
||||||
| `de-de` | `1.234,56 $` |
|
|
||||||
|
|
||||||
The locale is resolved in the following priority order:
|
|
||||||
|
|
||||||
1. **Explicit `locale=` argument** — highest priority, always wins
|
|
||||||
2. **Server `LANGUAGE_CODE`** — fallback
|
|
||||||
|
|
||||||
#### Leading Digits
|
|
||||||
|
|
||||||
The `leading` argument specifies the minimum number of digits to render before the decimal point (zero-padded). This works identically to `leading` in [`format_number`](#format_number):
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
{% load report %}
|
|
||||||
|
|
||||||
<!-- default: no padding -->
|
|
||||||
{% render_currency order.total_price currency='USD' %}
|
|
||||||
<!-- output: $1.23 -->
|
|
||||||
|
|
||||||
<!-- force at least 4 integer digits -->
|
|
||||||
{% render_currency order.total_price currency='USD' leading=4 %}
|
|
||||||
<!-- output: $0,001.23 -->
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom Format Strings
|
|
||||||
|
|
||||||
The `fmt` argument accepts a [Unicode number pattern](https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns) string (same syntax as [`format_number`](#custom-format-strings)). **When `fmt` is provided, it takes complete priority over `decimal_places`, `max_decimal_places`, and `leading`** — those arguments are ignored.
|
|
||||||
|
|
||||||
The `locale`, `currency`, `multiplier`, and `include_symbol` arguments are still applied when `fmt` is set.
|
|
||||||
|
|
||||||
To include the currency symbol in a `fmt` pattern, use the `¤` placeholder. Without it, no symbol appears regardless of `include_symbol`.
|
|
||||||
|
|
||||||
| Pattern | Example output (en-us, USD) |
|
|
||||||
| --- | --- |
|
|
||||||
| `#,##0.00` | `1,234.56` (no symbol) |
|
|
||||||
| `¤#,##0.00` | `$1,234.56` |
|
|
||||||
| `¤#,##0.0000` | `$1,234.5600` |
|
|
||||||
| `¤ #,##0.00` | `$ 1,234.56` |
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
{% load report %}
|
|
||||||
|
|
||||||
<!-- No symbol (no ¤ in pattern) -->
|
|
||||||
{% render_currency order.total_price currency='USD' fmt='#,##0.00' %}
|
|
||||||
<!-- output: 1,234.56 -->
|
|
||||||
|
|
||||||
<!-- Symbol via ¤ placeholder -->
|
|
||||||
{% render_currency order.total_price currency='USD' fmt='¤#,##0.0000' locale='en-us' %}
|
|
||||||
<!-- output: $1,234.5600 -->
|
|
||||||
|
|
||||||
<!-- fmt + locale: de-de separators -->
|
|
||||||
{% render_currency order.total_price currency='USD' fmt='#,##0.00' locale='de-de' %}
|
|
||||||
<!-- output: 1.234,56 -->
|
|
||||||
|
|
||||||
<!-- fmt takes priority — decimal_places=2 is ignored -->
|
|
||||||
{% render_currency order.total_price currency='USD' fmt='0.0000' decimal_places=2 %}
|
|
||||||
<!-- output: 1234.5600 -->
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|
@ -626,12 +256,8 @@ To include the currency symbol in a `fmt` pattern, use the `¤` placeholder. Wit
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!-- Force 2 decimal places, convert to NZD -->
|
|
||||||
Total Price: {% render_currency order.total_price currency='NZD' decimal_places=2 %}
|
Total Price: {% render_currency order.total_price currency='NZD' decimal_places=2 %}
|
||||||
|
|
||||||
<!-- US-style symbol, regardless of server locale -->
|
|
||||||
Total Price: {% render_currency order.total_price currency='USD' locale='en-us' %}
|
|
||||||
|
|
||||||
{% endraw %}
|
{% endraw %}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -907,7 +533,7 @@ If you have a custom logo, but explicitly wish to load the InvenTree logo itself
|
||||||
|
|
||||||
## Report Assets
|
## Report Assets
|
||||||
|
|
||||||
[Report Assets](./assets.md) are files specifically uploaded by the user for inclusion in generated reports and labels.
|
[Report Assets](./index.md#report-assets) are files specifically uploaded by the user for inclusion in generated reports and labels.
|
||||||
|
|
||||||
You can add asset images to the reports and labels by using the `{% raw %}{% asset ... %}{% endraw %}` template tag:
|
You can add asset images to the reports and labels by using the `{% raw %}{% asset ... %}{% endraw %}` template tag:
|
||||||
|
|
||||||
|
|
@ -921,18 +547,14 @@ You can add asset images to the reports and labels by using the `{% raw %}{% ass
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
### 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
|
::: report.templatetags.report.parameter
|
||||||
options:
|
options:
|
||||||
show_docstring_description: false
|
show_docstring_description: false
|
||||||
show_source: 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:
|
The following example assumes that you have a report or label which contains a valid [Part](../part/index.md) instance:
|
||||||
|
|
||||||
|
|
@ -958,27 +580,6 @@ A [Parameter](../concepts/parameters.md) has the following available attributes:
|
||||||
| Units | The *units* of the parameter (e.g. "km") |
|
| Units | The *units* of the parameter (e.g. "km") |
|
||||||
| Template | A reference to a [ParameterTemplate](../concepts/parameters.md#parameter-templates) |
|
| 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
|
## Rendering Markdown
|
||||||
|
|
||||||
Some data fields (such as the *Notes* field available on many internal database models) support [markdown formatting](https://en.wikipedia.org/wiki/Markdown). To render markdown content in a custom report, there are template filters made available through the [django-markdownify](https://github.com/erwinmatijsen/django-markdownify) library. This library provides functionality for converting markdown content to HTML representation, allowing it to be then rendered to PDF by the InvenTree report generation pipeline.
|
Some data fields (such as 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.
|
||||||
|
|
|
||||||
|
|
@ -53,43 +53,29 @@ To read more about the capabilities of the report templating engine, and how to
|
||||||
|
|
||||||
## Creating Templates
|
## Creating Templates
|
||||||
|
|
||||||
Report and label templates are managed from the [Admin Center](../settings/admin.md#admin-center), which provides dedicated panels (under the *Reporting* group) for each template type:
|
Report and label templates can be created (and edited) via the [admin interface](../settings/admin.md), under the *Report* section.
|
||||||
|
|
||||||
- **Label Templates** - Create and edit [label templates](./labels.md)
|
Select the type of template you are wanting to create (a *Report Template* or *Label Template*) and press the *Add* button in the top right corner:
|
||||||
- **Report Templates** - Create and edit [report templates](./report.md)
|
|
||||||
- **Report Snippets** - Manage reusable [snippet](#report-snippets) files
|
|
||||||
- **Report Assets** - Manage uploaded [asset](#report-assets) files
|
|
||||||
|
|
||||||
Label and report templates are created and edited using the built-in [template editor](./template_editor.md), which allows templates to be written directly within the browser, with a live preview of the rendered output.
|
{{ image("report/report_template_admin.png", "Report template admin") }}
|
||||||
|
|
||||||
!!! tip "Staff Access Only"
|
!!! tip "Staff Access Only"
|
||||||
Only users with staff access can create, upload or edit templates, snippets and assets.
|
Only users with staff access can upload or edit report template files.
|
||||||
|
|
||||||
!!! info "Backend Admin Interface"
|
!!! info "Editing Reports"
|
||||||
Templates can also be managed at a lower level via the [backend admin interface](../settings/admin.md#backend-admin-interface), under the *Report* section. This is recommended for advanced users only.
|
Existing reports can be edited from the admin interface, in the same location as described above. To change the contents of the template, re-upload a template file, to override the existing template data.
|
||||||
|
|
||||||
|
!!! tip "Template Editor"
|
||||||
|
InvenTree also provides a powerful [template editor](./template_editor.md) which allows for the creation and editing of report templates directly within the browser.
|
||||||
|
|
||||||
### Name and Description
|
### Name and Description
|
||||||
|
|
||||||
Each report template requires a name and description, which identify and describe the report template.
|
Each report template requires a name and description, which identify and describe the report template.
|
||||||
|
|
||||||
### Revision
|
|
||||||
|
|
||||||
Each template has a revision number, which is automatically incremented each time the template is updated. This provides a simple mechanism for tracking changes to a template over time. The revision number is read-only, and cannot be edited directly.
|
|
||||||
|
|
||||||
!!! info "Template Revision Context"
|
|
||||||
The revision number of the template is made available when rendering, via the `template_revision` [context variable](./context_variables.md#global-context).
|
|
||||||
|
|
||||||
### Enabled Status
|
### Enabled Status
|
||||||
|
|
||||||
Boolean field which determines if the specific report template is enabled, and available for use. Reports can be disabled to remove them from the list of available templates, but without deleting them from the database.
|
Boolean field which determines if the specific report template is enabled, and available for use. Reports can be disabled to remove them from the list of available templates, but without deleting them from the database.
|
||||||
|
|
||||||
### Attach to Model
|
|
||||||
|
|
||||||
If the *Attach to Model on Print* option is enabled, a copy of the generated report is automatically saved as a file attachment against the item (model instance) for which it was generated, each time the template is printed.
|
|
||||||
|
|
||||||
!!! warning "Attachment Support"
|
|
||||||
The report output is only attached if the target model type supports file attachments.
|
|
||||||
|
|
||||||
### Filename Pattern
|
### Filename Pattern
|
||||||
|
|
||||||
The filename pattern used to generate the output `.pdf` file. Defaults to "report.pdf".
|
The filename pattern used to generate the output `.pdf` file. Defaults to "report.pdf".
|
||||||
|
|
@ -161,44 +147,86 @@ Setting the *Debug Mode* option renders the template as raw HTML instead of PDF,
|
||||||
|
|
||||||
## Report Assets
|
## Report Assets
|
||||||
|
|
||||||
User can upload asset files (e.g. images) which can be used when generating reports. For example, you may wish to generate a report with your company logo in the header.
|
User can upload asset files (e.g. images) which can be used when generating reports. For example, you may wish to generate a report with your company logo in the header. Asset files are uploaded via the admin interface.
|
||||||
|
|
||||||
|
Asset files can be rendered directly into the template as follows
|
||||||
|
|
||||||
|
```html
|
||||||
|
{% raw %}
|
||||||
|
<!-- Need to include the report template tags at the start of the template file -->
|
||||||
|
{% load report %}
|
||||||
|
|
||||||
|
<!-- Simple stylesheet -->
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
.company-logo {
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<!-- Report template code here -->
|
||||||
|
|
||||||
|
<!-- Render an uploaded asset image -->
|
||||||
|
<img src="{% asset 'company_image.png' %}" class="company-logo">
|
||||||
|
|
||||||
|
<!-- ... -->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
{% endraw %}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "Asset Naming"
|
||||||
|
If the requested asset name does not match the name of an uploaded asset, the template will continue without loading the image.
|
||||||
|
|
||||||
|
!!! info "Assets location"
|
||||||
|
Upload new assets via the [admin interface](../settings/admin.md) to ensure they are uploaded to the correct location on the server.
|
||||||
|
|
||||||
Refer to the [report assets](./assets.md) documentation for further information.
|
|
||||||
|
|
||||||
## Report Snippets
|
## Report Snippets
|
||||||
|
|
||||||
InvenTree provides report "snippets" - reusable template files which cannot be rendered by themselves, but can be included in other templates.
|
A powerful feature provided by the django / WeasyPrint templating framework is the ability to include external template files. This allows commonly used template features to be broken out into separate files and reused across multiple templates.
|
||||||
|
|
||||||
Refer to the [report snippets](./snippets.md) documentation for further information.
|
To support this, InvenTree provides report "snippets" - short (or not so short) template files which cannot be rendered by themselves, but can be called from other templates.
|
||||||
|
|
||||||
## Security
|
Similar to assets files, snippet template files are uploaded via the admin interface.
|
||||||
|
|
||||||
Report templates are powerful by design — they have access to the full Django template language and to model data across the InvenTree database. For this reason, **template upload is restricted to staff users only**.
|
Snippets are included in a template as follows:
|
||||||
|
|
||||||
### URL Fetching
|
```
|
||||||
|
{% raw %}{% include 'snippets/<snippet_name.html>' %}{% endraw %}
|
||||||
|
```
|
||||||
|
|
||||||
When WeasyPrint renders a template to PDF it can make outbound requests to load images, stylesheets, and fonts referenced in the HTML. InvenTree restricts this through a custom URL fetcher with the following rules:
|
For example, consider a custom stocktake report for a particular stock location, where we wish to render a table with a row for each item in that location.
|
||||||
|
|
||||||
| URL Type | Behavior |
|
```html
|
||||||
|---|---|
|
{% raw %}
|
||||||
| `data:` URIs | Always permitted — self-contained, no network access |
|
|
||||||
| `file://` | Always blocked — assets and images must be inlined as `data:` URIs before reaching WeasyPrint |
|
|
||||||
| `http` / `https` | Disabled by default, but can be enabled - see *Remote URL Fetching* below |
|
|
||||||
| Any other scheme | Always blocked |
|
|
||||||
|
|
||||||
HTTP redirects are also disabled: a URL that passes validation cannot redirect to an internal address.
|
<table class='stock-table'>
|
||||||
|
<thead>
|
||||||
|
<!-- table header data -->
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in location.stock_items %}
|
||||||
|
{% include 'snippets/stock_row.html' with item=item %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
|
||||||
### Remote URL Fetching
|
{% endraw %}
|
||||||
|
```
|
||||||
|
|
||||||
The **Report URL Fetching** system setting (`REPORT_FETCH_URLS`) controls whether `http://` and `https://` URLs in templates are permitted. It defaults to **disabled**.
|
!!! info "Snippet Arguments"
|
||||||
|
Note above that named argument variables can be passed through to the snippet!
|
||||||
|
|
||||||
When enabled, URLs are still validated against private, loopback, link-local, and reserved IP ranges before the request is made, preventing templates from being used as a vector for [Server-Side Request Forgery (SSRF)](https://owasp.org/www-community/attacks/Server_Side_Request_Forgery) attacks against internal network services.
|
And the snippet file `stock_row.html` may be written as follows:
|
||||||
|
|
||||||
!!! warning "Enable with care"
|
```html
|
||||||
Enabling remote URL fetching allows report templates to trigger outbound HTTP requests from the InvenTree server. Only enable this if your templates genuinely require it, and ensure that templates are reviewed before deployment.
|
{% raw %}
|
||||||
|
<!-- stock_row snippet -->
|
||||||
### Asset Files
|
<tr>
|
||||||
|
<td>{{ item.part.full_name }}</td>
|
||||||
[Asset files](./assets.md) uploaded through the admin interface are embedded directly into the rendered PDF as base64 `data:` URIs — they are read via the Django storage API and never loaded through WeasyPrint's URL fetcher. This means assets work correctly regardless of whether remote URL fetching is enabled, and also work with remote storage backends such as S3.
|
<td>{{ item.quantity }}</td>
|
||||||
|
</tr>
|
||||||
There are various [helper functions](./helpers.md#report-assets) available to assist with embedding assets into templates.
|
{% endraw %}
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -128,25 +128,6 @@ As an example, consider a label template for a StockItem. A user may wish to def
|
||||||
|
|
||||||
To restrict the label accordingly, we could set the *filters* value to `part__IPN=IPN123`.
|
To restrict the label accordingly, we could set the *filters* value to `part__IPN=IPN123`.
|
||||||
|
|
||||||
## Printing Labels
|
|
||||||
|
|
||||||
Labels are printed directly from the web interface, from the pages where the target items are displayed. To print labels against one or more items:
|
|
||||||
|
|
||||||
1. Select the items to print - either from a table (using the row checkboxes), or by viewing the detail page of a single item
|
|
||||||
2. Select the *Print* action, and choose the *Print Label* option
|
|
||||||
3. Select the desired label template - only *enabled* templates which match the selected model type (and pass any template filters) are available for selection
|
|
||||||
4. Select the *printer* (plugin) to use for printing the labels
|
|
||||||
|
|
||||||
### Label Printing Plugins
|
|
||||||
|
|
||||||
The actual printing of labels is handled by a [label printing plugin](../plugins/mixins/label.md). InvenTree provides a number of built-in printing plugins:
|
|
||||||
|
|
||||||
- The default [InvenTree Label Printer](../plugins/builtin/inventree_label.md) plugin generates a PDF file, which is then made available for download.
|
|
||||||
- The [Label Sheet](../plugins/builtin/inventree_label_sheet.md) plugin arranges multiple labels onto a single sheet for printing.
|
|
||||||
- The [Label Machine](../plugins/builtin/inventree_label_machine.md) plugin sends the label to an external [label printer machine](../plugins/machines/label_printer.md).
|
|
||||||
|
|
||||||
Custom label printing plugins (e.g. for driving a specific hardware printer) can be installed to extend this list - refer to the [label mixin documentation](../plugins/mixins/label.md) for further information.
|
|
||||||
|
|
||||||
## Built-In Templates
|
## Built-In Templates
|
||||||
|
|
||||||
The InvenTree installation provides a number of simple *default* templates which can be used as a starting point for creating custom labels. These built-in templates can be disabled if they are not required.
|
The InvenTree installation provides a number of simple *default* templates which can be used as a starting point for creating custom labels. These built-in templates can be disabled if they are not required.
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,53 @@
|
||||||
---
|
---
|
||||||
title: Report Templates
|
title: Report and LabelGeneration
|
||||||
---
|
---
|
||||||
|
|
||||||
## Report Templates
|
## Custom Reports
|
||||||
|
|
||||||
Report templates are used to generate formal PDF documents - such as order reports, packing lists, or test reports - rendered against a particular database item (or list of items).
|
InvenTree supports a customizable reporting ecosystem, allowing the user to develop document templates that meet their particular needs.
|
||||||
|
|
||||||
Like all InvenTree templates, report templates are written using a mixture of HTML / CSS and the django template language, and are rendered to a PDF file using the WeasyPrint engine:
|
PDF files are generated from custom HTML template files which are written by the user.
|
||||||
|
|
||||||
- Refer to the [template overview](./index.md) for information on creating and managing templates.
|
Templates can be used to generate *reports* or *labels* which can be used in a variety of situations to format data in a friendly format for printing, distribution, conformance and testing.
|
||||||
- Refer to the [template rendering documentation](./weasyprint.md) for information on the WeasyPrint engine and the django template language.
|
|
||||||
- Refer to the [context variables documentation](./context_variables.md) for the variables available when rendering a template.
|
|
||||||
|
|
||||||
## Report Options
|
In addition to providing the ability for end-users to provide their own reporting templates, some report types offer "built-in" report templates ready for use.
|
||||||
|
|
||||||
In addition to the [options common to all templates](./index.md#creating-templates), each report template provides the following options:
|
### WeasyPrint Templates
|
||||||
|
|
||||||
### Page Size
|
InvenTree report templates utilize the powerful [WeasyPrint](https://weasyprint.org/) PDF generation engine.
|
||||||
|
|
||||||
The page size (e.g. `A4` or `Letter`) used when rendering the report to a PDF file. When a new report template is created, this value defaults to the [default page size](./index.md#default-page-size) specified in the global settings.
|
!!! info "WeasyPrint"
|
||||||
|
WeasyPrint is an extremely powerful and flexible reporting library. Refer to the [WeasyPrint docs](https://doc.courtbouillon.org/weasyprint/stable/) for further information.
|
||||||
|
|
||||||
### Landscape
|
### Stylesheets
|
||||||
|
|
||||||
If enabled, the report is rendered in landscape orientation, rather than the default portrait orientation.
|
Templates are rendered using standard HTML / CSS - if you are familiar with web page layout, you're ready to go!
|
||||||
|
|
||||||
### Merge
|
### Template Language
|
||||||
|
|
||||||
If enabled, a single report document is generated for all selected items, rather than a separate document for each item. Refer to the [merging reports](#merging-reports) section below for further information.
|
Uploaded report template files are passed through the [django template rendering framework]({% include "django.html" %}/topics/templates/), and as such accept the same variable template strings as any other django template file. Different variables are passed to the report template (based on the context of the report) and can be used to customize the contents of the generated PDF.
|
||||||
|
|
||||||
!!! info "Context Variables"
|
### Variables
|
||||||
The `page_size`, `landscape` and `merge` values are also made available to the template as [context variables](./context_variables.md#report-context).
|
|
||||||
|
|
||||||
## Generating Reports
|
Each report template is provided a set of *context variables* which can be used when rendering the template.
|
||||||
|
|
||||||
Reports are generated directly from the web interface, from the pages where the target items are displayed. To generate a report against one or more items:
|
For example, rendering the name of a part (which is available in the particular template context as `part`) is as follows:
|
||||||
|
|
||||||
1. Select the items to print - either from a table (using the row checkboxes), or by viewing the detail page of a single item
|
```html
|
||||||
2. Select the *Print* action, and choose the *Print Report* option
|
{% raw %}
|
||||||
3. Select the desired report template - only *enabled* templates which match the selected model type (and pass any [template filters](./index.md#template-filters)) are available for selection
|
|
||||||
4. The report is generated by the server, and the resulting PDF file is made available for download
|
|
||||||
|
|
||||||
!!! info "Enable Reports"
|
<!-- Template variables use {{ double_curly_braces }} -->
|
||||||
Report generation must be [enabled in the global settings](./index.md#enable-reports) before reports can be generated.
|
<h2>Part: {{ part.name }}</h3>
|
||||||
|
<p><i>
|
||||||
|
Description:<br>
|
||||||
|
{{ part.description }}
|
||||||
|
</p></i>
|
||||||
|
{% endraw %}
|
||||||
|
```
|
||||||
|
|
||||||
## Merging Reports
|
## Merging Reports
|
||||||
|
|
||||||
When rendering reports for multiple items, the default behaviour is that each item is rendered as a separate report. The chosen template is rendered multiple times, once for each item selected, and expects a single item in the context variable.
|
When rendering reports for multiple items, the default behaviour is that each item is rendered as a separate report. The chosen templeate is rendered multiple times, once for each item selected, and expects a single item in the context variable.
|
||||||
|
|
||||||
However, it is possible to merge multiple items into a single report document. This is achieved by enabling the `merge` attribute of the report template:
|
However, it is possible to merge multiple items into a single report document. This is achieved by enabling the `merge` attribute of the report template:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ The following report templates are provided "out of the box" and can be used as
|
||||||
| [Sales Order Shipment](#sales-order-shipment) | [SalesOrderShipment](../sales/sales_order.md) | Sales Order Shipment report |
|
| [Sales Order Shipment](#sales-order-shipment) | [SalesOrderShipment](../sales/sales_order.md) | Sales Order Shipment report |
|
||||||
| [Stock Location](#stock-location) | [StockLocation](../stock/index.md#stock-location) | Stock Location report |
|
| [Stock Location](#stock-location) | [StockLocation](../stock/index.md#stock-location) | Stock Location report |
|
||||||
| [Test Report](#test-report) | [StockItem](../stock/index.md#stock-item) | Test Report |
|
| [Test Report](#test-report) | [StockItem](../stock/index.md#stock-item) | Test Report |
|
||||||
| [Transfer Order](#transfer-order) | [TransferOrder](../stock/transfer_order.md) | Transfer Order report |
|
|
||||||
| [Selected Stock Items Report](#selected-stock-items-report) | [StockItem](../stock/index.md#stock-item) | Selected Stock Items report |
|
| [Selected Stock Items Report](#selected-stock-items-report) | [StockItem](../stock/index.md#stock-item) | Selected Stock Items report |
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,10 +57,6 @@ The following report templates are provided "out of the box" and can be used as
|
||||||
|
|
||||||
{{ templatefile("report/inventree_test_report.html") }}
|
{{ templatefile("report/inventree_test_report.html") }}
|
||||||
|
|
||||||
### Transfer Order
|
|
||||||
|
|
||||||
{{ templatefile("report/inventree_transfer_order_report.html") }}
|
|
||||||
|
|
||||||
### Selected Stock Items Report
|
### Selected Stock Items Report
|
||||||
|
|
||||||
{{ templatefile("report/inventree_stock_report_merge.html") }}
|
{{ templatefile("report/inventree_stock_report_merge.html") }}
|
||||||
|
|
@ -73,11 +68,9 @@ The following label templates are provided "out of the box" and can be used as a
|
||||||
| Template | Model Type | Description |
|
| Template | Model Type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [Build Line](#build-line-label) | [Build line item](../manufacturing/build.md) | Build Line label |
|
| [Build Line](#build-line-label) | [Build line item](../manufacturing/build.md) | Build Line label |
|
||||||
| [Part](#part-label) | [Part](../part/index.md) | Part label (QR code and part name) |
|
| [Part](#part-label) | [Part](../part/index.md) | Part label |
|
||||||
| [Part (Code128)](#part-label-code128) | [Part](../part/index.md) | Part label (Code128 barcode) |
|
|
||||||
| [Stock Item](#stock-item-label) | [StockItem](../stock/index.md#stock-item) | Stock Item label |
|
| [Stock Item](#stock-item-label) | [StockItem](../stock/index.md#stock-item) | Stock Item label |
|
||||||
| [Stock Location](#stock-location-label) | [StockLocation](../stock/index.md#stock-location) | Stock Location label (QR code) |
|
| [Stock Location](#stock-location-label) | [StockLocation](../stock/index.md#stock-location) | Stock Location label |
|
||||||
| [Stock Location (with text)](#stock-location-label-with-text) | [StockLocation](../stock/index.md#stock-location) | Stock Location label (QR code and location details) |
|
|
||||||
|
|
||||||
### Build Line Label
|
### Build Line Label
|
||||||
|
|
||||||
|
|
@ -85,10 +78,6 @@ The following label templates are provided "out of the box" and can be used as a
|
||||||
|
|
||||||
### Part Label
|
### Part Label
|
||||||
|
|
||||||
{{ templatefile("label/part_label.html") }}
|
|
||||||
|
|
||||||
### Part Label (Code128)
|
|
||||||
|
|
||||||
{{ templatefile("label/part_label_code128.html") }}
|
{{ templatefile("label/part_label_code128.html") }}
|
||||||
|
|
||||||
### Stock Item Label
|
### Stock Item Label
|
||||||
|
|
@ -97,8 +86,4 @@ The following label templates are provided "out of the box" and can be used as a
|
||||||
|
|
||||||
### Stock Location Label
|
### Stock Location Label
|
||||||
|
|
||||||
{{ templatefile("label/stocklocation_qr.html") }}
|
|
||||||
|
|
||||||
### Stock Location Label (with text)
|
|
||||||
|
|
||||||
{{ templatefile("label/stocklocation_qr_and_text.html") }}
|
{{ templatefile("label/stocklocation_qr_and_text.html") }}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
---
|
|
||||||
title: Report Snippets
|
|
||||||
---
|
|
||||||
|
|
||||||
## Report Snippets
|
|
||||||
|
|
||||||
A powerful feature provided by the django / WeasyPrint templating framework is the ability to include external template files. This allows commonly used template features to be broken out into separate files and reused across multiple templates.
|
|
||||||
|
|
||||||
To support this, InvenTree provides report "snippets" - short (or not so short) template files which cannot be rendered by themselves, but can be called from other templates.
|
|
||||||
|
|
||||||
Snippet files are managed from the [Admin Center](../settings/admin.md#admin-center), via the *Report Snippets* panel. Staff users can upload new snippet files, and edit or remove existing snippets.
|
|
||||||
|
|
||||||
Additionally, the content of an existing snippet can be modified directly within the browser - simply select a snippet from the table to open it in the built-in [template editor](./template_editor.md#editing-snippets).
|
|
||||||
|
|
||||||
Snippets are included in a template as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
{% raw %}{% include 'snippets/<snippet_name.html>' %}{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
For example, consider a custom stocktake report for a particular stock location, where we wish to render a table with a row for each item in that location.
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
|
|
||||||
<table class='stock-table'>
|
|
||||||
<thead>
|
|
||||||
<!-- table header data -->
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for item in location.stock_items %}
|
|
||||||
{% include 'snippets/stock_row.html' with item=item %}
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! info "Snippet Arguments"
|
|
||||||
Note above that named argument variables can be passed through to the snippet!
|
|
||||||
|
|
||||||
And the snippet file `stock_row.html` may be written as follows:
|
|
||||||
|
|
||||||
```html
|
|
||||||
{% raw %}
|
|
||||||
<!-- stock_row snippet -->
|
|
||||||
<tr>
|
|
||||||
<td>{{ item.part.full_name }}</td>
|
|
||||||
<td>{{ item.quantity }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endraw %}
|
|
||||||
```
|
|
||||||
|
|
@ -4,11 +4,11 @@ title: Template editor
|
||||||
|
|
||||||
## Template editor
|
## Template editor
|
||||||
|
|
||||||
The Template Editor is integrated into the [Admin Center](../settings/admin.md#admin-center) of the Web UI. It allows users to create and edit label and report templates directly with a side by side preview for a more productive workflow.
|
The Template Editor is integrated into the [Admin Center](../settings//admin.md#admin-center) of the Web UI. It allows users to create and edit label and report templates directly with a side by side preview for a more productive workflow.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
On the left side (1) are all possible template types for labels and reports listed. With the "+" button (2), above the template table (3), new templates for the selected type can be created. To switch to the template editor click on a template.
|
On the left side (1) are all possible possible template types for labels and reports listed. With the "+" button (2), above the template table (3), new templates for the selected type can be created. To switch to the template editor click on a template.
|
||||||
|
|
||||||
### Editing Templates
|
### Editing Templates
|
||||||
|
|
||||||
|
|
@ -31,10 +31,3 @@ If you don't want to override the template, but just render a preview for a temp
|
||||||
#### Edit template metadata
|
#### Edit template metadata
|
||||||
|
|
||||||
Editing metadata such as name, description, filters and even width/height for labels and orientation/page size for reports can be done from the edit modal accessible when clicking on the three dots (4) and select "Edit" in the dropdown menu.
|
Editing metadata such as name, description, filters and even width/height for labels and orientation/page size for reports can be done from the edit modal accessible when clicking on the three dots (4) and select "Edit" in the dropdown menu.
|
||||||
|
|
||||||
### Editing Snippets
|
|
||||||
|
|
||||||
[Report snippets](./index.md#report-snippets) can also be edited directly within the browser, from the *Report Snippets* panel in the Admin Center. Selecting a snippet from the table opens it in the same code editor as used for report and label templates.
|
|
||||||
|
|
||||||
!!! info "No Preview"
|
|
||||||
As snippets cannot be rendered by themselves (they must be included in a report or label template), no preview area is available when editing a snippet.
|
|
||||||
|
|
|
||||||