Merge branch 'master' into add-changelog

This commit is contained in:
Matthias Mair 2024-06-07 16:21:42 +02:00 committed by GitHub
commit 816b187ec7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
660 changed files with 199160 additions and 148324 deletions

View File

@ -13,6 +13,12 @@ services:
POSTGRES_USER: inventree_user
POSTGRES_PASSWORD: inventree_password
redis:
image: redis:7.0
restart: always
expose:
- 6379
inventree:
build:
context: ..
@ -31,6 +37,8 @@ services:
INVENTREE_DB_HOST: db
INVENTREE_DB_USER: inventree_user
INVENTREE_DB_PASSWORD: inventree_password
INVENTREE_CACHE_HOST: redis
INVENTREE_CACHE_PORT: 6379
INVENTREE_PLUGINS_ENABLED: True
INVENTREE_SITE_URL: http://localhost:8000
INVENTREE_CORS_ORIGIN_ALLOW_ALL: True

View File

@ -49,9 +49,10 @@ runs:
shell: bash
run: |
python3 -m pip install -U pip
pip3 install invoke wheel uv
- name: Set the VIRTUAL_ENV variable for uv to work
run: echo "VIRTUAL_ENV=${Python_ROOT_DIR}" >> $GITHUB_ENV
pip3 install -U invoke wheel
pip3 install 'uv<0.3.0'
- name: Allow uv to use the system Python by default
run: echo "UV_SYSTEM_PYTHON=1" >> $GITHUB_ENV
shell: bash
- name: Install Specific Python Dependencies
if: ${{ inputs.pip-dependency }}
@ -82,7 +83,7 @@ runs:
# Invoke commands
- name: Install dev requirements
if: ${{ inputs.dev-install == 'true' ||inputs.install == 'true' }}
if: ${{ inputs.dev-install == 'true' || inputs.install == 'true' }}
shell: bash
run: uv pip install --require-hashes -r src/backend/requirements-dev.txt
- name: Run invoke install

View File

@ -15,26 +15,11 @@ updates:
interval: weekly
- package-ecosystem: pip
directory: /contrib/container
schedule:
interval: weekly
- package-ecosystem: pip
directory: /docs
schedule:
interval: weekly
- package-ecosystem: npm
directory: /src/backend
schedule:
interval: weekly
groups:
dependencies:
patterns:
- "*" # Include all dependencies
- package-ecosystem: pip
directory: /src/backend
directories:
- /contrib/container
- /docs
- /.github
- /src/backend
schedule:
interval: weekly
groups:
@ -43,7 +28,9 @@ updates:
- "*" # Include all dependencies
- package-ecosystem: npm
directory: /src/frontend
directories:
- /src/frontend
- /src/backend
schedule:
interval: weekly
groups:

3
.github/release.yml vendored
View File

@ -31,6 +31,9 @@ changelog:
- setup
- demo
- CI
- title: Dependencies
labels:
- dependency
- title: Other Changes
labels:
- "*"

View File

@ -1,3 +0,0 @@
# Packages needed for CI
requests==2.31.0
pyyaml==6.0.1

View File

@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:

View File

@ -39,19 +39,16 @@ jobs:
docker: ${{ steps.filter.outputs.docker }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
id: filter
with:
filters: |
docker:
- .github/workflows/docker.yaml
- docker/**
- docker-compose.yml
- docker.dev.env
- Dockerfile
- InvenTree/settings.py
- requirements.txt
- contrib/container/**
- src/backend/InvenTree/InvenTree/settings.py
- src/backend/requirements.txt
- tasks.py
# Build the docker image
@ -69,14 +66,14 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Set Up Python ${{ env.python_version }}
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # pin@v5.1.0
with:
python-version: ${{ env.python_version }}
- name: Version Check
run: |
pip install --require-hashes -r .github/requirements.txt
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
python3 .github/scripts/version_check.py
echo "git_commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
echo "git_commit_date=$(git show -s --format=%ci)" >> $GITHUB_ENV
@ -88,12 +85,17 @@ jobs:
docker run --rm inventree-test invoke --list
docker run --rm inventree-test gunicorn --version
docker run --rm inventree-test pg_dump --version
docker run --rm inventree-test test -f /home/inventree/init.sh
docker run --rm inventree-test test -f /home/inventree/tasks.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/InvenTree/manage.py
- name: Build Docker Image
# Build the development docker image (using docker-compose.yml)
run: docker compose --project-directory . -f contrib/container/dev-docker-compose.yml build --no-cache
- name: Update Docker Image
run: |
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run inventree-dev-server invoke install
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run inventree-dev-server invoke update
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run inventree-dev-server invoke setup-dev
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml up -d
@ -124,7 +126,7 @@ jobs:
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # pin@v3.0.0
- name: Set up Docker Buildx
if: github.event_name != 'pull_request'
uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # pin@v3.2.0
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # pin@v3.3.0
- name: Set up cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # pin@v3.5.0

View File

@ -40,7 +40,7 @@ jobs:
force: ${{ steps.force.outputs.force }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
id: filter
with:
@ -72,7 +72,7 @@ jobs:
needs: ["pre-commit"]
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -94,7 +94,7 @@ jobs:
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Set up Python ${{ env.python_version }}
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # pin@v5.1.0
with:
@ -104,7 +104,7 @@ jobs:
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # pin@v3.0.1
- name: Check Version
run: |
pip install --require-hashes -r .github/requirements.txt
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
python3 .github/scripts/version_check.py
mkdocs:
@ -115,15 +115,15 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Set up Python ${{ env.python_version }}
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # pin@v5.1.0
with:
python-version: ${{ env.python_version }}
- name: Check Config
run: |
pip install --require-hashes -r .github/requirements.txt
pip install -r docs/requirements.txt
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
pip install --require-hashes -r docs/requirements.txt
python docs/ci/check_mkdocs_config.py
- name: Check Links
uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1
@ -151,7 +151,7 @@ jobs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -161,14 +161,14 @@ jobs:
- name: Export API Documentation
run: invoke schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
- name: Upload schema
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # pin@v4.3.1
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # pin@v4.3.3
with:
name: schema.yml
path: src/backend/InvenTree/schema.yml
- name: Download public schema
if: needs.paths-filter.outputs.api == 'false'
run: |
pip install --require-hashes -r .github/requirements.txt >/dev/null 2>&1
pip install --require-hashes -r contrib/dev_reqs/requirements.txt >/dev/null 2>&1
version="$(python3 .github/scripts/version_check.py only_version 2>&1)"
echo "Version: $version"
url="https://raw.githubusercontent.com/inventree/schema/main/export/${version}/api.yaml"
@ -187,7 +187,7 @@ jobs:
id: version
if: github.ref == 'refs/heads/master' && needs.paths-filter.outputs.api == 'true'
run: |
pip install --require-hashes -r .github/requirements.txt >/dev/null 2>&1
pip install --require-hashes -r contrib/dev_reqs/requirements.txt >/dev/null 2>&1
version="$(python3 .github/scripts/version_check.py only_version 2>&1)"
echo "Version: $version"
echo "version=$version" >> "$GITHUB_OUTPUT"
@ -201,12 +201,12 @@ jobs:
version: ${{ needs.schema.outputs.version }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
repository: inventree/schema
token: ${{ secrets.SCHEMA_PAT }}
- name: Download schema artifact
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
with:
name: schema.yml
- name: Move schema to correct location
@ -238,7 +238,7 @@ jobs:
INVENTREE_SITE_URL: http://127.0.0.1:12345
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -261,21 +261,25 @@ jobs:
coverage run -m unittest discover -s test/
coverage:
name: Tests - DB [SQLite] + Coverage
name: Tests - DB [SQLite] + Coverage ${{ matrix.python_version }}
runs-on: ubuntu-20.04
needs: ["pre-commit", "paths-filter"]
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
continue-on-error: true # continue if a step fails so that coverage gets pushed
strategy:
matrix:
python_version: [3.9, 3.12]
env:
INVENTREE_DB_NAME: ./inventree.sqlite
INVENTREE_DB_ENGINE: sqlite3
INVENTREE_PLUGINS_ENABLED: true
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
python_version: ${{ matrix.python_version }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -291,7 +295,7 @@ jobs:
- name: Coverage Tests
run: invoke test --coverage
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v4.3.0
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # pin@v4.4.1
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
@ -329,7 +333,7 @@ jobs:
- 6379:6379
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -373,7 +377,7 @@ jobs:
- 3306:3306
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -412,7 +416,7 @@ jobs:
- 5432:5432
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -423,7 +427,7 @@ jobs:
- name: Run Tests
run: invoke test --migrations --report --coverage
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v4.3.0
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # pin@v4.4.1
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
@ -443,7 +447,7 @@ jobs:
INVENTREE_PLUGINS_ENABLED: false
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
name: Checkout Code
- name: Environment Setup
uses: ./.github/actions/setup
@ -500,7 +504,7 @@ jobs:
VITE_COVERAGE: true
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -518,7 +522,7 @@ jobs:
- name: Run Playwright tests
id: tests
run: cd src/frontend && npx nyc playwright test
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # pin@v4
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # pin@v4
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
with:
name: playwright-report
@ -528,7 +532,7 @@ jobs:
if: always()
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@84508663e988701840491b86de86b666e8a86bed # pin@v4.3.0
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # pin@v4.4.1
if: always()
with:
token: ${{ secrets.CODECOV_TOKEN }}
@ -541,7 +545,7 @@ jobs:
timeout-minutes: 60
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:
@ -554,7 +558,7 @@ jobs:
run: |
cd src/backend/InvenTree/web/static
zip -r frontend-build.zip web/ web/.vite
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # pin@v4.3.1
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # pin@v4.3.3
with:
name: frontend-build
path: src/backend/InvenTree/web/static/web

View File

@ -5,20 +5,20 @@ on:
release:
types: [published]
permissions:
contents: read
jobs:
stable:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Version Check
run: |
pip install --require-hashes -r .github/requirements.txt
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
python3 .github/scripts/version_check.py
- name: Push to Stable Branch
uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df # pin@v0.8.0
@ -30,8 +30,11 @@ jobs:
publish-build:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:

View File

@ -32,12 +32,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
with:
results_file: results.sarif
results_format: sarif
@ -59,7 +59,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: SARIF file
path: results.sarif
@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
with:
sarif_file: results.sarif

View File

@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # pin@v4.1.6
- name: Environment Setup
uses: ./.github/actions/setup
with:

1
.gitignore vendored
View File

@ -108,5 +108,6 @@ src/backend/InvenTree/web/static
InvenTree/web/static
# Generated docs files
docs/schema.yml
docs/docs/api/*.yml
docs/docs/api/schema/*.yml

View File

@ -39,8 +39,16 @@ repos:
files: src/backend/requirements\.(in|txt)$
- id: pip-compile
name: pip-compile requirements.txt
args: [.github/requirements.in, -o, .github/requirements.txt,--python-version=3.9, --no-strip-extras, --generate-hashes]
files: .github/requirements\.(in|txt)$
args: [contrib/dev_reqs/requirements.in, -o, contrib/dev_reqs/requirements.txt,--python-version=3.9, --no-strip-extras, --generate-hashes]
files: contrib/dev_reqs/requirements\.(in|txt)$
- id: pip-compile
name: pip-compile requirements.txt
args: [docs/requirements.in, -o, docs/requirements.txt,--python-version=3.9, --no-strip-extras, --generate-hashes]
files: docs/requirements\.(in|txt)$
- id: pip-compile
name: pip-compile requirements.txt
args: [contrib/container/requirements.in, -o, contrib/container/requirements.txt,--python-version=3.11, --no-strip-extras, --generate-hashes]
files: contrib/container/requirements\.(in|txt)$
- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.34.1
hooks:
@ -84,3 +92,7 @@ repos:
# rev: 3.0.0
# hooks:
# - id: shellcheck
- repo: https://github.com/isidentical/teyit
rev: 0.4.3
hooks:
- id: teyit

View File

@ -123,7 +123,7 @@ Refer to the [getting started guide](https://docs.inventree.org/en/latest/start/
<!-- Mobile App -->
## :iphone: Mobile App
InvenTree is supported by a [companion mobile app](https://docs.inventree.org/en/latest/app/app/) which allows users access to stock control information and functionality.
InvenTree is supported by a [companion mobile app](https://docs.inventree.org/app/) which allows users access to stock control information and functionality.
<div align="center"><h4>
<a href="https://play.google.com/store/apps/details?id=inventree.inventree_app">Android Play Store</a>

View File

@ -28,6 +28,7 @@ INVENTREE_DB_PASSWORD=pgpassword
# Un-comment the following lines to enable Redis cache
# Note that you will also have to run docker-compose with the --profile redis command
# Refer to settings.py for other cache options
#INVENTREE_CACHE_ENABLED=True
#INVENTREE_CACHE_HOST=inventree-cache
#INVENTREE_CACHE_PORT=6379

View File

@ -1,7 +1,10 @@
# Caddyfile for Inventree
# Example Caddyfile for Inventree
# The following environment variables may be used:
# - 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)
#
# Note that while this file is a good starting point, it may need to be modified to suit your specific requirements
(log_common) {
log {

View File

@ -97,14 +97,14 @@ FROM inventree_base AS prebuild
ENV PATH=/root/.local/bin:$PATH
RUN ./install_build_packages.sh --no-cache --virtual .build-deps && \
pip install --user -r base_requirements.txt --no-cache && \
pip install --user --require-hashes -r base_requirements.txt --no-cache && \
pip install --user --require-hashes -r requirements.txt --no-cache && \
apk --purge del .build-deps
# Frontend builder image:
FROM prebuild AS frontend
RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 --ignore-scripts
RUN apk add --no-cache --update nodejs npm yarn
RUN yarn config set network-timeout 600000 -g
COPY src ${INVENTREE_HOME}/src
COPY tasks.py ${INVENTREE_HOME}/tasks.py
@ -128,6 +128,7 @@ COPY --from=prebuild /root/.local /root/.local
# Copy source code
COPY src/backend/InvenTree ${INVENTREE_BACKEND_DIR}/InvenTree
COPY src/backend/requirements.txt ${INVENTREE_BACKEND_DIR}/requirements.txt
COPY --from=frontend ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web
# Launch the production server
@ -141,11 +142,11 @@ EXPOSE 5173
# Install packages required for building python packages
RUN ./install_build_packages.sh
RUN pip install uv==0.1.26 --no-cache-dir && pip install -r base_requirements.txt --no-cache
RUN pip install --require-hashes -r base_requirements.txt --no-cache
# Install nodejs / npm / yarn
RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 --ignore-scripts
RUN apk add --no-cache --update nodejs npm yarn
RUN yarn config set network-timeout 600000 -g
# The development image requires the source code to be mounted to /home/inventree/

View File

@ -53,14 +53,9 @@ services:
restart: unless-stopped
# redis acts as database cache manager
# only runs under the "redis" profile : https://docs.docker.com/compose/profiles/
inventree-cache:
image: redis:7.0
container_name: inventree-cache
depends_on:
- inventree-db
profiles:
- redis
env_file:
- .env
expose:
@ -114,9 +109,23 @@ services:
env_file:
- .env
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./Caddyfile:/etc/caddy/Caddyfile:ro,z
- ${INVENTREE_EXT_VOLUME}/static:/var/www/static:z
- ${INVENTREE_EXT_VOLUME}/media:/var/www/media:z
- ${INVENTREE_EXT_VOLUME}:/var/log:z
- ${INVENTREE_EXT_VOLUME}:/data:z
- ${INVENTREE_EXT_VOLUME}:/config:z
# alternative: run nginx as reverse proxy
# inventree-proxy:
# container_name: inventree-proxy
# image: nginx:stable
# restart: always
# depends_on:
# - inventree-server
# ports:
# - ${INVENTREE_WEB_PORT:-80}:80
# - 443:443
# volumes:
# - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro,z
# - ${INVENTREE_EXT_VOLUME}:/var/www:z

View File

@ -0,0 +1,70 @@
# An example configuration file for running InvenTree container behind an nginx proxy
# While suitable for a simple installation, this file will likely require some modification
# if you are running a more complex setup (e.g behind another proxy, or with HTTPS)
server {
# Listen for connection on (internal) port 80
# If you are exposing this server to the internet, you should use HTTPS!
# In which case, you should also set up a redirect from HTTP to HTTPS, and listen on port 443
# See the Nginx documentation for more details
listen 80;
real_ip_header proxy_protocol;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CLIENT_IP $remote_addr;
proxy_pass_request_headers on;
proxy_redirect off;
client_max_body_size 100M;
proxy_buffering off;
proxy_request_buffering off;
# Do not touch this unless you have a specific reason - this and the docker-compose need to match
proxy_pass http://inventree-server:8000;
}
# Redirect any requests for static files
location /static/ {
alias /var/www/static/;
autoindex on;
# Caching settings
expires 30d;
add_header Pragma public;
add_header Cache-Control "public";
}
# Redirect any requests for media files
location /media/ {
alias /var/www/media/;
# Media files require user authentication
auth_request /auth;
# Content header to force download
add_header Content-disposition "attachment";
}
# Use the 'user' API endpoint for auth
location /auth {
internal;
proxy_pass http://inventree-server:8000/auth/;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}

View File

@ -0,0 +1,22 @@
# Base python requirements for docker containers
# Basic package requirements
invoke>=2.2.0 # Invoke build tool
pyyaml>=6.0.1
setuptools>=69.0.0
wheel>=0.41.0
# Database links
psycopg[binary, pool]
mysqlclient>=2.2.0
mariadb>=1.1.8
# gunicorn web server
gunicorn>=22.0.0
# LDAP required packages
django-auth-ldap # Django integration for ldap auth
python-ldap # LDAP auth support
# Upgraded python package installer
uv

View File

@ -1,22 +1,220 @@
# Base python requirements for docker containers
# Basic package requirements
invoke>=2.2.0 # Invoke build tool
pyyaml>=6.0.1
setuptools>=69.0.0
wheel>=0.41.0
# Database links
psycopg[binary, pool]
mysqlclient>=2.2.0
mariadb>=1.1.8
# gunicorn web server
gunicorn>=22.0.0
# LDAP required packages
django-auth-ldap # Django integration for ldap auth
python-ldap # LDAP auth support
# Upgraded python package installer
uv
# 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.11 --no-strip-extras --generate-hashes
asgiref==3.8.1 \
--hash=sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47 \
--hash=sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590
# via django
django==4.2.11 \
--hash=sha256:6e6ff3db2d8dd0c986b4eec8554c8e4f919b5c1ff62a5b4390c17aff2ed6e5c4 \
--hash=sha256:ddc24a0a8280a0430baa37aff11f28574720af05888c62b7cfe71d219f4599d3
# via django-auth-ldap
django-auth-ldap==4.8.0 \
--hash=sha256:4b4b944f3c28bce362f33fb6e8db68429ed8fd8f12f0c0c4b1a4344a7ef225ce \
--hash=sha256:604250938ddc9fda619f247c7a59b0b2f06e53a7d3f46a156f28aa30dd71a738
gunicorn==22.0.0 \
--hash=sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9 \
--hash=sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63
invoke==2.2.0 \
--hash=sha256:6ea924cc53d4f78e3d98bc436b08069a03077e6f85ad1ddaa8a116d7dad15820 \
--hash=sha256:ee6cbb101af1a859c7fe84f2a264c059020b0cb7fe3535f9424300ab568f6bd5
mariadb==1.1.10 \
--hash=sha256:03d6284ef713d1cad40146576a4cc2d6cbc1662060f2a0e59b174e1694521698 \
--hash=sha256:1ce87971c02375236ff8933e6c593c748e7b2f2950b86eabfab4289fd250ea63 \
--hash=sha256:1d81b22efbaaf4c5bc5e4cc4e2ef3c459538c1a939371089d8c5591d6f26a62e \
--hash=sha256:29040e426f877ddc45f337c6eb381b6bbab63cc6bf8431a28effe30162142513 \
--hash=sha256:4521aa721f926946bd71491f872e8babc78fa97755ed2114f5684b77363107cb \
--hash=sha256:49200378c614984f5ec875481662a49d7c97c2be27970b01b32fa4b7520d4e22 \
--hash=sha256:5d652117e2fdf12b9723e7452a05fce9e6ccbae6ea48871b21a3a8fde259dc48 \
--hash=sha256:8c8c6b27486b0e1772a23002c702b5fd244eecf9f05633dd6cb345fc26755a20 \
--hash=sha256:a332893e3ef7ceb7970ab4bd7c844bcb4bd68a051ca51313566f9808d7411f2d \
--hash=sha256:d7b09ec4abd02ed235257feb769f90cd4066e8f536b55b92f5166103d5b66a63 \
--hash=sha256:dff8b28ce4044574870d7bdd2d9f9f5da8e5f95a7ff6d226185db733060d1a93
mysqlclient==2.2.4 \
--hash=sha256:329e4eec086a2336fe3541f1ce095d87a6f169d1cc8ba7b04ac68bcb234c9711 \
--hash=sha256:33bc9fb3464e7d7c10b1eaf7336c5ff8f2a3d3b88bab432116ad2490beb3bf41 \
--hash=sha256:3c318755e06df599338dad7625f884b8a71fcf322a9939ef78c9b3db93e1de7a \
--hash=sha256:4e80dcad884dd6e14949ac6daf769123223a52a6805345608bf49cdaf7bc8b3a \
--hash=sha256:9d3310295cb682232cadc28abd172f406c718b9ada41d2371259098ae37779d3 \
--hash=sha256:9d4c015480c4a6b2b1602eccd9846103fc70606244788d04aa14b31c4bd1f0e2 \
--hash=sha256:ac44777eab0a66c14cb0d38965572f762e193ec2e5c0723bcd11319cc5b693c5 \
--hash=sha256:d43987bb9626096a302ca6ddcdd81feaeca65ced1d5fe892a6a66b808326aa54 \
--hash=sha256:e1ebe3f41d152d7cb7c265349fdb7f1eca86ccb0ca24a90036cde48e00ceb2ab
packaging==24.0 \
--hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \
--hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9
# via
# gunicorn
# mariadb
psycopg[binary, pool]==3.1.18 \
--hash=sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b \
--hash=sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e
psycopg-binary==3.1.18 \
--hash=sha256:02bd4da45d5ee9941432e2e9bf36fa71a3ac21c6536fe7366d1bd3dd70d6b1e7 \
--hash=sha256:0f68ac2364a50d4cf9bb803b4341e83678668f1881a253e1224574921c69868c \
--hash=sha256:13bcd3742112446037d15e360b27a03af4b5afcf767f5ee374ef8f5dd7571b31 \
--hash=sha256:1729d0e3dfe2546d823841eb7a3d003144189d6f5e138ee63e5227f8b75276a5 \
--hash=sha256:1859aeb2133f5ecdd9cbcee155f5e38699afc06a365f903b1512c765fd8d457e \
--hash=sha256:1c9b6bd7fb5c6638cb32469674707649b526acfe786ba6d5a78ca4293d87bae4 \
--hash=sha256:247474af262bdd5559ee6e669926c4f23e9cf53dae2d34c4d991723c72196404 \
--hash=sha256:258d2f0cb45e4574f8b2fe7c6d0a0e2eb58903a4fd1fbaf60954fba82d595ab7 \
--hash=sha256:2e2484ae835dedc80cdc7f1b1a939377dc967fed862262cfd097aa9f50cade46 \
--hash=sha256:320047e3d3554b857e16c2b6b615a85e0db6a02426f4d203a4594a2f125dfe57 \
--hash=sha256:39242546383f6b97032de7af30edb483d237a0616f6050512eee7b218a2aa8ee \
--hash=sha256:3c2b039ae0c45eee4cd85300ef802c0f97d0afc78350946a5d0ec77dd2d7e834 \
--hash=sha256:3c7afcd6f1d55992f26d9ff7b0bd4ee6b475eb43aa3f054d67d32e09f18b0065 \
--hash=sha256:3e4b0bb91da6f2238dbd4fbb4afc40dfb4f045bb611b92fce4d381b26413c686 \
--hash=sha256:3e7ce4d988112ca6c75765c7f24c83bdc476a6a5ce00878df6c140ca32c3e16d \
--hash=sha256:4085f56a8d4fc8b455e8f44380705c7795be5317419aa5f8214f315e4205d804 \
--hash=sha256:4575da95fc441244a0e2ebaf33a2b2f74164603341d2046b5cde0a9aa86aa7e2 \
--hash=sha256:489aa4fe5a0b653b68341e9e44af247dedbbc655326854aa34c163ef1bcb3143 \
--hash=sha256:4e4de16a637ec190cbee82e0c2dc4860fed17a23a35f7a1e6dc479a5c6876722 \
--hash=sha256:531381f6647fc267383dca88dbe8a70d0feff433a8e3d0c4939201fea7ae1b82 \
--hash=sha256:55ff0948457bfa8c0d35c46e3a75193906d1c275538877ba65907fd67aa059ad \
--hash=sha256:59701118c7d8842e451f1e562d08e8708b3f5d14974eefbce9374badd723c4ae \
--hash=sha256:5c323103dfa663b88204cf5f028e83c77d7a715f9b6f51d2bbc8184b99ddd90a \
--hash=sha256:5d6e860edf877d4413e4a807e837d55e3a7c7df701e9d6943c06e460fa6c058f \
--hash=sha256:639dd78ac09b144b0119076783cb64e1128cc8612243e9701d1503c816750b2e \
--hash=sha256:6432047b8b24ef97e3fbee1d1593a0faaa9544c7a41a2c67d1f10e7621374c83 \
--hash=sha256:67284e2e450dc7a9e4d76e78c0bd357dc946334a3d410defaeb2635607f632cd \
--hash=sha256:6ebecbf2406cd6875bdd2453e31067d1bd8efe96705a9489ef37e93b50dc6f09 \
--hash=sha256:7121acc783c4e86d2d320a7fb803460fab158a7f0a04c5e8c5d49065118c1e73 \
--hash=sha256:74e498586b72fb819ca8ea82107747d0cb6e00ae685ea6d1ab3f929318a8ce2d \
--hash=sha256:780a90bcb69bf27a8b08bc35b958e974cb6ea7a04cdec69e737f66378a344d68 \
--hash=sha256:7ac1785d67241d5074f8086705fa68e046becea27964267ab3abd392481d7773 \
--hash=sha256:812726266ab96de681f2c7dbd6b734d327f493a78357fcc16b2ac86ff4f4e080 \
--hash=sha256:824a1bfd0db96cc6bef2d1e52d9e0963f5bf653dd5bc3ab519a38f5e6f21c299 \
--hash=sha256:87dd9154b757a5fbf6d590f6f6ea75f4ad7b764a813ae04b1d91a70713f414a1 \
--hash=sha256:887f8d856c91510148be942c7acd702ccf761a05f59f8abc123c22ab77b5a16c \
--hash=sha256:888a72c2aca4316ca6d4a619291b805677bae99bba2f6e31a3c18424a48c7e4d \
--hash=sha256:8f54978c4b646dec77fefd8485fa82ec1a87807f334004372af1aaa6de9539a5 \
--hash=sha256:91074f78a9f890af5f2c786691575b6b93a4967ad6b8c5a90101f7b8c1a91d9c \
--hash=sha256:9d684227ef8212e27da5f2aff9d4d303cc30b27ac1702d4f6881935549486dd5 \
--hash=sha256:9e24e7b6a68a51cc3b162d0339ae4e1263b253e887987d5c759652f5692b5efe \
--hash=sha256:9ffcbbd389e486d3fd83d30107bbf8b27845a295051ccabde240f235d04ed921 \
--hash=sha256:a87e9eeb80ce8ec8c2783f29bce9a50bbcd2e2342a340f159c3326bf4697afa1 \
--hash=sha256:ad35ac7fd989184bf4d38a87decfb5a262b419e8ba8dcaeec97848817412c64a \
--hash=sha256:b15e3653c82384b043d820fc637199b5c6a36b37fa4a4943e0652785bb2bad5d \
--hash=sha256:b293e01057e63c3ac0002aa132a1071ce0fdb13b9ee2b6b45d3abdb3525c597d \
--hash=sha256:b2f7f95746efd1be2dc240248cc157f4315db3fd09fef2adfcc2a76e24aa5741 \
--hash=sha256:bd27f713f2e5ef3fd6796e66c1a5203a27a30ecb847be27a78e1df8a9a5ae68c \
--hash=sha256:c38a4796abf7380f83b1653c2711cb2449dd0b2e5aca1caa75447d6fa5179c69 \
--hash=sha256:c76659ae29a84f2c14f56aad305dd00eb685bd88f8c0a3281a9a4bc6bd7d2aa7 \
--hash=sha256:c84a0174109f329eeda169004c7b7ca2e884a6305acab4a39600be67f915ed38 \
--hash=sha256:cd2a9f7f0d4dacc5b9ce7f0e767ae6cc64153264151f50698898c42cabffec0c \
--hash=sha256:d322ba72cde4ca2eefc2196dad9ad7e52451acd2f04e3688d590290625d0c970 \
--hash=sha256:d4422af5232699f14b7266a754da49dc9bcd45eba244cf3812307934cd5d6679 \
--hash=sha256:d46ae44d66bf6058a812467f6ae84e4e157dee281bfb1cfaeca07dee07452e85 \
--hash=sha256:da917f6df8c6b2002043193cb0d74cc173b3af7eb5800ad69c4e1fbac2a71c30 \
--hash=sha256:dea4a59da7850192fdead9da888e6b96166e90608cf39e17b503f45826b16f84 \
--hash=sha256:e05f6825f8db4428782135e6986fec79b139210398f3710ed4aa6ef41473c008 \
--hash=sha256:e1cf59e0bb12e031a48bb628aae32df3d0c98fd6c759cb89f464b1047f0ca9c8 \
--hash=sha256:e252d66276c992319ed6cd69a3ffa17538943954075051e992143ccbf6dc3d3e \
--hash=sha256:e262398e5d51563093edf30612cd1e20fedd932ad0994697d7781ca4880cdc3d \
--hash=sha256:e28ff8f3de7b56588c2a398dc135fd9f157d12c612bd3daa7e6ba9872337f6f5 \
--hash=sha256:eea5f14933177ffe5c40b200f04f814258cc14b14a71024ad109f308e8bad414 \
--hash=sha256:f876ebbf92db70125f6375f91ab4bc6b27648aa68f90d661b1fc5affb4c9731c \
--hash=sha256:f8ff3bc08b43f36fdc24fedb86d42749298a458c4724fb588c4d76823ac39f54
# via psycopg
psycopg-pool==3.2.1 \
--hash=sha256:060b551d1b97a8d358c668be58b637780b884de14d861f4f5ecc48b7563aafb7 \
--hash=sha256:6509a75c073590952915eddbba7ce8b8332a440a31e77bba69561483492829ad
# via psycopg
pyasn1==0.6.0 \
--hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \
--hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473
# via
# pyasn1-modules
# python-ldap
pyasn1-modules==0.4.0 \
--hash=sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6 \
--hash=sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b
# via python-ldap
python-ldap==3.4.4 \
--hash=sha256:7edb0accec4e037797705f3a05cbf36a9fde50d08c8f67f2aef99a2628fab828
# via django-auth-ldap
pyyaml==6.0.1 \
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
--hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
--hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
--hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
--hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
--hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
--hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
--hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
--hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
--hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
--hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
--hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
--hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
--hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
--hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
--hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
--hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
--hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
--hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
--hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
setuptools==69.5.1 \
--hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \
--hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32
sqlparse==0.5.0 \
--hash=sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93 \
--hash=sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663
# via django
typing-extensions==4.11.0 \
--hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \
--hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a
# via
# psycopg
# psycopg-pool
uv==0.1.38 \
--hash=sha256:03242a734a572733f2b9a5dbb94517e918fe26fc01114b7c51d12296dfbb8f8b \
--hash=sha256:067af2d986329db4fa3c7373017d49f0e16ddff23e483b7e5bc3a5a18ce08ea6 \
--hash=sha256:0937ad16ae0e0b6bb6dd3c386f8fb33141ad08d1762eaacffb4d2b27fb466a17 \
--hash=sha256:0e1d64ac437b0a14fbcec55b1c3f162fa24860711e0d855fcd9c672b149a122a \
--hash=sha256:1be7aa46936c0351ccb1400ea95e5381b3f05fef772fa3b9f23af728cc175dea \
--hash=sha256:309e73a3ec3a5a536a3efaf434270fc94b483069f1425765165c1c9d786c27fd \
--hash=sha256:4251f9771d392d7badc1e5fb934b397b12ca00fef9d955207ade169cc1f7e872 \
--hash=sha256:43772e7589f70e954b1ae29230e575ef9e4d8d769138a94dfa5ae7eaf1e26ac5 \
--hash=sha256:4a6024256d38b77151e32876be9fcb99cf75df7a86b26e0161cc202bed558adf \
--hash=sha256:5a98d6aacd4b57b7e00daf154919e7c9206fefdf40bd28cfb13efe0e0324d491 \
--hash=sha256:8de6dbd8f348ee90af044f4cc7b6650521d25ba2d20a813c1e157a3f90069dd9 \
--hash=sha256:9133e24db9bdd4f412eab69586d03294419825432a9a27ee1b510a4c01eb7b0b \
--hash=sha256:92f65b6e4e5c8126501785af3629dc537d7c82caa56ac9336a86929c73d0e138 \
--hash=sha256:afd85029923e712b6b2c45ddc1680c785392220876c766521e45778db3f71f8e \
--hash=sha256:b0b15e51a0f8240969bc412ed0dd60cfe3f664b30173139ef263d71c596d631f \
--hash=sha256:ea44c07605d1359a7d82bf42706dd86d341f15f4ca2e1f36e51626a7111c2ad5 \
--hash=sha256:f87c9711493c53d32012a96b49c4d53aabdf7ed666cbf2c3fb55dd402a6b31a8
wheel==0.43.0 \
--hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \
--hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81

View File

@ -0,0 +1,4 @@
# Packages needed for CI/packages
requests==2.32.2
pyyaml==6.0.1
jc==1.25.2

View File

@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile .github/requirements.in -o .github/requirements.txt --python-version=3.9 --no-strip-extras --generate-hashes
# uv pip compile contrib/dev_reqs/requirements.in -o contrib/dev_reqs/requirements.txt --python-version=3.9 --no-strip-extras --generate-hashes
certifi==2024.2.2 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
@ -100,6 +100,13 @@ idna==3.7 \
--hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
--hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
# via requests
jc==1.25.2 \
--hash=sha256:26e412a65a478f9da3097653db6277f915cfae5c0f0a3f42026b405936abd358 \
--hash=sha256:97ada193495f79550f06fe0cbfb119ff470bcca57c1cc593a5cdb0008720e0b3
pygments==2.17.2 \
--hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \
--hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367
# via jc
pyyaml==6.0.1 \
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
@ -152,10 +159,70 @@ pyyaml==6.0.1 \
--hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
requests==2.31.0 \
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
requests==2.32.2 \
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
ruamel-yaml==0.18.6 \
--hash=sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636 \
--hash=sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b
# via jc
ruamel-yaml-clib==0.2.8 \
--hash=sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d \
--hash=sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001 \
--hash=sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462 \
--hash=sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9 \
--hash=sha256:1707814f0d9791df063f8c19bb51b0d1278b8e9a2353abbb676c2f685dee6afe \
--hash=sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b \
--hash=sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b \
--hash=sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615 \
--hash=sha256:1dc67314e7e1086c9fdf2680b7b6c2be1c0d8e3a8279f2e993ca2a7545fecf62 \
--hash=sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15 \
--hash=sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b \
--hash=sha256:305889baa4043a09e5b76f8e2a51d4ffba44259f6b4c72dec8ca56207d9c6fe1 \
--hash=sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9 \
--hash=sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675 \
--hash=sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899 \
--hash=sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7 \
--hash=sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7 \
--hash=sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312 \
--hash=sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa \
--hash=sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91 \
--hash=sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b \
--hash=sha256:77159f5d5b5c14f7c34073862a6b7d34944075d9f93e681638f6d753606c6ce6 \
--hash=sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3 \
--hash=sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334 \
--hash=sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5 \
--hash=sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3 \
--hash=sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe \
--hash=sha256:a1a45e0bb052edf6a1d3a93baef85319733a888363938e1fc9924cb00c8df24c \
--hash=sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed \
--hash=sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337 \
--hash=sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880 \
--hash=sha256:aa2267c6a303eb483de8d02db2871afb5c5fc15618d894300b88958f729ad74f \
--hash=sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d \
--hash=sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248 \
--hash=sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d \
--hash=sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf \
--hash=sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512 \
--hash=sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069 \
--hash=sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb \
--hash=sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942 \
--hash=sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d \
--hash=sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31 \
--hash=sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92 \
--hash=sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5 \
--hash=sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28 \
--hash=sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d \
--hash=sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1 \
--hash=sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2 \
--hash=sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875 \
--hash=sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412
# via ruamel-yaml
urllib3==2.2.1 \
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \
--hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19
# via requests
xmltodict==0.13.0 \
--hash=sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56 \
--hash=sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852
# via jc

View File

@ -44,6 +44,18 @@ function detect_ip() {
echo "IP address is ${INVENTREE_IP}"
}
function detect_python() {
# Detect if there is already a python version installed in /opt/inventree/env/lib
if test -f "${APP_HOME}/env/bin/python"; then
echo "# Python environment already present"
# Extract earliest python version initialised from /opt/inventree/env/lib
SETUP_PYTHON=$(ls -1 ${APP_HOME}/env/bin/python* | sort | head -n 1)
echo "# Found earliest version: ${SETUP_PYTHON}"
else
echo "# No python environment found - using ${SETUP_PYTHON}"
fi
}
function get_env() {
envname=$1
@ -90,7 +102,7 @@ function detect_envs() {
echo "# Using existing config file: ${INVENTREE_CONFIG_FILE}"
# Install parser
pip install jc==1.25.2 -q
pip install --require-hashes -r ${APP_HOME}/contrib/dev_reqs/requirements.txt -q
# Load config
local CONF=$(cat ${INVENTREE_CONFIG_FILE} | jc --yaml)
@ -163,12 +175,20 @@ function create_initscripts() {
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && ${SETUP_PYTHON} -m venv env"
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && env/bin/pip install invoke wheel"
# Check INSTALLER_EXTRA exists and load it
if test -f "${APP_HOME}/INSTALLER_EXTRA"; then
echo "# Loading extra packages from INSTALLER_EXTRA"
source ${APP_HOME}/INSTALLER_EXTRA
fi
if [ -n "${SETUP_EXTRA_PIP}" ]; then
echo "# Installing extra pip packages"
if [ -n "${SETUP_DEBUG}" ]; then
echo "# Extra pip packages: ${SETUP_EXTRA_PIP}"
fi
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && env/bin/pip install ${SETUP_EXTRA_PIP}"
# Write extra packages to INSTALLER_EXTRA
echo "SETUP_EXTRA_PIP='${SETUP_EXTRA_PIP}'" >>${APP_HOME}/INSTALLER_EXTRA
fi
fi
@ -283,6 +303,20 @@ function set_env() {
chown ${APP_USER}:${APP_GROUP} ${DATA_DIR} ${INVENTREE_CONFIG_FILE}
}
function set_site() {
# Ensure IP is known
if [ -z "${INVENTREE_IP}" ]; then
echo "# No IP address found - skipping"
return
fi
# Check if INVENTREE_SITE_URL in inventree config
if [ -z "$(inventree config:get INVENTREE_SITE_URL)" ]; then
echo "# Setting up InvenTree site URL"
inventree config:set INVENTREE_SITE_URL=http://${INVENTREE_IP}
fi
}
function final_message() {
echo -e "####################################################################################"
echo -e "This InvenTree install uses nginx, the settings for the webserver can be found in"

View File

@ -33,6 +33,7 @@ detect_envs
detect_docker
detect_initcmd
detect_ip
detect_python
# create processes
create_initscripts
@ -45,6 +46,7 @@ update_or_install
if [ "${SETUP_CONF_LOADED}" = "true" ]; then
set_env
fi
set_site
start_inventree
# show info

4
docs/.gitignore vendored
View File

@ -13,6 +13,10 @@ site/
# Generated API schema files
docs/api/schema/*.yml
# Temporary cache files
url_cache.txt
invoke-commands.txt
# Temp files
releases.json
versions.json

View File

@ -12,7 +12,7 @@ Run the following commands from the top-level project directory:
```
$ git clone https://github.com/inventree/inventree
$ pip install -r docs/requirements.txt
$ pip install --require-hashes -r docs/requirements.txt
```
## Serve Locally

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -79,6 +79,18 @@ Each *Build Order* has an associated *Status* flag, which indicates the state of
| `Cancelled` | Build has been cancelled |
| `Completed` | Build has been completed |
**Source Code**
Refer to the source code for the Build Order status codes:
::: build.status_codes.BuildStatus
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Stock Allocations
When a *Build Order* is created, we then have the ability to *allocate* stock items against that build order. The particular parts we need to allocate against the build are specified by the BOM for the part we are assembling.

View File

@ -22,9 +22,9 @@ To setup a development environment using [docker](../start/docker.md), run the f
```bash
git clone https://github.com/inventree/InvenTree.git && cd InvenTree
docker compose run inventree-dev-server invoke install
docker compose run inventree-dev-server invoke setup-test --dev
docker compose up -d
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run --rm inventree-dev-server invoke install
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run --rm inventree-dev-server invoke setup-test --dev
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml up -d
```
### Bare Metal
@ -54,13 +54,23 @@ invoke setup-dev
InvenTree roughly follow the [GitLab flow](https://docs.gitlab.com/ee/topics/gitlab_flow.html) 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:
- `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`)
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
InvenTree version numbering follows the [semantic versioning](https://semver.org/) specification.
### Master Branch
### Main Development Branch
The HEAD of the "main" or "master" branch of InvenTree represents the current "latest" state of code development.
The HEAD of the "master" branch of InvenTree represents the current "latest" state of code development.
- All feature branches are merged into master
- All bug fixes are merged into master
@ -73,7 +83,6 @@ Feature branches should be branched *from* the *master* branch.
- One major feature per branch / pull request
- Feature pull requests are merged back *into* the master branch
- Features *may* also be merged into a release candidate branch
### Stable Branch
@ -82,21 +91,28 @@ The HEAD of the "stable" branch represents the latest stable release code.
- Versioned releases are merged into the "stable" branch
- Bug fix branches are made *from* the "stable" branch
#### Release Candidate Branches
- Release candidate branches are made from master, and merged into stable.
- RC branches are targeted at a major/minor version e.g. "0.5"
- When a release candidate branch is merged into *stable*, the release is tagged
#### Bugfix Branches
### Bugfix Branches
- If a bug is discovered in a tagged release version of InvenTree, a "bugfix" or "hotfix" branch should be made *from* that tagged release
- When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2)
- The bugfix *must* also be cherry picked into the *master* branch.
- A bugfix *might* also be backported from *master* to the *stable* branch automatically if marked with the `backport` label.
### Translation Branches
Crowdin is used for web-based translation management. The handling of files is fully automated, the `l10n` and `l10_crowdin` branches are used to manage the translation process and are not meant to be touched manually by anyone.
The translation process is as follows:
1. Commits to `master` trigger CI by GitHub Actions
2. Translation source files are created and automatically pushed to the `l10n` branch - this is the source branch for Crowdin
3. Crowdin picks up on the new source files and makes them available for translation
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
## API versioning
The [API version](https://github.com/inventree/InvenTree/blob/master/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.
## Environment

View File

@ -18,12 +18,13 @@ You need to make sure that you have the following tools installed before continu
#### Docker Containers
The InvenTree devcontainer setup will install two docker containers:
The InvenTree devcontainer setup will install the following docker containers:
| Container | Description |
| --- | --- |
| inventree | InvenTree host server |
| db | InvenTree database (postgresql) |
| inventree | InvenTree server |
| redis | Redis server for caching |
#### Setup/Installation
@ -119,3 +120,9 @@ If you are running a devcontainer in Windows, you may experience some performanc
For a significant improvement in performance, the source code should be installed into the **WSL 2** filesystem (not on your "Windows" filesystem). This will greatly improve file access performance, and also make the devcontainer much more responsive to file system changes.
You can also refer to the [Improve disk performance guide](https://code.visualstudio.com/remote/advancedcontainers/improve-performance) for more information.
### Redis Caching
The devcontainer setup provides a [redis](https://redis.io/) container which can be used for managing global cache. By default this is disabled, but it can be easily enabled for testing or developing with the [redis cache](../start/config.md#caching) enabled.
To enable the cache, locate the InvenTree configuration file (`./dev/config.yaml`) and set the `cache.enabled` setting to `True`.

View File

@ -149,7 +149,7 @@ class SampleActionPlugin(ActionMixin, InvenTreePlugin):
# metadata
AUTHOR = "Sample Author"
DESCRIPTION = "A very basic plugin with one mixin"
PUBLISH_DATE = "22.02.2222"
PUBLISH_DATE = "2222-02-22"
VERSION = "1.2.3" # We recommend semver and increase the major version with each new major release of InvenTree
WEBSITE = "https://example.com/"
LICENSE = "MIT" # use what you want - OSI approved is &hearts;

View File

@ -9,14 +9,14 @@ The InvenTree server code supports an extensible plugin architecture, allowing c
Plugins can be added from multiple sources:
- Plugins can be installed in InvenTrees venv via PIP (python package manager)
- Custom plugins should be placed in the directory `./src/backend/InvenTree/plugins`.
- Custom plugins should be placed in the directory `./data/plugins`.
- InvenTree built-in plugins are located in the directory `./src/backend/InvenTree/plugin/builtin`.
For further information, read more about [installing plugins](./plugins/install.md).
### Plugin Base Class
Custom plugins must inherit from the [InvenTreePlugin class](https://github.com/inventree/InvenTree/blob/2d1776a151721d65d0ae007049d358085b2fcfd5/InvenTree/plugin/plugin.py#L204). Any plugins installed via the methods outlined above will be "discovered" when the InvenTree server launches.
Custom plugins must inherit from the [InvenTreePlugin class]({{ sourcefile("src/backend/InvenTree/plugin/plugin.py") }}). Any plugins installed via the methods outlined above will be "discovered" when the InvenTree server launches.
!!! warning "Namechange"
The name of the base class was changed with `0.7.0` from `IntegrationPluginBase` to `InvenTreePlugin`. While the old name is still available till `0.8.0` we strongly suggest upgrading your plugins. Deprecation warnings are raised if the old name is used.
@ -28,7 +28,7 @@ Please read all release notes and watch out for warnings - we generally provide
#### Plugins
General classes and mechanisms are provided under the `plugin` [namespaces](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/plugin/__init__.py). These include:
General classes and mechanisms are provided under the `plugin` [namespaces]({{ sourcefile("src/backend/InvenTree/plugin/__init__.py") }}). These include:
```python
# Management objects
@ -44,7 +44,7 @@ MixinNotImplementedError # Is raised if a mixin was not implemented (core mec
#### Mixins
Mixins are split up internally to keep the source tree clean and enable better testing separation. All public APIs that should be used are exposed under `plugin.mixins`. These include all built-in mixins and notification methods. An up-to-date reference can be found in the source code (current master can be [found here](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/plugin/mixins/__init__.py)).
Mixins are split up internally to keep the source tree clean and enable better testing separation. All public APIs that should be used are exposed under `plugin.mixins`. These include all built-in mixins and notification methods. An up-to-date reference can be found in the source code [can be found here]({{ sourcefile("src/backend/InvenTree/plugin/mixins/__init__.py") }}).
#### Models and other internal InvenTree APIs
@ -72,7 +72,7 @@ MIN_VERSION = None # Lowest InvenTree version number that is supported by the p
MAX_VERSION = None # Highest InvenTree version number that is supported by the plugin
```
Refer to the [sample plugins](https://github.com/inventree/InvenTree/tree/master/src/backend/InvenTree/plugin/samples) for further examples.
Refer to the [sample plugins]({{ sourcedir("src/backend/InvenTree/plugin/samples") }}) for further examples.
### Plugin Config

View File

@ -15,4 +15,14 @@ POST {
}
```
For an example of a very simple action plugin, refer to `/src/backend/InvenTree/plugin/samples/integratoni/simpleactionplugin.py`
### Sample Plugin
A sample action plugin is provided in the `InvenTree` source code, which can be used as a template for creating custom action plugins:
::: plugin.samples.integration.simpleactionplugin.SimpleActionPlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []

View File

@ -5,3 +5,15 @@ title: Schedule Mixin
## APICallMixin
The APICallMixin class provides basic functionality for integration with an external API.
### Sample Plugin
The following example demonstrates how to use the `APICallMixin` class to make a simple API call:
::: plugin.samples.integration.api_caller.SampleApiCallerPlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []

View File

@ -2,11 +2,11 @@
title: Barcode Mixin
---
### Barcode Plugins
## Barcode Plugins
InvenTree supports decoding of arbitrary barcode data via a **Barcode Plugin** interface. Barcode data POSTed to the `/api/barcode/` endpoint will be supplied to all loaded barcode plugins, and the first plugin to successfully interpret the barcode data will return a response to the client.
InvenTree can generate native QR codes to represent database objects (e.g. a single StockItem). This barcode can then be used to perform quick lookup of a stock item or location in the database. A client application (for example the InvenTree mobile app) scans a barcode, and sends the barcode data to the InvenTree server. The server then uses the **InvenTreeBarcodePlugin** (found at `/src/backend/InvenTree/plugins/barcode/inventree.py`) to decode the supplied barcode data.
InvenTree can generate native QR codes to represent database objects (e.g. a single StockItem). This barcode can then be used to perform quick lookup of a stock item or location in the database. A client application (for example the InvenTree mobile app) scans a barcode, and sends the barcode data to the InvenTree server. The server then uses the **InvenTreeBarcodePlugin** (found at `src/backend/InvenTree/plugin/builtin/barcodes/inventree_barcode.py`) to decode the supplied barcode data.
Any third-party barcodes can be decoded by writing a matching plugin to decode the barcode data. These plugins could then perform a server-side action or render a JSON response back to the client for further action.
@ -24,7 +24,21 @@ POST {
}
```
### Example
### Builtin Plugin
The InvenTree server includes a builtin barcode plugin which can decode QR codes generated by the server. This plugin is enabled by default.
::: plugin.builtin.barcodes.inventree_barcode.InvenTreeInternalBarcodePlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Example Plugin
Please find below a very simple example that is executed each time a barcode is scanned.
```python

View File

@ -6,7 +6,24 @@ title: Currency Exchange Mixin
The `CurrencyExchangeMixin` class enabled plugins to provide custom backends for updating currency exchange rate information.
Any implementing classes must provide the `update_exchange_rates` method. A simple example is shown below (with fake data).
Any implementing classes must provide the `update_exchange_rates` method.
### Builtin Plugin
The default builtin plugin for handling currency exchange rates is the `InvenTreeCurrencyExchangePlugin` class.
::: plugin.builtin.integration.currency_exchange.InvenTreeCurrencyExchange
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Sample Plugin
A simple example is shown below (with fake data).
```python

View File

@ -15,56 +15,34 @@ When a certain (server-side) event occurs, the background worker passes the even
{% include 'img.html' %}
{% endwith %}
### Example (all events)
### Sample Plugin - All events
Implementing classes must at least provide a `process_event` function:
```python
class EventPlugin(EventMixin, InvenTreePlugin):
"""
A simple example plugin which responds to events on the InvenTree server.
::: plugin.samples.event.event_sample.EventPluginSample
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
This example simply prints out the event information.
A more complex plugin could respond to specific events however it wanted.
"""
NAME = "EventPlugin"
SLUG = "event"
TITLE = "Triggered Events"
def process_event(self, event, *args, **kwargs):
print(f"Processing triggered event: '{event}'")
```
### Example (specific events)
### Sample Plugin - Specific Events
If you want to process just some specific events, you can also implement the `wants_process_event` function to decide if you want to process this event or not. This function will be executed synchronously, so be aware that it should contain simple logic.
Overall this function can reduce the workload on the background workers significantly since less events are queued to be processed.
```python
class EventPlugin(EventMixin, InvenTreePlugin):
"""
A simple example plugin which responds to 'salesordershipment.completed' event on the InvenTree server.
::: plugin.samples.event.filtered_event_sample.FilteredEventPluginSample
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
This example simply prints out the event information.
A more complex plugin can run enhanced logic on this event.
"""
NAME = "EventPlugin"
SLUG = "event"
TITLE = "Triggered Events"
def wants_process_event(self, event):
"""Here you can decide if this event should be send to `process_event` or not."""
return event == "salesordershipment.completed"
def process_event(self, event, *args, **kwargs):
"""Here you can run you'r specific logic."""
print(f"Sales order was completely shipped: '{args}' '{kwargs}'")
```
### Events
## Events
Events are passed through using a string identifier, e.g. `build.completed`

View File

@ -74,10 +74,10 @@ Admin users can install plugins directly from the web interface, via the "Plugin
#### Local Directory
Custom plugins can be placed in the `src/InvenTree/plugins/` directory, where they will be automatically discovered. This can be useful for developing and testing plugins, but can prove more difficult in production (e.g. when using Docker).
Custom plugins can be placed in the `data/plugins/` directory, where they will be automatically discovered. This can be useful for developing and testing plugins, but can prove more difficult in production (e.g. when using Docker).
!!! info "Git Tracking"
The `src/backend/InvenTree/plugins/` directory is excluded from Git version tracking - any plugin files here will be hidden from Git
The `data/plugins/` directory is excluded from Git version tracking - any plugin files here will be hidden from Git
!!! warning "Not Recommended For Production"
Loading plugins via the local *plugins* directory is not recommended for production. If you cannot use PIP installation (above), specify a custom plugin directory (below) or use a [VCS](https://pip.pypa.io/en/stable/topics/vcs-support/) as a plugin install source.

View File

@ -172,6 +172,14 @@ InvenTree supplies the `InvenTreeLabelPlugin` out of the box, which generates a
The default plugin also features a *DEBUG* mode which generates a raw HTML output, rather than PDF. This can be handy for tracking down any template rendering errors in your labels.
::: plugin.builtin.labels.inventree_label.InvenTreeLabelPlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Available Data
The *label* data are supplied to the plugin in both `PDF` and `PNG` formats. This provides compatibility with a great range of label printers "out of the box". Conversion to other formats, if required, is left as an exercise for the plugin developer.

View File

@ -28,4 +28,16 @@ If a locate plugin is installed and activated, the [InvenTree mobile app](../../
### Implementation
Refer to the [InvenTree source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/plugin/samples/locate/locate_sample.py) for a simple implementation example.
Refer to the [InvenTree source code]({{ sourcefile("src/backend/InvenTree/plugin/samples/locate/locate_sample.py") }}) for a simple implementation example.
### Sample Plugin
A simple example is provided in the InvenTree code base:
::: plugin.samples.locate.locate_sample.SampleLocatePlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []

View File

@ -52,6 +52,18 @@ Or to add a template file that will be rendered as javascript code, from the plu
Note : see convention for template directory above.
## Sample Plugin
A sample plugin is provided in the InvenTree code base:
::: plugin.samples.integration.custom_panel_sample.CustomPanelSample
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
## Example Implementations
Refer to the `CustomPanelSample` example class in the `./plugin/samples/integration/` directory, for a fully worked example of how custom UI panels can be implemented.

View File

@ -14,48 +14,14 @@ A plugin which implements the ReportMixin mixin can define the `add_report_conte
Additionally the `add_label_context` method, allowing custom context data to be added to a label template at time of printing.
### Example
### Sample Plugin
A sample plugin which provides additional context data to the report templates can be found [in the InvenTree source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/plugin/samples/integration/report_plugin_sample.py):
A sample plugin which provides additional context data to the report templates is available:
```python
"""Sample plugin for extending reporting functionality"""
import random
from plugin import InvenTreePlugin
from plugin.mixins import ReportMixin
from report.models import PurchaseOrderReport
class SampleReportPlugin(ReportMixin, InvenTreePlugin):
"""Sample plugin which provides extra context data to a report"""
NAME = "Sample Report Plugin"
SLUG = "reportexample"
TITLE = "Sample Report Plugin"
DESCRIPTION = "A sample plugin which provides extra context data to a report"
VERSION = "1.0"
def some_custom_function(self):
"""Some custom function which is not required for the plugin to function"""
return random.randint(0, 100)
def add_report_context(self, report_instance, model_instance, request, context):
"""Add example content to the report instance"""
# We can add any extra context data we want to the report
# Generate a random string of data
context['random_text'] = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=20))
# Call a custom method
context['random_int'] = self.some_custom_function()
# We can also add extra data to the context which is specific to the report type
context['is_purchase_order'] = isinstance(report_instance, PurchaseOrderReport)
# We can also use the 'request' object to add extra context data
context['request_method'] = request.method
```
::: plugin.samples.integration.report_plugin_sample.SampleReportPlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []

View File

@ -18,45 +18,14 @@ The ScheduleMixin class provides a plugin with the ability to call functions at
{% include 'img.html' %}
{% endwith %}
### Example
### SamplePlugin
An example of a plugin which supports scheduled tasks:
```python
class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, InvenTreePlugin):
"""
Sample plugin which runs a scheduled task, and provides user configuration.
"""
NAME = "Scheduled Tasks"
SLUG = 'schedule'
SCHEDULED_TASKS = {
'global': {
'func': 'some_module.function',
'schedule': 'H', # Run every hour
},
'member': {
'func': 'foo',
'schedule': 'I', # Minutes
'minutes': 15,
},
}
SETTINGS = {
'SECRET': {
'name': 'A secret',
'description': 'User configurable value',
},
}
def foo(self):
"""
This function runs every 15 minutes
"""
secret_value = self.get_setting('SECRET')
print(f"foo - SECRET = {secret_value})
```
!!! info "More Info"
For more information on any of the methods described below, refer to the InvenTree source code. [A working example is available as a starting point](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/plugin/samples/integration/scheduled_task.py).
::: plugin.samples.integration.scheduled_task.ScheduledTaskPlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []

View File

@ -15,7 +15,7 @@ The dict must be formatted similar to the following sample that shows how to use
Take a look at the settings defined in `InvenTree.common.models.InvenTreeSetting` for all possible parameters.
### Example
### Example Plugin
Below is a simple example of how a plugin can implement settings:

View File

@ -65,7 +65,7 @@ Additionally, add the following imports after the extended line.
#### Blocks
The page_base file is split into multiple sections called blocks. This allows you to implement sections of the webpage while getting many items like navbars, sidebars, and general layout provided for you.
The current default page base can be found [here](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/templates/page_base.html). Look through this file to determine overridable blocks. The [stock app](https://github.com/inventree/InvenTree/tree/master/src/backend/InvenTree/stock) offers a great example of implementing these blocks.
The current default page base can be found [here]({{ sourcefile("src/backend/InvenTree/templates/page_base.html") }}). Look through this file to determine overridable blocks. The [stock app]({{ sourcedir("src/backend/InvenTree/stock") }}) offers a great example of implementing these blocks.
!!! warning "Sidebar Block"
You may notice that implementing the `sidebar` block doesn't initially work. Be sure to enable the sidebar using JavaScript. This can be achieved by appending the following code, replacing `label` with a label of your choosing, to the end of your template file.

View File

@ -9,34 +9,40 @@ The `ValidationMixin` class enables plugins to perform custom validation of obje
Any of the methods described below can be implemented in a custom plugin to provide functionality as required.
!!! info "More Info"
For more information on any of the methods described below, refer to the InvenTree source code. [A working example is available as a starting point](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/plugin/samples/integration/validation_sample.py).
For more information on any of the methods described below, refer to the InvenTree source code. [A working example is available as a starting point]({{ sourcefile("src/backend/InvenTree/plugin/samples/integration/validation_sample.py") }}).
!!! info "Multi Plugin Support"
It is possible to have multiple plugins loaded simultaneously which support validation methods. For example when validating a field, if one plugin returns a null value (`None`) then the *next* plugin (if available) will be queried.
## Model Deletion
Any model which inherits the `PluginValidationMixin` class is exposed to the plugin system for custom deletion validation. Before the model is deleted from the database, it is first passed to the plugin ecosystem to check if it really should be deleted.
A custom plugin may implement the `validate_model_deletion` method to perform custom validation on the model instance before it is deleted.
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_model_deletion
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_sources: True
summary: False
members: []
## Model Validation
Any model which inherits the `PluginValidationMixin` mixin class is exposed to the plugin system for custom validation. Before the model is saved to the database (either when created, or updated), it is first passed to the plugin ecosystem for validation.
Any plugin which inherits the `ValidationMixin` can implement the `validate_model_instance` method, and run a custom validation routine.
The `validate_model_instance` method is passed the following arguments:
| Argument | Description |
| --- | --- |
| `instance` | The model instance to be validated |
| `deltas` | A dict of field deltas (if the instance is being updated) |
```python
def validate_model_instance(self, instance, deltas=None):
"""Validate the supplied model instance.
Arguments:
instance: The model instance to be validated
deltas: A dict of field deltas (if the instance is being updated)
"""
...
```
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_model_instance
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_sources: True
summary: False
members: []
### Error Messages
@ -52,7 +58,7 @@ To indicate a *field* validation error (i.e. the validation error applies only t
Note that an error can be which corresponds to multiple model instance fields.
### Example
### Example Plugin
Presented below is a simple working example for a plugin which implements the `validate_model_instance` method:
@ -116,7 +122,7 @@ Validation of the Part IPN (Internal Part Number) field is exposed to custom plu
The `validate_batch_code` method allows plugins to raise an error if a batch code input by the user does not meet a particular pattern.
The `generate_batch_code` method can be implemented to generate a new batch code.
The `generate_batch_code` method can be implemented to generate a new batch code, based on a set of provided information.
### Serial Numbers
@ -182,3 +188,15 @@ def increment_serial_number(self, serial: str):
return val
```
## Sample Plugin
A sample plugin which implements custom validation routines is provided in the InvenTree source code:
::: plugin.samples.integration.validation_sample.SampleValidatorPlugin
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []

View File

@ -14,7 +14,7 @@ Navigate to the "Settings" page and click on the "Display" tab, you should see t
{% include 'img.html' %}
{% endwith %}
The drop-down list let's you select any other color theme found in your static folder (see next section to find out how to [add color themes](#add-color-themes)). Once selected, click on the "Apply Theme" button for the new color theme to be activated.
The drop-down list let's you select any other color theme found in your static folder (see next section to find out how to [add color themes](#add-color-theme)). Once selected, click on the "Apply Theme" button for the new color theme to be activated.
!!! info "Per-user Setting"
Color themes are "user specific" which means that changing the color theme in your own settings won't affect other users.

View File

@ -22,6 +22,20 @@ Each Purchase Order has a specific status code which indicates the current state
| In Progress | The purchase order has been issued to the supplier, and is in progress |
| Complete | The purchase order has been completed, and is now closed |
| Cancelled | The purchase order was cancelled, and is now closed |
| Lost | The purchase order was lost, and is now closed |
| Returned | The purchase order was returned, and is now closed |
**Source Code**
Refer to the source code for the Purchase Order status codes:
::: order.status_codes.PurchaseOrderStatus
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Purchase Order Currency

View File

@ -48,6 +48,18 @@ Each Return Order has a specific status code, as follows:
| Complete | The return order was marked as complete, and is now closed |
| Cancelled | The return order was cancelled, and is now closed |
**Source Code**
Refer to the source code for the Return Order status codes:
::: order.status_codes.ReturnOrderStatus
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
## Create a Return Order
From the Return Order index, click on <span class='badge inventree add'><span class='fas fa-plus-circle'></span> New Return Order</span> which opens the "Create Return Order" form.
@ -98,7 +110,7 @@ While [line items](#line-items) must reference a particular stock item, extra li
## Return Order Reports
Custom [reports](../report/return_order.md) can be generated against each Return Order.
Custom [reports](../report/templates.md) can be generated against each Return Order.
### Calendar view

View File

@ -20,8 +20,23 @@ Each Sales Order has a specific status code, which represents the state of the o
| --- | --- |
| Pending | The sales order has been created, but has not been finalized or submitted |
| In Progress | The sales order has been issued, and is in progress |
| Shipped | The sales order has been completed, and is now closed |
| Shipped | The sales order has been shipped, but is not yet complete |
| Complete | The sales order is fully completed, and is now closed |
| Cancelled | The sales order was cancelled, and is now closed |
| Lost | The sales order was lost, and is now closed |
| Returned | The sales order was returned, and is now closed |
**Source Code**
Refer to the source code for the Sales Order status codes:
::: order.status_codes.SalesOrderStatus
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Sales Order Currency
@ -83,7 +98,7 @@ To view all the completed shipment, click on the <span class="badge inventree na
### Complete Order
Once all items in the sales order have been shipped, click on <span class="badge inventree add"><span class='fas fa-check-circle'></span> Complete Order</span> to mark the sales order as complete.
Once all items in the sales order have been shipped, click on <span class="badge inventree add"><span class='fas fa-check-circle'></span> Complete Order</span> to mark the sales order as shipped. Confirm then click on <span class="badge inventree confirm">Submit</span> to complete the order.
### Cancel Order

View File

@ -24,7 +24,7 @@ Refer to the [report documentation](../report/report.md) for further information
!!! warning "LaTeX Support"
LaTeX report templates are no longer supported for a number of technical and ideological reasons
[#1292](https://github.com/inventree/InvenTree/pull/1292) adds support for build order / work order reports. Refer to the [build report documentation](../report/build.md) for further information.
[#1292](https://github.com/inventree/InvenTree/pull/1292) adds support for build order / work order reports. Refer to the [report documentation](../report/templates.md) for further information.
### Inherited BOM Items

View File

@ -32,7 +32,7 @@ Details on how to create and manage manufacturer parts were added
[#1462](https://github.com/inventree/InvenTree/pull/1417) adds the ability to
create a QR code containing the URL of a StockItem, which can be opened directly
on a portable device using the camera or a QR code scanner. More details [here](../report/labels.md#url-style-qr-code).
on a portable device using the camera or a QR code scanner. More details [here](../report/labels.md).
## Major Bug Fixes

View File

@ -53,7 +53,7 @@ This release also provides a marked improvement in unit testing and code coverag
[#2372](https://github.com/inventree/InvenTree/pull/2372) provides an overhaul of notifications, allowing users to view their notifications directly in the InvenTree interface.
### Why are you hiding my name?
[#2861](https://github.com/inventree/InvenTree/pull/2861) adds several changes to enable admins to remove more of InvenTrees branding. Change logo, hide the about-modal for all but superusers and add custom messages to login and main navbar. Check out [the docs](../start/config.md#customisation-options).
[#2861](https://github.com/inventree/InvenTree/pull/2861) adds several changes to enable admins to remove more of InvenTrees branding. Change logo, hide the about-modal for all but superusers and add custom messages to login and main navbar. Check out [the docs](../start/config.md#customization-options)
### Label Printing Plugin

View File

@ -1,186 +0,0 @@
---
title: BOM Generation
---
## BOM Generation
The bill of materials is an essential part of the documentation that needs to be sent to the factory. A simple csv export is OK to be important into SMT machines. But for human readable documentation it might not be sufficient. Additional information is needed. The Inventree report system allows to generate BOM well formatted BOM reports.
### Context variables
| Variable | Description |
| --- | --- |
| bom_items | Query set that contains all BOM items |
| bom_items...sub_part | One component of the BOM |
| bom_items...quantity | Number of parts |
| bom_items...reference | Reference designators of the part |
| bom_items...substitutes | Query set that contains substitutes of the part if any exist in the BOM |
### Examples
#### BOM
The following picture shows a simple example for a PCB with just three components from two different parts.
{% with id="report-options", url="report/bom_example.png", description="BOM example" %} {% include 'img.html' %} {% endwith %}
This example has been created using the following html template:
```html
{% raw %}
{% extends "report/inventree_report_base.html" %}
{% load i18n %}
{% load report %}
{% load inventree_extras %}
{% block page_margin %}
margin-left: 2cm;
margin-right: 1cm;
margin-top: 4cm;
{% endblock %}
{% block bottom_left %}
content: "v{{report_revision}} - {% format_date date %}";
{% endblock %}
{% block bottom_center %}
content: "InvenTree v{% inventree_version %}";
{% endblock %}
{% block style %}
.header-left {
text-align: left;
float: left;
}
table {
border: 1px solid #eee;
border-radius: 3px;
border-collapse: collapse;
width: 100%;
font-size: 80%;
}
table td {
border: 1px solid #eee;
}
{% endblock %}
{% block header_content %}
<div class='header-left'>
<h3>{% trans "Bill of Materials" %}</h3>
</div>
{% endblock %}
{% block page_content %}
<table>
<tr> <td>Board</td><td>{{ part.IPN }}</td> </tr>
<tr> <td>Description</td><td>{{ part.description }}</td> </tr>
<tr> <td>User</td><td>{{ user }}</td> </tr>
<tr> <td>Date</td><td>{{ date }}</td> </tr>
<tr> <td>Number of different components (codes)</td><td>{{ bom_items.count }}</td> </tr>
</table>
<br>
<table class='table table-striped table-condensed'>
<thead>
<tr>
<th>{% trans "IPN" %}</th>
<th>{% trans "MPN" %}</th>
<th>{% trans "Manufacturer" %}</th>
<th>{% trans "Quantity" %}</th>
<th>{% trans "Reference" %}</th>
<th>{% trans "Substitute" %}</th>
</tr>
</thead>
<tbody>
{% for line in bom_items.all %}
<tr>
<td>{{ line.sub_part.IPN }}</td>
<td>{{ line.sub_part.name }}</td>
<td>
{% for manf in line.sub_part.manufacturer_parts.all %}
{{ manf.manufacturer.name }}
{% endfor %}
</td>
<td>{% decimal line.quantity %}</td>
<td>{{ line.reference }}</td>
<td>
{% for sub in line.substitutes.all %}
{{ sub.part.IPN }}<br>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% endraw %}
```
#### Pick List
When all material has been allocated someone has to pick all things from the warehouse.
In case you need a printed pick list you can use the following template. This it just the
table. All other info and CSS has been left out for simplicity. Please have a look at the
BOM report for details.
{% raw %}
```html
<table class='changes-table'>
<thead>
<tr>
<th>Original IPN</th>
<th>Allocated Part</th>
<th>Location</th>
<th>PCS</th>
</tr>
</thead>
<tbody>
{% for line in build.allocated_stock.all %}
<tr>
<td> {{ line.bom_item.sub_part.IPN }} </td>
{% if line.stock_item.part.IPN != line.bom_item.sub_part.IPN %}
<td class='chg'> {{ line.stock_item.part.IPN }} </td>
{% else %}
<td> {{ line.stock_item.part.IPN }} </td>
{% endif %}
<td> {{ line.stock_item.location.pathstring }} </td>
<td> {{ line.quantity }} </td>
</tr>
{% endfor %}
</tbody>
</table>
```
{% endraw %}
Here we have a loop that runs through all allocated parts for the build. For each part
we list the original IPN from the BOM and the IPN of the allocated part. These can differ
in case you have substitutes or template/variants in the BOM. In case the parts differ
we use a different format for the table cell e.g. print bold font or red color.
For the picker we list the full path names of the stock locations and the quantity
that is needed for the build. This will result in the following printout:
{% with id="picklist", url="report/picklist.png", description="Picklist Example" %} {% include "img.html" %} {% endwith %}
For those of you who would like to replace the "/" by something else because it is hard
to read in some fonts use the following trick:
{% raw %}
```html
<td> {% for loc in line.stock_item.location.path %}{{ loc.name }}{% if not forloop.last %}-{% endif %}{% endfor %} </td>
```
{% endraw %}
Here we use location.path which is a query set that contains the location path up to the
topmost parent. We use a loop to cycle through that and print the .name of the entry followed
by a "-". The foorloop.last is a Django trick that allows us to not print the "-" after
the last entry. The result looks like here:
{% with id="picklist_with_path", url="report/picklist_with_path.png", description="Picklist Example" %} {% include "img.html" %} {% endwith %}
Finally added a `{% raw %}|floatformat:0{% endraw %}` to the quantity that removes the trailing zeros.
### Default Report Template
A default *BOM Report* template is provided out of the box, which is useful for generating simple test reports. Furthermore, it may be used as a starting point for developing custom BOM reports:
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_bill_of_materials_report.html) for the default test report template.

View File

@ -1,324 +0,0 @@
---
title: Build Order Report
---
## Build Order Report
Custom build order reports may be generated against any given [Build Order](../build/build.md). For example, build order reports can be used to generate work orders.
### Build Filters
A build order report template may define a set of filters against which [Build Order](../build/build.md) items are sorted.
### Context Variables
In addition to the default report context variables, the following context variables are made available to the build order report template for rendering:
| Variable | Description |
| --- | --- |
| build | The build object the report is being generated against |
| part | The [Part](./context_variables.md#part) object that the build references |
| line_items | A shortcut for [build.line_items](#build) |
| bom_items | A shortcut for [build.bom_items](#build) |
| build_outputs | A shortcut for [build.build_outputs](#build) |
| reference | The build order reference string |
| quantity | Build order quantity (number of assemblies being built) |
#### build
The following variables are accessed by build.variable
| Variable | Description |
| --- | --- |
| active | Boolean that tells if the build is active |
| batch | Batch code transferred to build parts (optional) |
| line_items | A query set with all the build line items associated with the build |
| bom_items | A query set with all BOM items for the part being assembled |
| build_outputs | A queryset containing all build output ([Stock Item](../stock/stock.md)) objects associated with this build |
| can_complete | Boolean that tells if the build can be completed. Means: All material allocated and all parts have been build. |
| are_untracked_parts_allocated | Boolean that tells if all bom_items have allocated stock_items. |
| creation_date | Date where the build has been created |
| completion_date | Date the build was completed (or, if incomplete, the expected date of completion) |
| completed_by | The [User](./context_variables.md#user) that completed the build |
| is_overdue | Boolean that tells if the build is overdue |
| is_complete | Boolean that tells if the build is complete |
| issued_by | The [User](./context_variables.md#user) who created the build |
| link | External URL for extra information |
| notes | Text notes |
| parent | Reference to a parent build object if this is a sub build |
| part | The [Part](./context_variables.md#part) to be built (from component BOM items) |
| quantity | Build order quantity (total number of assembly outputs) |
| completed | The number out outputs which have been completed |
| reference | Build order reference (required, must be unique) |
| required_parts | A query set with all parts that are required for the build |
| responsible | Owner responsible for completing the build. This can be a user or a group. Depending on that further context variables differ |
| sales_order | References to a [Sales Order](./context_variables.md#salesorder) object for which this build is required (e.g. the output of this build will be used to fulfil a sales order) |
| status | The status of the build. 20 means 'Production' |
| sub_build_count | Number of sub builds |
| sub_builds | Query set with all sub builds |
| target_date | Date the build will be overdue |
| take_from | [StockLocation](./context_variables.md#stocklocation) to take stock from to make this build (if blank, can take from anywhere) |
| title | The full name of the build |
| description | The description of the build |
| allocated_stock.all | A query set with all allocated stock items for the build |
As usual items in a query sets can be selected by adding a .n to the set e.g. build.required_parts.0
will result in the first part of the list. Each query set has again its own context variables.
#### line_items
The `line_items` variable is a list of all build line items associated with the selected build. The following attributes are available for each individual line_item instance:
| Attribute | Description |
| --- | --- |
| .build | A reference back to the parent build order |
| .bom_item | A reference to the BOMItem which defines this line item |
| .quantity | The required quantity which is to be allocated against this line item |
| .part | A shortcut for .bom_item.sub_part |
| .allocations | A list of BuildItem objects which allocate stock items against this line item |
| .allocated_quantity | The total stock quantity which has been allocated against this line |
| .unallocated_quantity | The remaining quantity to allocate |
| .is_fully_allocated | Boolean value, returns True if the line item has sufficient stock allocated against it |
| .is_overallocated | Boolean value, returns True if the line item has more allocated stock than is required |
#### bom_items
| Attribute | Description |
| --- | --- |
| .reference | The reference designators of the components |
| .quantity | The number of components required to build |
| .overage | The extra amount required to assembly |
| .consumable | Boolean field, True if this is a "consumable" part which is not tracked through builds |
| .sub_part | The part at this position |
| .substitutes.all | A query set with all allowed substitutes for that part |
| .note | Extra text field which can contain additional information |
#### allocated_stock.all
| Attribute | Description |
| --- | --- |
| .bom_item | The bom item where this part belongs to |
| .stock_item | The allocated [StockItem](./context_variables.md#stockitem) |
| .quantity | The number of components needed for the build (components in BOM x parts to build) |
### Example
The following example will create a report with header and BOM. In the BOM table substitutes will be listed.
{% raw %}
```html
{% extends "report/inventree_report_base.html" %}
{% load i18n %}
{% load report %}
{% load barcode %}
{% load inventree_extras %}
{% load markdownify %}
{% block page_margin %}
margin: 2cm;
margin-top: 4cm;
{% endblock %}
{% block style %}
.header-right {
text-align: right;
float: right;
}
.logo {
height: 20mm;
vertical-align: middle;
}
.details {
width: 100%;
border: 1px solid;
border-radius: 3px;
padding: 5px;
min-height: 42mm;
}
.details table {
overflow-wrap: break-word;
word-wrap: break-word;
width: 65%;
table-layout: fixed;
font-size: 75%;
}
.changes table {
overflow-wrap: break-word;
word-wrap: break-word;
width: 100%;
table-layout: fixed;
font-size: 75%;
border: 1px solid;
}
.changes-table th {
font-size: 100%;
border: 1px solid;
}
.changes-table td {
border: 1px solid;
}
.details table td:not(:last-child){
white-space: nowrap;
}
.details table td:last-child{
width: 50%;
padding-left: 1cm;
padding-right: 1cm;
}
.details-table td {
padding-left: 10px;
padding-top: 5px;
padding-bottom: 5px;
border-bottom: 1px solid #555;
}
{% endblock %}
{% block bottom_left %}
content: "v{{report_revision}} - {% format_date date %}";
{% endblock %}
{% block header_content %}
<!-- TODO - Make the company logo asset generic -->
<img class='logo' src="{% asset 'company_logo.png' %}" alt="logo" width="150">
<div class='header-right'>
<h3>
Build Order {{ build }}
</h3>
<br>
</div>
<hr>
{% endblock %}
{% block page_content %}
<div class='details'>
<table class='details-table'>
<tr>
<th>{% trans "Build Order" %}</th>
<td>{% internal_link build.get_absolute_url build %}</td>
</tr>
<tr>
<th>{% trans "Order" %}</th>
<td>{{ reference }}</td>
</tr>
<tr>
<th>{% trans "Part" %}</th>
<td>{% internal_link part.get_absolute_url part.IPN %}</td>
</tr>
<tr>
<th>{% trans "Quantity" %}</th>
<td>{{ build.quantity }}</td>
</tr>
<tr>
<th>{% trans "Description" %}</th>
<td>{{ build.title }}</td>
</tr>
<tr>
<th>{% trans "Issued" %}</th>
<td>{% format_date build.creation_date %}</td>
</tr>
<tr>
<th>{% trans "Target Date" %}</th>
<td>
{% if build.target_date %}
{% format_date build.target_date %}
{% else %}
<em>Not specified</em>
{% endif %}
</td>
</tr>
{% if build.parent %}
<tr>
<th>{% trans "Required For" %}</th>
<td>{% internal_link build.parent.get_absolute_url build.parent %}</td>
</tr>
{% endif %}
{% if build.issued_by %}
<tr>
<th>{% trans "Issued By" %}</th>
<td>{{ build.issued_by }}</td>
</tr>
{% endif %}
{% if build.responsible %}
<tr>
<th>{% trans "Responsible" %}</th>
<td>{{ build.responsible }}</td>
</tr>
{% endif %}
<tr>
<th>{% trans "Sub builds count" %}</th>
<td>{{ build.sub_build_count }}</td>
</tr>
{% if build.sub_build_count > 0 %}
<tr>
<th>{% trans "Sub Builds" %}</th>
<td>{{ build.sub_builds }}</td>
</tr>
{% endif %}
<tr>
<th>{% trans "Overdue" %}</th>
<td>{{ build.is_overdue }}</td>
</tr>
<tr>
<th>{% trans "Can complete" %}</th>
<td>{{ build.can_complete }}</td>
</tr>
</table>
</div>
<h3>{% trans "Notes" %}</h3>
{% if build.notes %}
{{ build.notes|markdownify }}
{% endif %}
<h3>{% trans "Parts" %}</h3>
<div class='changes'>
<table class='changes-table'>
<thead>
<tr>
<th>Original IPN</th>
<th>Reference</th>
<th>Replace width IPN</th>
</tr>
</thead>
<tbody>
{% for line in build.bom_items %}
<tr>
<td> {{ line.sub_part.IPN }} </td>
<td> {{ line.reference }} </td>
<td> {{ line.substitutes.all.0.part.IPN }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
```
{% endraw %}
This will result a report page like this:
{% with id="report-options", url="build/report-61.png", description="Report Example Builds" %} {% include "img.html" %} {% endwith %}
### Default Report Template
A default *Build Report* template is provided out of the box, which is useful for generating simple test reports. Furthermore, it may be used as a starting point for developing custom BOM reports:
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_build_order_base.html) for the default build report template.

View File

@ -2,64 +2,244 @@
title: Context Variables
---
## Context Variables
### Report
Context variables are provided to each template when it is rendered. The available context variables depend on the model type for which the template is being rendered.
!!! info "Specific Report Context"
Specific report types may have additional context variables, see below.
### Global Context
Each report has access to a number of context variables by default. The following context variables are provided to every report template:
In addition to the model-specific context variables, the following global context variables are available to all templates:
| Variable | Description |
| --- | --- |
| base_url | The base URL for the InvenTree instance |
| date | Current date, represented as a Python datetime.date object |
| datetime | Current datetime, represented as a Python datetime object |
| page_size | The specified page size for this report, e.g. `A4` or `Letter landscape` |
| report_template | The report template model instance |
| report_name | Name of the report template |
| report_description | Description of the report template |
| report_revision | Revision of the report template |
| request | Django request object |
| template | The report template instance which is being rendered against |
| template_description | Description of the report template |
| template_name | Name of the report template |
| template_revision | Revision of the report template |
| user | User who made the request to render the template |
#### Label
::: report.models.ReportTemplateBase.base_context
options:
show_source: True
Certain types of labels have different context variables then other labels.
### Report Context
##### Stock Item Label
The following variables are made available to the StockItem label template:
In addition to the [global context](#global-context), all *report* templates have access to the following context variables:
| Variable | Description |
| -------- | ----------- |
| item | The [StockItem](./context_variables.md#stockitem) object itself |
| part | The [Part](./context_variables.md#part) object which is referenced by the [StockItem](./context_variables.md#stockitem) object |
| name | The `name` field of the associated Part object |
| ipn | The `IPN` field of the associated Part object |
| revision | The `revision` field of the associated Part object |
| quantity | The `quantity` field of the StockItem object |
| serial | The `serial` field of the StockItem object |
| uid | The `uid` field of the StockItem object |
| tests | Dict object of TestResult data associated with the StockItem |
| --- | --- |
| page_size | The page size of the report |
| landscape | Boolean value, True if the report is in landscape mode |
Note that custom plugins may also add additional context variables to the report context.
::: report.models.ReportTemplate.get_context
options:
show_source: True
### Label Context
In addition to the [global context](#global-context), all *label* templates have access to the following context variables:
| Variable | Description |
| --- | --- |
| width | The width of the label (in mm) |
| height | The height of the label (in mm) |
Note that custom plugins may also add additional context variables to the label context.
::: report.models.LabelTemplate.get_context
options:
show_source: True
## Template Types
Templates (whether for generating [reports](./report.md) or [labels](./labels.md)) are rendered against a particular "model" type. The following model types are supported, and can have templates renderer against them:
| Model Type | Description |
| --- | --- |
| [build](#build-order) | A [Build Order](../build/build.md) instance |
| [buildline](#build-line) | A [Build Order Line Item](../build/build.md) instance |
| [salesorder](#sales-order) | A [Sales Order](../order/sales_order.md) instance |
| [returnorder](#return-order) | A [Return Order](../order/return_order.md) instance |
| [purchaseorder](#purchase-order) | A [Purchase Order](../order/purchase_order.md) instance |
| [stockitem](#stock-item) | A [StockItem](../stock/stock.md#stock-item) instance |
| [stocklocation](#stock-location) | A [StockLocation](../stock/stock.md#stock-location) instance |
| [part](#part) | A [Part](../part/part.md) instance |
### Build Order
When printing a report or label against a [Build Order](../build/build.md) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| bom_items | Query set of all BuildItem objects associated with the BuildOrder |
| build | The BuildOrder instance itself |
| build_outputs | Query set of all BuildItem objects associated with the BuildOrder |
| line_items | Query set of all build line items associated with the BuildOrder |
| part | The Part object which is being assembled in the build order |
| quantity | The total quantity of the part being assembled |
| reference | The reference field of the BuildOrder |
| title | The title field of the BuildOrder |
::: build.models.Build.report_context
options:
show_source: True
### Build Line
When printing a report or label against a [BuildOrderLineItem](../build/build.md) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| allocated_quantity | The quantity of the part which has been allocated to this build |
| allocations | A query set of all StockItem objects which have been allocated to this build line |
| bom_item | The BomItem associated with this line item |
| build | The BuildOrder instance associated with this line item |
| build_line | The build line instance itself |
| part | The sub-part (component) associated with the linked BomItem instance |
| quantity | The quantity required for this line item |
::: build.models.BuildLine.report_context
options:
show_source: True
### Sales Order
When printing a report or label against a [SalesOrder](../order/sales_order.md) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| customer | The customer object associated with the SalesOrder |
| description | The description field of the SalesOrder |
| extra_lines | Query set of all extra lines associated with the SalesOrder |
| lines | Query set of all line items associated with the SalesOrder |
| order | The SalesOrder instance itself |
| reference | The reference field of the SalesOrder |
| title | The title (string representation) of the SalesOrder |
::: order.models.Order.report_context
options:
show_source: True
### Return Order
When printing a report or label against a [ReturnOrder](../order/return_order.md) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| customer | The customer object associated with the ReturnOrder |
| description | The description field of the ReturnOrder |
| extra_lines | Query set of all extra lines associated with the ReturnOrder |
| lines | Query set of all line items associated with the ReturnOrder |
| order | The ReturnOrder instance itself |
| reference | The reference field of the ReturnOrder |
| title | The title (string representation) of the ReturnOrder |
### Purchase Order
When printing a report or label against a [PurchaseOrder](../order/purchase_order.md) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| description | The description field of the PurchaseOrder |
| extra_lines | Query set of all extra lines associated with the PurchaseOrder |
| lines | Query set of all line items associated with the PurchaseOrder |
| order | The PurchaseOrder instance itself |
| reference | The reference field of the PurchaseOrder |
| supplier | The supplier object associated with the PurchaseOrder |
| title | The title (string representation) of the PurchaseOrder |
### Stock Item
When printing a report or label against a [StockItem](../stock/stock.md#stock-item) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| barcode_data | Generated barcode data for the StockItem |
| barcode_hash | Hash of the barcode data |
| batch | The batch code for the StockItem |
| child_items | Query set of all StockItem objects which are children of this StockItem |
| ipn | The IPN (internal part number) of the associated Part |
| installed_items | Query set of all StockItem objects which are installed in this StockItem |
| item | The StockItem object itself |
| name | The name of the associated Part |
| part | The Part object which is associated with the StockItem |
| qr_data | Generated QR code data for the StockItem |
| qr_url | Generated URL for embedding in a QR code |
| parameters | Dict object containing the parameters associated with the base Part |
| quantity | The quantity of the StockItem |
| result_list | FLattened list of TestResult data associated with the stock item |
| results | Dict object of TestResult data associated with the StockItem |
| serial | The serial number of the StockItem |
| stock_item | The StockItem object itself (shadow of 'item') |
| tests | Dict object of TestResult data associated with the StockItem (shadow of 'results') |
| test_keys | List of test keys associated with the StockItem |
| test_template_list | List of test templates associated with the StockItem |
| test_templates | Dict object of test templates associated with the StockItem |
::: stock.models.StockItem.report_context
options:
show_source: True
##### Stock Location Label
### Stock Location
The following variables are made available to the StockLocation label template:
When printing a report or label against a [StockLocation](../stock/stock.md#stock-location) object, the following context variables are available:
| Variable | Description |
| -------- | ----------- |
| location | The [StockLocation](./context_variables.md#stocklocation) object itself |
| --- | --- |
| location | The StockLocation object itself |
| qr_data | Formatted QR code data for the StockLocation |
| parent | The parent StockLocation object |
| stock_location | The StockLocation object itself (shadow of 'location') |
| stock_items | Query set of all StockItem objects which are located in the StockLocation |
::: stock.models.StockLocation.report_context
options:
show_source: True
### Part
When printing a report or label against a [Part](../part/part.md) object, the following context variables are available:
| Variable | Description |
| --- | --- |
| bom_items | Query set of all BomItem objects associated with the Part |
| category | The PartCategory object associated with the Part |
| description | The description field of the Part |
| IPN | The IPN (internal part number) of the Part |
| name | The name of the Part |
| parameters | Dict object containing the parameters associated with the Part |
| part | The Part object itself |
| qr_data | Formatted QR code data for the Part |
| qr_url | Generated URL for embedding in a QR code |
| revision | The revision of the Part |
| test_template_list | List of test templates associated with the Part |
| test_templates | Dict object of test templates associated with the Part |
::: part.models.Part.report_context
options:
show_source: True
## 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 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.
### Parts
!!! incomplete "TODO"
This section requires further work
#### Part
Each part object has access to a lot of context variables about the part. The following context variables are provided when accessing a `Part` object:
Each part object has access to a lot of context variables about the part. The following context variables are provided when accessing a `Part` object from within the template.
| Variable | Description |
|----------|-------------|
@ -106,6 +286,7 @@ Each part object has access to a lot of context variables about the part. The fo
#### Part Category
| Variable | Description |
|----------|-------------|
| name | Name of this category |
@ -117,6 +298,7 @@ Each part object has access to a lot of context variables about the part. The fo
#### StockItem
| Variable | Description |
|----------|-------------|
| parent | Link to another [StockItem](./context_variables.md#stockitem) from which this StockItem was created |
@ -139,7 +321,7 @@ Each part object has access to a lot of context variables about the part. The fo
| notes | Extra notes field |
| 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") |
| purchase_order | Link to a [PurchaseOrder](./context_variables.md#purchaseorder) (if this stock item was created from a PurchaseOrder) |
| purchase_order | Link to a [PurchaseOrder](./context_variables.md#purchase-order) (if this stock item was created from a PurchaseOrder) |
| infinite | If True this [StockItem](./context_variables.md#stockitem) can never be exhausted |
| sales_order | Link to a [SalesOrder](./context_variables.md#salesorder) object (if the StockItem has been assigned to a SalesOrder) |
| purchase_price | The unit purchase price for this [StockItem](./context_variables.md#stockitem) - this is the unit price at time of purchase (if this item was purchased from an external supplier) |
@ -164,6 +346,7 @@ Each part object has access to a lot of context variables about the part. The fo
#### Company
| Variable | Description |
|----------|-------------|
| name | Name of the company |
@ -184,6 +367,7 @@ Each part object has access to a lot of context variables about the part. The fo
#### Address
| Variable | Description |
|----------|-------------|
| line1 | First line of the postal address |
@ -194,9 +378,6 @@ Each part object has access to a lot of context variables about the part. The fo
#### Contact
Contacts are added to companies. Actually the company has no link to the contacts.
You can search the company object of the contact.
| Variable | Description |
|----------|-------------|
| company | Company object where the contact belongs to |
@ -207,6 +388,7 @@ You can search the company object of the contact.
#### SupplierPart
| Variable | Description |
|----------|-------------|
| part | Link to the master Part (Obsolete) |
@ -226,24 +408,13 @@ You can search the company object of the contact.
| has_price_breaks | Whether this [SupplierPart](./context_variables.md#supplierpart) has price breaks |
| manufacturer_string | Format a MPN string for this [SupplierPart](./context_variables.md#supplierpart). Concatenates manufacture name and part number. |
### Manufacturers
!!! incomplete "TODO"
This section requires further work
#### Manufacturer
| Variable | Description |
|----------|-------------|
#### ManufacturerPart
| Variable | Description |
|----------|-------------|
### Orders
The [Purchase Order](../order/purchase_order.md) context variables are described in the [Purchase Order](./purchase_order.md) section.
#### Purchase Order
!!! note "TODO"
This section is incomplete
#### SalesOrder

View File

@ -12,13 +12,13 @@ Some common functions are provided for use in custom report and label templates.
```
!!! tip "Use the Source, Luke"
To see the full range of available helper functions, refer to the source file [report.py](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templatetags/report.py) where these functions are defined!
To see the full range of available helper functions, refer to the source file [report.py]({{ sourcefile("src/backend/InvenTree/report/templatetags/report.py") }}) where these functions are defined!
## Assigning Variables
When making use of helper functions within a template, it can be useful to store the result of the function to a variable, rather than immediately rendering the output.
For example, using the [render_currency](#rendering-currency) helper function, we can store the output to a variable which can be used at a later point in the template:
For example, using the [render_currency](#currency-formatting) helper function, we can store the output to a variable which can be used at a later point in the template:
```html
{% raw %}
@ -272,7 +272,7 @@ A template tag is provided to load the InvenTree logo image into a report. You c
### Custom Logo
If the system administrator has enabled a [custom logo](../start/config.md#customisation-options), then this logo will be used instead of the base InvenTree logo.
If the system administrator has enabled a [custom logo](../start/config.md#customization-options) then this logo will be used instead of the base InvenTree logo.
This is a useful way to get a custom company logo into your reports.
@ -287,7 +287,7 @@ If you have a custom logo, but explicitly wish to load the InvenTree logo itself
## Report Assets
[Report Assets](./report.md#report-assets) are files specifically uploaded by the user for inclusion in generated reports and labels.
[Report Assets](./templates.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:

View File

@ -11,17 +11,6 @@ Custom labels can be generated using simple HTML templates, with support for QR-
Simple (generic) label templates are supplied 'out of the box' with InvenTree - however support is provided for generation of extremely specific custom labels, to meet any particular requirement.
## Label Types
The following types of labels are available
| Label Type | Description |
| --- | --- |
| [Part Labels](./labels/part_labels.md) | Print labels for individual parts |
| [Stock Labels](./labels/stock_labels.md) | Print labels for individual stock items |
| [Location Labels](./labels/location_labels.md) | Print labels for individual stock locations
| [Build Labels](./labels/build_labels.md) | Print labels for individual build order line items |
## Label Templates
Label templates are written using a mixture of [HTML](https://www.w3schools.com/html/) and [CSS](https://www.w3schools.com/css). [Weasyprint](https://weasyprint.org/) templates support a *subset* of HTML and CSS features. In addition to supporting HTML and CSS formatting, the label templates support the Django templating engine, allowing conditional formatting of the label data.

View File

@ -1,118 +0,0 @@
---
title: Build Labels
---
## Build Line Labels
Build label templates are used to generate labels for individual build order line items.
### Creating Build Line Label Templates
Build label templates are added (and edited) via the [admin interface](../../settings/admin.md).
### Printing Build Line Labels
Build line labels are printed from the Build Order page, under the *Allocate Stock* tab. Multiple line items can be selected for printing:
{% with id='print_build_labels', url='report/label_build_print.png', description='Print build line labels' %}
{% include 'img.html' %}
{% endwith %}
### Context Data
The following context variables are made available to the Build Line label template:
| Variable | Description |
| --- | --- |
| build_line | The build_line instance |
| build | The build order to which the build_line is linked |
| bom_item | The bom_item to which the build_line is linked |
| part | The required part for this build_line instance. References bom_item.sub_part |
| quantity | The total quantity required for the build line |
| allocated_quantity | The total quantity which has been allocated against the build line |
| allocations | A queryset containing the allocations made against the build_line |
## Example
A simple example template is shown below:
```html
{% raw %}
{% extends "label/label_base.html" %}
{% load barcode report %}
{% load inventree_extras %}
{% block style %}
{{ block.super }}
.label {
margin: 1mm;
}
.qr {
height: 28mm;
width: 28mm;
position: relative;
top: 0mm;
right: 0mm;
float: right;
}
.label-table {
width: 100%;
border-collapse: collapse;
border: 1pt solid black;
}
.label-table tr {
width: 100%;
border-bottom: 1pt solid black;
padding: 2.5mm;
}
.label-table td {
padding: 3mm;
}
{% endblock style %}
{% block content %}
<div class='label'>
<table class='label-table'>
<tr>
<td>
<b>Build Order:</b> {{ build.reference }}<br>
<b>Build Qty:</b> {% decimal build.quantity %}<br>
</td>
<td>
<img class='qr' alt='build qr' src='{% qrcode build.barcode %}'>
</td>
</tr>
<tr>
<td>
<b>Part:</b> {{ part.name }}<br>
{% if part.IPN %}
<b>IPN:</b> {{ part.IPN }}<br>
{% endif %}
<b>Qty / Unit:</b> {% decimal bom_item.quantity %} {% if part.units %}[{{ part.units }}]{% endif %}<br>
<b>Qty Total:</b> {% decimal quantity %} {% if part.units %}[{{ part.units }}]{% endif %}
</td>
<td>
<img class='qr' alt='part qr' src='{% qrcode part.barcode %}'>
</td>
</tr>
</table>
</div>
{% endblock content %}
{% endraw %}
```
Which results in a label like:
{% with id='build_label_example', url='report/label_build_example.png', description='Example build line labels' %}
{% include 'img.html' %}
{% endwith %}

View File

@ -1,24 +0,0 @@
---
title: Location Labels
---
## Stock Location Labels
Stock Location label templates are used to generate labels for individual Stock Locations.
### Creating Stock Location Label Templates
Stock Location label templates are added (and edited) via the admin interface.
### Printing Stock Location Labels
To print a single label from the Stock Location detail view, select the *Print Label* option.
### Context Data
The following variables are made available to the StockLocation label template:
| Variable | Description |
| -------- | ----------- |
| location | The [StockLocation](../context_variables.md#stocklocation) object itself |

View File

@ -1,91 +0,0 @@
---
title: Part Labels
---
## Part Labels
Part label templates are used to generate labels for individual Part instances.
### Creating Part Label Templates
Part label templates are added (and edited) via the admin interface.
### Printing Part Labels
Part label can be printed using the following approaches:
To print a single part label from the Part detail view, select the *Print Label* option.
To print multiple part labels, select multiple parts in the part table and select the *Print Labels* option.
### Context Data
The following context variables are made available to the Part label template:
| Variable | Description |
| -------- | ----------- |
| part | The [Part](../context_variables.md#part) object |
| category | The [Part Category](../context_variables.md#part-category) which contains the Part |
| name | The name of the part |
| description | The description text for the part |
| IPN | Internal part number (IPN) for the part |
| revision | Part revision code |
| qr_data | String data which can be rendered to a QR code |
| parameters | Map (Python dictionary) object containing the parameters associated with the part instance |
#### Parameter Values
The part parameter *values* can be accessed by parameter name lookup in the template, as follows:
```html
{% raw %}
Part: {{ part.name }}
Length: {{ parameters.length }}
{% endraw %}
```
!!! warning "Spaces"
Note that for parameters which include a `space` character in their name, lookup using the "dot" notation won't work! In this case, try using the [key lookup](../helpers.md#key-access) method:
```html
{% raw %}
Voltage Rating: {% getkey parameters "Voltage Rating" %}
{% endraw %}
```
#### Parameter Data
If you require access to the parameter data itself, and not just the "value" of a particular parameter, you can use the `part_parameter` [helper function](../helpers.md#part-parameters).
For example, the following label template can be used to generate a label which contains parameter data in addition to parameter units:
```html
{% raw %}
{% extends "label/label_base.html" %}
{% load report %}
{% block content %}
{% part_parameter part "Width" as width %}
{% part_parameter part "Length" as length %}
<div>
Part: {{ part.full_name }}<br>
Width: {{ width.data }} [{{ width.units }}]<br>
Length: {{ length.data }} [{{ length.units }}]
</div>
{% endblock content %}
{% endraw %}
```
The following label is produced:
{% with id="report-parameters", url="report/label_with_parameters.png", description="Label with parameters" %}
{% include 'img.html' %}
{% endwith %}

View File

@ -1,61 +0,0 @@
---
title: Stock Labels
---
## Stock Item Labels
Stock Item label templates are used to generate labels for individual Stock Items.
### Creating Stock Item Label Templates
Stock Item label templates are added (and edited) via the admin interface.
### Printing Stock Item Labels
Stock Item labels can be printed using the following approaches:
To print a single stock item from the Stock Item detail view, select the *Print Label* option as shown below:
{% with id='item_label_single', url='report/label_stock_print_single.png', description='Print single stock item label' %}
{% include 'img.html' %}
{% endwith %}
To print multiple stock items from the Stock table view, select the *Print Labels* option as shown below:
{% with id='item_label_multiple', url='report/label_stock_print_multiple.png', description='Print multiple stock item labels' %}
{% include 'img.html' %}
{% endwith %}
### Context Data
The following variables are made available to the StockItem label template:
| Variable | Description |
| -------- | ----------- |
| item | The [StockItem](../context_variables.md#stockitem) object itself |
| part | The [Part](../context_variables.md#part) object which is referenced by the [StockItem](../context_variables.md#stockitem) object |
| name | The `name` field of the associated Part object |
| ipn | The `IPN` field of the associated Part object |
| revision | The `revision` field of the associated Part object |
| quantity | The `quantity` field of the StockItem object |
| serial | The `serial` field of the StockItem object |
| uid | The `uid` field of the StockItem object |
| tests | Dict object of TestResult data associated with the StockItem |
| parameters | Dict object containing the parameters associated with the base Part |
### URL-style QR code
Stock Item labels support [QR code](../barcodes.md#qr-code) containing the stock item URL, which can be
scanned and opened directly
on a portable device using the camera or a QR code scanner. To generate a URL-style QR code for stock item in the [label HTML template](../labels.md#label-templates), add the
following HTML tag:
``` html
{% raw %}
<img class='custom_qr_class' src='{% qrcode qr_url %}'>
{% endraw %}
```
Make sure to customize the `custom_qr_class` CSS class to define the position of the QR code
on the label.

View File

@ -1,65 +0,0 @@
---
title: Purchase Order Report
---
## Purchase Order Reports
Custom purchase order reports may be generated against any given [Purchase Order](../order/purchase_order.md). For example, purchase order reports could be used to generate a pdf of the order to send to a supplier.
### Purchase Order Filters
The report template can be filtered against available [Purchase Order](../order/purchase_order.md) instances.
### Context Variables
In addition to the default report context variables, the following variables are made available to the purchase order report template for rendering:
| Variable | Description |
| --- | --- |
| order | The specific Purchase Order object |
| reference | The order reference field (can also be accessed as `{% raw %}{{ order.reference }}{% endraw %}`) |
| description | The order description field |
| supplier | The [supplier](../order/company.md#suppliers) associated with this purchase order |
| lines | A list of available line items for this order |
| extra_lines | A list of available *extra* line items for this order |
| order.created_by | The user who created the order |
| order.responsible | The user or group who is responsible for the order |
| order.creation_date | The date when the order was created |
| order.target_date | The date when the order should arrive |
| order.if_overdue | Boolean value that tells if the target date has passed |
| order.currency | The currency code associated with this order, e.g. 'AUD' |
| order.contact | The [contact](./context_variables.md#contact) object associated with this order |
#### Lines
Each line item (available within the `lines` list) has sub variables, as follows:
| Variable | Description |
| --- | --- |
| quantity | The quantity of the part to be ordered |
| part | The [supplierpart ](./context_variables.md#supplierpart) object to be ordered |
| reference | The reference given in the part of the order |
| notes | The notes given in the part of the order |
| target_date | The date when the part should arrive. Each part can have an individual date |
| price | The unit price the line item |
| total_line_price | The total price for this line item, calculated from the unit price and quantity |
| destination | The stock location where the part will be stored |
A simple example below shows how to use the context variables for each line item:
```html
{% raw %}
{% for line in lines %}
Internal Part: {{ line.part.part.name }} - <i>{{ line.part.part.description }}</i>
SKU: {{ line.part.SKU }}
Price: {% render_currency line.total_line_price %}
{% endfor %}
{% endraw %}
```
### Default Report Template
A default *Purchase Order Report* template is provided out of the box, which is useful for generating simple test reports. Furthermore, it may be used as a starting point for developing custom BOM reports:
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_po_report_base.html) for the default purchase order report template.

View File

@ -1,14 +1,14 @@
---
title: Report Generation
title: Report and LabelGeneration
---
## Custom Reporting
## Custom Reports
InvenTree supports a customizable reporting ecosystem, allowing the user to develop reporting templates that meet their particular needs.
InvenTree supports a customizable reporting ecosystem, allowing the user to develop document templates that meet their particular needs.
PDF reports are generated from custom HTML template files which are written by the user.
PDF files are generated from custom HTML template files which are written by the user.
Reports are used in a variety of situations to format data in a friendly format for printing, distribution, conformance and testing.
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.
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.
@ -44,290 +44,3 @@ For example, rendering the name of a part (which is available in the particular
</p></i>
{% endraw %}
```
### Context Variables
!!! info "Context Variables"
Templates will have different variables available to them depending on the report type. Read the detailed information on each available report type for further information.
Please refer to the [Context variables](./context_variables.md) page.
### Conditional Rendering
The django template system allows for conditional rendering, providing conditional flow statements such as:
```
{% raw %}
{% if <condition> %}
{% do_something %}
{% elif <other_condition> %}
<!-- something else -->
{% else %}
<!-- finally -->
{% endif %}
{% endraw %}
```
```
{% raw %}
{% for <item> in <list> %}
Item: {{ item }}
{% endfor %}
{% endraw %}
```
!!! info "Conditionals"
Refer to the [django template language documentation]({% include "django.html" %}/ref/templates/language/) for more information.
### Localization Issues
Depending on your localization scheme, inputting raw numbers into the formatting section template can cause some unintended issues. Consider the block below which specifies the page size for a rendered template:
```html
{% raw %}
<head>
<style>
@page {
size: {{ width }}mm {{ height }}mm;
margin: 0mm;
}
</style>
</head>
{% endraw %}
```
If localization settings on the InvenTree server use a comma (`,`) character as a decimal separator, this may produce an output like:
```html
{% raw %}
{% endraw %}
<head>
<style>
@page {
size: 57,3mm 99,0mm;
margin: 0mm;
}
</style>
</head>
```
The resulting `{% raw %}<style>{% endraw %}` CSS block will be *invalid*!
So, if you are writing a template which has custom formatting, (or any other sections which cannot handle comma decimal separators) you must wrap that section in a `{% raw %}{% localize off %}{% endraw %}` block:
```html
{% raw %}
{% load l10n %}
<head>
<style>
@page {
{% localize off %}
size: {{ width }}mm {{ height }}mm;
{% endlocalize %}
margin: 0mm;
}
</style>
</head>
{% endraw %}
```
!!! tip "Close it out"
Don't forget to end with a `{% raw %}{% endlocalize %}{% endraw %}` tag!
!!! tip "l10n"
You will need to add `{% raw %}{% load l10n %}{% endraw %}` to the top of your template file to use the `{% raw %}{% localize %}{% endraw %}` tag.
### Extending with Plugins
The [ReportMixin plugin class](../extend/plugins/report.md) allows reporting functionality to be extended with custom features.
## Report Types
InvenTree supports the following reporting functionality:
| Report Type | Description |
| --- | --- |
| [Test Report](./test.md) | Format results of a test report against for a particular StockItem |
| [Build Order Report](./build.md) | Format a build order report |
| [Purchase Order Report](./purchase_order.md) | Format a purchase order report |
| [Sales Order Report](./sales_order.md) | Format a sales order report |
| [Return Order Report](./return_order.md) | Format a return order report |
| [Stock Location Report](./stock_location.md) | Format a stock location report |
### Default Reports
InvenTree is supplied with a number of default templates "out of the box". These are generally quite simple, but serve as a starting point for building custom reports to suit a specific need.
!!! tip "Read the Source"
The source code for the default reports is [available on GitHub](https://github.com/inventree/InvenTree/tree/master/src/backend/InvenTree/report/templates/report). Use this as a guide for generating your own reports!
## Creating Reports
Report templates are created (and edited) via the [admin interface](../settings/admin.md), under the *Report* section. Select the certain type of report template you are wanting to create, and press the *Add* button in the top right corner:
{% with id="report-create", url="report/add_report_template.png", description="Create new report" %}
{% include 'img.html' %}
{% endwith %}
!!! tip "Staff Access Only"
Only users with staff access can upload or edit report template files.
!!! info "Editing Reports"
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.
### Name and Description
Each report template requires a name and description, which identify and describe the report template.
### 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.
### Filename Pattern
The filename pattern used to generate the output `.pdf` file. Defaults to "report.pdf".
The filename pattern allows custom rendering with any context variables which are available to the report. For example, a [test report](./test.md) for a particular [Stock Item](../stock/stock.md#stock-item) can use the part name and serial number of the stock item when generating the report name:
{% with id="report-filename-pattern", url="report/filename_pattern.png", description="Report filename pattern" %}
{% include 'img.html' %}
{% endwith %}
### Report Filters
Each type of report provides a *filters* field, which can be used to filter which items a report can be generated against. The target of the *filters* field depends on the type of report - refer to the documentation on the specific report type for more information.
For example, the [Test Report](./test.md) filter targets the linked [Stock Item](../stock/status.md) object, and can be used to select which stock items are allowed for the given report. Let's say that a certain test report should only be generated for "trackable" stock items. A filter could easily be constructed to accommodate this, by limiting available items to those where the associated [Part](../part/part.md) is *trackable*:
{% with id="report-filter-valid", url="report/filters_valid.png", description="Report filter selection" %}
{% include 'img.html' %}
{% endwith %}
If you enter an invalid option for the filter field, an error message will be displayed:
{% with id="report-filter-invalid", url="report/filters_invalid.png", description="Invalid filter selection" %}
{% include 'img.html' %}
{% endwith %}
!!! warning "Advanced Users"
Report filtering is an advanced topic, and requires a little bit of knowledge of the underlying data structure!
### Metadata
A JSON field made available to any [plugins](../extend/plugins.md) - but not used by internal code.
## Report Options
A number of global reporting options are available for customizing InvenTree reports:
{% with id="report-options", url="report/report.png", description="Report Options" %}
{% include 'img.html' %}
{% endwith %}
### Enable Reports
By default, the reporting feature is disabled. It must be enabled in the global settings.
### Default Page Size
The built-in InvenTree report templates (and any reports which are derived from the built-in templates) use the *Page Size* option to set the page size of the generated reports.
!!! info "Override Page Size"
Custom report templates do not have to make use of the *Page Size* option, although it is made available to the template context.
### Debug Mode
As templates are rendered directly to a PDF object, it can be difficult to debug problems when the PDF does not render exactly as expected.
Setting the *Debug Mode* option renders the template as raw HTML instead of PDF, allowing the rendering output to be introspected. This feature allows template designers to understand any issues with the generated HTML (before it is passed to the PDF generation engine).
!!! warning "HTML Rendering Limitations"
When rendered in debug mode, @page attributes (such as size, etc) will **not** be observed. Additionally, any asset files stored on the InvenTree server will not be rendered. Debug mode is not intended to produce "good looking" documents!
## 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. 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"
You need to ensure your asset images to the report/assets directory in the [data directory](../start/intro.md#file-storage). Upload new assets via the [admin interface](../settings/admin.md) to ensure they are uploaded to the correct location on the server.
## 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 re-used 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.
Similar to assets files, snippet template files are uploaded via the admin interface.
Snippets are included in a template as follows:
```
{% raw %}{% include 'snippets/<snippet_name.html>' %}{% endraw %}
```
For example, consider a 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 %}
```

View File

@ -1,26 +0,0 @@
---
title: Return Order Reports
---
## Return Order Reports
Custom reports may be generated against any given [Return Order](../order/return_order.md). For example, return order reports can be used to generate an RMA request to send to a customer.
### Context Variables
In addition to the default report context variables, the following context variables are made available to the return order report template for rendering:
| Variable | Description |
| --- | --- |
| order | The return order object the report is being generated against |
| description | The description of the order, also accessed through `order.description` |
| reference | The reference of the order, also accessed through `order.reference` |
| customer | The customer object related to this order |
| lines | The list of line items linked to this order |
| extra_lines | The list of extra line items linked to this order |
### Default Report Template
A default report template is provided out of the box, which can be used as a starting point for developing custom return order report templates.
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_return_order_report_base.html) for the default return order report template.

View File

@ -1,31 +0,0 @@
---
title: Sales Order Reports
---
## Sales Order Reports
Custom sales order reports may be generated against any given [Sales Order](../order/sales_order.md). For example, a sales order report could be used to generate an invoice to send to a customer.
### Sales Order Filters
The report template can be filtered against available [Sales Order](../order/sales_order.md) instances.
### Context Variables
In addition to the default report context variables, the following variables are made available to the sales order report template for rendering:
| Variable | Description |
| --- | --- |
| order | The specific Sales Order object |
| reference | The order reference field (can also be accessed as `{% raw %}{{ order.description }}{% endraw %}`) |
| description | The order description field |
| customer | The [customer](../order/company.md#customers) associated with the particular sales order |
| lines | A list of available line items for this order |
| extra_lines | A list of available *extra* line items for this order |
| order.currency | The currency code associated with this order, e.g. 'CAD' |
### Default Report Template
A default *Sales Order Report* template is provided out of the box, which is useful for generating simple test reports. Furthermore, it may be used as a starting point for developing custom BOM reports:
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_so_report_base.html) for the default sales order report template.

View File

@ -0,0 +1,78 @@
---
title: Sample Templates
---
## Sample Templates
A number of pre-built templates are provided with InvenTree, which can be used as a starting point for creating custom reports and labels.
Users can create their own custom templates, or modify the provided templates to suit their needs.
## Report Templates
The following report templates are provided "out of the box" and can be used as a starting point, or as a reference for creating custom reports templates:
| Template | Model Type | Description |
| --- | --- | --- |
| [Bill of Materials](#bill-of-materials-report) | [Part](../part/part.md) | Bill of Materials report |
| [Build Order](#build-order) | [BuildOrder](../build/build.md) | Build Order report |
| [Purchase Order](#purchase-order) | [PurchaseOrder](../order/purchase_order.md) | Purchase Order report |
| [Return Order](#return-order) | [ReturnOrder](../order/return_order.md) | Return Order report |
| [Sales Order](#sales-order) | [SalesOrder](../order/sales_order.md) | Sales Order report |
| [Stock Location](#stock-location) | [StockLocation](../stock/stock.md#stock-location) | Stock Location report |
| [Test Report](#test-report) | [StockItem](../stock/stock.md#stock-item) | Test Report |
### Bill of Materials Report
{{ templatefile("report/inventree_bill_of_materials_report.html") }}
### Build Order
{{ templatefile("report/inventree_build_order_report.html") }}
### Purchase Order
{{ templatefile("report/inventree_bill_of_materials_report.html") }}
### Return Order
{{ templatefile("report/inventree_return_order_report.html") }}
### Sales Order
{{ templatefile("report/inventree_sales_order_report.html") }}
### Stock Location
{{ templatefile("report/inventree_stock_location_report.html") }}
### Test Report
{{ templatefile("report/inventree_test_report.html") }}
## Label Templates
The following label templates are provided "out of the box" and can be used as a starting point, or as a reference for creating custom label templates:
| Template | Model Type | Description |
| --- | --- | --- |
| [Build Line](#build-line-label) | [Build line item](../build/build.md) | Build Line label |
| [Part](#part-label) | [Part](../part/part.md) | Part label |
| [Stock Item](#stock-item-label) | [StockItem](../stock/stock.md#stock-item) | Stock Item label |
| [Stock Location](#stock-location-label) | [StockLocation](../stock/stock.md#stock-location) | Stock Location label |
### Build Line Label
{{ templatefile("label/buildline_label.html") }}
### Part Label
{{ templatefile("label/part_label_code128.html") }}
### Stock Item Label
{{ templatefile("label/stockitem_qr.html") }}
### Stock Location Label
{{ templatefile("label/stocklocation_qr_and_text.html") }}

View File

@ -1,16 +0,0 @@
---
title: Stock Location Reports
---
## Stock location Reports
You can print a formatted report of a stock location. This makes sense if you have several parts inside one location, e.g. a box that is sent out to a manufacturing partner. Whit a report you can create a box content list.
### Context Variables
You can use all content variables from the [StockLocation](./context_variables.md#stocklocation) object.
### Default Report Template
A default report template is provided out of the box, which can be used as a starting point for developing custom return order report templates.
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_slr_report.html) for the default stock location report template.

View File

@ -0,0 +1,228 @@
---
title: InvenTree Templates
---
## Template Overview
InvenTree supports a customizable reporting ecosystem, allowing the user to develop document templates that meet their particular needs.
PDF files are generated from custom HTML template files which are written by the user.
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.
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.
## Template Types
The following types of templates are available:
### Reports
Reports are intended to serve as formal documents, and can be used to generate formatted PDF outputs for a variety of purposes.
Refer to the [report templates](./report.md) documentation for further information.
### Labels
Labels can also be generated using the templating system. Labels are intended to be used for printing small, formatted labels for items, parts, locations, etc.
Refer to the [label templates](./labels.md) documentation for further information.
### Template Model Types
When generating a particular template (to render a report or label output), the template is rendered against a particular "model" type. The model type determines the data that is available to the template, and how it is formatted.
To read more about the model types for which templates can be rendered, and the associated context information, refer to the [context variables](./context_variables.md) documentation.
### Default Reports
InvenTree is supplied with a number of default templates "out of the box" - for generating both labels and reports. These are generally quite simple, but serve as a starting point for building custom reports to suit a specific need.
!!! tip "Read the Source"
The source code for the default reports is [available on GitHub]({{ sourcedir("src/backend/InvenTree/report/templates/report") }}). Use this as a guide for generating your own reports!
### Extending with Plugins
The [ReportMixin plugin class](../extend/plugins/report.md) allows reporting functionality to be extended with custom features.
## WeasyPrint Template Rendering
InvenTree report templates utilize the powerful [WeasyPrint](https://weasyprint.org/) PDF generation engine.
To read more about the capabilities of the report templating engine, and how to use it, refer to the [weasyprint documentation](./weasyprint.md).
## Creating Templates
Report and label templates can be created (and edited) via the [admin interface](../settings/admin.md), under the *Report* section.
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:
{% with id="report-list", url="report/report_template_admin.png", description="Report templates in admin interface" %}
{% include 'img.html' %}
{% endwith %}
!!! tip "Staff Access Only"
Only users with staff access can upload or edit report template files.
!!! info "Editing Reports"
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
Each report template requires a name and description, which identify and describe the report template.
### 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.
### Filename Pattern
The filename pattern used to generate the output `.pdf` file. Defaults to "report.pdf".
The filename pattern allows custom rendering with any context variables which are available to the report. For example, a test report for a particular [Stock Item](../stock/stock.md#stock-item) can use the part name and serial number of the stock item when generating the report name:
{% with id="report-filename-pattern", url="report/filename_pattern.png", description="Report filename pattern" %}
{% include 'img.html' %}
{% endwith %}
### Template Filters
Each template instance provides a *filters* field, which can be used to filter which items a report or label template can be generated against. The target of the *filters* field depends on the model type associated with the particular template.
As an example, let's say that a certain `StockItem` report should only be generated for "trackable" stock items. A filter could easily be constructed to accommodate this, by limiting available items to those where the associated [Part](../part/part.md) is *trackable*:
{% with id="report-filter-valid", url="report/filters_valid.png", description="Report filter selection" %}
{% include 'img.html' %}
{% endwith %}
If you enter an invalid option for the filter field, an error message will be displayed:
{% with id="report-filter-invalid", url="report/filters_invalid.png", description="Invalid filter selection" %}
{% include 'img.html' %}
{% endwith %}
!!! warning "Advanced Users"
Report filtering is an advanced topic, and requires a little bit of knowledge of the underlying data structure!
### Metadata
A JSON field made available to any [plugins](../extend/plugins.md) - but not used by internal code.
## Reporting Options
A number of global reporting options are available for customizing InvenTree reports:
{% with id="report-options", url="report/report.png", description="Report Options" %}
{% include 'img.html' %}
{% endwith %}
### Enable Reports
By default, the reporting feature is disabled. It must be enabled in the global settings.
### Default Page Size
The built-in InvenTree report templates (and any reports which are derived from the built-in templates) use the *Page Size* option to set the page size of the generated reports.
!!! info "Override Page Size"
Custom report templates do not have to make use of the *Page Size* option, although it is made available to the template context.
### Debug Mode
As templates are rendered directly to a PDF object, it can be difficult to debug problems when the PDF does not render exactly as expected.
Setting the *Debug Mode* option renders the template as raw HTML instead of PDF, allowing the rendering output to be introspected. This feature allows template designers to understand any issues with the generated HTML (before it is passed to the PDF generation engine).
!!! warning "HTML Rendering Limitations"
When rendered in debug mode, @page attributes (such as size, etc) will **not** be observed. Additionally, any asset files stored on the InvenTree server will not be rendered. Debug mode is not intended to produce "good looking" documents!
## 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. 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"
You need to ensure your asset images to the report/assets directory in the [data directory](../start/intro.md#file-storage). Upload new assets via the [admin interface](../settings/admin.md) to ensure they are uploaded to the correct location on the server.
## 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 re-used 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.
Similar to assets files, snippet template files are uploaded via the admin interface.
Snippets are included in a template as follows:
```
{% raw %}{% include 'snippets/<snippet_name.html>' %}{% endraw %}
```
For example, consider a 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 %}
```

View File

@ -1,87 +0,0 @@
---
title: Test Report
---
## Test Report
InvenTree provides [test result](../stock/test.md) tracking functionality which allows the users to keep track of any tests which have been performed on a given [stock item](../stock/stock.md).
Custom test reports may be generated against any given stock item. All testing data is made available to the template for custom rendering as required.
For example, an "Acceptance Test" report template may be customized to the particular device, with the results for certain tests rendering in a particular part of the page, with any tests which have not passed highlighted.
### Stock Item Filters
A TestReport template may define a set of filters against which stock items are sorted. Any [StockItem](../stock/stock.md) objects which match the provided filters can use the given TestReport.
This allows each TestReport to easily be assigned to a particular StockItem, or even multiple items.
In the example below, a test report template is uploaded and available to any stock items linked to a part with the name *"My Widget"*. Any combination of fields relevant to the StockItem model can be used here.
{% with id="test-report-filters", url="report/test_report_filters.png", description="Test report filters" %}
{% include 'img.html' %}
{% endwith %}
### Context Variables
In addition to the default report context variables, the following context variables are made available to the TestReport template for rendering:
| Variable | Description |
| --- | --- |
| stock_item | The individual [Stock Item](./context_variables.md#stockitem) object for which this test report is being generated |
| serial | The serial number of the linked Stock Item |
| part | The [Part](./context_variables.md#part) object of which the stock_item is an instance |
| parameters | A dict object representing the [parameters](../part/parameter.md) of the referenced part |
| test_keys | A list of the available 'keys' for the test results recorded against the stock item |
| test_template_list | A list of the available [test templates](../part/test.md#part-test-templates) for the referenced part |
| test_template_map | A map / dict of the available test templates |
| results | A dict of test result objects, where the 'key' for each test result is a shortened version of the test name (see below) |
| result_list | A list of each test result object |
| installed_items | A flattened list representing all [Stock Item](./context_variables.md#stockitem) objects which are *installed inside* the referenced [Stock Item](./context_variables.md#stockitem) object |
#### Results
The *results* context variable provides a very convenient method of callout out a particular test result by name.
#### Example
Say for example that a Part "Electronic Widget" has a stock item with serial number #123, and has a test result uploaded called "Firmware Checksum". The templated file can reference this data as follows:
``` html
<h3>Part: {% raw %}{{ part.name }}{% endraw %}</h3>
<b>Serial Number: {% raw %}{{ stock_item.serial }}{% endraw %}</b>
<hr>
<p>
Firmware Checksum: {% raw %}{{ results.firmwarechecksum.value }}.
Uploaded by {{ results.firmwarechecksum.user }}{% endraw %}
</p>
```
#### Installed Items
The *installed_items* context variable is a list of all [StockItem](./context_variables.md#stockitem) instances which are installed inside the [StockItem](./context_variables.md#stockitem) referenced by the report template. Each [StockItem](./context_variables.md#stockitem) can be dereferenced as follows:
```html
{% raw %}
<table>
{% for sub_item in installed_items %}
<tr>
<td>{{ sub_item.full_name }}</td>
<td>Serial Number: {{ sub_item.serial }}</td>
<td>Pass: {{ sub_item.passedAllRequiredTests }}</td>
</tr>
{% endfor %}
</table>
{% endraw %}
```
### Default Report Template
A default *Test Report* template is provided out of the box, which is useful for generating simple test reports. Furthermore, it may be used as a starting point for developing custom test reports:
{% with id="test-report-example", url="report/test_report_example.png", description="Example Test Report" %}
{% include "img.html" %}
{% endwith %}
View the [source code](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/report/templates/report/inventree_test_report_base.html) for the default test report template.

View File

@ -0,0 +1,111 @@
---
title: Weasyprint Templates
---
## WeasyPrint Templates
We use the powerful [WeasyPrint](https://weasyprint.org/) PDF generation engine to create custom reports and labels.
!!! 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.
### Stylesheets
Templates are rendered using standard HTML / CSS - if you are familiar with web page layout, you're ready to go!
### Template Language
Uploaded report template files are passed through the [django template rendering framework]({% include "django.html" %}/topics/templates/), and as such accept the same variable template strings as any other django template file. Different variables are passed to the report template (based on the context of the report) and can be used to customize the contents of the generated PDF.
### Context Variables
!!! info "Context Variables"
Templates will have different variables available to them depending on the report type. Read the detailed information on each available report type for further information.
Please refer to the [Context variables](./context_variables.md) page.
### Conditional Rendering
The django template system allows for conditional rendering, providing conditional flow statements such as:
```
{% raw %}
{% if <condition> %}
{% do_something %}
{% elif <other_condition> %}
<!-- something else -->
{% else %}
<!-- finally -->
{% endif %}
{% endraw %}
```
```
{% raw %}
{% for <item> in <list> %}
Item: {{ item }}
{% endfor %}
{% endraw %}
```
!!! info "Conditionals"
Refer to the [django template language documentation]({% include "django.html" %}/ref/templates/language/) for more information.
### Localization Issues
Depending on your localization scheme, inputting raw numbers into the formatting section template can cause some unintended issues. Consider the block below which specifies the page size for a rendered template:
```html
{% raw %}
<head>
<style>
@page {
size: {{ width }}mm {{ height }}mm;
margin: 0mm;
}
</style>
</head>
{% endraw %}
```
If localization settings on the InvenTree server use a comma (`,`) character as a decimal separator, this may produce an output like:
```html
{% raw %}
{% endraw %}
<head>
<style>
@page {
size: 57,3mm 99,0mm;
margin: 0mm;
}
</style>
</head>
```
The resulting `{% raw %}<style>{% endraw %}` CSS block will be *invalid*!
So, if you are writing a template which has custom formatting, (or any other sections which cannot handle comma decimal separators) you must wrap that section in a `{% raw %}{% localize off %}{% endraw %}` block:
```html
{% raw %}
{% load l10n %}
<head>
<style>
@page {
{% localize off %}
size: {{ width }}mm {{ height }}mm;
{% endlocalize %}
margin: 0mm;
}
</style>
</head>
{% endraw %}
```
!!! tip "Close it out"
Don't forget to end with a `{% raw %}{% endlocalize %}{% endraw %}` tag!
!!! tip "l10n"
You will need to add `{% raw %}{% load l10n %}{% endraw %}` to the top of your template file to use the `{% raw %}{% localize %}{% endraw %}` tag.

View File

@ -8,9 +8,9 @@ To that end, we have implemented a number of security measures over the years, w
The InvenTree project is managed by a small team of developers, who are responsible for the ongoing development and maintenance of the software. Two geographically distributed users have administrative access to the InvenTree codebase. Merges are only done by one of these two users, the maintainer Oliver.
InvenTree is open-source, and we welcome contributions from the community. However, all contributions are reviewed and scrutinised before being merged into the codebase.
We provide a written [Security Policy](https://github.com/inventree/InvenTree/blob/master/SECURITY.md) in our main repo to ensure that all security issues are handled in a timely manner.
We provide a written [Security Policy]({{ sourcefile("SECURITY.md") }}) in our main repo to ensure that all security issues are handled in a timely manner.
If we become aware of a security issue, we will take immediate action to address the issue, and will provide a public disclosure of the issue once it has been resolved. We support assigning CVEs to security issues where appropriate. Our past security advisories can be found [here](https://github.com/inventree/InvenTree/security/advisories).
If we become aware of a security issue, we will take immediate action to address the issue, and will provide a public disclosure of the issue once it has been resolved. We support assigning CVEs to security issues where appropriate. Our [past security advisories can be found here](https://github.com/inventree/InvenTree/security/advisories).
## Technical measures

View File

@ -6,13 +6,13 @@ title: Currency Support
InvenTree provides support for multiple currencies, allowing pricing information to be stored with base currency rates.
### Configuration
### Supported Currencies
To specify which currencies are supported, refer to the [currency configuration](../start/config.md#supported-currencies) section
InvenTree uses the [django-money](https://github.com/django-money/django-money) library, which in turn uses the [py-moneyed library](https://py-moneyed.readthedocs.io/en/latest/index.html). `py-moneyed` supports any currency which is defined in the [ISO 3166 standard](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) standard.
### Currency Conversion
Currency conversion is provided via the [django-money](https://github.com/django-money/django-money) library. Pricing data can be converted seamlessly between the available currencies.
Currency conversion is provided via the `django-money` library. Pricing data can be converted seamlessly between the available currencies.
### Currency Rate Updates
@ -26,6 +26,11 @@ If a different currency exchange backend is needed, or a custom implementation i
In the [settings screen](./global.md), under the *Pricing* section, the following currency settings are available:
{% with id="currency-settings", url="settings/currency.png", description="Currency Exchange Settings" %}
{% include 'img.html' %}
{% endwith %}
| Setting | Description | Default Value |
| --- | --- |
| Default Currency | The selected *default* currency for the system. | USD |
| Supported Currencies | The list of supported currencies for the system. | AUD, CAD, CNY, EUR, GBP, JPY, NZD, USD |
#### Supported Currencies
While InvenTree can support any of the currencies defined in the ISO 3166 standard, the list of supported currencies can be limited to only those which are relevant to the user. The supported currencies are used to populate the currency selection dropdowns throughout the InvenTree interface.

View File

@ -22,7 +22,11 @@ The InvenTree server tries to locate the `config.yaml` configuration file on sta
!!! tip "Config File Location"
When the InvenTree server boots, it will report the location where it expects to find the configuration file
The configuration file *template* can be found on [GitHub](https://github.com/inventree/InvenTree/blob/master/src/backend/InvenTree/config_template.yaml)
#### Configuration File Template
The configuration file *template* can be found on [GitHub]({{ sourcefile("src/backend/InvenTree/config_template.yaml") }}), and is shown below:
{{ includefile("src/backend/InvenTree/config_template.yaml", "Configuration File Template", format="yaml") }}
!!! info "Template File"
The default configuration file (as defined by the template linked above) will be copied to the specified configuration file location on first run, if a configuration file is not found in that location.
@ -94,12 +98,14 @@ Depending on how your InvenTree installation is configured, you will need to pay
| --- | --- | --- | --- |
| INVENTREE_ALLOWED_HOSTS | allowed_hosts | List of allowed hosts | `*` |
| INVENTREE_TRUSTED_ORIGINS | trusted_origins | List of trusted origins. Refer to the [django documentation]({% include "django.html" %}/ref/settings/#csrf-trusted-origins) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | False |
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | `False` |
| INVENTREE_CORS_ORIGIN_WHITELIST | cors.whitelist | List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
| INVENTREE_CORS_ORIGIN_REGEX | cors.regex | List of regular expressions for CORS whitelisted URL patterns | *Empty list* |
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | False |
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | False |
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | True |
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | `True` |
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | `False` |
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | `False` |
| INVENTREE_SESSION_COOKIE_SECURE | cookie.secure | Enforce secure session cookies | `False` |
| INVENTREE_COOKIE_SAMESITE | cookie.samesite | Session cookie mode. Must be one of `Strict | Lax | None`. Refer to the [mozilla developer docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) for more information. | `None` |
### Proxy Settings
@ -196,6 +202,35 @@ If running with a MySQL database backend, the following additional options are a
| --- | --- | --- | --- |
| INVENTREE_DB_ISOLATION_SERIALIZABLE | database.serializable | Database isolation level configured to "serializable" | False |
## Caching
InvenTree can be configured to use [redis](https://redis.io) as a global cache backend.
Enabling a global cache can provide significant performance improvements for InvenTree.
### Cache Server
Enabling global caching requires connection to a redis server (which is separate from the InvenTree database and web server). Setup and configuration of this server is outside the scope of this documentation. It is assumed that if you are configuring a cache server, you have already set one up, and are comfortable configuring it.
!!! tip "Docker Support"
If you are running [InvenTree under docker](./docker.md), we provide a redis container as part of our docker compose file - so redis caching works out of the box.
### Cache Settings
The following cache settings are available:
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_CACHE_ENABLED | cache.enabled | Enable redis caching | False |
| INVENTREE_CACHE_HOST | cache.host | Cache server host | *Not specified* |
| INVENTREE_CACHE_PORT | cache.port | Cache server port | 6379 |
| INVENTREE_CACHE_CONNECT_TIMEOUT | cache.connect_timeout | Cache connection timeout (seconds) | 3 |
| INVENTREE_CACHE_TIMEOUT | cache.timeout | Cache timeout (seconds) | 3 |
| INVENTREE_CACHE_TCP_KEEPALIVE | cache.tcp_keepalive | Cache TCP keepalive | True |
| INVENTREE_CACHE_KEEPALIVE_COUNT | cache.keepalive_count | Cache keepalive count | 5 |
| INVENTREE_CACHE_KEEPALIVE_IDLE | cache.keepalive_idle | Cache keepalive idle | 1 |
| INVENTREE_CACHE_KEEPALIVE_INTERVAL | cache.keepalive_interval | Cache keepalive interval | 1 |
| INVENTREE_CACHE_USER_TIMEOUT | cache.user_timeout | Cache user timeout | 1000 |
## Email Settings
To enable [email functionality](../settings/email.md), email settings must be configured here, either via environment variables or within the configuration file.
@ -221,19 +256,6 @@ The "sender" email address is the address from which InvenTree emails are sent (
!!! info "Fallback"
If `INVENTREE_EMAIL_SENDER` is not provided, the system will fall back to `INVENTREE_EMAIL_USERNAME` (if the username is a valid email address)
## Supported Currencies
The currencies supported by InvenTree must be specified in the [configuration file](#configuration-file).
A list of currency codes (e.g. *AUD*, *CAD*, *JPY*, *USD*) can be specified using the `currencies` variable (or using the `INVENTREE_CURRENCIES` environment variable).
| Environment Variable | Configuration File | Description | Default |
| --- | --- | --- | --- |
| INVENTREE_CURRENCIES | currencies | List of supported currencies| `AUD`, `CAD`, `CNY`, `EUR`, `GBP`, `JPY`, `NZD`, `USD` |
!!! tip "More Info"
Read the [currencies documentation](../settings/currency.md) for more information on currency support in InvenTree
## File Storage Locations
InvenTree requires some external directories for storing files:
@ -324,6 +346,7 @@ The logo and custom messages can be changed/set:
| INVENTREE_CUSTOM_SPLASH | customize.splash | Path to custom splash screen in the static files directory | *Not specified* |
| INVENTREE_CUSTOMIZE | customize.login_message | Custom message for login page | *Not specified* |
| INVENTREE_CUSTOMIZE | customize.navbar_message | Custom message for navbar | *Not specified* |
| INVENTREE_CUSTOMIZE | customize.hide_pui_banner | Disable PUI banner | False |
If you want to remove the InvenTree branding as far as possible from your end-user also check the [global server settings](../settings/global.md#server-settings).

View File

@ -117,12 +117,15 @@ Runs an InvenTree web server instance, powered by a Gunicorn web server.
Runs the InvenTree background worker process. This spins up a second instance of the *inventree* container, with a different entrypoint command.
#### File Server
#### Proxy Server
Caddy working as a reverse proxy, separating requests for static and media files, and directing everything else to Gunicorn.
This container uses the official [caddy image](https://hub.docker.com/_/caddy).
!!! info "Nginx Proxy"
An alternative is to run nginx as the reverse proxy. A sample configuration file is provided in the `./contrib/container/` source directory.
#### Redis Cache
Redis is used as cache storage for the InvenTree server. This provides a more performant caching system which can useful in larger installations.
@ -133,10 +136,8 @@ This container uses the official [redis image](https://hub.docker.com/_/redis).
Docker adds an additional network layer - that might lead to lower performance than bare metal.
To optimize and configure your redis deployment follow the [official docker guide](https://redis.io/docs/getting-started/install-stack/docker/#configuration).
!!! warning "Disabled by default"
The *redis* container is not enabled in the default configuration. This is provided as an example for users wishing to use redis.
To enable the *redis* container, run any `docker compose` commands with the `--profile redis` flag.
You will also need to un-comment the `INVENTREE_CACHE_<...>` variables in the `.env` file.
!!! tip "Enable Cache"
While a redis container is provided in the default configuration, by default it is not enabled in the Inventree server. You can enable redis cache support by following the [caching configuration guide](./config.md#caching)
### Data Volume

View File

@ -27,13 +27,13 @@ The following guide provides a streamlined production InvenTree installation, wi
### Required Files
The following files required for this setup are provided with the InvenTree source, located in the `/contrib/container/` directory of the [InvenTree source code](https://github.com/inventree/InvenTree/tree/master/contrib/container/):
The following files required for this setup are provided with the InvenTree source, located in the `contrib/container/` directory of the [InvenTree source code]({{ sourcedir("/contrib/container/") }}):
| Filename | Description |
| --- | --- |
| [docker-compose.yml](https://raw.githubusercontent.com/inventree/InvenTree/master/contrib/container/docker-compose.yml)| The docker compose script |
| [.env](https://raw.githubusercontent.com/inventree/InvenTree/master/contrib/container/.env) | Environment variables |
| [Caddyfile](https://raw.githubusercontent.com/inventree/InvenTree/master/contrib/container/Caddyfile) | Caddy configuration file |
| [docker-compose.yml]({{ sourcefile("contrib/container/docker-compose.yml", raw=True) }}) | The docker compose script |
| [.env]({{ sourcefile("contrib/container/.env", raw=True) }}) | Environment variables |
| [Caddyfile]({{ sourcefile("contrib/container/Caddyfile", raw=True) }}) | Caddy configuration file |
Download these files to a directory on your local machine.

View File

@ -23,7 +23,7 @@ Install required system packages (as superuser):
The following packages are required on a debian system. A different distribution may require a slightly different set of packages
!!! info "Python Version"
InvenTree requires a modern Python version check [here](https://github.com/inventree/InvenTree/blob/master/CONTRIBUTING.md#target-version) for the current minimums.
InvenTree requires a modern Python version [check here]({{ sourcefile("CONTRIBUTING.md") }}) for the current minimums.
```
sudo apt-get update
@ -107,7 +107,7 @@ source ./env/bin/activate
The Python packages required by the InvenTree server must be installed into the virtual environment.
```
pip install -U -r src/requirements.txt
pip install --require-hashes -U -r src/backend/requirements.txt
```
This installs all required Python packages using pip package manager. It also creates a (default) database configuration file which needs to be edited to meet user needs before proceeding (see next step below).

View File

@ -15,6 +15,14 @@ wget -qO install.sh https://get.inventree.org && bash install.sh
This script does all manual steps without any input. The installation might take up to 5-10 minutes to finish.
#### Permission Denied Error
The above command may need to be run with `sudo` permissions, depending on the system configuration. So, if the script fails with a permission error, try:
```bash
sudo wget -qO install.sh https://get.inventree.org && sudo bash install.sh
```
### File Locations
The installer creates the following directories:
@ -171,4 +179,4 @@ The packages are provided by [packager.io](https://packager.io/). They are built
The package sets up [services](#controlling-inventree) that run the needed processes as the unprivileged user `inventree`. This keeps the privileges of InvenTree as low as possible.
A CLI is provided to interface with low-level management functions like [variable management](#enviroment-variables), log access, commands, process scaling, etc.
A CLI is provided to interface with low-level management functions like [variable management](#environment-variables), log access, commands, process scaling, etc.

View File

@ -84,6 +84,12 @@ To display a list of the available InvenTree administration actions, run the fol
invoke --list
```
This provides a list of the available invoke commands - also displayed below:
```
{{ invoke_commands() }}
```
### Virtual Environment
Installing the required Python packages inside a virtual environment allows a local install separate to the system-wide Python installation. While not strictly necessary, using a virtual environment is **highly recommended** as it prevents conflicts between the different Python installations.

View File

@ -26,6 +26,18 @@ The *status* of a given stock item is displayed on the stock item detail page:
{% include 'img.html' %}
{% endwith %}
**Source Code**
Refer to the source code for the Stock status codes:
::: stock.status_codes.StockStatus
options:
show_bases: False
show_root_heading: False
show_root_toc_entry: False
show_source: True
members: []
### Default Status Code
The default status code for any newly created Stock Item is <span class='badge inventree success'>OK</span>

View File

@ -1,11 +1,154 @@
"""Main entry point for the documentation build process."""
import os
import subprocess
import textwrap
import requests
import yaml
def get_repo_url(raw=False):
"""Return the repository URL for the current project."""
mkdocs_yml = os.path.join(os.path.dirname(__file__), 'mkdocs.yml')
with open(mkdocs_yml, 'r') as f:
mkdocs_config = yaml.safe_load(f)
repo_name = mkdocs_config['repo_name']
if raw:
return f'https://raw.githubusercontent.com/{repo_name}'
else:
return f'https://github.com/{repo_name}'
def check_link(url) -> bool:
"""Check that a provided URL is valid.
We allow a number attempts and a lengthy timeout,
as we do not want false negatives.
"""
CACHE_FILE = os.path.join(os.path.dirname(__file__), 'url_cache.txt')
# Keep a local cache file of URLs we have already checked
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, 'r') as f:
cache = f.read().splitlines()
if url in cache:
return True
attempts = 5
while attempts > 0:
response = requests.head(url, timeout=5000)
if response.status_code == 200:
# Update the cache file
with open(CACHE_FILE, 'a') as f:
f.write(f'{url}\n')
return True
attempts -= 1
return False
def define_env(env):
"""Define custom environment variables for the documentation build process."""
@env.macro
def sourcedir(dirname, branch='master'):
"""Return a link to a directory within the source code repository.
Arguments:
- dirname: The name of the directory to link to (relative to the top-level directory)
Returns:
- A fully qualified URL to the source code directory on GitHub
Raises:
- FileNotFoundError: If the directory does not exist, or the generated URL is invalid
"""
if dirname.startswith('/'):
dirname = dirname[1:]
# This file exists at ./docs/main.py, so any directory we link to must be relative to the top-level directory
here = os.path.dirname(__file__)
root = os.path.abspath(os.path.join(here, '..'))
directory = os.path.join(root, dirname)
directory = os.path.abspath(directory)
if not os.path.exists(directory) or not os.path.isdir(directory):
raise FileNotFoundError(f'Source directory {dirname} does not exist.')
repo_url = get_repo_url()
url = f'{repo_url}/tree/{branch}/{dirname}'
# Check that the URL exists before returning it
if not check_link(url):
raise FileNotFoundError(f'URL {url} does not exist.')
return url
@env.macro
def sourcefile(filename, branch='master', raw=False):
"""Return a link to a file within the source code repository.
Arguments:
- filename: The name of the file to link to (relative to the top-level directory)
Returns:
- A fully qualified URL to the source code file on GitHub
Raises:
- FileNotFoundError: If the file does not exist, or the generated URL is invalid
"""
if filename.startswith('/'):
filename = filename[1:]
# This file exists at ./docs/main.py, so any file we link to must be relative to the top-level directory
here = os.path.dirname(__file__)
root = os.path.abspath(os.path.join(here, '..'))
file_path = os.path.join(root, filename)
if not os.path.exists(file_path):
raise FileNotFoundError(f'Source file {filename} does not exist.')
repo_url = get_repo_url(raw=raw)
if raw:
url = f'{repo_url}/{branch}/{filename}'
else:
url = f'{repo_url}/blob/{branch}/{filename}'
# Check that the URL exists before returning it
if not check_link(url):
raise FileNotFoundError(f'URL {url} does not exist.')
return url
@env.macro
def invoke_commands():
"""Provides an output of the available commands."""
here = os.path.dirname(__file__)
base = os.path.join(here, '..')
base = os.path.abspath(base)
tasks = os.path.join(base, 'tasks.py')
output = os.path.join(here, 'invoke-commands.txt')
command = f'invoke -f {tasks} --list > {output}'
assert subprocess.call(command, shell=True) == 0
with open(output, 'r') as f:
content = f.read()
return content
@env.macro
def listimages(subdir):
"""Return a listing of all asset files in the provided subdir."""
@ -22,3 +165,39 @@ def define_env(env):
assets.append(os.path.join(subdir, asset))
return assets
@env.macro
def includefile(filename: str, title: str, format: str = ''):
"""Include a file in the documentation, in a 'collapse' block.
Arguments:
- filename: The name of the file to include (relative to the top-level directory)
- title:
"""
here = os.path.dirname(__file__)
path = os.path.join(here, '..', filename)
path = os.path.abspath(path)
if not os.path.exists(path):
raise FileNotFoundError(f'Required file {path} does not exist.')
with open(path, 'r') as f:
content = f.read()
data = f'??? abstract "{title}"\n\n'
data += f' ```{format}\n'
data += textwrap.indent(content, ' ')
data += '\n\n'
data += ' ```\n\n'
return data
@env.macro
def templatefile(filename):
"""Include code for a provided template file."""
base = os.path.basename(filename)
fn = os.path.join(
'src', 'backend', 'InvenTree', 'report', 'templates', filename
)
return includefile(fn, f'Template: {base}', format='html')

View File

@ -135,25 +135,15 @@ nav:
- Return Orders: order/return_order.md
- Project Codes: order/project_codes.md
- Report:
- Templates: report/report.md
- Templates: report/templates.md
- Template Rendering: report/weasyprint.md
- Template Editor: report/template_editor.md
- Report Types:
- Test Reports: report/test.md
- Build Order: report/build.md
- Purchase Order: report/purchase_order.md
- Sales Order: report/sales_order.md
- Return Order: report/return_order.md
- BOM: report/bom.md
- Stock Location: report/stock_location.md
- Labels:
- Custom Labels: report/labels.md
- Part Labels: report/labels/part_labels.md
- Stock Labels: report/labels/stock_labels.md
- Location Labels: report/labels/location_labels.md
- Build Labels: report/labels/build_labels.md
- Reports: report/report.md
- Labels: report/labels.md
- Context Variables: report/context_variables.md
- Helper Functions: report/helpers.md
- Barcodes: report/barcodes.md
- Context Variables: report/context_variables.md
- Sample Templates: report/samples.md
- Admin:
- Global Settings: settings/global.md
- User Settings: settings/user.md
@ -241,6 +231,7 @@ plugins:
on_config: "docs.docs.hooks:on_config"
- macros:
include_dir: docs/_includes
module_name: main
- mkdocstrings:
default_handler: python
handlers:
@ -250,6 +241,8 @@ plugins:
options:
show_symbol_type_heading: true
show_symbol_type_toc: true
show_root_heading: false
show_root_toc_entry: false
# Extensions
markdown_extensions:

8
docs/requirements.in Normal file
View File

@ -0,0 +1,8 @@
mkdocs==1.6.0
mkdocs-macros-plugin>=0.7,<2.0
mkdocs-material>=9.0,<10.0
mkdocs-git-revision-date-localized-plugin>=1.1,<2.0
mkdocs-simple-hooks>=0.1,<1.0
mkdocs-include-markdown-plugin
neoteroi-mkdocs
mkdocstrings[python]>=0.25.0

View File

@ -1,8 +1,590 @@
mkdocs==1.5.3
mkdocs-macros-plugin>=0.7,<2.0
mkdocs-material>=9.0,<10.0
mkdocs-git-revision-date-localized-plugin>=1.1,<2.0
mkdocs-simple-hooks>=0.1,<1.0
mkdocs-include-markdown-plugin
neoteroi-mkdocs
mkdocstrings[python]>=0.24.0
# This file was autogenerated by uv via the following command:
# uv pip compile docs/requirements.in -o docs/requirements.txt --python-version=3.9 --no-strip-extras --generate-hashes
anyio==4.3.0 \
--hash=sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8 \
--hash=sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6
# via httpx
babel==2.15.0 \
--hash=sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb \
--hash=sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413
# via
# mkdocs-git-revision-date-localized-plugin
# mkdocs-material
bracex==2.4 \
--hash=sha256:a27eaf1df42cf561fed58b7a8f3fdf129d1ea16a81e1fadd1d17989bc6384beb \
--hash=sha256:efdc71eff95eaff5e0f8cfebe7d01adf2c8637c8c92edaf63ef348c241a82418
# via wcmatch
certifi==2024.2.2 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
# via
# httpcore
# httpx
# requests
charset-normalizer==3.3.2 \
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
--hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \
--hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \
--hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \
--hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \
--hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \
--hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \
--hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \
--hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \
--hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \
--hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \
--hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \
--hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \
--hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \
--hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \
--hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \
--hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \
--hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \
--hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \
--hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \
--hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \
--hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \
--hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \
--hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \
--hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \
--hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \
--hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \
--hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \
--hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \
--hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \
--hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \
--hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \
--hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \
--hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \
--hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \
--hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \
--hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \
--hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \
--hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \
--hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \
--hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \
--hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \
--hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \
--hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \
--hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \
--hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \
--hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \
--hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \
--hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \
--hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \
--hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \
--hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \
--hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \
--hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \
--hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \
--hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \
--hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \
--hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \
--hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \
--hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \
--hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \
--hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \
--hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \
--hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \
--hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \
--hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \
--hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \
--hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \
--hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \
--hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \
--hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \
--hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \
--hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \
--hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \
--hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \
--hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \
--hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \
--hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \
--hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \
--hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \
--hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \
--hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \
--hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \
--hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \
--hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \
--hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \
--hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \
--hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \
--hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \
--hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561
# via requests
click==8.1.7 \
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
# via
# mkdocs
# mkdocstrings
# neoteroi-mkdocs
colorama==0.4.6 \
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
# via
# griffe
# mkdocs-material
essentials==1.1.5 \
--hash=sha256:8736f738bb2c51d5069b2de2cf9146f7d402f25f9f95636781e59a422c908c46 \
--hash=sha256:905fa4a69fcd2b2cf41ecc6cc65827e30c87ef91f3f5c71540bcc5e984fa8360
# via essentials-openapi
essentials-openapi==1.0.9 \
--hash=sha256:1431e98ef0a442f1919fd9833385bf44d832c355fd05919dc06d43d4da0f8ef4 \
--hash=sha256:ebc46aac41c0b917a658f77caaa0ca93a6e4a4519de8a272f82c1538ccd5619f
# via neoteroi-mkdocs
exceptiongroup==1.2.1 \
--hash=sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad \
--hash=sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16
# via anyio
ghp-import==2.1.0 \
--hash=sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619 \
--hash=sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343
# via mkdocs
gitdb==4.0.11 \
--hash=sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4 \
--hash=sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b
# via gitpython
gitpython==3.1.43 \
--hash=sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c \
--hash=sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff
# via mkdocs-git-revision-date-localized-plugin
griffe==0.45.1 \
--hash=sha256:12194c10ae07a7f46708741ad78419362cf8e5c883f449c7c48de1686611b853 \
--hash=sha256:84ce9243a9e63c07d55563a735a0d07ef70b46c455616c174010e7fc816f4648
# via mkdocstrings-python
h11==0.14.0 \
--hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
--hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
# via httpcore
httpcore==1.0.5 \
--hash=sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61 \
--hash=sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5
# via httpx
httpx==0.27.0 \
--hash=sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 \
--hash=sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5
# via neoteroi-mkdocs
idna==3.7 \
--hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
--hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
# via
# anyio
# httpx
# requests
importlib-metadata==7.1.0 \
--hash=sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570 \
--hash=sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2
# via
# markdown
# mkdocs
# mkdocs-get-deps
# mkdocstrings
jinja2==3.1.4 \
--hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \
--hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d
# via
# mkdocs
# mkdocs-macros-plugin
# mkdocs-material
# mkdocstrings
# neoteroi-mkdocs
markdown==3.6 \
--hash=sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f \
--hash=sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224
# via
# mkdocs
# mkdocs-autorefs
# mkdocs-material
# mkdocstrings
# pymdown-extensions
markdown-it-py==3.0.0 \
--hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \
--hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb
# via rich
markupsafe==2.1.5 \
--hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \
--hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \
--hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \
--hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \
--hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \
--hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \
--hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \
--hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \
--hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \
--hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \
--hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \
--hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \
--hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \
--hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \
--hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \
--hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \
--hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \
--hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \
--hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \
--hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \
--hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \
--hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \
--hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \
--hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \
--hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \
--hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \
--hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \
--hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \
--hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \
--hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \
--hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \
--hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \
--hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \
--hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \
--hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \
--hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \
--hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \
--hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \
--hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \
--hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \
--hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \
--hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \
--hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \
--hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \
--hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \
--hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \
--hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \
--hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \
--hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \
--hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \
--hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \
--hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \
--hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \
--hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \
--hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \
--hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \
--hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \
--hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \
--hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \
--hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68
# via
# essentials-openapi
# jinja2
# mkdocs
# mkdocs-autorefs
# mkdocstrings
mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
mergedeep==1.3.4 \
--hash=sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8 \
--hash=sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307
# via
# mkdocs
# mkdocs-get-deps
mkdocs==1.6.0 \
--hash=sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7 \
--hash=sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512
# via
# mkdocs-autorefs
# mkdocs-git-revision-date-localized-plugin
# mkdocs-include-markdown-plugin
# mkdocs-macros-plugin
# mkdocs-material
# mkdocs-simple-hooks
# mkdocstrings
# neoteroi-mkdocs
mkdocs-autorefs==1.0.1 \
--hash=sha256:aacdfae1ab197780fb7a2dac92ad8a3d8f7ca8049a9cbe56a4218cd52e8da570 \
--hash=sha256:f684edf847eced40b570b57846b15f0bf57fb93ac2c510450775dcf16accb971
# via mkdocstrings
mkdocs-get-deps==0.2.0 \
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
# via mkdocs
mkdocs-git-revision-date-localized-plugin==1.2.5 \
--hash=sha256:0c439816d9d0dba48e027d9d074b2b9f1d7cd179f74ba46b51e4da7bb3dc4b9b \
--hash=sha256:d796a18b07cfcdb154c133e3ec099d2bb5f38389e4fd54d3eb516a8a736815b8
mkdocs-include-markdown-plugin==6.0.6 \
--hash=sha256:7c80258b2928563c75cc057a7b9a0014701c40804b1b6aa290f3b4032518b43c \
--hash=sha256:7ccafbaa412c1e5d3510c4aff46d1fe64c7a810c01dace4c636253d1aa5bc193
mkdocs-macros-plugin==1.0.5 \
--hash=sha256:f60e26f711f5a830ddf1e7980865bf5c0f1180db56109803cdd280073c1a050a \
--hash=sha256:fe348d75f01c911f362b6d998c57b3d85b505876dde69db924f2c512c395c328
mkdocs-material==9.5.24 \
--hash=sha256:02d5aaba0ee755e707c3ef6e748f9acb7b3011187c0ea766db31af8905078a34 \
--hash=sha256:e12cd75954c535b61e716f359cf2a5056bf4514889d17161fdebd5df4b0153c6
mkdocs-material-extensions==1.3.1 \
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
--hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31
# via mkdocs-material
mkdocs-simple-hooks==0.1.5 \
--hash=sha256:dddbdf151a18723c9302a133e5cf79538be8eb9d274e8e07d2ac3ac34890837c \
--hash=sha256:efeabdbb98b0850a909adee285f3404535117159d5cb3a34f541d6eaa644d50a
mkdocstrings[python]==0.25.1 \
--hash=sha256:c3a2515f31577f311a9ee58d089e4c51fc6046dbd9e9b4c3de4c3194667fe9bf \
--hash=sha256:da01fcc2670ad61888e8fe5b60afe9fee5781017d67431996832d63e887c2e51
# via mkdocstrings-python
mkdocstrings-python==1.10.2 \
--hash=sha256:38a4fd41953defb458a107033440c229c7e9f98f35a24e84d888789c97da5a63 \
--hash=sha256:e8e596b37f45c09b67bec253e035fe18988af5bbbbf44e0ccd711742eed750e5
# via mkdocstrings
neoteroi-mkdocs==1.0.5 \
--hash=sha256:1f3b372dee79269157361733c0f45b3a89189077078e0e3224d829a144ef3579 \
--hash=sha256:29875ef444b08aec5619a384142e16f1b4e851465cab4e380fb2b8ae730fe046
packaging==24.0 \
--hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \
--hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9
# via mkdocs
paginate==0.5.6 \
--hash=sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d
# via mkdocs-material
pathspec==0.12.1 \
--hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \
--hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712
# via mkdocs
platformdirs==4.2.2 \
--hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
--hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
# via
# mkdocs-get-deps
# mkdocstrings
pygments==2.18.0 \
--hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \
--hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a
# via
# mkdocs-material
# rich
pymdown-extensions==10.8.1 \
--hash=sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940 \
--hash=sha256:f938326115884f48c6059c67377c46cf631c733ef3629b6eed1349989d1b30cb
# via
# mkdocs-material
# mkdocstrings
python-dateutil==2.9.0.post0 \
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
# via
# ghp-import
# mkdocs-macros-plugin
pytz==2024.1 \
--hash=sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812 \
--hash=sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319
# via mkdocs-git-revision-date-localized-plugin
pyyaml==6.0.1 \
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
--hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
--hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
--hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
--hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
--hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
--hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
--hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
--hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
--hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
--hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
--hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
--hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
--hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
--hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
--hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
--hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
--hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
--hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
--hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
--hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
--hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
--hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
--hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
--hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
--hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
--hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
--hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
--hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \
--hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
--hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
--hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
--hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
--hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
--hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
--hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
--hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
--hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
--hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
--hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
--hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
--hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
--hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
--hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
--hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
--hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
--hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
--hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
# via
# essentials-openapi
# mkdocs
# mkdocs-get-deps
# mkdocs-macros-plugin
# pymdown-extensions
# pyyaml-env-tag
pyyaml-env-tag==0.1 \
--hash=sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb \
--hash=sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069
# via mkdocs
regex==2024.5.15 \
--hash=sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649 \
--hash=sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35 \
--hash=sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb \
--hash=sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68 \
--hash=sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5 \
--hash=sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133 \
--hash=sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0 \
--hash=sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d \
--hash=sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da \
--hash=sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f \
--hash=sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d \
--hash=sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53 \
--hash=sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa \
--hash=sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a \
--hash=sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890 \
--hash=sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67 \
--hash=sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c \
--hash=sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2 \
--hash=sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced \
--hash=sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741 \
--hash=sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f \
--hash=sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa \
--hash=sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf \
--hash=sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4 \
--hash=sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5 \
--hash=sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2 \
--hash=sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384 \
--hash=sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7 \
--hash=sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014 \
--hash=sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704 \
--hash=sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5 \
--hash=sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2 \
--hash=sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49 \
--hash=sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1 \
--hash=sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694 \
--hash=sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629 \
--hash=sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6 \
--hash=sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435 \
--hash=sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c \
--hash=sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835 \
--hash=sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e \
--hash=sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201 \
--hash=sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62 \
--hash=sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5 \
--hash=sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16 \
--hash=sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f \
--hash=sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1 \
--hash=sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f \
--hash=sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f \
--hash=sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145 \
--hash=sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3 \
--hash=sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed \
--hash=sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143 \
--hash=sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca \
--hash=sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9 \
--hash=sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa \
--hash=sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850 \
--hash=sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80 \
--hash=sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe \
--hash=sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656 \
--hash=sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388 \
--hash=sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1 \
--hash=sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294 \
--hash=sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3 \
--hash=sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d \
--hash=sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b \
--hash=sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40 \
--hash=sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600 \
--hash=sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c \
--hash=sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569 \
--hash=sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456 \
--hash=sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9 \
--hash=sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb \
--hash=sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e \
--hash=sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f \
--hash=sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d \
--hash=sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a \
--hash=sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a \
--hash=sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796
# via mkdocs-material
requests==2.32.2 \
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
# via mkdocs-material
rich==13.7.1 \
--hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \
--hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432
# via neoteroi-mkdocs
six==1.16.0 \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
# via python-dateutil
smmap==5.0.1 \
--hash=sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62 \
--hash=sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da
# via gitdb
sniffio==1.3.1 \
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
# via
# anyio
# httpx
termcolor==2.4.0 \
--hash=sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63 \
--hash=sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a
# via mkdocs-macros-plugin
typing-extensions==4.11.0 \
--hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \
--hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a
# via
# anyio
# mkdocstrings
urllib3==2.2.1 \
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \
--hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19
# via requests
watchdog==4.0.0 \
--hash=sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257 \
--hash=sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca \
--hash=sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b \
--hash=sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85 \
--hash=sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b \
--hash=sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19 \
--hash=sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50 \
--hash=sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92 \
--hash=sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269 \
--hash=sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f \
--hash=sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c \
--hash=sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b \
--hash=sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87 \
--hash=sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b \
--hash=sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b \
--hash=sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8 \
--hash=sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c \
--hash=sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3 \
--hash=sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7 \
--hash=sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605 \
--hash=sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935 \
--hash=sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b \
--hash=sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927 \
--hash=sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101 \
--hash=sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07 \
--hash=sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec \
--hash=sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4 \
--hash=sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245 \
--hash=sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d
# via mkdocs
wcmatch==8.5.2 \
--hash=sha256:17d3ad3758f9d0b5b4dedc770b65420d4dac62e680229c287bf24c9db856a478 \
--hash=sha256:a70222b86dea82fb382dd87b73278c10756c138bd6f8f714e2183128887b9eb2
# via mkdocs-include-markdown-plugin
zipp==3.18.2 \
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
# via importlib-metadata

View File

@ -8,6 +8,7 @@ from pathlib import Path
from django.conf import settings
from django.db import transaction
from django.http import JsonResponse
from django.urls import include, path
from django.utils.translation import gettext_lazy as _
from django_q.models import OrmQ
@ -73,8 +74,24 @@ class LicenseView(APIView):
logger.exception("Exception while reading license file '%s': %s", path, e)
return []
# Ensure consistent string between backend and frontend licenses
return [{key.lower(): value for key, value in entry.items()} for entry in data]
output = []
names = set()
# Ensure we do not have any duplicate 'name' values in the list
for entry in data:
name = None
for key in entry.keys():
if key.lower() == 'name':
name = entry[key]
break
if name is None or name in names:
continue
names.add(name)
output.append({key.lower(): value for key, value in entry.items()})
return output
@extend_schema(responses={200: OpenApiResponse(response=LicenseViewSerializer)})
def get(self, request, *args, **kwargs):
@ -534,6 +551,10 @@ class MetadataView(RetrieveUpdateAPI):
"""Return the model type associated with this API instance."""
model = self.kwargs.get(self.MODEL_REF, None)
if 'lookup_field' in self.kwargs:
# Set custom lookup field (instead of default 'pk' value) if supplied
self.lookup_field = self.kwargs.pop('lookup_field')
if model is None:
raise ValidationError(
f"MetadataView called without '{self.MODEL_REF}' parameter"

View File

@ -1,10 +1,52 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 192
INVENTREE_API_VERSION = 205
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v205 - 2024-06-03 : https://github.com/inventree/InvenTree/pull/7284
- Added model_type and model_id fields to the "NotesImage" serializer
v204 - 2024-06-03 : https://github.com/inventree/InvenTree/pull/7393
- Fixes previous API update which resulted in inconsistent ordering of currency codes
v203 - 2024-06-03 : https://github.com/inventree/InvenTree/pull/7390
- Currency codes are now configurable as a run-time setting
v202 - 2024-05-27 : https://github.com/inventree/InvenTree/pull/7343
- Adjust "required" attribute of Part.category field to be optional
v201 - 2024-05-21 : https://github.com/inventree/InvenTree/pull/7074
- Major refactor of the report template / report printing interface
- This is a *breaking change* to the report template API
v200 - 2024-05-20 : https://github.com/inventree/InvenTree/pull/7000
- Adds API endpoint for generating custom batch codes
- Adds API endpoint for generating custom serial numbers
v199 - 2024-05-20 : https://github.com/inventree/InvenTree/pull/7264
- Expose "bom_valid" filter for the Part API
- Expose "starred" filter for the Part API
v198 - 2024-05-19 : https://github.com/inventree/InvenTree/pull/7258
- Fixed lookup field conflicts in the plugins API
v197 - 2024-05-14 : https://github.com/inventree/InvenTree/pull/7224
- Refactor the plugin API endpoints to use the plugin "key" for lookup, rather than the PK value
v196 - 2024-05-05 : https://github.com/inventree/InvenTree/pull/7160
- Adds "location" field to BuildOutputComplete API endpoint
v195 - 2024-05-03 : https://github.com/inventree/InvenTree/pull/7153
- Fixes bug in BuildOrderCancel API endpoint
v194 - 2024-05-01 : https://github.com/inventree/InvenTree/pull/7147
- Adds field description to the currency_exchange_retrieve API call
v193 - 2024-04-30 : https://github.com/inventree/InvenTree/pull/7144
- Adds "assigned_to" filter to PurchaseOrder / SalesOrder / ReturnOrder API endpoints
v192 - 2024-04-23 : https://github.com/inventree/InvenTree/pull/7106
- Adds 'trackable' ordering option to BuildLineLabel API endpoint

View File

@ -74,6 +74,7 @@ class InvenTreeConfig(AppConfig):
obsolete = [
'InvenTree.tasks.delete_expired_sessions',
'stock.tasks.delete_old_stock_items',
'label.tasks.cleanup_old_label_outputs',
]
try:
@ -83,7 +84,14 @@ class InvenTreeConfig(AppConfig):
# Remove any existing obsolete tasks
try:
Schedule.objects.filter(func__in=obsolete).delete()
obsolete_tasks = Schedule.objects.filter(func__in=obsolete)
if obsolete_tasks.exists():
logger.info(
'Removing %s obsolete background tasks', obsolete_tasks.count()
)
obsolete_tasks.delete()
except Exception:
logger.exception('Failed to remove obsolete tasks - database not ready')
@ -177,7 +185,7 @@ class InvenTreeConfig(AppConfig):
try:
from djmoney.contrib.exchange.models import ExchangeBackend
from common.settings import currency_code_default
from common.currency import currency_code_default
from InvenTree.tasks import update_exchange_rates
except AppRegistryNotReady: # pragma: no cover
pass

View File

@ -0,0 +1,101 @@
"""Configuration options for InvenTree external cache."""
import logging
import socket
import InvenTree.config
import InvenTree.ready
logger = logging.getLogger('inventree')
def cache_setting(name, default=None, **kwargs):
"""Return a cache setting."""
return InvenTree.config.get_setting(
f'INVENTREE_CACHE_{name.upper()}', f'cache.{name.lower()}', default, **kwargs
)
def cache_host():
"""Return the cache host address."""
return cache_setting('host', None)
def cache_port():
"""Return the cache port."""
return cache_setting('port', '6379', typecast=int)
def is_global_cache_enabled():
"""Check if the global cache is enabled.
- Test if the user has enabled and configured global cache
- Test if it is appropriate to enable global cache based on the current operation.
"""
host = cache_host()
# Test if cache is enabled
# If the cache host is set, then the "default" action is to enable the cache
if not cache_setting('enabled', host is not None, typecast=bool):
return False
# Test if the cache is configured
if not cache_host():
logger.warning('Global cache is enabled, but no cache host is configured!')
return False
# The cache should not be used during certain operations
if not InvenTree.ready.canAppAccessDatabase(
allow_test=False, allow_plugins=False, allow_shell=True
):
logger.info('Global cache bypassed for this operation')
return False
logger.info('Global cache enabled')
return True
def get_cache_config(global_cache: bool) -> dict:
"""Return the cache configuration options.
Args:
global_cache: True if the global cache is enabled.
Returns:
A dictionary containing the cache configuration options.
"""
if global_cache:
return {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f'redis://{cache_host()}:{cache_port()}/0',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'SOCKET_CONNECT_TIMEOUT': cache_setting(
'connect_timeout', 5, typecast=int
),
'SOCKET_TIMEOUT': cache_setting('timeout', 3, typecast=int),
'CONNECTION_POOL_KWARGS': {
'socket_keepalive': cache_setting(
'tcp_keepalive', True, typecast=bool
),
'socket_keepalive_options': {
socket.TCP_KEEPCNT: cache_setting(
'keepalive_count', 5, typecast=int
),
socket.TCP_KEEPIDLE: cache_setting(
'keepalive_idle', 1, typecast=int
),
socket.TCP_KEEPINTVL: cache_setting(
'keepalive_interval', 1, typecast=int
),
socket.TCP_USER_TIMEOUT: cache_setting(
'user_timeout', 1000, typecast=int
),
},
},
},
}
# Default: Use django local memory cache
return {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}

View File

@ -7,7 +7,7 @@ from django.db.transaction import atomic
from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
from djmoney.contrib.exchange.models import ExchangeBackend, Rate
from common.settings import currency_code_default, currency_codes
from common.currency import currency_code_default, currency_codes
logger = logging.getLogger('inventree')

View File

@ -59,7 +59,7 @@ class InvenTreeURLField(models.URLField):
def money_kwargs(**kwargs):
"""Returns the database settings for MoneyFields."""
from common.settings import currency_code_default, currency_code_mappings
from common.currency import currency_code_default, currency_code_mappings
# Default values (if not specified)
if 'max_digits' not in kwargs:

View File

@ -166,3 +166,5 @@ SEARCH_ORDER_FILTER_ALIAS = [
]
ORDER_FILTER = [rest_filters.DjangoFilterBackend, filters.OrderingFilter]
ORDER_FILTER_ALIAS = [rest_filters.DjangoFilterBackend, InvenTreeOrderingFilter]

View File

@ -28,7 +28,7 @@ from djmoney.money import Money
from PIL import Image
import InvenTree.version
from common.settings import currency_code_default
from common.currency import currency_code_default
from .settings import MEDIA_URL, STATIC_URL
@ -293,13 +293,13 @@ def increment(value):
QQQ -> QQQ
"""
value = str(value).strip()
# Ignore empty strings
if value in ['', None]:
# Provide a default value if provided with a null input
return '1'
value = str(value).strip()
pattern = r'(.*?)(\d+)?$'
result = re.search(pattern, value)

View File

@ -63,7 +63,7 @@ def get_base_url(request=None):
# Check if a global InvenTree setting is provided
try:
if site_url := common.models.InvenTreeSetting.get_setting(
'INVENTREE_BASE_URL', create=False, cache=False
'INVENTREE_BASE_URL', create=False
):
return site_url
except (ProgrammingError, OperationalError):

View File

@ -45,6 +45,7 @@ LOCALES = [
('sv', _('Swedish')),
('th', _('Thai')),
('tr', _('Turkish')),
('uk', _('Ukrainian')),
('vi', _('Vietnamese')),
('zh-hans', _('Chinese (Simplified)')),
('zh-hant', _('Chinese (Traditional)')),

View File

@ -115,16 +115,57 @@ class InvenTreeMetadata(SimpleMetadata):
return metadata
def override_value(self, field_name, field_value, model_value):
"""Override a value on the serializer with a matching value for the model.
This is used to override the serializer values with model values,
if (and *only* if) the model value should take precedence.
The values are overridden under the following conditions:
- field_value is None
- model_value is callable, and field_value is not (this indicates that the model value is translated)
- model_value is not a string, and field_value is a string (this indicates that the model value is translated)
"""
if model_value and not field_value:
return model_value
if field_value and not model_value:
return field_value
if callable(model_value) and not callable(field_value):
return model_value
if type(model_value) is not str and type(field_value) is str:
return model_value
return field_value
def get_serializer_info(self, serializer):
"""Override get_serializer_info so that we can add 'default' values to any fields whose Meta.model specifies a default value."""
self.serializer = serializer
serializer_info = super().get_serializer_info(serializer)
# Look for any dynamic fields which were not available when the serializer was instantiated
if hasattr(serializer, 'Meta'):
for field_name in serializer.Meta.fields:
if field_name in serializer_info:
# Already know about this one
continue
if hasattr(serializer, field_name):
field = getattr(serializer, field_name)
serializer_info[field_name] = self.get_field_info(field)
model_class = None
# Attributes to copy extra attributes from the model to the field (if they don't exist)
extra_attributes = ['help_text', 'max_length']
# Note that the attributes may be named differently on the underlying model!
extra_attributes = {
'help_text': 'help_text',
'max_length': 'max_length',
'label': 'verbose_name',
}
try:
model_class = serializer.Meta.model
@ -155,10 +196,12 @@ class InvenTreeMetadata(SimpleMetadata):
elif name in model_default_values:
serializer_info[name]['default'] = model_default_values[name]
for attr in extra_attributes:
if attr not in serializer_info[name]:
if hasattr(field, attr):
serializer_info[name][attr] = getattr(field, attr)
for field_key, model_key in extra_attributes.items():
field_value = serializer_info[name].get(field_key, None)
model_value = getattr(field, model_key, None)
if value := self.override_value(name, field_value, model_value):
serializer_info[name][field_key] = value
# Iterate through relations
for name, relation in model_fields.relations.items():
@ -176,13 +219,12 @@ class InvenTreeMetadata(SimpleMetadata):
relation.model_field.get_limit_choices_to()
)
for attr in extra_attributes:
if attr not in serializer_info[name] and hasattr(
relation.model_field, attr
):
serializer_info[name][attr] = getattr(
relation.model_field, attr
)
for field_key, model_key in extra_attributes.items():
field_value = serializer_info[name].get(field_key, None)
model_value = getattr(relation.model_field, model_key, None)
if value := self.override_value(name, field_value, model_value):
serializer_info[name][field_key] = value
if name in model_default_values:
serializer_info[name]['default'] = model_default_values[name]
@ -264,7 +306,9 @@ class InvenTreeMetadata(SimpleMetadata):
# Introspect writable related fields
if field_info['type'] == 'field' and not field_info['read_only']:
# If the field is a PrimaryKeyRelatedField, we can extract the model from the queryset
if isinstance(field, serializers.PrimaryKeyRelatedField):
if isinstance(field, serializers.PrimaryKeyRelatedField) or issubclass(
field.__class__, serializers.PrimaryKeyRelatedField
):
model = field.queryset.model
else:
logger.debug(
@ -285,6 +329,9 @@ class InvenTreeMetadata(SimpleMetadata):
else:
field_info['api_url'] = model.get_api_url()
# Handle custom 'primary key' field
field_info['pk_field'] = getattr(field, 'pk_field', 'pk') or 'pk'
# Add more metadata about dependent fields
if field_info['type'] == 'dependent field':
field_info['depends_on'] = field.depends_on

View File

@ -70,7 +70,8 @@ class AuthRequiredMiddleware(object):
# API requests are handled by the DRF library
if request.path_info.startswith('/api/'):
return self.get_response(request)
response = self.get_response(request)
return response
# Is the function exempt from auth requirements?
path_func = resolve(request.path).func

View File

@ -122,6 +122,20 @@ class PluginValidationMixin(DiffMixin):
self.run_plugin_validation()
super().save(*args, **kwargs)
def delete(self):
"""Run plugin validation on model delete.
Allows plugins to prevent model instances from being deleted.
Note: Each plugin may raise a ValidationError to prevent deletion.
"""
from plugin.registry import registry
for plugin in registry.with_mixin('validation'):
plugin.validate_model_deletion(self)
super().delete()
class MetadataMixin(models.Model):
"""Model mixin class which adds a JSON metadata field to a model, for use by any (and all) plugins.
@ -1017,6 +1031,30 @@ class InvenTreeNotesMixin(models.Model):
abstract = True
def delete(self):
"""Custom delete method for InvenTreeNotesMixin.
- Before deleting the object, check if there are any uploaded images associated with it.
- If so, delete the notes first
"""
from common.models import NotesImage
images = NotesImage.objects.filter(
model_type=self.__class__.__name__.lower(), model_id=self.pk
)
if images.exists():
logger.info(
'Deleting %s uploaded images associated with %s <%s>',
images.count(),
self.__class__.__name__,
self.pk,
)
images.delete()
super().delete()
notes = InvenTree.fields.InvenTreeNotesField(
verbose_name=_('Notes'), help_text=_('Markdown notes (optional)')
)

View File

@ -114,6 +114,7 @@ def canAppAccessDatabase(
'wait_for_db',
'makemessages',
'compilemessages',
'spectactular',
]
if not allow_shell:

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