Merge branch 'master' of https://github.com/inventree/InvenTree into matmair/issue6281
@ -31,7 +31,8 @@
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"batisteo.vscode-django",
|
||||
"eamodio.gitlens"
|
||||
"eamodio.gitlens",
|
||||
"biomejs.biome"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:13
|
||||
|
@ -11,10 +11,10 @@ python3 -m venv /home/inventree/dev/venv --system-site-packages --upgrade-deps
|
||||
invoke update -s
|
||||
|
||||
# Configure dev environment
|
||||
invoke setup-dev
|
||||
invoke dev.setup-dev
|
||||
|
||||
# Install required frontend packages
|
||||
invoke frontend-install
|
||||
invoke int.frontend-install
|
||||
|
||||
# remove existing gitconfig created by "Avoiding Dubious Ownership" step
|
||||
# so that it gets copied from host to the container to have your global
|
||||
|
4
.github/FUNDING.yml
vendored
@ -1,5 +1,3 @@
|
||||
github: inventree
|
||||
ko_fi: inventree
|
||||
patreon: inventree
|
||||
polar: inventree
|
||||
github: inventree
|
||||
custom: [paypal.me/inventree]
|
||||
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
2
.github/actions/migration/action.yaml
vendored
@ -9,7 +9,7 @@ runs:
|
||||
shell: bash
|
||||
run: |
|
||||
invoke migrate
|
||||
invoke import-fixtures
|
||||
invoke dev.import-fixtures
|
||||
invoke export-records -f data.json
|
||||
python3 ./src/backend/InvenTree/manage.py flush --noinput
|
||||
invoke migrate
|
||||
|
13
.github/actions/setup/action.yaml
vendored
@ -35,7 +35,9 @@ runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Python installs
|
||||
- name: Set up Python ${{ env.python_version }}
|
||||
@ -70,13 +72,6 @@ runs:
|
||||
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # pin to v3.8.2
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: src/backend/package-lock.json
|
||||
- name: Install npm packages
|
||||
if: ${{ inputs.npm == 'true' }}
|
||||
shell: bash
|
||||
run: cd src/backend && npm install
|
||||
|
||||
# OS installs
|
||||
- name: Install OS Dependencies
|
||||
if: ${{ inputs.apt-dependency }}
|
||||
@ -98,4 +93,4 @@ runs:
|
||||
- name: Run invoke update
|
||||
if: ${{ inputs.update == 'true' }}
|
||||
shell: bash
|
||||
run: invoke update --uv
|
||||
run: invoke update --uv --skip-backup --skip-static
|
||||
|
2
.github/dependabot.yml
vendored
@ -18,7 +18,7 @@ updates:
|
||||
directories:
|
||||
- /contrib/container
|
||||
- /docs
|
||||
- /.github
|
||||
- /contrib/dev_reqs
|
||||
- /src/backend
|
||||
schedule:
|
||||
interval: weekly
|
||||
|
4
.github/release.yml
vendored
@ -4,6 +4,7 @@ changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- translation
|
||||
- translations
|
||||
- documentation
|
||||
categories:
|
||||
- title: Breaking Changes
|
||||
@ -13,6 +14,9 @@ changelog:
|
||||
- title: Security Patches
|
||||
labels:
|
||||
- security
|
||||
- title: Database Changes
|
||||
labels:
|
||||
- migration
|
||||
- title: New Features
|
||||
labels:
|
||||
- Semver-Minor
|
||||
|
103
.github/scripts/check_js_templates.py
vendored
@ -1,103 +0,0 @@
|
||||
"""Test that the "translated" javascript files to not contain template tags which need to be determined at "run time".
|
||||
|
||||
This is because the "translated" javascript files are compiled into the "static" directory.
|
||||
They should only contain template tags that render static information.
|
||||
"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
template_dir = os.path.abspath(os.path.join(here, '..', 'InvenTree', 'templates'))
|
||||
|
||||
# We only care about the 'translated' files
|
||||
js_i18n_dir = os.path.join(template_dir, 'js', 'translated')
|
||||
js_dynamic_dir = os.path.join(template_dir, 'js', 'dynamic')
|
||||
|
||||
errors = 0
|
||||
|
||||
print('=================================')
|
||||
print('Checking static javascript files:')
|
||||
print('=================================')
|
||||
|
||||
|
||||
def check_invalid_tag(data):
|
||||
"""Check for invalid tags."""
|
||||
pattern = r'{%(\w+)'
|
||||
|
||||
err_count = 0
|
||||
|
||||
for idx, line in enumerate(data):
|
||||
results = re.findall(pattern, line)
|
||||
|
||||
for result in results:
|
||||
err_count += 1
|
||||
|
||||
print(f' - Error on line {idx + 1}: %{{{result[0]}')
|
||||
|
||||
return err_count
|
||||
|
||||
|
||||
def check_prohibited_tags(data):
|
||||
"""Check for prohibited tags."""
|
||||
allowed_tags = [
|
||||
'if',
|
||||
'elif',
|
||||
'else',
|
||||
'endif',
|
||||
'for',
|
||||
'endfor',
|
||||
'trans',
|
||||
'load',
|
||||
'include',
|
||||
'url',
|
||||
]
|
||||
|
||||
pattern = r'{% (\w+)\s'
|
||||
|
||||
err_count = 0
|
||||
|
||||
for idx, line in enumerate(data):
|
||||
for tag in re.findall(pattern, line):
|
||||
if tag not in allowed_tags:
|
||||
print(f" > Line {idx + 1} contains prohibited template tag '{tag}'")
|
||||
err_count += 1
|
||||
|
||||
return err_count
|
||||
|
||||
|
||||
for filename in pathlib.Path(js_i18n_dir).rglob('*.js'):
|
||||
print(f"Checking file 'translated/{os.path.basename(filename)}':")
|
||||
|
||||
with open(filename, 'r') as js_file:
|
||||
data = js_file.readlines()
|
||||
|
||||
errors += check_invalid_tag(data)
|
||||
errors += check_prohibited_tags(data)
|
||||
|
||||
for filename in pathlib.Path(js_dynamic_dir).rglob('*.js'):
|
||||
print(f"Checking file 'dynamic/{os.path.basename(filename)}':")
|
||||
|
||||
# Check that the 'dynamic' files do not contains any translated strings
|
||||
with open(filename, 'r') as js_file:
|
||||
data = js_file.readlines()
|
||||
|
||||
invalid_tags = ['blocktrans', 'blocktranslate', 'trans', 'translate']
|
||||
|
||||
err_count = 0
|
||||
|
||||
for idx, line in enumerate(data):
|
||||
for tag in invalid_tags:
|
||||
tag = '{% ' + tag
|
||||
if tag in line:
|
||||
err_count += 1
|
||||
|
||||
print(f" > Error on line {idx + 1}: Prohibited tag '{tag}' found")
|
||||
|
||||
|
||||
if errors > 0:
|
||||
print(f'Found {errors} incorrect template tags')
|
||||
|
||||
sys.exit(errors)
|
4
.github/scripts/check_migration_files.py
vendored
@ -20,9 +20,9 @@ for line in str(out.decode()).split('\n'):
|
||||
if len(migrations) == 0:
|
||||
sys.exit(0)
|
||||
|
||||
print('There are {n} unstaged migration files:'.format(n=len(migrations)))
|
||||
print(f'There are {len(migrations)} unstaged migration files:')
|
||||
|
||||
for m in migrations:
|
||||
print(' - {m}'.format(m=m))
|
||||
print(f' - {m}')
|
||||
|
||||
sys.exit(len(migrations))
|
||||
|
100
.github/scripts/check_source_strings.py
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
"""Script to check source strings for translations."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import rapidfuzz
|
||||
|
||||
BACKEND_SOURCE_FILE = [
|
||||
'..',
|
||||
'..',
|
||||
'src',
|
||||
'backend',
|
||||
'InvenTree',
|
||||
'locale',
|
||||
'en',
|
||||
'LC_MESSAGES',
|
||||
'django.po',
|
||||
]
|
||||
|
||||
FRONTEND_SOURCE_FILE = [
|
||||
'..',
|
||||
'..',
|
||||
'src',
|
||||
'frontend',
|
||||
'src',
|
||||
'locales',
|
||||
'en',
|
||||
'messages.po',
|
||||
]
|
||||
|
||||
|
||||
def extract_source_strings(file_path):
|
||||
"""Extract source strings from the provided file."""
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
abs_file_path = os.path.abspath(os.path.join(here, *file_path))
|
||||
|
||||
sources = []
|
||||
|
||||
with open(abs_file_path, encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('msgid '):
|
||||
msgid = line[6:].strip()
|
||||
|
||||
if msgid in sources:
|
||||
print(f'Duplicate source string: {msgid}')
|
||||
else:
|
||||
sources.append(msgid)
|
||||
|
||||
return sources
|
||||
|
||||
|
||||
def compare_source_strings(sources, threshold):
|
||||
"""Compare source strings to find duplicates (or close matches)."""
|
||||
issues = 0
|
||||
|
||||
for i, source in enumerate(sources):
|
||||
for other in sources[i + 1 :]:
|
||||
if other.lower() == source.lower():
|
||||
print(f'- Duplicate: {source} ~ {other}')
|
||||
issues += 1
|
||||
continue
|
||||
|
||||
ratio = rapidfuzz.fuzz.ratio(source, other)
|
||||
if ratio > threshold:
|
||||
print(f'- Close match: {source} ~ {other} ({ratio:.1f}%)')
|
||||
issues += 1
|
||||
|
||||
if issues:
|
||||
print(f' - Found {issues} issues.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Check source strings for translations.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--backend', action='store_true', help='Check backend source strings'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--frontend', action='store_true', help='Check frontend source strings'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--threshold',
|
||||
type=int,
|
||||
help='Set the threshold for string comparison',
|
||||
default=99,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.backend:
|
||||
backend_sources = extract_source_strings(BACKEND_SOURCE_FILE)
|
||||
print('Backend source strings:', len(backend_sources))
|
||||
compare_source_strings(backend_sources, args.threshold)
|
||||
|
||||
if args.frontend:
|
||||
frontend_sources = extract_source_strings(FRONTEND_SOURCE_FILE)
|
||||
print('Frontend source strings:', len(frontend_sources))
|
||||
compare_source_strings(frontend_sources, args.threshold)
|
42
.github/scripts/version_check.py
vendored
@ -10,6 +10,7 @@ tagged branch:
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@ -18,8 +19,11 @@ from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
REPO = os.getenv('GITHUB_REPOSITORY', 'inventree/inventree')
|
||||
GITHUB_API_URL = os.getenv('GITHUB_API_URL', 'https://api.github.com')
|
||||
|
||||
def get_existing_release_tags():
|
||||
|
||||
def get_existing_release_tags(include_prerelease=True):
|
||||
"""Request information on existing releases via the GitHub API."""
|
||||
# Check for github token
|
||||
token = os.getenv('GITHUB_TOKEN', None)
|
||||
@ -28,9 +32,7 @@ def get_existing_release_tags():
|
||||
if token:
|
||||
headers = {'Authorization': f'Bearer {token}'}
|
||||
|
||||
response = requests.get(
|
||||
'https://api.github.com/repos/inventree/inventree/releases', headers=headers
|
||||
)
|
||||
response = requests.get(f'{GITHUB_API_URL}/repos/{REPO}/releases', headers=headers)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise ValueError(
|
||||
@ -50,6 +52,9 @@ def get_existing_release_tags():
|
||||
print(f"Version '{tag}' did not match expected pattern")
|
||||
continue
|
||||
|
||||
if not include_prerelease and release['prerelease']:
|
||||
continue
|
||||
|
||||
tags.append([int(x) for x in match.groups()])
|
||||
|
||||
return tags
|
||||
@ -73,7 +78,7 @@ def check_version_number(version_string, allow_duplicate=False):
|
||||
version_tuple = [int(x) for x in match.groups()]
|
||||
|
||||
# Look through the existing releases
|
||||
existing = get_existing_release_tags()
|
||||
existing = get_existing_release_tags(include_prerelease=False)
|
||||
|
||||
# Assume that this is the highest release, unless told otherwise
|
||||
highest_release = True
|
||||
@ -84,12 +89,17 @@ def check_version_number(version_string, allow_duplicate=False):
|
||||
|
||||
if release > version_tuple:
|
||||
highest_release = False
|
||||
print(f'Found newer release: {str(release)}')
|
||||
print(f'Found newer release: {release!s}')
|
||||
|
||||
return highest_release
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Ensure that we are running in GH Actions
|
||||
if os.environ.get('GITHUB_ACTIONS', '') != 'true':
|
||||
print('This script is intended to be run within a GitHub Action!')
|
||||
sys.exit(1)
|
||||
|
||||
if 'only_version' in sys.argv:
|
||||
here = Path(__file__).parent.absolute()
|
||||
version_file = here.joinpath(
|
||||
@ -97,16 +107,18 @@ if __name__ == '__main__':
|
||||
)
|
||||
text = version_file.read_text()
|
||||
results = re.findall(r"""INVENTREE_API_VERSION = (.*)""", text)
|
||||
# If 2. args is true lower the version number by 1
|
||||
if len(sys.argv) > 2 and sys.argv[2] == 'true':
|
||||
results[0] = str(int(results[0]) - 1)
|
||||
print(results[0])
|
||||
exit(0)
|
||||
|
||||
# GITHUB_REF_TYPE may be either 'branch' or 'tag'
|
||||
GITHUB_REF_TYPE = os.environ['GITHUB_REF_TYPE']
|
||||
|
||||
# GITHUB_REF may be either 'refs/heads/<branch>' or 'refs/heads/<tag>'
|
||||
GITHUB_REF = os.environ['GITHUB_REF']
|
||||
|
||||
GITHUB_REF_NAME = os.environ['GITHUB_REF_NAME']
|
||||
|
||||
GITHUB_BASE_REF = os.environ['GITHUB_BASE_REF']
|
||||
|
||||
# Print out version information, makes debugging actions *much* easier!
|
||||
@ -122,7 +134,7 @@ if __name__ == '__main__':
|
||||
|
||||
version = None
|
||||
|
||||
with open(version_file, 'r') as f:
|
||||
with open(version_file, encoding='utf-8') as f:
|
||||
text = f.read()
|
||||
|
||||
# Extract the InvenTree software version
|
||||
@ -163,10 +175,7 @@ if __name__ == '__main__':
|
||||
print(f"Version number '{version}' does not match tag '{version_tag}'")
|
||||
sys.exit
|
||||
|
||||
if highest_release:
|
||||
docker_tags = [version_tag, 'stable']
|
||||
else:
|
||||
docker_tags = [version_tag]
|
||||
docker_tags = [version_tag, 'stable'] if highest_release else [version_tag]
|
||||
|
||||
elif GITHUB_REF_TYPE == 'branch':
|
||||
# Otherwise we know we are targeting the 'master' branch
|
||||
@ -187,10 +196,13 @@ if __name__ == '__main__':
|
||||
print(f"Version check passed for '{version}'!")
|
||||
print(f"Docker tags: '{docker_tags}'")
|
||||
|
||||
target_repos = [REPO.lower(), f'ghcr.io/{REPO.lower()}']
|
||||
|
||||
# Ref: https://getridbug.com/python/how-to-set-environment-variables-in-github-actions-using-python/
|
||||
with open(os.getenv('GITHUB_ENV'), 'a') as env_file:
|
||||
with open(os.getenv('GITHUB_ENV'), 'a', encoding='utf-8') as env_file:
|
||||
# Construct tag string
|
||||
tags = ','.join([f'inventree/inventree:{tag}' for tag in docker_tags])
|
||||
tag_list = [[f'{r}:{t}' for t in docker_tags] for r in target_repos]
|
||||
tags = ','.join(itertools.chain(*tag_list))
|
||||
|
||||
env_file.write(f'docker_tags={tags}\n')
|
||||
|
||||
|
2
.github/workflows/backport.yaml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
)
|
||||
steps:
|
||||
- name: Backport Action
|
||||
uses: sqren/backport-github-action@f54e19901f2a57f8b82360f2490d47ee82ec82c6 # pin@v9.2.2
|
||||
uses: sqren/backport-github-action@ad888e978060bc1b2798690dd9d03c4036560947 # pin@v9.2.2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
auto_backport_label_prefix: backport-to-
|
||||
|
10
.github/workflows/check_translations.yaml
vendored
@ -30,13 +30,19 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
install: true
|
||||
apt-dependency: gettext
|
||||
- name: Test Translations
|
||||
run: invoke translate
|
||||
run: invoke dev.translate
|
||||
- name: Check for Duplicates
|
||||
run: |
|
||||
python ./.github/scripts/check_source_strings.py --frontend --backend
|
||||
- name: Check Migration Files
|
||||
run: python3 .github/scripts/check_migration_files.py
|
||||
|
37
.github/workflows/docker.yaml
vendored
@ -39,7 +39,9 @@ jobs:
|
||||
docker: ${{ steps.filter.outputs.docker }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
|
||||
id: filter
|
||||
with:
|
||||
@ -66,9 +68,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set Up Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # pin@v5.1.0
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # pin@v5.3.0
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
- name: Version Check
|
||||
@ -97,7 +101,7 @@ jobs:
|
||||
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 run inventree-dev-server invoke dev.setup-dev
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml up -d
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run inventree-dev-server invoke wait
|
||||
- name: Check Data Directory
|
||||
@ -115,40 +119,40 @@ jobs:
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> contrib/container/docker.dev.env
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run --rm inventree-dev-server invoke test --disable-pty
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run --rm inventree-dev-server invoke dev.test --disable-pty
|
||||
- name: Run Migration Tests
|
||||
run: |
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run --rm inventree-dev-server invoke test --migrations
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml run --rm inventree-dev-server invoke dev.test --migrations
|
||||
- name: Clean up test folder
|
||||
run: |
|
||||
rm -rf InvenTree/_testfolder
|
||||
- name: Set up QEMU
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # pin@v3.0.0
|
||||
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # pin@v3.2.0
|
||||
- name: Set up Docker Buildx
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # pin@v3.3.0
|
||||
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # pin@v3.8.0
|
||||
- name: Set up cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # pin@v3.5.0
|
||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # pin@v3.7.0
|
||||
- name: Check if Dockerhub login is required
|
||||
id: docker_login
|
||||
run: |
|
||||
if [ -z "${{ secrets.DOCKER_USERNAME }}" ]; then
|
||||
echo "skip_dockerhub_login=true" >> $GITHUB_ENV
|
||||
echo "skip_dockerhub_login=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "skip_dockerhub_login=false" >> $GITHUB_ENV
|
||||
echo "skip_dockerhub_login=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Login to Dockerhub
|
||||
if: github.event_name != 'pull_request' && steps.docker_login.outputs.skip_dockerhub_login != 'true'
|
||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # pin@v3.2.0
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # pin@v3.3.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Log into registry ghcr.io
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # pin@v3.2.0
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # pin@v3.3.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@ -157,17 +161,18 @@ jobs:
|
||||
- name: Extract Docker metadata
|
||||
if: github.event_name != 'pull_request'
|
||||
id: meta
|
||||
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # pin@v5.5.1
|
||||
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # pin@v5.6.1
|
||||
with:
|
||||
images: |
|
||||
inventree/inventree
|
||||
ghcr.io/${{ github.repository }}
|
||||
|
||||
- uses: depot/setup-action@v1
|
||||
- name: Push Docker Images
|
||||
id: push-docker
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 # pin@v6.1.0
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
project: jczzbjkk68
|
||||
context: .
|
||||
file: ./contrib/container/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
200
.github/workflows/qc_checks.yaml
vendored
@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
python_version: 3.9
|
||||
node_version: 18
|
||||
node_version: 20
|
||||
# The OS version must be set per job
|
||||
server_start_sleep: 60
|
||||
|
||||
@ -36,9 +36,12 @@ jobs:
|
||||
frontend: ${{ steps.filter.outputs.frontend }}
|
||||
api: ${{ steps.filter.outputs.api }}
|
||||
force: ${{ steps.force.outputs.force }}
|
||||
cicd: ${{ steps.filter.outputs.cicd }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
|
||||
id: filter
|
||||
with:
|
||||
@ -56,6 +59,8 @@ jobs:
|
||||
- 'src/backend/InvenTree/InvenTree/api_version.py'
|
||||
frontend:
|
||||
- 'src/frontend/**'
|
||||
cicd:
|
||||
- '.github/workflows/**'
|
||||
- name: Is CI being forced?
|
||||
run: echo "force=true" >> $GITHUB_OUTPUT
|
||||
id: force
|
||||
@ -63,28 +68,6 @@ jobs:
|
||||
contains(github.event.pull_request.labels.*.name, 'dependency') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'full-run')
|
||||
|
||||
javascript:
|
||||
name: Style - Classic UI [JS]
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
needs: ["pre-commit"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
npm: true
|
||||
install: true
|
||||
- name: Check Templated JS Files
|
||||
run: |
|
||||
cd .github/scripts
|
||||
python3 check_js_templates.py
|
||||
- name: Lint Javascript Files
|
||||
run: |
|
||||
python src/backend/InvenTree/manage.py prerender
|
||||
cd src/backend && npx eslint InvenTree/InvenTree/static_i18n/i18n/*.js
|
||||
|
||||
pre-commit:
|
||||
name: Style [pre-commit]
|
||||
runs-on: ubuntu-20.04
|
||||
@ -92,9 +75,11 @@ jobs:
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # pin@v5.1.0
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # pin@v5.3.0
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: "pip"
|
||||
@ -113,9 +98,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # pin@v5.1.0
|
||||
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # pin@v5.3.0
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
- name: Check Config
|
||||
@ -124,7 +111,7 @@ jobs:
|
||||
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
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # pin@v1
|
||||
with:
|
||||
folder-path: docs
|
||||
config-file: docs/mlc_config.json
|
||||
@ -149,7 +136,9 @@ jobs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -157,29 +146,45 @@ jobs:
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Export API Documentation
|
||||
run: invoke schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
|
||||
run: invoke dev.schema --ignore-warnings --filename src/backend/InvenTree/schema.yml
|
||||
- name: Upload schema
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # pin@v4.3.3
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # pin@v4.4.3
|
||||
with:
|
||||
name: schema.yml
|
||||
path: src/backend/InvenTree/schema.yml
|
||||
- name: Download public schema
|
||||
if: needs.paths-filter.outputs.api == 'false'
|
||||
env:
|
||||
API: ${{ needs.paths-filter.outputs.api }}
|
||||
run: |
|
||||
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)"
|
||||
version="$(python3 .github/scripts/version_check.py only_version ${API} 2>&1)"
|
||||
echo "Version: $version"
|
||||
url="https://raw.githubusercontent.com/inventree/schema/main/export/${version}/api.yaml"
|
||||
echo "URL: $url"
|
||||
curl -s -o api.yaml $url
|
||||
code=$(curl -s -o api.yaml $url --write-out '%{http_code}' --silent)
|
||||
if [ "$code" != "200" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "Downloaded api.yaml"
|
||||
- name: Running OpenAPI Spec diff action
|
||||
id: breaking_changes
|
||||
uses: oasdiff/oasdiff-action/diff@1c611ffb1253a72924624aa4fb662e302b3565d3 # pin@main
|
||||
with:
|
||||
base: 'api.yaml'
|
||||
revision: 'src/backend/InvenTree/schema.yml'
|
||||
format: 'html'
|
||||
- name: Echoing diff to step
|
||||
env:
|
||||
DIFF: ${{ steps.breaking_changes.outputs.diff }}
|
||||
run: echo "${DIFF}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Check for differences in API Schema
|
||||
if: needs.paths-filter.outputs.api == 'false'
|
||||
run: |
|
||||
diff --color -u src/backend/InvenTree/schema.yml api.yaml
|
||||
diff -u src/backend/InvenTree/schema.yml api.yaml && echo "no difference in API schema " || exit 2
|
||||
- name: Check schema - including warnings
|
||||
run: invoke schema
|
||||
run: invoke dev.schema
|
||||
continue-on-error: true
|
||||
- name: Extract version for publishing
|
||||
id: version
|
||||
@ -199,12 +204,14 @@ jobs:
|
||||
version: ${{ needs.schema.outputs.version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
name: Checkout Code
|
||||
with:
|
||||
repository: inventree/schema
|
||||
token: ${{ secrets.SCHEMA_PAT }}
|
||||
persist-credentials: false
|
||||
- name: Download schema artifact
|
||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # pin@v4.1.8
|
||||
with:
|
||||
name: schema.yml
|
||||
- name: Move schema to correct location
|
||||
@ -212,9 +219,10 @@ jobs:
|
||||
echo "Version: $version"
|
||||
mkdir export/${version}
|
||||
mv schema.yml export/${version}/api.yaml
|
||||
- uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1
|
||||
- uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # pin@v5.0.1
|
||||
name: Commit schema changes
|
||||
with:
|
||||
commit_message: "Update API schema for ${version}"
|
||||
commit_message: "Update API schema for ${{ env.version }} / ${{ github.sha }}"
|
||||
|
||||
python:
|
||||
name: Tests - inventree-python
|
||||
@ -224,7 +232,7 @@ jobs:
|
||||
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
env:
|
||||
wrapper_name: inventree-python
|
||||
WRAPPER_NAME: inventree-python
|
||||
INVENTREE_DB_ENGINE: django.db.backends.sqlite3
|
||||
INVENTREE_DB_NAME: ../inventree_unit_test_db.sqlite3
|
||||
INVENTREE_ADMIN_USER: testuser
|
||||
@ -236,25 +244,26 @@ jobs:
|
||||
INVENTREE_SITE_URL: http://127.0.0.1:12345
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: true
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
apt-dependency: gettext poppler-utils
|
||||
dev-install: true
|
||||
update: true
|
||||
npm: true
|
||||
- name: Download Python Code For `${{ env.wrapper_name }}`
|
||||
run: git clone --depth 1 https://github.com/inventree/${{ env.wrapper_name }} ./${{ env.wrapper_name }}
|
||||
- name: Download Python Code For `${WRAPPER_NAME}`
|
||||
run: git clone --depth 1 https://github.com/inventree/${WRAPPER_NAME} ./${WRAPPER_NAME}
|
||||
- name: Start InvenTree Server
|
||||
run: |
|
||||
invoke delete-data -f
|
||||
invoke import-fixtures
|
||||
invoke server -a 127.0.0.1:12345 &
|
||||
invoke dev.delete-data -f
|
||||
invoke dev.import-fixtures
|
||||
invoke dev.server -a 127.0.0.1:12345 &
|
||||
invoke wait
|
||||
- name: Run Tests For `${{ env.wrapper_name }}`
|
||||
- name: Run Tests For `${WRAPPER_NAME}`
|
||||
run: |
|
||||
cd ${{ env.wrapper_name }}
|
||||
cd ${WRAPPER_NAME}
|
||||
invoke check-server
|
||||
coverage run -m unittest discover -s test/
|
||||
|
||||
@ -267,7 +276,8 @@ jobs:
|
||||
continue-on-error: true # continue if a step fails so that coverage gets pushed
|
||||
strategy:
|
||||
matrix:
|
||||
python_version: [3.9, 3.12]
|
||||
python_version: [3.9]
|
||||
# python_version: [3.9, 3.12] # Disabled due to requirement issues
|
||||
|
||||
env:
|
||||
INVENTREE_DB_NAME: ./inventree.sqlite
|
||||
@ -277,7 +287,9 @@ jobs:
|
||||
python_version: ${{ matrix.python_version }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -287,13 +299,13 @@ jobs:
|
||||
- name: Data Export Test
|
||||
uses: ./.github/actions/migration
|
||||
- name: Test Translations
|
||||
run: invoke translate
|
||||
run: invoke dev.translate
|
||||
- name: Check Migration Files
|
||||
run: python3 .github/scripts/check_migration_files.py
|
||||
- name: Coverage Tests
|
||||
run: invoke test --coverage
|
||||
run: invoke dev.test --coverage
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # pin@v4.5.0
|
||||
uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # pin@v5.1.1
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@ -331,7 +343,9 @@ jobs:
|
||||
- 6379:6379
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -340,7 +354,7 @@ jobs:
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
run: invoke dev.test
|
||||
- name: Data Export Test
|
||||
uses: ./.github/actions/migration
|
||||
|
||||
@ -375,7 +389,9 @@ jobs:
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -384,7 +400,7 @@ jobs:
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
run: invoke dev.test
|
||||
- name: Data Export Test
|
||||
uses: ./.github/actions/migration
|
||||
|
||||
@ -414,7 +430,9 @@ jobs:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -423,9 +441,9 @@ jobs:
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Run Tests
|
||||
run: invoke test --migrations --report --coverage
|
||||
run: invoke dev.test --migrations --report --coverage
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # pin@v4.5.0
|
||||
uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # pin@v5.1.1
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@ -445,7 +463,9 @@ jobs:
|
||||
INVENTREE_PLUGINS_ENABLED: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
name: Checkout Code
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
@ -502,7 +522,9 @@ jobs:
|
||||
VITE_COVERAGE: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -510,17 +532,17 @@ jobs:
|
||||
install: true
|
||||
update: true
|
||||
- name: Set up test data
|
||||
run: invoke setup-test -i
|
||||
run: invoke dev.setup-test -i
|
||||
- name: Rebuild thumbnails
|
||||
run: invoke rebuild-thumbnails
|
||||
run: invoke int.rebuild-thumbnails
|
||||
- name: Install dependencies
|
||||
run: inv frontend-compile
|
||||
run: invoke int.frontend-compile
|
||||
- name: Install Playwright Browsers
|
||||
run: cd src/frontend && npx playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
id: tests
|
||||
run: cd src/frontend && npx nyc playwright test
|
||||
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # pin@v4
|
||||
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # pin@v4.4.3
|
||||
if: ${{ !cancelled() && steps.tests.outcome == 'failure' }}
|
||||
with:
|
||||
name: playwright-report
|
||||
@ -530,12 +552,19 @@ 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@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # pin@v4.5.0
|
||||
uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # pin@v5.1.1
|
||||
if: always()
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
slug: inventree/InvenTree
|
||||
flags: pui
|
||||
- name: Upload bundler info
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
run: |
|
||||
cd src/frontend
|
||||
yarn install
|
||||
yarn run build
|
||||
|
||||
platform_ui_build:
|
||||
name: Build - UI Platform
|
||||
@ -543,7 +572,9 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -552,11 +583,38 @@ jobs:
|
||||
run: cd src/frontend && yarn install
|
||||
- name: Build frontend
|
||||
run: cd src/frontend && yarn run compile && yarn run build
|
||||
- name: Write version file - SHA
|
||||
run: cd src/backend/InvenTree/web/static/web/.vite && echo "$GITHUB_SHA" > sha.txt
|
||||
- name: Zip frontend
|
||||
run: |
|
||||
cd src/backend/InvenTree/web/static
|
||||
zip -r frontend-build.zip web/ web/.vite
|
||||
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # pin@v4.3.3
|
||||
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # pin@v4.4.3
|
||||
with:
|
||||
name: frontend-build
|
||||
path: src/backend/InvenTree/web/static/web
|
||||
include-hidden-files: true
|
||||
|
||||
zizmor:
|
||||
name: Security [Zizmor]
|
||||
runs-on: ubuntu-20.04
|
||||
needs: ['pre-commit', 'paths-filter']
|
||||
if: needs.paths-filter.outputs.cicd == 'true' || needs.paths-filter.outputs.force == 'true'
|
||||
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: hynek/setup-cached-uv@757bedc3f972eb7227a1aa657651f15a8527c817 # pin@v2
|
||||
- name: Run zizmor
|
||||
run: uvx zizmor --format sarif . > results.sarif
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # pin@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
category: zizmor
|
||||
|
47
.github/workflows/release.yaml
vendored
@ -1,13 +1,16 @@
|
||||
# Runs on releases
|
||||
|
||||
name: Publish release notes
|
||||
name: Publish release
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stable:
|
||||
runs-on: ubuntu-latest
|
||||
name: Write release to stable branch
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
@ -15,7 +18,9 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Version Check
|
||||
run: |
|
||||
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
|
||||
@ -28,13 +33,17 @@ jobs:
|
||||
branch: stable
|
||||
force: true
|
||||
|
||||
publish-build:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and attest frontend
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
@ -43,14 +52,40 @@ jobs:
|
||||
run: cd src/frontend && yarn install
|
||||
- name: Build frontend
|
||||
run: cd src/frontend && npm run compile && npm run build
|
||||
- name: Create SBOM for frontend
|
||||
uses: anchore/sbom-action@df80a981bc6edbc4e220a492d3cbe9f5547a6e75 # pin@v0
|
||||
with:
|
||||
artifact-name: frontend-build.spdx
|
||||
path: src/frontend
|
||||
- name: Write version file - SHA
|
||||
run: cd src/backend/InvenTree/web/static/web/.vite && echo "$GITHUB_SHA" > sha.txt
|
||||
- name: Write version file - TAG
|
||||
run: cd src/backend/InvenTree/web/static/web/.vite && echo "${REF_NAME}" > tag.txt
|
||||
env:
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
- name: Zip frontend
|
||||
run: |
|
||||
cd src/backend/InvenTree/web/static/web
|
||||
zip -r ../frontend-build.zip * .vite
|
||||
- uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # pin@2.9.0
|
||||
- name: Attest Build Provenance
|
||||
id: attest
|
||||
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # pin@v1
|
||||
with:
|
||||
subject-path: "${{ github.workspace }}/src/backend/InvenTree/web/static/frontend-build.zip"
|
||||
|
||||
- name: Upload frontend
|
||||
uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # pin@2.9.0
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: src/backend/InvenTree/web/static/frontend-build.zip
|
||||
asset_name: frontend-build.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
- name: Upload Attestation
|
||||
uses: svenstaro/upload-release-action@04733e069f2d7f7f0b4aebc4fbdbce8613b03ccd # pin@2.9.0
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
asset_name: frontend-build.intoto.jsonl
|
||||
file: ${{ steps.attest.outputs.bundle-path}}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
|
8
.github/workflows/scorecard.yaml
vendored
@ -32,12 +32,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.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@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
||||
uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
43
.github/workflows/translations.yaml
vendored
@ -7,16 +7,17 @@ on:
|
||||
|
||||
env:
|
||||
python_version: 3.9
|
||||
node_version: 18
|
||||
node_version: 20
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
synchronize-with-crowdin:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@ -30,25 +31,39 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: true
|
||||
- name: Environment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
install: true
|
||||
npm: true
|
||||
apt-dependency: gettext
|
||||
- name: Make Translations
|
||||
run: invoke translate
|
||||
- name: Commit files
|
||||
run: invoke dev.translate
|
||||
- name: Remove compiled static files
|
||||
run: rm -rf src/backend/InvenTree/static
|
||||
- name: Remove all local changes that are not *.po files
|
||||
run: |
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git checkout -b l10_local
|
||||
git add "*.po"
|
||||
git commit -m "updated translation base"
|
||||
- name: Push changes
|
||||
uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df # pin@v0.8.0
|
||||
git add src/backend/InvenTree/locale/en/LC_MESSAGES/django.po src/frontend/src/locales/en/messages.po
|
||||
git commit -m "add translations" || true
|
||||
git reset --hard
|
||||
git reset HEAD~
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@8dfaf9c206381653e3767e3cb5ea5f08b45f02bf # pin@v2
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: l10
|
||||
force: true
|
||||
upload_sources: true
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
localization_branch_name: l10_crowdin
|
||||
create_pull_request: true
|
||||
pull_request_title: 'New Crowdin updates'
|
||||
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
|
||||
pull_request_base_branch_name: 'master'
|
||||
pull_request_labels: 'translations'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
4
.github/workflows/update.yml.disabled
vendored
@ -9,7 +9,9 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup
|
||||
run: pip install --require-hashes -r requirements-dev.txt
|
||||
- name: Update requirements.txt
|
||||
|
6
.gitignore
vendored
@ -31,6 +31,7 @@ var/
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
*.sqlite3-journal
|
||||
*.backup
|
||||
@ -87,6 +88,10 @@ env/
|
||||
src/backend/InvenTree/InvenTree/locale_stats.json
|
||||
src/backend/InvenTree/InvenTree/licenses.txt
|
||||
|
||||
# Logs
|
||||
src/backend/InvenTree/logs.json
|
||||
src/backend/InvenTree/logs.log
|
||||
|
||||
# node.js
|
||||
node_modules/
|
||||
|
||||
@ -111,3 +116,4 @@ InvenTree/web/static
|
||||
docs/schema.yml
|
||||
docs/docs/api/*.yml
|
||||
docs/docs/api/schema/*.yml
|
||||
inventree_settings.json
|
||||
|
@ -14,7 +14,10 @@ env:
|
||||
- INVENTREE_BACKUP_DIR=/opt/inventree/backup
|
||||
- INVENTREE_PLUGIN_FILE=/opt/inventree/plugins.txt
|
||||
- INVENTREE_CONFIG_FILE=/opt/inventree/config.yaml
|
||||
- APP_REPO=inventree/InvenTree
|
||||
before_install: contrib/packager.io/preinstall.sh
|
||||
after_install: contrib/packager.io/postinstall.sh
|
||||
before_remove: contrib/packager.io/preinstall.sh
|
||||
before:
|
||||
- contrib/packager.io/before.sh
|
||||
dependencies:
|
||||
@ -32,7 +35,7 @@ dependencies:
|
||||
- gettext
|
||||
- nginx
|
||||
- jq
|
||||
- libffi7
|
||||
- "libffi7 | libffi8"
|
||||
targets:
|
||||
ubuntu-20.04: true
|
||||
debian-11: true
|
||||
|
@ -10,53 +10,56 @@ exclude: |
|
||||
)$
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.6.0
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: mixed-line-ending
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.4.1
|
||||
rev: v0.7.3
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
args: [--preview]
|
||||
- id: ruff
|
||||
args: [
|
||||
--fix,
|
||||
# --unsafe-fixes,
|
||||
--preview
|
||||
]
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
rev: 0.1.35
|
||||
rev: 0.5.1
|
||||
hooks:
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements-dev.in
|
||||
args: [src/backend/requirements-dev.in, -o, src/backend/requirements-dev.txt]
|
||||
args: [src/backend/requirements-dev.in, -o, src/backend/requirements-dev.txt, --no-strip-extras, --generate-hashes]
|
||||
files: src/backend/requirements-dev\.(in|txt)$
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements.txt
|
||||
args: [src/backend/requirements.in, -o, src/backend/requirements.txt]
|
||||
args: [src/backend/requirements.in, -o, src/backend/requirements.txt, --no-strip-extras, --generate-hashes]
|
||||
files: src/backend/requirements\.(in|txt)$
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements.txt
|
||||
args: [contrib/dev_reqs/requirements.in, -o, contrib/dev_reqs/requirements.txt]
|
||||
args: [contrib/dev_reqs/requirements.in, -o, contrib/dev_reqs/requirements.txt, --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]
|
||||
args: [docs/requirements.in, -o, docs/requirements.txt, --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
|
||||
rev: v1.36.1
|
||||
hooks:
|
||||
- id: djlint-django
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.2.6
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: codespell
|
||||
additional_dependencies:
|
||||
- tomli
|
||||
exclude: >
|
||||
(?x)^(
|
||||
docs/docs/stylesheets/.*|
|
||||
@ -66,28 +69,14 @@ repos:
|
||||
pyproject.toml |
|
||||
src/frontend/vite.config.ts |
|
||||
)$
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: "v4.0.0-alpha.8"
|
||||
- repo: https://github.com/biomejs/pre-commit
|
||||
rev: "v0.5.0"
|
||||
hooks:
|
||||
- id: prettier
|
||||
files: ^src/frontend/.*\.(js|jsx|ts|tsx)$
|
||||
additional_dependencies:
|
||||
- "prettier@^2.4.1"
|
||||
- "@trivago/prettier-plugin-sort-imports"
|
||||
- repo: https://github.com/pre-commit/mirrors-eslint
|
||||
rev: "v9.4.0"
|
||||
hooks:
|
||||
- id: eslint
|
||||
additional_dependencies:
|
||||
- eslint@^8.41.0
|
||||
- eslint-config-google@^0.14.0
|
||||
- eslint-plugin-react@6.10.3
|
||||
- babel-eslint@6.1.2
|
||||
- "@typescript-eslint/eslint-plugin@latest"
|
||||
- "@typescript-eslint/parser"
|
||||
files: ^src/frontend/.*\.(js|jsx|ts|tsx)$
|
||||
- id: biome-check
|
||||
additional_dependencies: ["@biomejs/biome@1.9.4"]
|
||||
files: ^src/frontend/.*\.(js|ts|tsx)$
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.18.3
|
||||
rev: v8.21.2
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
#- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||
|
5
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"biomejs.biome"
|
||||
]
|
||||
}
|
26
.vscode/launch.json
vendored
@ -6,19 +6,37 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "InvenTree Server",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
|
||||
"args": ["runserver"],
|
||||
"args": [
|
||||
"runserver",
|
||||
// "0.0.0.0:8000", // expose server in network (useful for testing with mobile app)
|
||||
// "--noreload" // disable auto-reload
|
||||
],
|
||||
"django": true,
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "InvenTree Server - Tests",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
|
||||
"args": [
|
||||
"test",
|
||||
// "part.test_api.PartCategoryAPITest", // run only a specific test
|
||||
],
|
||||
"django": true,
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "InvenTree Server - 3rd party",
|
||||
"type": "python",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/backend/InvenTree/manage.py",
|
||||
"args": ["runserver"],
|
||||
"args": [
|
||||
"runserver"
|
||||
],
|
||||
"django": true,
|
||||
"justMyCode": false
|
||||
},
|
||||
|
8
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"quickfix.biome": "explicit"
|
||||
}
|
||||
}
|
20
.vscode/tasks.json
vendored
@ -9,61 +9,61 @@
|
||||
{
|
||||
"label": "worker",
|
||||
"type": "shell",
|
||||
"command": "inv worker",
|
||||
"command": "invoke worker",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "clean-settings",
|
||||
"type": "shell",
|
||||
"command": "inv clean-settings",
|
||||
"command": "invoke int.clean-settings",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "delete-data",
|
||||
"type": "shell",
|
||||
"command": "inv delete-data",
|
||||
"command": "invoke dev.delete-data",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "migrate",
|
||||
"type": "shell",
|
||||
"command": "inv migrate",
|
||||
"command": "invoke migrate",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "server",
|
||||
"type": "shell",
|
||||
"command": "inv server",
|
||||
"command": "invoke dev.server",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "setup-dev",
|
||||
"type": "shell",
|
||||
"command": "inv setup-dev",
|
||||
"command": "invoke dev.setup-dev",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "setup-test",
|
||||
"type": "shell",
|
||||
"command": "inv setup-test -i --path dev/inventree-demo-dataset",
|
||||
"command": "invoke dev.setup-test -i --path dev/inventree-demo-dataset",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "superuser",
|
||||
"type": "shell",
|
||||
"command": "inv superuser",
|
||||
"command": "invoke superuser",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "test",
|
||||
"type": "shell",
|
||||
"command": "inv test",
|
||||
"command": "invoke dev.test",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
"label": "update",
|
||||
"type": "shell",
|
||||
"command": "inv update",
|
||||
"command": "invoke update",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
]
|
||||
|
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2022 InvenTree
|
||||
Copyright (c) 2017 - InvenTree Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
30
README.md
@ -4,13 +4,14 @@
|
||||
<p>Open Source Inventory Management System </p>
|
||||
|
||||
<!-- Badges -->
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://opensource.org/license/MIT)
|
||||

|
||||
[](https://inventree.readthedocs.io/en/latest/?badge=latest)
|
||||

|
||||
[](https://app.netlify.com/sites/inventree/deploys)
|
||||
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/7179)
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/inventree/InvenTree)
|
||||
[](https://app.netlify.com/sites/inventree/deploys)
|
||||
[](https://sonarcloud.io/summary/new_code?id=inventree_InvenTree)
|
||||
|
||||
[](https://codecov.io/gh/inventree/InvenTree)
|
||||
@ -18,10 +19,10 @@
|
||||

|
||||
[](https://hub.docker.com/r/inventree/inventree)
|
||||
|
||||

|
||||
[](https://github.com/inventree/InvenTree/)
|
||||
[](https://twitter.com/inventreedb)
|
||||
[](https://www.reddit.com/r/InvenTree/)
|
||||
|
||||
[](https://chaos.social/@InvenTree)
|
||||
|
||||
<h4>
|
||||
<a href="https://demo.inventree.org/">View Demo</a>
|
||||
@ -66,7 +67,7 @@ InvenTree is designed to be **extensible**, and provides multiple options for **
|
||||
<li><a href="https://www.djangoproject.com/">Django</a></li>
|
||||
<li><a href="https://www.django-rest-framework.org/">DRF</a></li>
|
||||
<li><a href="https://django-q.readthedocs.io/">Django Q</a></li>
|
||||
<li><a href="https://django-allauth.readthedocs.io/">Django-Allauth</a></li>
|
||||
<li><a href="https://docs.allauth.org/">Django-Allauth</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@ -83,9 +84,14 @@ InvenTree is designed to be **extensible**, and provides multiple options for **
|
||||
<details>
|
||||
<summary>Client</summary>
|
||||
<ul>
|
||||
<li><a href="https://getbootstrap.com/">Bootstrap</a></li>
|
||||
<li><a href="https://jquery.com/">jQuery</a></li>
|
||||
<li><a href="https://bootstrap-table.com/">Bootstrap-Table</a></li>
|
||||
<li><a href="https://react.dev/">React</a></li>
|
||||
<li><a href="https://lingui.dev/">Lingui</a></li>
|
||||
<li><a href="https://reactrouter.com/">React Router</a></li>
|
||||
<li><a href="https://tanstack.com/query/">TanStack Query</a></li>
|
||||
<li><a href="https://github.com/pmndrs/zustand">Zustand</a></li>
|
||||
<li><a href="https://mantine.dev/">Mantine</a></li>
|
||||
<li><a href="https://icflorescu.github.io/mantine-datatable/">Mantine Data Table</a></li>
|
||||
<li><a href="https://codemirror.net/">CodeMirror</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
@ -95,7 +101,7 @@ InvenTree is designed to be **extensible**, and provides multiple options for **
|
||||
<li><a href="https://hub.docker.com/r/inventree/inventree">Docker</a></li>
|
||||
<li><a href="https://crowdin.com/project/inventree">Crowdin</a></li>
|
||||
<li><a href="https://app.codecov.io/gh/inventree/InvenTree">Codecov</a></li>
|
||||
<li><a href="https://app.deepsource.com/gh/inventree/InvenTree">DeepSource</a></li>
|
||||
<li><a href="https://sonarcloud.io/project/overview?id=inventree_InvenTree">SonarCloud</a></li>
|
||||
<li><a href="https://packager.io/gh/inventree/InvenTree">Packager.io</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
@ -149,10 +155,7 @@ If you use InvenTree and find it to be useful, please consider [sponsoring the p
|
||||
<!-- Acknowledgments -->
|
||||
## :gem: Acknowledgements
|
||||
|
||||
We would like to acknowledge a few special projects:
|
||||
- [PartKeepr](https://github.com/partkeepr/PartKeepr) as a valuable predecessor and inspiration
|
||||
- [Readme Template](https://github.com/Louis3797/awesome-readme-template) for the template of this page
|
||||
|
||||
We want to acknowledge [PartKeepr](https://github.com/partkeepr/PartKeepr) as a valuable predecessor and inspiration.
|
||||
Find a full list of used third-party libraries in [our documentation](https://docs.inventree.org/en/latest/credits/).
|
||||
|
||||
## :heart: Support
|
||||
@ -175,6 +178,7 @@ Find a full list of used third-party libraries in [our documentation](https://do
|
||||
<p>With ongoing resources provided by:</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://depot.dev?utm_source=inventree"><img src="https://depot.dev/badges/built-with-depot.svg" alt="Built with Depot" /></a>
|
||||
<a href="https://inventree.org/digitalocean">
|
||||
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px" alt="Servers by Digital Ocean">
|
||||
</a>
|
||||
|
40
biome.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"jsxQuoteStyle": "single",
|
||||
"trailingCommas": "none",
|
||||
"indentStyle": "space"
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"rules": {
|
||||
"suspicious" : {
|
||||
"noExplicitAny": "off",
|
||||
"noDoubleEquals": "off",
|
||||
"noArrayIndexKey": "off",
|
||||
"useDefaultSwitchClauseLast": "off"
|
||||
},
|
||||
"style": {
|
||||
"noUselessElse": "off",
|
||||
"noNonNullAssertion": "off",
|
||||
"noParameterAssign": "off"
|
||||
}, "correctness":{
|
||||
"useExhaustiveDependencies": "off",
|
||||
"useJsxKeyInIterable": "off",
|
||||
"noUnsafeOptionalChaining": "off",
|
||||
"noSwitchDeclarations": "off",
|
||||
"noUnusedImports":"error"
|
||||
}, "complexity": {
|
||||
"noBannedTypes": "off",
|
||||
"noExtraBooleanCast": "off",
|
||||
"noForEach": "off",
|
||||
"noUselessSwitchCase": "off",
|
||||
"useLiteralKeys":"off"
|
||||
}, "performance": {
|
||||
"noDelete":"off"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -27,3 +27,11 @@ flag_management:
|
||||
statuses:
|
||||
- type: project
|
||||
target: 45%
|
||||
|
||||
comment:
|
||||
require_bundle_changes: True
|
||||
bundle_change_threshold: "1Kb"
|
||||
|
||||
bundle_analysis:
|
||||
warning_threshold: "5%"
|
||||
status: "informational"
|
||||
|
@ -24,13 +24,11 @@ INVENTREE_DB_PORT=5432
|
||||
INVENTREE_DB_USER=pguser
|
||||
INVENTREE_DB_PASSWORD=pgpassword
|
||||
|
||||
# Redis cache setup (disabled by default)
|
||||
# Un-comment the following lines to enable Redis cache
|
||||
# Note that you will also have to run docker-compose with the --profile redis command
|
||||
# Redis cache setup
|
||||
# Refer to settings.py for other cache options
|
||||
#INVENTREE_CACHE_ENABLED=True
|
||||
#INVENTREE_CACHE_HOST=inventree-cache
|
||||
#INVENTREE_CACHE_PORT=6379
|
||||
INVENTREE_CACHE_ENABLED=True
|
||||
INVENTREE_CACHE_HOST=inventree-cache
|
||||
INVENTREE_CACHE_PORT=6379
|
||||
|
||||
# Options for gunicorn server
|
||||
INVENTREE_GUNICORN_TIMEOUT=90
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Example 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)
|
||||
# - 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
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
ARG base_image=python:3.11-alpine3.18
|
||||
FROM ${base_image} AS inventree_base
|
||||
ARG base_image
|
||||
|
||||
# Build arguments for this image
|
||||
ARG commit_tag=""
|
||||
@ -48,13 +49,18 @@ ENV INVENTREE_BACKGROUND_WORKERS="4"
|
||||
ENV INVENTREE_WEB_ADDR=0.0.0.0
|
||||
ENV INVENTREE_WEB_PORT=8000
|
||||
|
||||
LABEL org.label-schema.schema-version="1.0" \
|
||||
org.label-schema.build-date=${DATE} \
|
||||
org.label-schema.vendor="inventree" \
|
||||
org.label-schema.name="inventree/inventree" \
|
||||
org.label-schema.url="https://hub.docker.com/r/inventree/inventree" \
|
||||
org.label-schema.vcs-url="https://github.com/inventree/InvenTree.git" \
|
||||
org.label-schema.vcs-ref=${commit_tag}
|
||||
LABEL org.opencontainers.image.created=${DATE} \
|
||||
org.opencontainers.image.vendor="inventree" \
|
||||
org.opencontainers.image.title="InvenTree backend server" \
|
||||
org.opencontainers.image.description="InvenTree is the open-source inventory management system" \
|
||||
org.opencontainers.image.url="https://inventree.org" \
|
||||
org.opencontainers.image.documentation="https://docs.inventree.org" \
|
||||
org.opencontainers.image.source="https://github.com/inventree/InvenTree" \
|
||||
org.opencontainers.image.revision=${commit_hash} \
|
||||
org.opencontainers.image.licenses="MIT" \
|
||||
org.opencontainers.image.base.name="docker.io/library/${base_image}" \
|
||||
org.opencontainers.image.version=${commit_tag}
|
||||
|
||||
|
||||
# Install required system level packages
|
||||
RUN apk add --no-cache \
|
||||
@ -68,8 +74,9 @@ RUN apk add --no-cache \
|
||||
# MySQL / MariaDB client
|
||||
mariadb-client mariadb-connector-c \
|
||||
&& \
|
||||
# fonts
|
||||
apk --update --upgrade --no-cache add fontconfig ttf-freefont font-noto terminus-font && fc-cache -f
|
||||
# font support
|
||||
apk --update --upgrade --no-cache add fontconfig ttf-freefont font-terminus font-noto font-noto-cjk font-noto-extra \
|
||||
&& fc-cache -f
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
@ -108,7 +115,7 @@ 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
|
||||
RUN cd ${INVENTREE_HOME} && inv frontend-compile
|
||||
RUN cd ${INVENTREE_HOME} && invoke int.frontend-compile
|
||||
|
||||
# InvenTree production image:
|
||||
# - Copies required files from local directory
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.8"
|
||||
|
||||
# Docker compose recipe for InvenTree development server
|
||||
# - Runs PostgreSQL as the database backend
|
||||
# - Uses built-in django webserver
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.8"
|
||||
|
||||
# Docker compose recipe for a production-ready InvenTree setup, with the following containers:
|
||||
# - PostgreSQL as the database backend
|
||||
# - gunicorn as the InvenTree web server
|
||||
@ -73,6 +71,7 @@ services:
|
||||
- 8000
|
||||
depends_on:
|
||||
- inventree-db
|
||||
- inventree-cache
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/ash
|
||||
|
||||
# Install system packages required for building InvenTree python libraries
|
||||
# Note that for postgreslql, we use the 13 version, which matches the version used in the InvenTree docker image
|
||||
# Note that for postgreslql, we use the version 13, which matches the version used in the InvenTree docker image
|
||||
|
||||
apk add gcc g++ musl-dev openssl-dev libffi-dev cargo python3-dev openldap-dev \
|
||||
libstdc++ build-base linux-headers py3-grpcio \
|
||||
|
@ -17,6 +17,7 @@ gunicorn>=22.0.0
|
||||
# LDAP required packages
|
||||
django-auth-ldap # Django integration for ldap auth
|
||||
python-ldap # LDAP auth support
|
||||
django<5.0 # Force lower to match main project
|
||||
|
||||
# Upgraded python package installer
|
||||
uv
|
||||
|
@ -4,217 +4,232 @@ 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
|
||||
django==4.2.17 \
|
||||
--hash=sha256:3a93350214ba25f178d4045c0786c61573e7dbfa3c509b3551374f1e11ba8de0 \
|
||||
--hash=sha256:6b56d834cc94c8b21a8f4e775064896be3b4a4ca387f2612d4406a5927cd2fdc
|
||||
# via
|
||||
# -r contrib/container/requirements.in
|
||||
# django-auth-ldap
|
||||
django-auth-ldap==5.1.0 \
|
||||
--hash=sha256:9c607e8d9c53cf2a0ccafbe0acfc33eb1d1fd474c46ec52d30aee0dca1da9668 \
|
||||
--hash=sha256:a5f7bdb54b2ab80e4e9eb080cd3e06e89e4c9d2d534ddb39b66cd970dd6d3536
|
||||
# via -r contrib/container/requirements.in
|
||||
gunicorn==23.0.0 \
|
||||
--hash=sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d \
|
||||
--hash=sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec
|
||||
# via -r contrib/container/requirements.in
|
||||
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 -r contrib/container/requirements.in
|
||||
mariadb==1.1.11 \
|
||||
--hash=sha256:0f8de8d66ca71bd102f34a970a331b7d75bdf7f8050d80e37cdcc6ff3c85cf7a \
|
||||
--hash=sha256:2e72ea65f1d7d8563ee84e172f2a583193092bdb6ff83c470ca9722873273ecc \
|
||||
--hash=sha256:3f64b520089cb60c4f8302f365ed0ae057c4c859ab70fc8b1c4358192c3c8f27 \
|
||||
--hash=sha256:579420293fa790d5ae0a6cb4bdb7e8be8facc2ceefb6123c2b0e8042b3fa725d \
|
||||
--hash=sha256:6f28d8ccc597a3a1368be14078110f743900dbb3b0c7f1cce3072d83bec59c8a \
|
||||
--hash=sha256:c1992ebf9c6f012ac158e33fef9f2c4ba899f721064c4ae3a3489233793296c0 \
|
||||
--hash=sha256:cf6647cee081e21d0994b409ba8c8fa2077f3972f1de3627c5502fb31d14f806 \
|
||||
--hash=sha256:d7302ccd15f0beee7b286885cbf6ac71ddc240374691d669784d99f89ba34d79 \
|
||||
--hash=sha256:dbc4cf0e302ca82d46f9431a0b04f048e9c21ee56d6f3162c29605f84d63b40c \
|
||||
--hash=sha256:e94f1738bec09c97b601ddbb1908eb24524ba4630f507a775d82ffdb6c5794b3 \
|
||||
--hash=sha256:f6dfdc954edf02b6519419a054798cda6034dc459d1d482e3329e37aa27d34f0
|
||||
# via -r contrib/container/requirements.in
|
||||
mysqlclient==2.2.6 \
|
||||
--hash=sha256:3da70a07753ba6be881f7d75e795e254f6a0c12795778034acc69769b0649d37 \
|
||||
--hash=sha256:43c5b30be0675080b9c815f457d73397f0442173e7be83d089b126835e2617ae \
|
||||
--hash=sha256:794857bce4f9a1903a99786dd29ad7887f45a870b3d11585b8c51c4a753c4174 \
|
||||
--hash=sha256:b0a5cddf1d3488b254605041070086cac743401d876a659a72d706a0d89c8ebb \
|
||||
--hash=sha256:c0b46d9b78b461dbb62482089ca8040fa916595b1b30f831ebbd1b0a82b43d53 \
|
||||
--hash=sha256:e940b41d85dfd7b190fa47d52f525f878cfa203d4653bf6a35b271b3c3be125b \
|
||||
--hash=sha256:e94a92858203d97fd584bdb6d7ee8c56f2590db8d77fd44215c0dcf5e739bc37 \
|
||||
--hash=sha256:f3efb849d6f7ef4b9788a0eda2e896b975e0ebf1d6bf3dcabea63fd698e5b0b5
|
||||
# via -r contrib/container/requirements.in
|
||||
packaging==24.2 \
|
||||
--hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \
|
||||
--hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f
|
||||
# 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
|
||||
psycopg[binary, pool]==3.2.3 \
|
||||
--hash=sha256:644d3973fe26908c73d4be746074f6e5224b03c1101d302d9a53bf565ad64907 \
|
||||
--hash=sha256:a5764f67c27bec8bfac85764d23c534af2c27b893550377e37ce59c12aac47a2
|
||||
# via -r contrib/container/requirements.in
|
||||
psycopg-binary==3.2.3 \
|
||||
--hash=sha256:0463a11b1cace5a6aeffaf167920707b912b8986a9c7920341c75e3686277920 \
|
||||
--hash=sha256:05a1bdce30356e70a05428928717765f4a9229999421013f41338d9680d03a63 \
|
||||
--hash=sha256:06b5cc915e57621eebf2393f4173793ed7e3387295f07fed93ed3fb6a6ccf585 \
|
||||
--hash=sha256:07d019a786eb020c0f984691aa1b994cb79430061065a694cf6f94056c603d26 \
|
||||
--hash=sha256:09baa041856b35598d335b1a74e19a49da8500acedf78164600694c0ba8ce21b \
|
||||
--hash=sha256:1303bf8347d6be7ad26d1362af2c38b3a90b8293e8d56244296488ee8591058e \
|
||||
--hash=sha256:192a5f8496e6e1243fdd9ac20e117e667c0712f148c5f9343483b84435854c78 \
|
||||
--hash=sha256:1985ab05e9abebfbdf3163a16ebb37fbc5d49aff2bf5b3d7375ff0920bbb54cd \
|
||||
--hash=sha256:1f8b0d0e99d8e19923e6e07379fa00570be5182c201a8c0b5aaa9a4d4a4ea20b \
|
||||
--hash=sha256:257c4aea6f70a9aef39b2a77d0658a41bf05c243e2bf41895eb02220ac6306f3 \
|
||||
--hash=sha256:261f0031ee6074765096a19b27ed0f75498a8338c3dcd7f4f0d831e38adf12d1 \
|
||||
--hash=sha256:2773f850a778575dd7158a6dd072f7925b67f3ba305e2003538e8831fec77a1d \
|
||||
--hash=sha256:2a29f5294b0b6360bfda69653697eff70aaf2908f58d1073b0acd6f6ab5b5a4f \
|
||||
--hash=sha256:2bb342a01c76f38a12432848e6013c57eb630103e7556cf79b705b53814c3949 \
|
||||
--hash=sha256:2c0419cdad8c70eaeb3116bb28e7b42d546f91baf5179d7556f230d40942dc78 \
|
||||
--hash=sha256:3bffb61e198a91f712cc3d7f2d176a697cb05b284b2ad150fb8edb308eba9002 \
|
||||
--hash=sha256:41fdec0182efac66b27478ac15ef54c9ebcecf0e26ed467eb7d6f262a913318b \
|
||||
--hash=sha256:48f8ca6ee8939bab760225b2ab82934d54330eec10afe4394a92d3f2a0c37dd6 \
|
||||
--hash=sha256:4926ea5c46da30bec4a85907aa3f7e4ea6313145b2aa9469fdb861798daf1502 \
|
||||
--hash=sha256:4c57615791a337378fe5381143259a6c432cdcbb1d3e6428bfb7ce59fff3fb5c \
|
||||
--hash=sha256:4e76ce2475ed4885fe13b8254058be710ec0de74ebd8ef8224cf44a9a3358e5f \
|
||||
--hash=sha256:5361ea13c241d4f0ec3f95e0bf976c15e2e451e9cc7ef2e5ccfc9d170b197a40 \
|
||||
--hash=sha256:5905729668ef1418bd36fbe876322dcb0f90b46811bba96d505af89e6fbdce2f \
|
||||
--hash=sha256:5938b257b04c851c2d1e6cb2f8c18318f06017f35be9a5fe761ee1e2e344dfb7 \
|
||||
--hash=sha256:5e37d5027e297a627da3551a1e962316d0f88ee4ada74c768f6c9234e26346d9 \
|
||||
--hash=sha256:64a607e630d9f4b2797f641884e52b9f8e239d35943f51bef817a384ec1678fe \
|
||||
--hash=sha256:64dc6e9ec64f592f19dc01a784e87267a64a743d34f68488924251253da3c818 \
|
||||
--hash=sha256:69320f05de8cdf4077ecd7fefdec223890eea232af0d58f2530cbda2871244a0 \
|
||||
--hash=sha256:6d8f2144e0d5808c2e2aed40fbebe13869cd00c2ae745aca4b3b16a435edb056 \
|
||||
--hash=sha256:700679c02f9348a0d0a2adcd33a0275717cd0d0aee9d4482b47d935023629505 \
|
||||
--hash=sha256:709447bd7203b0b2debab1acec23123eb80b386f6c29e7604a5d4326a11e5bd6 \
|
||||
--hash=sha256:71adcc8bc80a65b776510bc39992edf942ace35b153ed7a9c6c573a6849ce308 \
|
||||
--hash=sha256:71db8896b942770ed7ab4efa59b22eee5203be2dfdee3c5258d60e57605d688c \
|
||||
--hash=sha256:74fbf5dd3ef09beafd3557631e282f00f8af4e7a78fbfce8ab06d9cd5a789aae \
|
||||
--hash=sha256:79498df398970abcee3d326edd1d4655de7d77aa9aecd578154f8af35ce7bbd2 \
|
||||
--hash=sha256:7ad357e426b0ea5c3043b8ec905546fa44b734bf11d33b3da3959f6e4447d350 \
|
||||
--hash=sha256:7d784f614e4d53050cbe8abf2ae9d1aaacf8ed31ce57b42ce3bf2a48a66c3a5c \
|
||||
--hash=sha256:80a2337e2dfb26950894c8301358961430a0304f7bfe729d34cc036474e9c9b1 \
|
||||
--hash=sha256:824c867a38521d61d62b60aca7db7ca013a2b479e428a0db47d25d8ca5067410 \
|
||||
--hash=sha256:842da42a63ecb32612bb7f5b9e9f8617eab9bc23bd58679a441f4150fcc51c96 \
|
||||
--hash=sha256:8b7be9a6c06518967b641fb15032b1ed682fd3b0443f64078899c61034a0bca6 \
|
||||
--hash=sha256:9099e443d4cc24ac6872e6a05f93205ba1a231b1a8917317b07c9ef2b955f1f4 \
|
||||
--hash=sha256:94253be2b57ef2fea7ffe08996067aabf56a1eb9648342c9e3bad9e10c46e045 \
|
||||
--hash=sha256:949551752930d5e478817e0b49956350d866b26578ced0042a61967e3fcccdea \
|
||||
--hash=sha256:96334bb64d054e36fed346c50c4190bad9d7c586376204f50bede21a913bf942 \
|
||||
--hash=sha256:965455eac8547f32b3181d5ec9ad8b9be500c10fe06193543efaaebe3e4ce70c \
|
||||
--hash=sha256:967b47a0fd237aa17c2748fdb7425015c394a6fb57cdad1562e46a6eb070f96d \
|
||||
--hash=sha256:9994f7db390c17fc2bd4c09dca722fd792ff8a49bb3bdace0c50a83f22f1767d \
|
||||
--hash=sha256:9b60b465773a52c7d4705b0a751f7f1cdccf81dd12aee3b921b31a6e76b07b0e \
|
||||
--hash=sha256:aeddf7b3b3f6e24ccf7d0edfe2d94094ea76b40e831c16eff5230e040ce3b76b \
|
||||
--hash=sha256:c64c4cd0d50d5b2288ab1bcb26c7126c772bbdebdfadcd77225a77df01c4a57e \
|
||||
--hash=sha256:cb987f14af7da7c24f803111dbc7392f5070fd350146af3345103f76ea82e339 \
|
||||
--hash=sha256:dc4fa2240c9fceddaa815a58f29212826fafe43ce80ff666d38c4a03fb036955 \
|
||||
--hash=sha256:e56b1fd529e5dde2d1452a7d72907b37ed1b4f07fdced5d8fb1e963acfff6749 \
|
||||
--hash=sha256:e8630943143c6d6ca9aefc88bbe5e76c90553f4e1a3b2dc339e67dc34aa86f7e \
|
||||
--hash=sha256:e8eb9a4e394926b93ad919cad1b0a918e9b4c846609e8c1cfb6b743683f64da0 \
|
||||
--hash=sha256:e90352d7b610b4693fad0feea48549d4315d10f1eba5605421c92bb834e90170 \
|
||||
--hash=sha256:f0b018e37608c3bfc6039a1dc4eb461e89334465a19916be0153c757a78ea426 \
|
||||
--hash=sha256:f73adc05452fb85e7a12ed3f69c81540a8875960739082e6ea5e28c373a30774 \
|
||||
--hash=sha256:fa33ead69ed133210d96af0c63448b1385df48b9c0247eda735c5896b9e6dbbf \
|
||||
--hash=sha256:fc6d87a1c44df8d493ef44988a3ded751e284e02cdf785f746c2d357e99782a6 \
|
||||
--hash=sha256:fd40af959173ea0d087b6b232b855cfeaa6738f47cb2a0fd10a7f4fa8b74293f \
|
||||
--hash=sha256:fd65774ed7d65101b314808b6893e1a75b7664f680c3ef18d2e5c84d570fa393 \
|
||||
--hash=sha256:fda0162b0dbfa5eaed6cdc708179fa27e148cb8490c7d62e5cf30713909658ea
|
||||
# via psycopg
|
||||
psycopg-pool==3.2.1 \
|
||||
--hash=sha256:060b551d1b97a8d358c668be58b637780b884de14d861f4f5ecc48b7563aafb7 \
|
||||
--hash=sha256:6509a75c073590952915eddbba7ce8b8332a440a31e77bba69561483492829ad
|
||||
psycopg-pool==3.2.4 \
|
||||
--hash=sha256:61774b5bbf23e8d22bedc7504707135aaf744679f8ef9b3fe29942920746a6ed \
|
||||
--hash=sha256:f6a22cff0f21f06d72fb2f5cb48c618946777c49385358e0c88d062c59cbd224
|
||||
# via psycopg
|
||||
pyasn1==0.6.0 \
|
||||
--hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \
|
||||
--hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473
|
||||
pyasn1==0.6.1 \
|
||||
--hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \
|
||||
--hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034
|
||||
# via
|
||||
# pyasn1-modules
|
||||
# python-ldap
|
||||
pyasn1-modules==0.4.0 \
|
||||
--hash=sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6 \
|
||||
--hash=sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b
|
||||
pyasn1-modules==0.4.1 \
|
||||
--hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \
|
||||
--hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c
|
||||
# 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
|
||||
# -r contrib/container/requirements.in
|
||||
# django-auth-ldap
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via -r contrib/container/requirements.in
|
||||
setuptools==75.6.0 \
|
||||
--hash=sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6 \
|
||||
--hash=sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d
|
||||
# via -r contrib/container/requirements.in
|
||||
sqlparse==0.5.3 \
|
||||
--hash=sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272 \
|
||||
--hash=sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca
|
||||
# via django
|
||||
typing-extensions==4.11.0 \
|
||||
--hash=sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0 \
|
||||
--hash=sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
# 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
|
||||
uv==0.5.7 \
|
||||
--hash=sha256:071b57c934bdee8d7502a70e9ea0739a10e9b2d1d0c67e923a09e7a23d9a181b \
|
||||
--hash=sha256:13961a8116515eb288c4f91849fba11ebda0dfeec44cc356e388b3b03b2dbbe1 \
|
||||
--hash=sha256:1c5b89c64fb627f52f1e9c9bbc4dcc7bae29c4c5ab8eff46da3c966bbd4caed2 \
|
||||
--hash=sha256:27c630780e1856a70fbeb267e1ed6835268a1b50963ab9a984fafa4184389def \
|
||||
--hash=sha256:46b03a9a78438219fb3060c096773284e2f22417a9c1f8fdd602f0650b3355c2 \
|
||||
--hash=sha256:4d22a5046a6246af85c92257d110ed8fbcd98b16824e4efa9d825d001222b2cb \
|
||||
--hash=sha256:737a06b15c4e6b8ab7dd0a577ba766380bda4c18ba4ecfcfff37d336f1b03a00 \
|
||||
--hash=sha256:747c011da9f631354a1c89b62b19b8572e040d3fe01c6fb8d650facc7a09fdbb \
|
||||
--hash=sha256:76b514c79136e779cccf90cce5d60f317a0d42074e9f4c059f198ef435f2f6ab \
|
||||
--hash=sha256:78c3c040e52c09a410b9788656d6e760d557f223058537081cb03a3e25ce89de \
|
||||
--hash=sha256:a141b40444c4184efba9fdc10abb3c1cff32154c7f8b0ad46ddc180d65a82d90 \
|
||||
--hash=sha256:a45648db157d2aaff859fe71ec738efea09b972b8864feb2fd61ef856a15b24f \
|
||||
--hash=sha256:a4fc62749bda8e7ae62212b1d85cdf6c7bad41918b3c8ac5a6d730dd093d793d \
|
||||
--hash=sha256:b79e32438390add793bebc41b0729054e375be30bc53f124ee212d9c97affc39 \
|
||||
--hash=sha256:ba25eb99891b95b5200d5e369b788d443fae370b097e7268a71e9ba753f2af3f \
|
||||
--hash=sha256:c1e7b5bcc8b380e333e948c01f6f4c6203067b5de60a05f8ed786332af7a9132 \
|
||||
--hash=sha256:d0600d2b2fbd9a9446bfbb7f03d88bc3d0293b949ce40e326429dd4fe246c926 \
|
||||
--hash=sha256:fb4a3ccbe13072b98919413ac8378dd3e2b5480352f75c349a4f71f423801485
|
||||
# via -r contrib/container/requirements.in
|
||||
wheel==0.45.1 \
|
||||
--hash=sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729 \
|
||||
--hash=sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248
|
||||
# via -r contrib/container/requirements.in
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Packages needed for CI/packages
|
||||
requests==2.32.2
|
||||
pyyaml==6.0.1
|
||||
jc==1.25.2
|
||||
requests==2.32.3
|
||||
pyyaml==6.0.2
|
||||
jc==1.25.4
|
||||
|
@ -1,228 +1,239 @@
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile contrib/dev_reqs/requirements.in -o contrib/dev_reqs/requirements.txt
|
||||
certifi==2024.2.2 \
|
||||
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
|
||||
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
|
||||
# uv pip compile contrib/dev_reqs/requirements.in -o contrib/dev_reqs/requirements.txt --no-strip-extras --generate-hashes
|
||||
certifi==2024.8.30 \
|
||||
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
|
||||
--hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
|
||||
# via 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
|
||||
charset-normalizer==3.4.0 \
|
||||
--hash=sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621 \
|
||||
--hash=sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6 \
|
||||
--hash=sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8 \
|
||||
--hash=sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912 \
|
||||
--hash=sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c \
|
||||
--hash=sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b \
|
||||
--hash=sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d \
|
||||
--hash=sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d \
|
||||
--hash=sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95 \
|
||||
--hash=sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e \
|
||||
--hash=sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565 \
|
||||
--hash=sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64 \
|
||||
--hash=sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab \
|
||||
--hash=sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be \
|
||||
--hash=sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e \
|
||||
--hash=sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907 \
|
||||
--hash=sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0 \
|
||||
--hash=sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2 \
|
||||
--hash=sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62 \
|
||||
--hash=sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62 \
|
||||
--hash=sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23 \
|
||||
--hash=sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc \
|
||||
--hash=sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284 \
|
||||
--hash=sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca \
|
||||
--hash=sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455 \
|
||||
--hash=sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858 \
|
||||
--hash=sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b \
|
||||
--hash=sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594 \
|
||||
--hash=sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc \
|
||||
--hash=sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db \
|
||||
--hash=sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b \
|
||||
--hash=sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea \
|
||||
--hash=sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6 \
|
||||
--hash=sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920 \
|
||||
--hash=sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749 \
|
||||
--hash=sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7 \
|
||||
--hash=sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd \
|
||||
--hash=sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99 \
|
||||
--hash=sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242 \
|
||||
--hash=sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee \
|
||||
--hash=sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129 \
|
||||
--hash=sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2 \
|
||||
--hash=sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51 \
|
||||
--hash=sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee \
|
||||
--hash=sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8 \
|
||||
--hash=sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b \
|
||||
--hash=sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613 \
|
||||
--hash=sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742 \
|
||||
--hash=sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe \
|
||||
--hash=sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3 \
|
||||
--hash=sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5 \
|
||||
--hash=sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631 \
|
||||
--hash=sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7 \
|
||||
--hash=sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15 \
|
||||
--hash=sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c \
|
||||
--hash=sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea \
|
||||
--hash=sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417 \
|
||||
--hash=sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250 \
|
||||
--hash=sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88 \
|
||||
--hash=sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca \
|
||||
--hash=sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa \
|
||||
--hash=sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99 \
|
||||
--hash=sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149 \
|
||||
--hash=sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41 \
|
||||
--hash=sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574 \
|
||||
--hash=sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0 \
|
||||
--hash=sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f \
|
||||
--hash=sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d \
|
||||
--hash=sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654 \
|
||||
--hash=sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3 \
|
||||
--hash=sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19 \
|
||||
--hash=sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90 \
|
||||
--hash=sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578 \
|
||||
--hash=sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9 \
|
||||
--hash=sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1 \
|
||||
--hash=sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51 \
|
||||
--hash=sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719 \
|
||||
--hash=sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236 \
|
||||
--hash=sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a \
|
||||
--hash=sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c \
|
||||
--hash=sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade \
|
||||
--hash=sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944 \
|
||||
--hash=sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc \
|
||||
--hash=sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6 \
|
||||
--hash=sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6 \
|
||||
--hash=sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27 \
|
||||
--hash=sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6 \
|
||||
--hash=sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2 \
|
||||
--hash=sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12 \
|
||||
--hash=sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf \
|
||||
--hash=sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114 \
|
||||
--hash=sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7 \
|
||||
--hash=sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf \
|
||||
--hash=sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d \
|
||||
--hash=sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b \
|
||||
--hash=sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed \
|
||||
--hash=sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03 \
|
||||
--hash=sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4 \
|
||||
--hash=sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67 \
|
||||
--hash=sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365 \
|
||||
--hash=sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a \
|
||||
--hash=sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748 \
|
||||
--hash=sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b \
|
||||
--hash=sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079 \
|
||||
--hash=sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482
|
||||
# via requests
|
||||
idna==3.7 \
|
||||
--hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
|
||||
--hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
|
||||
idna==3.10 \
|
||||
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
|
||||
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
|
||||
# via requests
|
||||
jc==1.25.2 \
|
||||
--hash=sha256:26e412a65a478f9da3097653db6277f915cfae5c0f0a3f42026b405936abd358 \
|
||||
--hash=sha256:97ada193495f79550f06fe0cbfb119ff470bcca57c1cc593a5cdb0008720e0b3
|
||||
pygments==2.17.2 \
|
||||
--hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \
|
||||
--hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367
|
||||
jc==1.25.4 \
|
||||
--hash=sha256:1e4f45d2e5b72cf9d300b0d9df0578c0d3b553843e3ad37a525d93bb0e94aca1 \
|
||||
--hash=sha256:a32eaf029c56b582dadae48895f20784d0f84f2fa28a8e2b32f377a8bffa8b39
|
||||
# via -r contrib/dev_reqs/requirements.in
|
||||
pygments==2.18.0 \
|
||||
--hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \
|
||||
--hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a
|
||||
# via jc
|
||||
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
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
pyyaml==6.0.2 \
|
||||
--hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \
|
||||
--hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \
|
||||
--hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \
|
||||
--hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \
|
||||
--hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \
|
||||
--hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \
|
||||
--hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \
|
||||
--hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \
|
||||
--hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \
|
||||
--hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \
|
||||
--hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \
|
||||
--hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \
|
||||
--hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \
|
||||
--hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \
|
||||
--hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \
|
||||
--hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \
|
||||
--hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \
|
||||
--hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \
|
||||
--hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \
|
||||
--hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \
|
||||
--hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \
|
||||
--hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \
|
||||
--hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \
|
||||
--hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \
|
||||
--hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \
|
||||
--hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \
|
||||
--hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \
|
||||
--hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \
|
||||
--hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \
|
||||
--hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \
|
||||
--hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \
|
||||
--hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \
|
||||
--hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \
|
||||
--hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \
|
||||
--hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \
|
||||
--hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \
|
||||
--hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \
|
||||
--hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \
|
||||
--hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \
|
||||
--hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \
|
||||
--hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \
|
||||
--hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \
|
||||
--hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \
|
||||
--hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \
|
||||
--hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \
|
||||
--hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \
|
||||
--hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \
|
||||
--hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \
|
||||
--hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \
|
||||
--hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \
|
||||
--hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \
|
||||
--hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \
|
||||
--hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4
|
||||
# via -r contrib/dev_reqs/requirements.in
|
||||
requests==2.32.3 \
|
||||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
|
||||
# via -r contrib/dev_reqs/requirements.in
|
||||
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
|
||||
ruamel-yaml-clib==0.2.12 \
|
||||
--hash=sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b \
|
||||
--hash=sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4 \
|
||||
--hash=sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef \
|
||||
--hash=sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5 \
|
||||
--hash=sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632 \
|
||||
--hash=sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6 \
|
||||
--hash=sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680 \
|
||||
--hash=sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf \
|
||||
--hash=sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da \
|
||||
--hash=sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6 \
|
||||
--hash=sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a \
|
||||
--hash=sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519 \
|
||||
--hash=sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6 \
|
||||
--hash=sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f \
|
||||
--hash=sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd \
|
||||
--hash=sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2 \
|
||||
--hash=sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52 \
|
||||
--hash=sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd \
|
||||
--hash=sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d \
|
||||
--hash=sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c \
|
||||
--hash=sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6 \
|
||||
--hash=sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb \
|
||||
--hash=sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969 \
|
||||
--hash=sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28 \
|
||||
--hash=sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e \
|
||||
--hash=sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45 \
|
||||
--hash=sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4 \
|
||||
--hash=sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12 \
|
||||
--hash=sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31 \
|
||||
--hash=sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642 \
|
||||
--hash=sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e \
|
||||
--hash=sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285 \
|
||||
--hash=sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed \
|
||||
--hash=sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1 \
|
||||
--hash=sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7 \
|
||||
--hash=sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3 \
|
||||
--hash=sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475 \
|
||||
--hash=sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5 \
|
||||
--hash=sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76 \
|
||||
--hash=sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987 \
|
||||
--hash=sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df
|
||||
# via ruamel-yaml
|
||||
urllib3==2.2.2 \
|
||||
--hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \
|
||||
--hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168
|
||||
urllib3==2.2.3 \
|
||||
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \
|
||||
--hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9
|
||||
# via requests
|
||||
xmltodict==0.13.0 \
|
||||
--hash=sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56 \
|
||||
--hash=sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852
|
||||
xmltodict==0.14.2 \
|
||||
--hash=sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553 \
|
||||
--hash=sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac
|
||||
# via jc
|
||||
|
@ -75,6 +75,7 @@ root_command() {
|
||||
;;
|
||||
"Debian GNU/Linux" | "debian gnu/linux" | Raspbian)
|
||||
if [[ $VER == "12" ]]; then
|
||||
DIST_VER="11"
|
||||
SUPPORTED=true
|
||||
elif [[ $VER == "11" ]]; then
|
||||
SUPPORTED=true
|
||||
|
@ -5,33 +5,40 @@
|
||||
|
||||
set -eu
|
||||
|
||||
VERSION="$APP_PKG_VERSION-$APP_PKG_ITERATION"
|
||||
echo "Setting VERSION information to $VERSION"
|
||||
echo "$VERSION" > VERSION
|
||||
|
||||
# The sha is the second element in APP_PKG_ITERATION
|
||||
VERSION="$APP_PKG_VERSION-$APP_PKG_ITERATION"
|
||||
SHA=$(echo $APP_PKG_ITERATION | cut -d'.' -f2)
|
||||
|
||||
# Download info
|
||||
echo "Getting info from github for commit $SHA"
|
||||
curl -L \
|
||||
echo "INFO collection | Getting info from github for commit $SHA"
|
||||
curl -L -s -f \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/InvenTree/InvenTree/commits/$SHA > commit.json
|
||||
curl -L \
|
||||
https://api.github.com/repos/$APP_REPO/commits/$SHA > commit.json
|
||||
echo "INFO collection | Got commit.json with size $(wc -c commit.json)"
|
||||
curl -L -s -f \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/InvenTree/InvenTree/commits/$SHA/branches-where-head > branches.json
|
||||
https://api.github.com/repos/$APP_REPO/commits/$SHA/branches-where-head > branches.json
|
||||
echo "INFO collection | Got branches.json with size $(wc -c branches.json)"
|
||||
curl -L -s -f \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/$APP_REPO/commits/$APP_PKG_VERSION > tag.json
|
||||
echo "INFO collection | Got tag.json with size $(wc -c tag.json)"
|
||||
|
||||
# Extract info
|
||||
echo "Extracting info from github"
|
||||
echo "INFO extract | Extracting info from github"
|
||||
DATE=$(jq -r '.commit.committer.date' commit.json)
|
||||
BRANCH=$(jq -r '.[].name' branches.json)
|
||||
NODE_ID=$(jq -r '.node_id' commit.json)
|
||||
SIGNATURE=$(jq -r '.commit.verification.signature' commit.json)
|
||||
FULL_SHA=$(jq -r '.sha' commit.json)
|
||||
|
||||
echo "Write VERSION information"
|
||||
echo "INFO write | Write VERSION information"
|
||||
echo "$VERSION" > VERSION
|
||||
echo "INVENTREE_COMMIT_HASH='$SHA'" >> VERSION
|
||||
echo "INVENTREE_COMMIT_SHA='$FULL_SHA'" >> VERSION
|
||||
echo "INVENTREE_COMMIT_DATE='$DATE'" >> VERSION
|
||||
echo "INVENTREE_PKG_INSTALLER='PKG'" >> VERSION
|
||||
echo "INVENTREE_PKG_BRANCH='$BRANCH'" >> VERSION
|
||||
@ -39,5 +46,22 @@ echo "INVENTREE_PKG_TARGET='$TARGET'" >> VERSION
|
||||
echo "NODE_ID='$NODE_ID'" >> VERSION
|
||||
echo "SIGNATURE='$SIGNATURE'" >> VERSION
|
||||
|
||||
echo "Written VERSION information"
|
||||
echo "INFO write | Written VERSION information"
|
||||
echo "### VERSION ###"
|
||||
cat VERSION
|
||||
echo "### VERSION ###"
|
||||
|
||||
# Try to get frontend
|
||||
echo "INFO frontend | Trying to get frontend"
|
||||
# Check if tag sha is the same as the commit sha
|
||||
TAG_SHA=$(jq -r '.sha' tag.json)
|
||||
if [ "$TAG_SHA" != "$FULL_SHA" ]; then
|
||||
echo "INFO frontend | Tag sha '$TAG_SHA' is not the same as commit sha $FULL_SHA, can not download frontend"
|
||||
else
|
||||
echo "INFO frontend | Getting frontend from github via tag"
|
||||
curl https://github.com/$APP_REPO/releases/download/$APP_PKG_VERSION/frontend-build.zip -L -O -f
|
||||
mkdir -p src/backend/InvenTree/web/static
|
||||
echo "INFO frontend | Unzipping frontend"
|
||||
unzip -qq frontend-build.zip -d src/backend/InvenTree/web/static/web
|
||||
echo "INFO frontend | Unzipped frontend"
|
||||
fi
|
||||
|
@ -4,6 +4,8 @@
|
||||
#
|
||||
Color_Off='\033[0m'
|
||||
On_Red='\033[41m'
|
||||
PYTHON_FROM=9
|
||||
PYTHON_TO=12
|
||||
|
||||
function detect_docker() {
|
||||
if [ -n "$(grep docker </proc/1/cgroup)" ]; then
|
||||
@ -11,6 +13,7 @@ function detect_docker() {
|
||||
else
|
||||
DOCKER="no"
|
||||
fi
|
||||
echo "# POI04| Running in docker: ${DOCKER}"
|
||||
}
|
||||
|
||||
function detect_initcmd() {
|
||||
@ -28,6 +31,7 @@ function detect_initcmd() {
|
||||
if [ "${DOCKER}" == "yes" ]; then
|
||||
INIT_CMD="initctl"
|
||||
fi
|
||||
echo "# POI05| Using init command: ${INIT_CMD}"
|
||||
}
|
||||
|
||||
function detect_ip() {
|
||||
@ -35,37 +39,52 @@ function detect_ip() {
|
||||
|
||||
if [ "${SETUP_NO_CALLS}" == "true" ]; then
|
||||
# Use local IP address
|
||||
echo "# Getting the IP address of the first local IP address"
|
||||
echo "# POI06| Getting the IP address of the first local IP address"
|
||||
export INVENTREE_IP=$(hostname -I | awk '{print $1}')
|
||||
else
|
||||
# Use web service to get the IP address
|
||||
echo "# Getting the IP address of the server via web service"
|
||||
echo "# POI06| Getting the IP address of the server via web service"
|
||||
export INVENTREE_IP=$(curl -s https://checkip.amazonaws.com)
|
||||
fi
|
||||
|
||||
echo "IP address is ${INVENTREE_IP}"
|
||||
echo "# POI06| 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"
|
||||
echo "# POI07| 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 earlier used version: ${SETUP_PYTHON}"
|
||||
echo "# POI07| Found earlier used version: ${SETUP_PYTHON}"
|
||||
else
|
||||
echo "# No python environment found - using environment variable: ${SETUP_PYTHON}"
|
||||
echo "# POI07| No python environment found - using environment variable: ${SETUP_PYTHON}"
|
||||
fi
|
||||
|
||||
# Try to detect a python between 3.9 and 3.12 in reverse order
|
||||
if [ -z "$(which ${SETUP_PYTHON})" ]; then
|
||||
echo "# POI07| Trying to detecting python3.${PYTHON_FROM} to python3.${PYTHON_TO} - using newest version"
|
||||
for i in $(seq $PYTHON_TO -1 $PYTHON_FROM); do
|
||||
echo "# POI07| Checking for python3.${i}"
|
||||
if [ -n "$(which python3.${i})" ]; then
|
||||
SETUP_PYTHON="python3.${i}"
|
||||
echo "# POI07| Found python3.${i} installed - using for setup ${SETUP_PYTHON}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Ensure python can be executed - abort if not
|
||||
if [ -z "$(which ${SETUP_PYTHON})" ]; then
|
||||
echo "${On_Red}"
|
||||
echo "# Python ${SETUP_PYTHON} not found - aborting!"
|
||||
echo "# Please ensure python can be executed with the command '$SETUP_PYTHON' by the current user '$USER'."
|
||||
echo "# If you are using a different python version, please set the environment variable SETUP_PYTHON to the correct command - eg. 'python3.10'."
|
||||
echo "# POI07| Python ${SETUP_PYTHON} not found - aborting!"
|
||||
echo "# POI07| Please ensure python can be executed with the command '$SETUP_PYTHON' by the current user '$USER'."
|
||||
echo "# POI07| If you are using a different python version, please set the environment variable SETUP_PYTHON to the correct command - eg. 'python3.10'."
|
||||
echo "${Color_Off}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "# POI07| Using python command: ${SETUP_PYTHON}"
|
||||
}
|
||||
|
||||
function get_env() {
|
||||
@ -80,7 +99,7 @@ function get_env() {
|
||||
done
|
||||
|
||||
if [ -n "${SETUP_DEBUG}" ]; then
|
||||
echo "Done getting env $envname: ${!envname}"
|
||||
echo "# POI02| Done getting env $envname: ${!envname}"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -88,7 +107,7 @@ function detect_local_env() {
|
||||
# Get all possible envs for the install
|
||||
|
||||
if [ -n "${SETUP_DEBUG}" ]; then
|
||||
echo "# Printing local envs - before #++#"
|
||||
echo "# POI02| Printing local envs - before #++#"
|
||||
printenv
|
||||
fi
|
||||
|
||||
@ -98,7 +117,7 @@ function detect_local_env() {
|
||||
done
|
||||
|
||||
if [ -n "${SETUP_DEBUG}" ]; then
|
||||
echo "# Printing local envs - after #++#"
|
||||
echo "# POI02| Printing local envs - after #++#"
|
||||
printenv
|
||||
fi
|
||||
}
|
||||
@ -106,38 +125,40 @@ function detect_local_env() {
|
||||
function detect_envs() {
|
||||
# Detect all envs that should be passed to setup commands
|
||||
|
||||
echo "# Setting base environment variables"
|
||||
echo "# POI03| Setting base environment variables"
|
||||
|
||||
export INVENTREE_CONFIG_FILE=${INVENTREE_CONFIG_FILE:-${CONF_DIR}/config.yaml}
|
||||
|
||||
if test -f "${INVENTREE_CONFIG_FILE}"; then
|
||||
echo "# Using existing config file: ${INVENTREE_CONFIG_FILE}"
|
||||
echo "# POI03| Using existing config file: ${INVENTREE_CONFIG_FILE}"
|
||||
|
||||
# Install parser
|
||||
echo "# POI03| Installing requirements"
|
||||
pip install --require-hashes -r ${APP_HOME}/contrib/dev_reqs/requirements.txt -q
|
||||
echo "# POI03| Installed requirements"
|
||||
|
||||
# Load config
|
||||
local CONF=$(cat ${INVENTREE_CONFIG_FILE} | jc --yaml)
|
||||
export INVENTREE_CONF_DATA=$(cat ${INVENTREE_CONFIG_FILE} | jc --yaml)
|
||||
|
||||
# Parse the config file
|
||||
export INVENTREE_MEDIA_ROOT=$(jq -r '.[].media_root' <<< ${CONF})
|
||||
export INVENTREE_STATIC_ROOT=$(jq -r '.[].static_root' <<< ${CONF})
|
||||
export INVENTREE_BACKUP_DIR=$(jq -r '.[].backup_dir' <<< ${CONF})
|
||||
export INVENTREE_PLUGINS_ENABLED=$(jq -r '.[].plugins_enabled' <<< ${CONF})
|
||||
export INVENTREE_PLUGIN_FILE=$(jq -r '.[].plugin_file' <<< ${CONF})
|
||||
export INVENTREE_SECRET_KEY_FILE=$(jq -r '.[].secret_key_file' <<< ${CONF})
|
||||
export INVENTREE_MEDIA_ROOT=$(jq -r '.[].media_root' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_STATIC_ROOT=$(jq -r '.[].static_root' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_BACKUP_DIR=$(jq -r '.[].backup_dir' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_PLUGINS_ENABLED=$(jq -r '.[].plugins_enabled' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_PLUGIN_FILE=$(jq -r '.[].plugin_file' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_SECRET_KEY_FILE=$(jq -r '.[].secret_key_file' <<< ${INVENTREE_CONF_DATA})
|
||||
|
||||
export INVENTREE_DB_ENGINE=$(jq -r '.[].database.ENGINE' <<< ${CONF})
|
||||
export INVENTREE_DB_NAME=$(jq -r '.[].database.NAME' <<< ${CONF})
|
||||
export INVENTREE_DB_USER=$(jq -r '.[].database.USER' <<< ${CONF})
|
||||
export INVENTREE_DB_PASSWORD=$(jq -r '.[].database.PASSWORD' <<< ${CONF})
|
||||
export INVENTREE_DB_HOST=$(jq -r '.[].database.HOST' <<< ${CONF})
|
||||
export INVENTREE_DB_PORT=$(jq -r '.[].database.PORT' <<< ${CONF})
|
||||
export INVENTREE_DB_ENGINE=$(jq -r '.[].database.ENGINE' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_DB_NAME=$(jq -r '.[].database.NAME' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_DB_USER=$(jq -r '.[].database.USER' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_DB_PASSWORD=$(jq -r '.[].database.PASSWORD' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_DB_HOST=$(jq -r '.[].database.HOST' <<< ${INVENTREE_CONF_DATA})
|
||||
export INVENTREE_DB_PORT=$(jq -r '.[].database.PORT' <<< ${INVENTREE_CONF_DATA})
|
||||
else
|
||||
echo "# No config file found: ${INVENTREE_CONFIG_FILE}, using envs or defaults"
|
||||
echo "# POI03| No config file found: ${INVENTREE_CONFIG_FILE}, using envs or defaults"
|
||||
|
||||
if [ -n "${SETUP_DEBUG}" ]; then
|
||||
echo "# Print current envs"
|
||||
echo "# POI03| Print current envs"
|
||||
printenv | grep INVENTREE_
|
||||
printenv | grep SETUP_
|
||||
fi
|
||||
@ -160,43 +181,43 @@ function detect_envs() {
|
||||
fi
|
||||
|
||||
# For debugging pass out the envs
|
||||
echo "# Collected environment variables:"
|
||||
echo "# INVENTREE_MEDIA_ROOT=${INVENTREE_MEDIA_ROOT}"
|
||||
echo "# INVENTREE_STATIC_ROOT=${INVENTREE_STATIC_ROOT}"
|
||||
echo "# INVENTREE_BACKUP_DIR=${INVENTREE_BACKUP_DIR}"
|
||||
echo "# INVENTREE_PLUGINS_ENABLED=${INVENTREE_PLUGINS_ENABLED}"
|
||||
echo "# INVENTREE_PLUGIN_FILE=${INVENTREE_PLUGIN_FILE}"
|
||||
echo "# INVENTREE_SECRET_KEY_FILE=${INVENTREE_SECRET_KEY_FILE}"
|
||||
echo "# INVENTREE_DB_ENGINE=${INVENTREE_DB_ENGINE}"
|
||||
echo "# INVENTREE_DB_NAME=${INVENTREE_DB_NAME}"
|
||||
echo "# INVENTREE_DB_USER=${INVENTREE_DB_USER}"
|
||||
echo "# POI03| Collected environment variables:"
|
||||
echo "# POI03| INVENTREE_MEDIA_ROOT=${INVENTREE_MEDIA_ROOT}"
|
||||
echo "# POI03| INVENTREE_STATIC_ROOT=${INVENTREE_STATIC_ROOT}"
|
||||
echo "# POI03| INVENTREE_BACKUP_DIR=${INVENTREE_BACKUP_DIR}"
|
||||
echo "# POI03| INVENTREE_PLUGINS_ENABLED=${INVENTREE_PLUGINS_ENABLED}"
|
||||
echo "# POI03| INVENTREE_PLUGIN_FILE=${INVENTREE_PLUGIN_FILE}"
|
||||
echo "# POI03| INVENTREE_SECRET_KEY_FILE=${INVENTREE_SECRET_KEY_FILE}"
|
||||
echo "# POI03| INVENTREE_DB_ENGINE=${INVENTREE_DB_ENGINE}"
|
||||
echo "# POI03| INVENTREE_DB_NAME=${INVENTREE_DB_NAME}"
|
||||
echo "# POI03| INVENTREE_DB_USER=${INVENTREE_DB_USER}"
|
||||
if [ -n "${SETUP_DEBUG}" ]; then
|
||||
echo "# INVENTREE_DB_PASSWORD=${INVENTREE_DB_PASSWORD}"
|
||||
echo "# POI03| INVENTREE_DB_PASSWORD=${INVENTREE_DB_PASSWORD}"
|
||||
fi
|
||||
echo "# INVENTREE_DB_HOST=${INVENTREE_DB_HOST}"
|
||||
echo "# INVENTREE_DB_PORT=${INVENTREE_DB_PORT}"
|
||||
echo "# POI03| INVENTREE_DB_HOST=${INVENTREE_DB_HOST}"
|
||||
echo "# POI03| INVENTREE_DB_PORT=${INVENTREE_DB_PORT}"
|
||||
}
|
||||
|
||||
function create_initscripts() {
|
||||
|
||||
# Make sure python env exists
|
||||
if test -f "${APP_HOME}/env"; then
|
||||
echo "# python environment already present - skipping"
|
||||
echo "# POI09| python environment already present - skipping"
|
||||
else
|
||||
echo "# Setting up python environment"
|
||||
echo "# POI09| Setting up python environment"
|
||||
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"
|
||||
echo "# POI09| Loading extra packages from INSTALLER_EXTRA"
|
||||
source ${APP_HOME}/INSTALLER_EXTRA
|
||||
fi
|
||||
|
||||
if [ -n "${SETUP_EXTRA_PIP}" ]; then
|
||||
echo "# Installing extra pip packages"
|
||||
echo "# POI09| Installing extra pip packages"
|
||||
if [ -n "${SETUP_DEBUG}" ]; then
|
||||
echo "# Extra pip packages: ${SETUP_EXTRA_PIP}"
|
||||
echo "# POI09| 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
|
||||
@ -206,37 +227,45 @@ function create_initscripts() {
|
||||
|
||||
# Unlink default config if it exists
|
||||
if test -f "/etc/nginx/sites-enabled/default"; then
|
||||
echo "# Unlinking default nginx config\n# Old file still in /etc/nginx/sites-available/default"
|
||||
echo "# POI09| Unlinking default nginx config\n# POI09| Old file still in /etc/nginx/sites-available/default"
|
||||
sudo unlink /etc/nginx/sites-enabled/default
|
||||
echo "# POI09| Unlinked default nginx config"
|
||||
fi
|
||||
|
||||
# Create InvenTree specific nginx config
|
||||
echo "# Stopping nginx"
|
||||
echo "# POI09| Stopping nginx"
|
||||
${INIT_CMD} stop nginx
|
||||
echo "# Setting up nginx to ${SETUP_NGINX_FILE}"
|
||||
echo "# POI09| Stopped nginx"
|
||||
echo "# POI09| Setting up nginx to ${SETUP_NGINX_FILE}"
|
||||
# Always use the latest nginx config; important if new headers are added / needed for security
|
||||
cp ${APP_HOME}/contrib/packager.io/nginx.prod.conf ${SETUP_NGINX_FILE}
|
||||
sed -i s/inventree-server:8000/localhost:6000/g ${SETUP_NGINX_FILE}
|
||||
sed -i s=var/www=opt/inventree/data=g ${SETUP_NGINX_FILE}
|
||||
# Start nginx
|
||||
echo "# Starting nginx"
|
||||
echo "# POI09| Starting nginx"
|
||||
${INIT_CMD} start nginx
|
||||
echo "# POI09| Started nginx"
|
||||
|
||||
echo "# (Re)creating init scripts"
|
||||
echo "# POI09| (Re)creating init scripts"
|
||||
# This resets scale parameters to a known state
|
||||
inventree scale web="1" worker="1"
|
||||
|
||||
echo "# Enabling InvenTree on boot"
|
||||
echo "# POI09| Enabling InvenTree on boot"
|
||||
${INIT_CMD} enable inventree
|
||||
echo "# POI09| Enabled InvenTree on boot"
|
||||
}
|
||||
|
||||
function create_admin() {
|
||||
# Create data for admin user
|
||||
# Create data for admin users - stop with setting SETUP_ADMIN_NOCREATION to true
|
||||
if [ "${SETUP_ADMIN_NOCREATION}" == "true" ]; then
|
||||
echo "# POI10| Admin creation is disabled - skipping"
|
||||
return
|
||||
fi
|
||||
|
||||
if test -f "${SETUP_ADMIN_PASSWORD_FILE}"; then
|
||||
echo "# Admin data already exists - skipping"
|
||||
echo "# POI10| Admin data already exists - skipping"
|
||||
else
|
||||
echo "# Creating admin user data"
|
||||
echo "# POI10| Creating admin user data"
|
||||
|
||||
# Static admin data
|
||||
export INVENTREE_ADMIN_USER=${INVENTREE_ADMIN_USER:-admin}
|
||||
@ -251,13 +280,15 @@ function create_admin() {
|
||||
}
|
||||
|
||||
function start_inventree() {
|
||||
echo "# Starting InvenTree"
|
||||
echo "# POI15| Starting InvenTree"
|
||||
${INIT_CMD} start inventree
|
||||
echo "# POI15| Started InvenTree"
|
||||
}
|
||||
|
||||
function stop_inventree() {
|
||||
echo "# Stopping InvenTree"
|
||||
echo "# POI11| Stopping InvenTree"
|
||||
${INIT_CMD} stop inventree
|
||||
echo "# POI11| Stopped InvenTree"
|
||||
}
|
||||
|
||||
function update_or_install() {
|
||||
@ -266,23 +297,23 @@ function update_or_install() {
|
||||
chown ${APP_USER}:${APP_GROUP} ${APP_HOME} -R
|
||||
|
||||
# Run update as app user
|
||||
echo "# Updating InvenTree"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && pip install wheel"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && invoke update | sed -e 's/^/# inv update| /;'"
|
||||
echo "# POI12| Updating InvenTree"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && pip install uv wheel"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && invoke update --uv | sed -e 's/^/# POI12| u | /;'"
|
||||
|
||||
# Make sure permissions are correct again
|
||||
echo "# Set permissions for data dir and media: ${DATA_DIR}"
|
||||
echo "# POI12| Set permissions for data dir and media: ${DATA_DIR}"
|
||||
chown ${APP_USER}:${APP_GROUP} ${DATA_DIR} -R
|
||||
chown ${APP_USER}:${APP_GROUP} ${CONF_DIR} -R
|
||||
}
|
||||
|
||||
function set_env() {
|
||||
echo "# Setting up InvenTree config values"
|
||||
echo "# POI13| Setting up InvenTree config values"
|
||||
|
||||
inventree config:set INVENTREE_CONFIG_FILE=${INVENTREE_CONFIG_FILE}
|
||||
|
||||
# Changing the config file
|
||||
echo "# Writing the settings to the config file ${INVENTREE_CONFIG_FILE}"
|
||||
echo "# POI13| Writing the settings to the config file ${INVENTREE_CONFIG_FILE}"
|
||||
# Media Root
|
||||
sed -i s=#media_root:\ \'/home/inventree/data/media\'=media_root:\ \'${INVENTREE_MEDIA_ROOT}\'=g ${INVENTREE_CONFIG_FILE}
|
||||
# Static Root
|
||||
@ -299,37 +330,42 @@ function set_env() {
|
||||
sed -i s=debug:\ True=debug:\ False=g ${INVENTREE_CONFIG_FILE}
|
||||
|
||||
# Database engine
|
||||
sed -i s=#ENGINE:\ sampleengine=ENGINE:\ ${INVENTREE_DB_ENGINE}=g ${INVENTREE_CONFIG_FILE}
|
||||
sed -i s=#\ ENGINE:\ Database\ engine.\ Selection\ from:=ENGINE:\ ${INVENTREE_DB_ENGINE}=g ${INVENTREE_CONFIG_FILE}
|
||||
# Database name
|
||||
sed -i s=#NAME:\ \'/path/to/database\'=NAME:\ \'${INVENTREE_DB_NAME}\'=g ${INVENTREE_CONFIG_FILE}
|
||||
sed -i s=#\ NAME:\ Database\ name=NAME:\ \'${INVENTREE_DB_NAME}\'=g ${INVENTREE_CONFIG_FILE}
|
||||
# Database user
|
||||
sed -i s=#USER:\ sampleuser=USER:\ ${INVENTREE_DB_USER}=g ${INVENTREE_CONFIG_FILE}
|
||||
sed -i s=#\ USER:\ Database\ username\ \(if\ required\)=USER:\ ${INVENTREE_DB_USER}=g ${INVENTREE_CONFIG_FILE}
|
||||
# Database password
|
||||
sed -i s=#PASSWORD:\ samplepassword=PASSWORD:\ ${INVENTREE_DB_PASSWORD}=g ${INVENTREE_CONFIG_FILE}
|
||||
sed -i s=#\ PASSWORD:\ Database\ password\ \(if\ required\)=PASSWORD:\ ${INVENTREE_DB_PASSWORD}=g ${INVENTREE_CONFIG_FILE}
|
||||
# Database host
|
||||
sed -i s=#HOST:\ samplehost=HOST:\ ${INVENTREE_DB_HOST}=g ${INVENTREE_CONFIG_FILE}
|
||||
sed -i s=#\ HOST:\ Database\ host\ address\ \(if\ required\)=HOST:\ ${INVENTREE_DB_HOST}=g ${INVENTREE_CONFIG_FILE}
|
||||
# Database port
|
||||
sed -i s=#PORT:\ 123456=PORT:\ ${INVENTREE_DB_PORT}=g ${INVENTREE_CONFIG_FILE}
|
||||
sed -i s=#\ PORT:\ Database\ host\ port\ \(if\ required\)=PORT:\ ${INVENTREE_DB_PORT}=g ${INVENTREE_CONFIG_FILE}
|
||||
|
||||
# Fixing the permissions
|
||||
chown ${APP_USER}:${APP_GROUP} ${DATA_DIR} ${INVENTREE_CONFIG_FILE}
|
||||
|
||||
echo "# POI13| Done setting up InvenTree config values"
|
||||
}
|
||||
|
||||
function set_site() {
|
||||
# Ensure IP is known
|
||||
if [ -z "${INVENTREE_IP}" ]; then
|
||||
echo "# No IP address found - skipping"
|
||||
echo "# POI14| 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"
|
||||
echo "# POI14| Setting up InvenTree site URL"
|
||||
inventree config:set INVENTREE_SITE_URL=http://${INVENTREE_IP}
|
||||
else
|
||||
echo "# POI14| Site URL already set - skipping"
|
||||
fi
|
||||
}
|
||||
|
||||
function final_message() {
|
||||
echo "# POI16| Printing Final message"
|
||||
echo -e "####################################################################################"
|
||||
echo -e "This InvenTree install uses nginx, the settings for the webserver can be found in"
|
||||
echo -e "${SETUP_NGINX_FILE}"
|
||||
@ -340,3 +376,46 @@ function final_message() {
|
||||
echo -e " Password: ${INVENTREE_ADMIN_PASSWORD}"
|
||||
echo -e "####################################################################################"
|
||||
}
|
||||
|
||||
|
||||
function update_checks() {
|
||||
echo "# POI08| Running upgrade"
|
||||
local old_version=$1
|
||||
local old_version_rev=$(echo ${old_version} | cut -d'-' -f1 | cut -d'.' -f2)
|
||||
local new_version=$(dpkg-query --show --showformat='${Version}' inventree)
|
||||
local new_version_rev=$(echo ${new_version} | cut -d'-' -f1 | cut -d'.' -f2)
|
||||
echo "# POI08| Old version is: ${old_version} | ${old_version_rev} - updating to ${new_version} | ${old_version_rev}"
|
||||
|
||||
local ABORT=false
|
||||
function check_config_value() {
|
||||
local env_key=$1
|
||||
local config_key=$2
|
||||
local name=$3
|
||||
|
||||
local value=$(inventree config:get ${env_key})
|
||||
if [ -z "${value}" ] || [ "$value" == "null" ]; then
|
||||
value=$(jq -r ".[].${config_key}" <<< ${INVENTREE_CONF_DATA})
|
||||
fi
|
||||
if [ -z "${value}" ] || [ "$value" == "null" ]; then
|
||||
echo "# POI08| No setting for ${name} found - please set it manually either in ${INVENTREE_CONFIG_FILE} under '${config_key}' or with 'inventree config:set ${env_key}=value'"
|
||||
ABORT=true
|
||||
else
|
||||
echo "# POI08| Found setting for ${name} - ${value}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Custom checks if old version is below 0.8.0
|
||||
if [ "${old_version_rev}" -lt "9" ]; then
|
||||
echo "# POI08| Old version is below 0.9.0 - You might be missing some configs"
|
||||
|
||||
# Check for BACKUP_DIR and SITE_URL in INVENTREE_CONF_DATA and config
|
||||
check_config_value "INVENTREE_SITE_URL" "site_url" "site URL"
|
||||
check_config_value "INVENTREE_BACKUP_DIR" "backup_dir" "backup dir"
|
||||
|
||||
if [ "${ABORT}" = true ]; then
|
||||
echo "# POI08| Aborting - please set the missing values and run the update again"
|
||||
exit 1
|
||||
fi
|
||||
echo "# POI08| All checks passed - continuing with the update"
|
||||
fi
|
||||
}
|
||||
|
@ -3,15 +3,18 @@
|
||||
# packager.io postinstall script
|
||||
#
|
||||
|
||||
echo "# POI01| Running postinstall script - start - $(date)"
|
||||
exec > >(tee ${APP_HOME}/log/setup_$(date +"%F_%H_%M_%S").log) 2>&1
|
||||
|
||||
PATH=${APP_HOME}/env/bin:${APP_HOME}/:/sbin:/bin:/usr/sbin:/usr/bin:
|
||||
|
||||
# import functions
|
||||
echo "# POI01| Importing functions"
|
||||
. ${APP_HOME}/contrib/packager.io/functions.sh
|
||||
echo "# POI01| Functions imported"
|
||||
|
||||
# Envs that should be passed to setup commands
|
||||
export SETUP_ENVS=PATH,APP_HOME,INVENTREE_MEDIA_ROOT,INVENTREE_STATIC_ROOT,INVENTREE_BACKUP_DIR,INVENTREE_PLUGINS_ENABLED,INVENTREE_PLUGIN_FILE,INVENTREE_CONFIG_FILE,INVENTREE_SECRET_KEY_FILE,INVENTREE_DB_ENGINE,INVENTREE_DB_NAME,INVENTREE_DB_USER,INVENTREE_DB_PASSWORD,INVENTREE_DB_HOST,INVENTREE_DB_PORT,INVENTREE_ADMIN_USER,INVENTREE_ADMIN_EMAIL,INVENTREE_ADMIN_PASSWORD,SETUP_NGINX_FILE,SETUP_ADMIN_PASSWORD_FILE,SETUP_NO_CALLS,SETUP_DEBUG,SETUP_EXTRA_PIP,SETUP_PYTHON
|
||||
export SETUP_ENVS=PATH,APP_HOME,INVENTREE_MEDIA_ROOT,INVENTREE_STATIC_ROOT,INVENTREE_BACKUP_DIR,INVENTREE_SITE_URL,INVENTREE_PLUGINS_ENABLED,INVENTREE_PLUGIN_FILE,INVENTREE_CONFIG_FILE,INVENTREE_SECRET_KEY_FILE,INVENTREE_DB_ENGINE,INVENTREE_DB_NAME,INVENTREE_DB_USER,INVENTREE_DB_PASSWORD,INVENTREE_DB_HOST,INVENTREE_DB_PORT,INVENTREE_ADMIN_USER,INVENTREE_ADMIN_EMAIL,INVENTREE_ADMIN_PASSWORD,SETUP_NGINX_FILE,SETUP_ADMIN_PASSWORD_FILE,SETUP_NO_CALLS,SETUP_DEBUG,SETUP_EXTRA_PIP,SETUP_PYTHON,SETUP_ADMIN_NOCREATION
|
||||
|
||||
# Get the envs
|
||||
detect_local_env
|
||||
@ -24,6 +27,7 @@ export SETUP_NGINX_FILE=${SETUP_NGINX_FILE:-/etc/nginx/sites-enabled/inventree.c
|
||||
export SETUP_ADMIN_PASSWORD_FILE=${CONF_DIR}/admin_password.txt
|
||||
export SETUP_NO_CALLS=${SETUP_NO_CALLS:-false}
|
||||
export SETUP_PYTHON=${SETUP_PYTHON:-python3.9}
|
||||
export SETUP_ADMIN_NOCREATION=${SETUP_ADMIN_NOCREATION:-false}
|
||||
# SETUP_DEBUG can be set to get debug info
|
||||
# SETUP_EXTRA_PIP can be set to install extra pip packages
|
||||
# SETUP_PYTHON can be set to use a different python version
|
||||
@ -35,6 +39,14 @@ detect_initcmd
|
||||
detect_ip
|
||||
detect_python
|
||||
|
||||
# Check if we are updating and need to alert
|
||||
echo "# POI08| Checking if update checks are needed"
|
||||
if [ -z "$2" ]; then
|
||||
echo "# POI08| Normal install - no need for checks"
|
||||
else
|
||||
update_checks $2
|
||||
fi
|
||||
|
||||
# create processes
|
||||
create_initscripts
|
||||
create_admin
|
||||
@ -51,3 +63,4 @@ start_inventree
|
||||
|
||||
# show info
|
||||
final_message
|
||||
echo "# POI17| Running postinstall script - done - $(date)"
|
||||
|
23
contrib/packager.io/preinstall.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# packager.io preinstall/preremove script
|
||||
#
|
||||
echo "# PRI01| Running preinstall script - start - $(date)"
|
||||
PATH=${APP_HOME}/env/bin:${APP_HOME}/:/sbin:/bin:/usr/sbin:/usr/bin:
|
||||
|
||||
# Envs that should be passed to setup commands
|
||||
export SETUP_ENVS=PATH,APP_HOME,INVENTREE_MEDIA_ROOT,INVENTREE_STATIC_ROOT,INVENTREE_BACKUP_DIR,INVENTREE_PLUGINS_ENABLED,INVENTREE_PLUGIN_FILE,INVENTREE_CONFIG_FILE,INVENTREE_SECRET_KEY_FILE,INVENTREE_DB_ENGINE,INVENTREE_DB_NAME,INVENTREE_DB_USER,INVENTREE_DB_PASSWORD,INVENTREE_DB_HOST,INVENTREE_DB_PORT,INVENTREE_ADMIN_USER,INVENTREE_ADMIN_EMAIL,INVENTREE_ADMIN_PASSWORD,SETUP_NGINX_FILE,SETUP_ADMIN_PASSWORD_FILE,SETUP_NO_CALLS,SETUP_DEBUG,SETUP_EXTRA_PIP,SETUP_PYTHON
|
||||
|
||||
if test -f "${APP_HOME}/env/bin/pip"; then
|
||||
# Check if clear-generated is available
|
||||
if sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && invoke int.clear-generated --help" > /dev/null 2>&1; then
|
||||
echo "# PRI02| Clearing precompiled files"
|
||||
sudo -u ${APP_USER} --preserve-env=$SETUP_ENVS bash -c "cd ${APP_HOME} && invoke int.clear-generated"
|
||||
else
|
||||
echo "# PRI02| Clearing precompiled files - skipping"
|
||||
fi
|
||||
else
|
||||
echo "# PRI02| No python environment found - skipping"
|
||||
fi
|
||||
|
||||
echo "# PRI03| Running preinstall script - done - $(date)"
|
@ -1,8 +1,14 @@
|
||||
# Configuration file for Crowdin project integration
|
||||
# See: https://crowdin.com/project/inventree
|
||||
|
||||
"commit_message": "Fix: New translations %original_file_name% from Crowdin"
|
||||
"append_commit_message": false
|
||||
"preserve_hierarchy": true
|
||||
|
||||
files:
|
||||
- source: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po
|
||||
dest: /%original_path%/%original_file_name%
|
||||
translation: /src/backend/InvenTree/locale/%two_letters_code%/LC_MESSAGES/%original_file_name%
|
||||
- source: /src/frontend/src/locales/en/messages.po
|
||||
dest: /%original_path%/%original_file_name%
|
||||
translation: /src/frontend/src/locales/%two_letters_code%/%original_file_name%
|
||||
|
@ -10,7 +10,7 @@ tld = os.path.abspath(os.path.join(here, '..'))
|
||||
|
||||
config_file = os.path.join(tld, 'mkdocs.yml')
|
||||
|
||||
with open(config_file, 'r') as f:
|
||||
with open(config_file, encoding='utf-8') as f:
|
||||
data = yaml.load(f, yaml.BaseLoader)
|
||||
|
||||
assert data['strict'] == 'true'
|
||||
|
@ -22,10 +22,10 @@ The API is self-documenting, and the documentation is provided alongside any Inv
|
||||
|
||||
### Generating Schema File
|
||||
|
||||
If you want to generate the API schema file yourself (for example to use with an external client, use the `invoke schema` command. Run with the `-help` command to see available options.
|
||||
If you want to generate the API schema file yourself (for example to use with an external client, use the `invoke dev.schema` command. Run with the `-help` command to see available options.
|
||||
|
||||
```
|
||||
invoke schema -help
|
||||
invoke dev.schema -help
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
BIN
docs/docs/assets/images/build/allocated_stock_table.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
docs/docs/assets/images/build/build_panel_allocated_stock.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
docs/docs/assets/images/build/build_panel_details.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
docs/docs/assets/images/build/build_panel_line_items.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
docs/docs/assets/images/build/build_panel_test_results.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
docs/docs/assets/images/build/build_panel_test_statistics.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
docs/docs/assets/images/part/part_create_revision.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
docs/docs/assets/images/part/part_revision_b.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/docs/assets/images/part/part_revision_select.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
docs/docs/assets/images/part/part_revision_settings.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 84 KiB |
BIN
docs/docs/assets/images/stock/stock_external_icon.png
Normal file
After Width: | Height: | Size: 65 KiB |
@ -56,3 +56,11 @@ If no match is found for the scanned barcode, the following error message is dis
|
||||
## App Integration
|
||||
|
||||
Barcode scanning is a key feature of the [companion mobile app](../app/barcode.md).
|
||||
|
||||
## Barcode History
|
||||
|
||||
If enabled, InvenTree can retain logs of the most recent barcode scans. This can be very useful for debugging or auditing purpopes.
|
||||
|
||||
Refer to the [barcode settings](../settings/global.md#barcodes) to enable barcode history logging.
|
||||
|
||||
The barcode history can be viewed via the admin panel in the web interface.
|
||||
|
@ -4,7 +4,11 @@ title: Internal Barcodes
|
||||
|
||||
## Internal Barcodes
|
||||
|
||||
InvenTree defines an internal format for generating barcodes for various items. This format uses a simple JSON-style string to uniquely identify an item in the database.
|
||||
InvenTree ships with two integrated internal formats for generating barcodes for various items which are available through the built-in InvenTree Barcode plugin. The used format can be selected through the plugin settings of the InvenTree Barcode plugin.
|
||||
|
||||
### 1. JSON-based QR Codes
|
||||
|
||||
This format uses a simple JSON-style string to uniquely identify an item in the database.
|
||||
|
||||
Some simple examples of this format are shown below:
|
||||
|
||||
@ -12,10 +16,49 @@ Some simple examples of this format are shown below:
|
||||
| --- | --- |
|
||||
| Part | `{% raw %}{"part": 10}{% endraw %}` |
|
||||
| Stock Item | `{% raw %}{"stockitem": 123}{% endraw %}` |
|
||||
| Stock Location | `{% raw %}{"stocklocation": 1}{% endraw %}` |
|
||||
| Supplier Part | `{% raw %}{"supplierpart": 99}{% endraw %}` |
|
||||
|
||||
The numerical ID value used is the *Primary Key* (PK) of the particular object in the database.
|
||||
|
||||
#### Downsides
|
||||
|
||||
1. The JSON format includes binary only characters (`{% raw %}{{% endraw %}` and `{% raw %}"{% endraw %}`) which requires unnecessary use of the binary QR code encoding which means fewer amount of chars can be encoded with the same version of QR code.
|
||||
2. The model name key has not a fixed length. Some model names are longer than others. E.g. a part QR code with the shortest possible id requires 11 chars, while a stock location QR code with the same id would already require 20 chars, which already requires QR code version 2 and quickly version 3.
|
||||
|
||||
!!! info "QR code versions"
|
||||
There are 40 different qr code versions from 1-40. They all can encode more data than the previous version, but require more "squares". E.g. a V1 QR codes has 21x21 "squares" while a V2 already has 25x25. For more information see [QR code comparison](https://www.qrcode.com/en/about/version.html).
|
||||
|
||||
For a more detailed size analysis of the JSON-based QR codes refer to [this issue](https://github.com/inventree/InvenTree/issues/6612).
|
||||
|
||||
### 2. Short alphanumeric QR Codes
|
||||
|
||||
While JSON-based QR Codes encode all necessary information, they come with the described downsides. This new, short, alphanumeric only format is build to improve those downsides. The basic format uses an alphanumeric string: `INV-??x`
|
||||
|
||||
- `INV-` is a constant prefix. This is configurable in the InvenTree Barcode plugins settings per instance to support environments that use multiple instances.
|
||||
- `??` is a two character alphanumeric (`0-9A-Z $%*+-./:` (45 chars)) code, individual to each model.
|
||||
- `x` the actual pk of the model.
|
||||
|
||||
Now with an overhead of 6 chars for every model, this format supports the following amount of model instances using the described QR code modes:
|
||||
|
||||
| QR code mode | Alphanumeric mode | Mixed mode |
|
||||
| --- | --- | --- |
|
||||
| v1 M ECL (15%) | `10**14` items (~3.170 items per sec for 1000 years) | `10**20` items (~3.170.979.198 items per sec for 1000 years) |
|
||||
| v1 Q ECL (25%) | `10**10` items (~0.317 items per sec for 1000 years) | `10**13` items (~317 items per sec for 1000 years) |
|
||||
| v1 H ECL (30%) | `10**4` items (~100 items per day for 100 days) | `10**3` items (~100 items per day for 10 days (*even worse*)) |
|
||||
|
||||
!!! info "QR code mixed mode"
|
||||
Normally the QR code data is encoded only in one format (binary, alphanumeric, numeric). But the data can also be split into multiple chunks using different formats. This is especially useful with long model ids, because the first 6 chars can be encoded using the alphanumeric mode and the id using the more efficient numeric mode. Mixed mode is used by default, because the `qrcode` template tag uses a default value for optimize of 1.
|
||||
|
||||
Some simple examples of this format are shown below:
|
||||
|
||||
| Model Type | Example Barcode |
|
||||
| --- | --- |
|
||||
| Part | `INV-PA10` |
|
||||
| Stock Item | `INV-SI123` |
|
||||
| Stock Location | `INV-SL1` |
|
||||
| Supplier Part | `INV-SP99` |
|
||||
|
||||
## Report Integration
|
||||
|
||||
This barcode format can be used to generate 1D or 2D barcodes (e.g. for [labels and reports](../report/barcodes.md))
|
||||
|
104
docs/docs/build/build.md
vendored
@ -26,14 +26,6 @@ To navigate to the Build Order display, select *Build* from the main navigation
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
#### Tree View
|
||||
|
||||
*Tree View* also provides a tabulated view of Build Orders. Orders are displayed in a hierarchical manner, showing any parent / child relationships between different build orders.
|
||||
|
||||
{% with id="build_tree", url="build/build_tree.png", description="Build Tree" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
#### Calendar View
|
||||
|
||||
*Calendar View* shows a calendar display with upcoming build orders, based on the various dates specified for each build.
|
||||
@ -74,10 +66,11 @@ Each *Build Order* has an associated *Status* flag, which indicates the state of
|
||||
|
||||
| Status | Description |
|
||||
| ----------- | ----------- |
|
||||
| `Pending` | Build has been created and build is ready for subpart allocation |
|
||||
| `Production` | One or more build outputs have been created for this build |
|
||||
| `Cancelled` | Build has been cancelled |
|
||||
| `Completed` | Build has been completed |
|
||||
| `Pending` | Build order has been created, but is not yet in production |
|
||||
| `Production` | Build order is currently in production |
|
||||
| `On Hold` | Build order has been placed on hold, but is still active |
|
||||
| `Cancelled` | Build order has been cancelled |
|
||||
| `Completed` | Build order has been completed |
|
||||
|
||||
**Source Code**
|
||||
|
||||
@ -111,41 +104,29 @@ For further information, refer to the [stock allocation documentation](./allocat
|
||||
|
||||
## Build Order Display
|
||||
|
||||
The detail view for a single build order provides multiple display tabs, as follows:
|
||||
The detail view for a single build order provides multiple display panels, as follows:
|
||||
|
||||
### Build Details
|
||||
|
||||
The *Build Details* tab provides an overview of the Build Order:
|
||||
The *Build Details* panel provides an overview of the Build Order:
|
||||
|
||||
{% with id="build_details", url="build/build_details.png", description="Details tab" %}
|
||||
{% with id="build_details", url="build/build_panel_details.png", description="Build details panel" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
### Allocate Stock
|
||||
### Line Items
|
||||
|
||||
The *Allocate Stock* tab provides an interface to allocate required stock (as specified by the BOM) to the build:
|
||||
The *Line Items* panel displays all the line items (as defined by the [bill of materials](./bom.md)) required to complete the build order.
|
||||
|
||||
{% with id="build_allocate", url="build/build_allocate.png", description="Allocation tab" %}
|
||||
{% with id="build_allocate", url="build/build_panel_line_items.png", description="Build line items panel" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
The allocation table (as shown above) shows the stock allocation progress for this build. In the example above, there are two BOM lines, which have been partially allocated.
|
||||
The allocation table (as shown above) provides an interface to allocate required stock, and also shows the stock allocation progress for each line item in the build.
|
||||
|
||||
!!! info "Completed Builds"
|
||||
The *Allocate Stock* tab is not available if the build has been completed!
|
||||
### Incomplete Outputs
|
||||
|
||||
### Consumed Stock
|
||||
|
||||
The *Consumed Stock* tab displays all stock items which have been *consumed* by this build order. These stock items remain in the database after the build order has been completed, but are no longer available for use.
|
||||
|
||||
- [Tracked stock items](./allocate.md#tracked-stock) are consumed by specific build outputs
|
||||
- [Untracked stock items](./allocate.md#untracked-stock) are consumed by the build order
|
||||
|
||||
### Build Outputs
|
||||
|
||||
The *Build Outputs* tab shows the [build outputs](./output.md) (created stock items) associated with this build.
|
||||
|
||||
As shown below, there are separate panels for *incomplete* and *completed* build outputs.
|
||||
The *Incomplete Outputs* panel shows the list of in-progress [build outputs](./output.md) (created stock items) associated with this build.
|
||||
|
||||
{% with id="build_outputs", url="build/build_outputs.png", description="Outputs tab" %}
|
||||
{% include "img.html" %}
|
||||
@ -158,11 +139,48 @@ As shown below, there are separate panels for *incomplete* and *completed* build
|
||||
- Outputs which are "in progress" can be completed or cancelled
|
||||
- Completed outputs (which are simply *stock items*) can be viewed in the stock table at the bottom of the screen
|
||||
|
||||
### Completed Outputs
|
||||
|
||||
This panel displays all the completed build outputs (stock items) which have been created by this build order:
|
||||
|
||||
### Allocated Stock
|
||||
|
||||
The *Allocated Stock* tab displays all stock items which have been *allocated* to this build order. These stock items are reserved for this build, and will be consumed when the build is completed:
|
||||
|
||||
{% with id="allocated_stock_table", url="build/allocated_stock_table.png", description="Allocated Stock Table" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
### Consumed Stock
|
||||
|
||||
The *Consumed Stock* tab displays all stock items which have been *consumed* by this build order. These stock items remain in the database after the build order has been completed, but are no longer available for use.
|
||||
|
||||
- [Tracked stock items](./allocate.md#tracked-stock) are consumed by specific build outputs
|
||||
- [Untracked stock items](./allocate.md#untracked-stock) are consumed by the build order
|
||||
|
||||
### Child Builds
|
||||
|
||||
If there exist any build orders which are *children* of the selected build order, they are displayed in the *Child Builds* tab:
|
||||
|
||||
{% with id="build_childs", url="build/build_childs.png", description="Child builds tab" %}
|
||||
{% with id="build_childs", url="build/build_childs.png", description="Child builds panel" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
### Test Results
|
||||
|
||||
For *trackable* parts, test results can be recorded against each build output. These results are displayed in the *Test Results* panel:
|
||||
|
||||
{% with id="build_test_results", url="build/build_panel_test_results.png", description="Test Results panel" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
This table provides a summary of the test results for each build output, and allows test results to be quickly added for each build output.
|
||||
|
||||
### Test Statistics
|
||||
|
||||
For *trackable* parts, this panel displays a summary of the test results for all build outputs:
|
||||
|
||||
{% with id="build_test_stats", url="build/build_panel_test_statistics.png", description="Test Statistics panel" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
@ -203,6 +221,10 @@ To create a build order for your part, you have two options:
|
||||
|
||||
Fill-out the form as required, then click the "Submit" button to create the build.
|
||||
|
||||
### Create Child Builds
|
||||
|
||||
When creating a new build order, you have the option to automatically generate build orders for any subassembly parts. This can be useful to create a complete tree of build orders for a complex assembly. *However*, it must be noted that any build orders created for subassemblies will use the default BOM quantity for that part. Any child build orders created in this manner must be manually reviewed, to ensure that the correct quantity is being built as per your production requirements.
|
||||
|
||||
## Complete Build Order
|
||||
|
||||
To complete a build, click on <span class='fas fa-tools'></span> icon on the build detail page, the `Complete Build` form will be displayed.
|
||||
@ -246,3 +268,17 @@ Build orders may (optionally) have a target complete date specified. If this dat
|
||||
|
||||
- Builds can be filtered by overdue status in the build list
|
||||
- Overdue builds will be displayed on the home page
|
||||
|
||||
## Build Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for adjusting the behavior of build orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("BUILDORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_ACTIVE_PART") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_LOCKED_PART") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_VALID_BOM") }}
|
||||
{{ globalsetting("BUILDORDER_REQUIRE_CLOSED_CHILDS") }}
|
||||
{{ globalsetting("PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS") }}
|
||||
|
15
docs/docs/concepts/custom_states.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Custom States
|
||||
---
|
||||
|
||||
## Custom States
|
||||
|
||||
Several models within InvenTree support the use of custom states. The custom states are display only - the business logic is not affected by the state.
|
||||
|
||||
States can be added in the Admin Center under the "Custom States" section. Each state has a name, label and a color that are used to display the state in the user interface. Changes to these settings will only be reflected in the user interface after a full reload of the interface.
|
||||
|
||||
States need to be assigned to a model, state (for example status on a StockItem) and a logical key - that will be used for business logic. These 3 values combined need to be unique throughout the system.
|
||||
|
||||
Custom states can be used in the following models:
|
||||
- StockItem
|
||||
- Orders (PurchaseOrder, SalesOrder, ReturnOrder, ReturnOrderLine)
|
@ -4,4 +4,55 @@ title: InvenTree Demo
|
||||
|
||||
## InvenTree Demo
|
||||
|
||||
This page has moved to [https://inventree.org/demo.html](https://inventree.org/demo.html)
|
||||
If you are interested in trying out InvenTree, you can access the InvenTree demo instance at [https://demo.inventree.org](https://demo.inventree.org).
|
||||
|
||||
This page is populated with a sample dataset, which is reset every 24 hours.
|
||||
|
||||
You can read more about the InvenTree demo here: [https://inventree.org/demo.html](https://inventree.org/demo.html)
|
||||
|
||||
### User Accounts
|
||||
|
||||
The demo instance has a number of user accounts which you can use to explore the system:
|
||||
|
||||
| Username | Password | Staff Access | Enabled | Description |
|
||||
| -------- | -------- | ------------ | ------- | ----------- |
|
||||
| noaccess | youshallnotpass | No | Yes | Can login, but has no permissions |
|
||||
| allaccess | nolimits | No | Yes | View / create / edit all pages and items |
|
||||
| reader | readonly | No | Yes | Can view all pages but cannot create, edit or delete database records |
|
||||
| engineer | partsonly | No | Yes | Can manage parts, view stock, but no access to purchase orders or sales orders |
|
||||
| steven | wizardstaff | Yes | Yes | Staff account, can access some admin sections |
|
||||
| ian | inactive | No | No | Inactive account, cannot log in |
|
||||
| susan | inactive | No | No | Inactive account, cannot log in |
|
||||
| admin | inventree | Yes | Yes | Superuser account, can access all parts of the system |
|
||||
|
||||
### Dataset
|
||||
|
||||
The demo instance is populated with a sample dataset, which is reset every 24 hours.
|
||||
|
||||
The source data used in the demo instance can be found on our [GitHub page](https://github.com/inventree/demo-dataset).
|
||||
|
||||
### Local Setup
|
||||
|
||||
If you wish to install the demo dataset locally (for initial testing), you can run the following command (via [invoke](./start/invoke.md)):
|
||||
|
||||
```bash
|
||||
invoke dev.setup-test -i
|
||||
```
|
||||
|
||||
*(Note: The command above may be slightly different if you are running in docker.)*
|
||||
|
||||
This will install the demo dataset into your local InvenTree instance.
|
||||
|
||||
!!! warning "Warning"
|
||||
This command will **delete all existing data** in your InvenTree instance! It is not intended to be used on a production system, or loaded into an existing dataset.
|
||||
|
||||
### Clear Data
|
||||
|
||||
To clear demo data from your instance, and start afresh with a clean database, you can run the following command (via [invoke](./start/invoke.md)):
|
||||
|
||||
```bash
|
||||
invoke dev.delete-data
|
||||
```
|
||||
|
||||
!!! warning "Warning"
|
||||
This command will **delete all existing data** in your InvenTree instance, including any data that you have added yourself.
|
||||
|
@ -23,7 +23,7 @@ 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 --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 run --rm inventree-dev-server invoke dev.setup-test --dev
|
||||
docker compose --project-directory . -f contrib/container/dev-docker-compose.yml up -d
|
||||
```
|
||||
|
||||
@ -34,25 +34,28 @@ A "bare metal" development setup can be installed as follows:
|
||||
```bash
|
||||
git clone https://github.com/inventree/InvenTree.git && cd InvenTree
|
||||
python3 -m venv env && source env/bin/activate
|
||||
pip install invoke && invoke
|
||||
pip install invoke && invoke setup-dev --tests
|
||||
pip install django invoke && invoke
|
||||
invoke dev.setup-dev --tests
|
||||
```
|
||||
|
||||
Read the [InvenTree setup documentation](../start/intro.md) for a complete installation reference guide.
|
||||
|
||||
!!! note "Required Packages"
|
||||
Depending on your system, you may need to install additional software packages as required.
|
||||
|
||||
### Setup Devtools
|
||||
|
||||
Run the following command to set up all toolsets for development.
|
||||
|
||||
```bash
|
||||
invoke setup-dev
|
||||
invoke dev.setup-dev
|
||||
```
|
||||
|
||||
*We recommend you run this command before starting to contribute. This will install and set up `pre-commit` to run some checks before each commit and help reduce errors.*
|
||||
|
||||
## Branches and Versioning
|
||||
|
||||
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.
|
||||
InvenTree roughly follow the [GitLab flow](https://about.gitlab.com/topics/version-control/what-are-gitlab-flow-best-practices/) branching style, to allow simple management of multiple tagged releases, short-lived branches, and development on the main branch.
|
||||
|
||||
There are nominally 5 active branches:
|
||||
- `master` - The main development branch
|
||||
@ -125,7 +128,7 @@ The core software modules are targeting the following versions:
|
||||
| Python | {{ config.extra.min_python_version }} | Minimum required version |
|
||||
| Invoke | {{ config.extra.min_invoke_version }} | Minimum required version |
|
||||
| Django | {{ config.extra.django_version }} | Pinned version |
|
||||
| Node | 18 | Only needed for frontend development |
|
||||
| Node | 20 | Only needed for frontend development |
|
||||
|
||||
Any other software dependencies are handled by the project package config.
|
||||
|
||||
@ -169,21 +172,35 @@ The various github actions can be found in the `./github/workflows` directory
|
||||
### Run tests locally
|
||||
|
||||
To run test locally, use:
|
||||
|
||||
```
|
||||
invoke test
|
||||
invoke dev.test
|
||||
```
|
||||
|
||||
To run only partial tests, for example for a module use:
|
||||
```
|
||||
invoke test --runtest order
|
||||
invoke dev.test --runtest order
|
||||
```
|
||||
|
||||
To see all the available options:
|
||||
|
||||
```
|
||||
invoke test --help
|
||||
invoke dev.test --help
|
||||
```
|
||||
|
||||
#### Database Permission Issues
|
||||
|
||||
For local testing django creates a test database and removes it after testing. If you encounter permission issues while running unit test, ensure that your database user has permission to create new databases.
|
||||
|
||||
For example, in PostgreSQL, run:
|
||||
|
||||
```
|
||||
alter user myuser createdb;
|
||||
```
|
||||
|
||||
!!! info "Devcontainer"
|
||||
The default database container which is provided in the devcontainer is already setup with the required permissions
|
||||
|
||||
## Code Style
|
||||
|
||||
Code style is automatically checked as part of the project's CI pipeline on GitHub. This means that any pull requests which do not conform to the style guidelines will fail CI checks.
|
||||
|
@ -52,7 +52,7 @@ Tasks can help you executing scripts. You can run them by open the command panel
|
||||
|
||||
#### Setup demo dataset
|
||||
|
||||
If you need some demo test-data, run the `setup-test` task. This will import an `admin` user with the password `inventree`. For more info on what this dataset contains see [inventree/demo-dataset](https://github.com/inventree/demo-dataset).
|
||||
If you need some demo test-data, run the `setup-test` task. This will import an `admin` user with the password `inventree`. For more info on what this dataset contains see [inventree/demo-dataset](../demo.md).
|
||||
|
||||
#### Setup a superuser
|
||||
|
||||
@ -67,7 +67,7 @@ If you need to process your queue with background workers, run the `worker` task
|
||||
You can either only run InvenTree or use the integrated debugger for debugging. Goto the `Run and debug` side panel make sure `InvenTree Server` is selected. Click on the play button on the left.
|
||||
|
||||
!!! tip "Debug with 3rd party"
|
||||
Sometimes you need to debug also some 3rd party packages. Just select `InvenTree Servre - 3rd party`
|
||||
Sometimes you need to debug also some 3rd party packages. Just select `InvenTree Server - 3rd party`
|
||||
|
||||
You can now set breakpoints and vscode will automatically pause execution if that point is hit. You can see all variables available in that context and evaluate some code with the debugger console at the bottom. Use the play or step buttons to continue execution.
|
||||
|
||||
|
@ -1,37 +1,48 @@
|
||||
---
|
||||
title: Platform UI / React
|
||||
title: React Frontend Development
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
The new React-based UI will not be available by default. In order to set your development environment up to view the frontend, follow this guide.
|
||||
The new UI requires a separate frontend server to run to serve data for the new Frontend.
|
||||
The following documentation details how to setup and run a development installation of the InvenTree frontend user interface.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
To run the frontend development server, you will need to have the following installed:
|
||||
|
||||
- Node.js
|
||||
- Yarn
|
||||
|
||||
!!! note "Devcontainer"
|
||||
The [devcontainer](./devcontainer.md) setup already includes all prerequisite packages, and is ready to run the frontend server.
|
||||
|
||||
### Install
|
||||
|
||||
The React frontend requires its own packages that aren't installed via the usual invoke tasks.
|
||||
The React frontend requires its own packages that aren't installed via the usual [invoke](../start/invoke.md) tasks.
|
||||
|
||||
#### Docker
|
||||
|
||||
Run the following command:
|
||||
`docker compose run inventree-dev-server invoke frontend-compile`
|
||||
`docker compose run inventree-dev-server invoke int.frontend-compile`
|
||||
This will install the required packages for running the React frontend on your InvenTree dev server.
|
||||
|
||||
#### Devcontainer
|
||||
|
||||
!!! warning "This guide assumes you already have a running devcontainer"
|
||||
|
||||
!!! info "All these steps are performed within Visual Studio Code"
|
||||
|
||||
Open a new terminal from the top menu by clicking `Terminal > New Terminal`
|
||||
Make sure this terminal is running within the virtual env. The start of the last line should display `(venv)`
|
||||
|
||||
Run the command `invoke frontend-compile`. Wait for this to finish
|
||||
Run the command `invoke int.frontend-compile`. Wait for this to finish
|
||||
|
||||
### Running
|
||||
|
||||
After finishing the install, you need to launch a frontend server to be able to view the new UI.
|
||||
|
||||
Using the previously described ways of running commands, execute the following:
|
||||
`invoke frontend-dev` in your environment
|
||||
`invoke dev.frontend-server` in your environment
|
||||
This command does not run as a background daemon, and will occupy the window it's ran in.
|
||||
|
||||
### Accessing
|
||||
@ -40,7 +51,7 @@ When the frontend server is running, it will be available on port 5173.
|
||||
i.e: https://localhost:5173/
|
||||
|
||||
!!! note "Backend Server"
|
||||
The InvenTree backend server must also be running, for the frontend interface to have something to connect to! To launch a backend server, use the `invoke server` command.
|
||||
The InvenTree backend server must also be running, for the frontend interface to have something to connect to! To launch a backend server, use the `invoke dev.server` command.
|
||||
|
||||
### Debugging
|
||||
|
||||
@ -77,3 +88,49 @@ When running the frontend development server, some features may not work entirel
|
||||
#### SSO Login
|
||||
|
||||
When logging into the frontend dev server via SSO, the redirect URL may not redirect correctly.
|
||||
|
||||
## Testing
|
||||
|
||||
The frontend codebase it tested using [Playwright](https://playwright.dev/). There are a large number of tests that cover the frontend codebase, which are run automatically as part of the CI pipeline.
|
||||
|
||||
### Install Playwright
|
||||
|
||||
To install the required packages to run the tests, you can use the following command:
|
||||
|
||||
```bash
|
||||
cd src/frontend
|
||||
npx playwright install
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
To run the tests locally, in an interactive editor, you can use the following command:
|
||||
|
||||
```bash
|
||||
cd src/frontend
|
||||
npx playwright test --ui
|
||||
```
|
||||
|
||||
This will first launch the backend server (at `http://localhost:8000`), and then run the tests against the frontend server (at `http://localhost:5173`). An interactive browser window will open, and you can run the tests individually or as a group.
|
||||
|
||||
### Viewing Reports
|
||||
|
||||
The playwright tests are run automatically as part of the project's CI pipeline, and the results are stored as a downloadable report. The report file can be "replayed" using playwright, to view the results of the test run, as well as closely inspect any failed tests.
|
||||
|
||||
To view the report, you can use the following command, after downloading the report and extracting from the zipped file:
|
||||
|
||||
```bash
|
||||
npx playwright show-report path/to/report
|
||||
```
|
||||
|
||||
### No Tests Found
|
||||
|
||||
If there is any problem in the testing launch sequence, the playwright UI will display the message "No Tests". In this case, an error has occurred, likely launching the InvenTree server process (which runs in the background).
|
||||
|
||||
To debug this situation, and determine what error needs to be resolved, run the following command:
|
||||
|
||||
```bash
|
||||
npx playwright test --debug
|
||||
```
|
||||
|
||||
This will print out any errors to the console, allowing you to resolve issues before continuing. In all likelihood, your InvenTree installation needs to be updated, and simply running `invoke update` will allow you to continue.
|
||||
|
@ -6,6 +6,9 @@ title: Machines
|
||||
|
||||
InvenTree has a builtin machine registry. There are different machine types available where each type can have different drivers. Drivers and even custom machine types can be provided by plugins.
|
||||
|
||||
!!! info "Requires Redis"
|
||||
If the machines features is used in production setup using workers, a shared [redis cache](../../start/processes.md#cache-server) is required to function properly.
|
||||
|
||||
### Registry
|
||||
|
||||
The machine registry is the main component which gets initialized on server start and manages all configured machines.
|
||||
@ -21,6 +24,13 @@ The machine registry initialization process can be divided into three stages:
|
||||
2. The driver.init_driver function is called for each used driver
|
||||
3. The machine.initialize function is called for each machine, which calls the driver.init_machine function for each machine, then the machine.initialized state is set to true
|
||||
|
||||
#### Production setup (with a worker)
|
||||
|
||||
If a worker is connected, there exist multiple instances of the machine registry (one in each worker thread and one in the main thread) due to the nature of how python handles state in different processes. Therefore the machine instances and drivers are instantiated multiple times (The `__init__` method is called multiple times). But the init functions and update hooks (e.g. `init_machine`) are only called once from the main process.
|
||||
|
||||
The registry, driver and machine state (e.g. machine status codes, errors, ...) is stored in the cache. Therefore a shared redis cache is needed. (The local in-memory cache which is used by default is not capable to cache across multiple processes)
|
||||
|
||||
|
||||
### Machine types
|
||||
|
||||
Each machine type can provide a different type of connection functionality between inventree and a physical machine. These machine types are already built into InvenTree.
|
||||
@ -37,6 +47,8 @@ If you want to create your own machine type, please also take a look at the alre
|
||||
|
||||
```py
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from generic.states import ColorEnum
|
||||
from plugin.machine import BaseDriver, BaseMachineType, MachineStatus
|
||||
|
||||
class ABCBaseDriver(BaseDriver):
|
||||
@ -62,9 +74,9 @@ class ABCMachine(BaseMachineType):
|
||||
base_driver = ABCBaseDriver
|
||||
|
||||
class ABCStatus(MachineStatus):
|
||||
CONNECTED = 100, _('Connected'), 'success'
|
||||
STANDBY = 101, _('Standby'), 'success'
|
||||
PRINTING = 110, _('Printing'), 'primary'
|
||||
CONNECTED = 100, _('Connected'), ColorEnum.success
|
||||
STANDBY = 101, _('Standby'), ColorEnum.success
|
||||
PRINTING = 110, _('Printing'), ColorEnum.primary
|
||||
|
||||
MACHINE_STATUS = ABCStatus
|
||||
default_machine_status = ABCStatus.DISCONNECTED
|
||||
@ -86,6 +98,7 @@ The machine type class gets instantiated for each machine on server startup and
|
||||
- update
|
||||
- restart
|
||||
- handle_error
|
||||
- clear_errors
|
||||
- get_setting
|
||||
- set_setting
|
||||
- check_setting
|
||||
|
@ -83,7 +83,7 @@ The configuration entries must be enabled via the [InvenTree admin interface](..
|
||||
!!! warning "Disabled by Default"
|
||||
Newly discovered plugins are disabled by default, and must be manually enabled (in the admin interface) by a user with staff privileges.
|
||||
|
||||
### Plugin Mixins
|
||||
## Plugin Mixins
|
||||
|
||||
Common use cases are covered by pre-supplied modules in the form of *mixins* (similar to how [Django]({% include "django.html" %}/topics/class-based-views/mixins/) does it). Each mixin enables the integration into a specific area of InvenTree. Sometimes it also enhances the plugin with helper functions to supply often used functions out-of-the-box.
|
||||
|
||||
@ -100,9 +100,16 @@ Supported mixin classes are:
|
||||
| [LabelPrintingMixin](./plugins/label.md) | Custom label printing support |
|
||||
| [LocateMixin](./plugins/locate.md) | Locate and identify stock items |
|
||||
| [NavigationMixin](./plugins/navigation.md) | Add custom pages to the web interface |
|
||||
| [PanelMixin](./plugins/panel.md) | Add custom panels to web views |
|
||||
| [ReportMixin](./plugins/report.md) | Add custom context data to reports |
|
||||
| [ScheduleMixin](./plugins/schedule.md) | Schedule periodic tasks |
|
||||
| [SettingsMixin](./plugins/settings.md) | Integrate user configurable settings |
|
||||
| [UrlsMixin](./plugins/urls.md) | Respond to custom URL endpoints |
|
||||
| [ValidationMixin](./plugins/validation.md) | Provide custom validation of database models |
|
||||
|
||||
## Static Files
|
||||
|
||||
If your plugin requires static files (e.g. CSS, JavaScript, images), these should be placed in the top level `static` directory within the distributed plugin package. These files will be automatically collected by InvenTree when the plugin is installed, and copied to an appropriate location.
|
||||
|
||||
These files will be available to the InvenTree web interface, and can be accessed via the URL `/static/plugins/<plugin_name>/<filename>`. Static files are served by the [proxy server](../start/processes.md#proxy-server).
|
||||
|
||||
For example, if the plugin is named `my_plugin`, and contains a file `CustomPanel.js`, it can be accessed via the URL `/static/plugins/my_plugin/CustomPanel.js`.
|
||||
|
@ -4,7 +4,7 @@ title: Barcode Mixin
|
||||
|
||||
## 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 supports decoding of arbitrary barcode data and generation of internal barcode formats 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/plugin/builtin/barcodes/inventree_barcode.py`) to decode the supplied barcode data.
|
||||
|
||||
@ -26,7 +26,7 @@ POST {
|
||||
|
||||
### 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.
|
||||
The InvenTree server includes a builtin barcode plugin which can generate and decode the QR codes. This plugin is enabled by default.
|
||||
|
||||
::: plugin.builtin.barcodes.inventree_barcode.InvenTreeInternalBarcodePlugin
|
||||
options:
|
||||
@ -39,14 +39,12 @@ The InvenTree server includes a builtin barcode plugin which can decode QR codes
|
||||
|
||||
### Example Plugin
|
||||
|
||||
Please find below a very simple example that is executed each time a barcode is scanned.
|
||||
Please find below a very simple example that is used to return a part if the barcode starts with `PART-`
|
||||
|
||||
```python
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from InvenTree.models import InvenTreeBarcodeMixin
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import BarcodeMixin
|
||||
from part.models import Part
|
||||
|
||||
class InvenTreeBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||
|
||||
@ -56,16 +54,39 @@ class InvenTreeBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||
VERSION = "0.0.1"
|
||||
AUTHOR = "Michael"
|
||||
|
||||
status = 0
|
||||
|
||||
def scan(self, barcode_data):
|
||||
if barcode_data.startswith("PART-"):
|
||||
try:
|
||||
pk = int(barcode_data.split("PART-")[1])
|
||||
instance = Part.objects.get(pk=pk)
|
||||
label = Part.barcode_model_type()
|
||||
|
||||
self.status = self.status+1
|
||||
print('Started barcode plugin', self.status)
|
||||
print(barcode_data)
|
||||
response = {}
|
||||
return response
|
||||
|
||||
return {label: instance.format_matched_response()}
|
||||
except Part.DoesNotExist:
|
||||
pass
|
||||
```
|
||||
|
||||
To try it just copy the file to src/InvenTree/plugins and restart the server. Open the scan barcode window and start to scan codes or type in text manually. Each time the timeout is hit the plugin will execute and printout the result. The timeout can be changed in `Settings->Barcode Support->Barcode Input Delay`.
|
||||
|
||||
### Custom Internal Format
|
||||
|
||||
To implement a custom internal barcode format, the `generate(...)` method from the Barcode Mixin needs to be overridden. Then the plugin can be selected at `System Settings > Barcodes > Barcode Generation Plugin`.
|
||||
|
||||
```python
|
||||
from InvenTree.models import InvenTreeBarcodeMixin
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import BarcodeMixin
|
||||
|
||||
class InvenTreeBarcodePlugin(BarcodeMixin, InvenTreePlugin):
|
||||
NAME = "MyInternalBarcode"
|
||||
TITLE = "My Internal Barcodes"
|
||||
DESCRIPTION = "support for custom internal barcodes"
|
||||
VERSION = "0.0.1"
|
||||
AUTHOR = "InvenTree contributors"
|
||||
|
||||
def generate(self, model_instance: InvenTreeBarcodeMixin):
|
||||
return f'{model_instance.barcode_model_type()}: {model_instance.pk}'
|
||||
```
|
||||
|
||||
!!! info "Scanning implementation required"
|
||||
The parsing of the custom format needs to be implemented too, so that the scanning of the generated QR codes resolves to the correct part.
|
||||
|
@ -15,6 +15,151 @@ When a certain (server-side) event occurs, the background worker passes the even
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
## Events
|
||||
|
||||
Events are passed through using a string identifier, e.g. `build.completed`
|
||||
|
||||
The arguments (and keyword arguments) passed to the receiving function depend entirely on the type of event.
|
||||
|
||||
!!! info "Read the Code"
|
||||
Implementing a response to a particular event requires a working knowledge of the InvenTree code base, especially related to that event being received. While the *available* events are documented here, to implement a response to a particular event you will need to read the code to understand what data is passed to the event handler.
|
||||
|
||||
## Generic Events
|
||||
|
||||
There are a number of *generic* events which are generated on certain database actions. Whenever a database object is created, updated, or deleted, a corresponding event is generated.
|
||||
|
||||
#### Object Created
|
||||
|
||||
When a new object is created in the database, an event is generated with the following event name: `<app>_<model>.created`, where `<model>` is the name of the model class (e.g. `part`, `stockitem`, etc).
|
||||
|
||||
The event is called with the following keywords arguments:
|
||||
|
||||
- `model`: The model class of the object that was created
|
||||
- `id`: The primary key of the object that was created
|
||||
|
||||
**Example:**
|
||||
|
||||
A new `Part` object is created with primary key `123`, resulting in the following event being generated:
|
||||
|
||||
```python
|
||||
trigger_event('part_part.created', model='part', id=123)
|
||||
```
|
||||
|
||||
### Object Updated
|
||||
|
||||
When an object is updated in the database, an event is generated with the following event name: `<app>_<model>.saved`, where `<model>` is the name of the model class (e.g. `part`, `stockitem`, etc).
|
||||
|
||||
The event is called with the following keywords arguments:
|
||||
|
||||
- `model`: The model class of the object that was updated
|
||||
- `id`: The primary key of the object that was updated
|
||||
|
||||
**Example:**
|
||||
|
||||
A `Part` object with primary key `123` is updated, resulting in the following event being generated:
|
||||
|
||||
```python
|
||||
trigger_event('part_part.saved', model='part', id=123)
|
||||
```
|
||||
|
||||
### Object Deleted
|
||||
|
||||
When an object is deleted from the database, an event is generated with the following event name: `<app>_<model>.deleted`, where `<model>` is the name of the model class (e.g. `part`, `stockitem`, etc).
|
||||
|
||||
The event is called with the following keywords arguments:
|
||||
|
||||
- `model`: The model class of the object that was deleted
|
||||
- `id`: The primary key of the object that was deleted (if available)
|
||||
|
||||
**Example:**
|
||||
|
||||
A `Part` object with primary key `123` is deleted, resulting in the following event being generated:
|
||||
|
||||
```python
|
||||
trigger_event('part_part.deleted', model='part', id=123)
|
||||
```
|
||||
|
||||
!!! warning "Object Deleted"
|
||||
Note that the event is triggered *after* the object has been deleted from the database, so the object itself is no longer available.
|
||||
|
||||
## Specific Events
|
||||
|
||||
In addition to the *generic* events listed above, there are a number of other events which are triggered by *specific* actions within the InvenTree codebase.
|
||||
|
||||
The available events are provided in the enumerations listed below. Note that while the names of the events are documented here, the exact arguments passed to the event handler will depend on the specific event being triggered.
|
||||
|
||||
### Build Events
|
||||
|
||||
::: build.events.BuildEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### Part Events
|
||||
|
||||
::: part.events.PartEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### Stock Events
|
||||
|
||||
::: stock.events.StockEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### Purchase Order Events
|
||||
|
||||
::: order.events.PurchaseOrderEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### Sales Order Events
|
||||
|
||||
::: order.events.SalesOrderEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### Return Order Events
|
||||
|
||||
::: order.events.ReturnOrderEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### Plugin Events
|
||||
|
||||
::: plugin.events.PluginEvents
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
## Samples
|
||||
|
||||
### Sample Plugin - All events
|
||||
|
||||
Implementing classes must at least provide a `process_event` function:
|
||||
@ -40,12 +185,3 @@ Overall this function can reduce the workload on the background workers signific
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
|
||||
## Events
|
||||
|
||||
Events are passed through using a string identifier, e.g. `build.completed`
|
||||
|
||||
The arguments (and keyword arguments) passed to the receiving function depend entirely on the type of event.
|
||||
|
||||
Implementing a response to a particular event requires a working knowledge of the InvenTree code base, especially related to that event being received.
|
||||
|
19
docs/docs/extend/plugins/icon.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Icon Pack Mixin
|
||||
---
|
||||
|
||||
## IconPackMixin
|
||||
|
||||
The IconPackMixin class provides basic functionality for letting plugins expose custom icon packs that are available in the InvenTree UI. This is especially useful to provide a custom crafted icon pack with icons for different location types, e.g. different sizes and styles of drawers, bags, ESD bags, ... which are not available in the standard tabler icons library.
|
||||
|
||||
### Sample Plugin
|
||||
|
||||
The following example demonstrates how to use the `IconPackMixin` class to add a custom icon pack:
|
||||
|
||||
::: plugin.samples.icons.icon_sample.SampleIconPlugin
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
@ -1,384 +0,0 @@
|
||||
---
|
||||
title: Panel Mixin
|
||||
---
|
||||
|
||||
## PanelMixin
|
||||
|
||||
The `PanelMixin` enables plugins to render custom content to "panels" on individual pages in the web interface.
|
||||
|
||||
Most pages in the web interface support multiple panels, which are selected via the sidebar menu on the left side of the screen:
|
||||
|
||||
{% with id="panels", url="plugin/panels.png", description="Display panels" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
|
||||
Each plugin which implements this mixin can return zero or more custom panels for a particular page. The plugin can decide (at runtime) which panels it wishes to render. This determination can be made based on the page routing, the item being viewed, the particular user, or other considerations.
|
||||
|
||||
### Panel Content
|
||||
|
||||
Panel content can be rendered by returning HTML directly, or by rendering from a template file.
|
||||
|
||||
Each plugin can register templates simply by providing a 'templates' directory in its root path.
|
||||
|
||||
The convention is that each 'templates' directory contains a subdirectory with the same name as the plugin (e.g. `templates/myplugin/my_template.html`)
|
||||
|
||||
In this case, the template can then be loaded (from any plugin!) by loading `myplugin/my_template.html`.
|
||||
|
||||
|
||||
### Javascript
|
||||
|
||||
Custom code can be provided which will run when the particular panel is first loaded (by selecting it from the side menu).
|
||||
|
||||
To add some javascript code, you can add a reference to a function that will be called when the panel is loaded with the 'javascript' key in the panel description:
|
||||
|
||||
```python
|
||||
{
|
||||
'title': "Updates",
|
||||
'description': "Latest updates for this part",
|
||||
'javascript': 'alert("You just loaded this panel!")',
|
||||
}
|
||||
```
|
||||
|
||||
Or to add a template file that will be rendered as javascript code, from the plugin template folder, with the 'javascript_template' key in the panel description:
|
||||
|
||||
```python
|
||||
{
|
||||
'title': "Updates",
|
||||
'description': "Latest updates for this part",
|
||||
'javascript_template': 'pluginTemplatePath/myJavascriptFile.js',
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### An example with button and parameter
|
||||
|
||||
Let's have a look at another example. We like to have a new panel that contains a button.
|
||||
Each time the button is clicked, a python function in our plugin shall be executed and
|
||||
a parameter shall be transferred . The result will look like that:
|
||||
|
||||
{% with id="mouser", url="plugin/mouser.png", description="Panel example with button" %} {% include "img.html" %} {% endwith %}
|
||||
|
||||
|
||||
First we need to write the plugin code, similar as in the example above.
|
||||
|
||||
```python
|
||||
from django.urls import re_path
|
||||
from django.http import HttpResponse
|
||||
|
||||
from order.views import PurchaseOrderDetail
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import PanelMixin, UrlsMixin
|
||||
|
||||
class MouserCartPanel(PanelMixin, InvenTreePlugin, UrlsMixin):
|
||||
|
||||
value=1
|
||||
|
||||
NAME = "MouserCart"
|
||||
SLUG = "mousercart"
|
||||
TITLE = "Create Mouser Cart"
|
||||
DESCRIPTION = "An example plugin demonstrating a button calling a python function."
|
||||
VERSION = "0.1"
|
||||
|
||||
def get_custom_panels(self, view, request):
|
||||
panels = []
|
||||
|
||||
# This panel will *only* display on the PurchaseOrder view,
|
||||
if isinstance(view, PurchaseOrderDetail):
|
||||
panels.append({
|
||||
'title': 'Mouser Actions',
|
||||
'icon': 'fa-user',
|
||||
'content_template': 'mouser/mouser.html',
|
||||
})
|
||||
return panels
|
||||
|
||||
def setup_urls(self):
|
||||
return [
|
||||
re_path(r'transfercart/(?P<pk>\d+)/', self.TransferCart, name='get-cart')
|
||||
]
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
def TransferCart(self,request,pk):
|
||||
|
||||
print('User,pk:',request.user,pk)
|
||||
self.value=self.value+1
|
||||
return HttpResponse(f'OK')
|
||||
```
|
||||
|
||||
The code is simple and really stripped down to the minimum. In the plugin class we first define the plugin metadata.
|
||||
Afterwards we define the custom panel. Here we use a html template to describe the content of the panel. We need to
|
||||
add the path here because the template resides in the subdirectory templates/mouser.
|
||||
Then we setup the url. This is important. The url connects the http request with the function to be executed.
|
||||
May be it is worth to leave a few more words on this because the string looks a bit like white noise.
|
||||
*transfercart* is the url which can be chosen freely. The ? is well known for parameters. In this case we
|
||||
get just one parameter, the orders primary key.* \d+* is a regular expression that limits the parameters
|
||||
to a digital number with n digits. Let's have a look on the names and how they belong together:
|
||||
|
||||
{% with id="plugin_dataflow", url="plugin/plugin_dataflow.png", description="Dataflow between Javascript and Python" %} {% include "img.html" %} {% endwith %}
|
||||
|
||||
Finally we define the function. This is a simple increment of a class value.
|
||||
|
||||
Now lets have a look at the template file mouser.html
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% load i18n %}
|
||||
|
||||
<script>
|
||||
async function JGetCart(){
|
||||
response = await fetch( '{% url "plugin:mousercart:get-cart" order.pk %}');
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
|
||||
<button type='button' class='btn btn-info' onclick="JGetCart()" title='{% trans "Get Mouser shopping Cart" %}'>
|
||||
<span class='fas fa-redo-alt'></span> {% trans "Get Cart" %}
|
||||
</button>
|
||||
|
||||
<br>
|
||||
{{ order.description }}
|
||||
{{ plugin.value }}
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
We start with a bit of javascript. The function JGetCart just calls the url
|
||||
that has been defined in the python code above. The url consists of a full
|
||||
path `plugin:plugin-name:url-name`. The plugin-name is the SLUG that was
|
||||
defined in the plugin code. order.pk is the parameter that is passed to python.
|
||||
|
||||
The button is defined with `class="btn btn-info` This is an InvenTree predefined button. There a are lots of others available.
|
||||
Here are some examples of available colors:
|
||||
|
||||
{% with id="buttons", url="plugin/buttons.png", description="Button examples" %} {% include "img.html" %} {% endwith %}
|
||||
|
||||
Please have a look at the css files for more options. The last line renders the value that was defined in the plugin.
|
||||
|
||||
!!! tip "Give it a try"
|
||||
Each time you press the button, the value will be increased.
|
||||
|
||||
### Handling user input
|
||||
|
||||
A common user case is user input that needs to be passed from the panel into
|
||||
the plugin for further processing. Lets have a look at another example. We
|
||||
will define two user input fields. One is an integer the other one a string.
|
||||
A button will be defined to submit the data. Something like that:
|
||||
|
||||
{% with id="panel_with_userinput", url="plugin/panel_with_userinput.png", description="Panel with user input" %} {% include "img.html" %} {% endwith %}
|
||||
|
||||
Here is the plugin code:
|
||||
|
||||
```python
|
||||
from django.urls import path
|
||||
from django.http import HttpResponse
|
||||
|
||||
from plugin import InvenTreePlugin
|
||||
from plugin.mixins import PanelMixin, UrlsMixin
|
||||
|
||||
class ExamplePanel(PanelMixin, InvenTreePlugin, UrlsMixin):
|
||||
|
||||
NAME = "ExamplePanel"
|
||||
SLUG = "examplepanel"
|
||||
TITLE = "Example for data input"
|
||||
AUTHOR = "Michael"
|
||||
DESCRIPTION = "This plugin passes user input from the panel to the plugin"
|
||||
|
||||
# Create the panel that will display on build detail view
|
||||
def get_custom_panels(self, view, request):
|
||||
panels = []
|
||||
if isinstance(view, BuildDetail):
|
||||
self.build=view.get_object()
|
||||
panels.append({
|
||||
'title': 'Example Info',
|
||||
'icon': 'fa-industry',
|
||||
'content_template': 'example_panel/example.html',
|
||||
})
|
||||
return panels
|
||||
|
||||
def setup_urls(self):
|
||||
return [
|
||||
path("example/<int:layer>/<path:size>/",
|
||||
self.do_something, name = 'transfer'),
|
||||
]
|
||||
|
||||
# Define the function that will be called.
|
||||
def do_something(self, request, layer, size):
|
||||
|
||||
print('Example panel received:', layer, size)
|
||||
return HttpResponse(f'OK')
|
||||
```
|
||||
|
||||
The start is easy because it is the same as in the example above.
|
||||
Lets concentrate on the setup_urls. This time we use
|
||||
path (imported from django.urls) instead of url for definition. Using path makes it easier to
|
||||
define the data types. No regular expressions. The URL takes two parameters,
|
||||
layer and size, and passes them to the python function do_something for further processing.
|
||||
Now the html template:
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<script>
|
||||
async function example_select(){
|
||||
const layer_number = parseInt(document.getElementById("layer_number").value)
|
||||
const size = document.getElementById("string").value
|
||||
response = inventreeFormDataUpload(url="{% url 'plugin:examplepanel:transfer' '9999' 'Size' %}"
|
||||
.replace("9999", layer_number)
|
||||
.replace("Size", size)
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<form>
|
||||
Number of Layers<br>
|
||||
<input id="layer_number" type="number" value="2"><br>
|
||||
Size of Board in mm<br>
|
||||
<input id="string" type="text" value="100x160">
|
||||
</form>
|
||||
|
||||
<input type="submit" value="Save" onclick="example_select()" title='Save Data'>
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
The HTML defines the form for user input, one number and one string. Each
|
||||
form has an ID that is used in the javascript code to get the input of the form.
|
||||
The response URL must match the URL defined in the plugin. Here we have a number
|
||||
(999999) and a string (Size). These get replaced with the content of the fields
|
||||
upon execution using replace. Watch out for the ticks around the 999999 and Size. They prevent
|
||||
them from being interpreted by the django template engine and replaced by
|
||||
something else.
|
||||
|
||||
The function inventreeFormDataUpload is a helper function defined by InvenTree
|
||||
that does the POST request, handles errors and the csrftoken.
|
||||
|
||||
!!! tip "Give it a try"
|
||||
change the values in the fields and push Save. You will see the values
|
||||
in the InvenTree log.
|
||||
|
||||
#### If things are getting more complicated
|
||||
|
||||
In the example above we code all parameters into the URL. This is easy and OK
|
||||
if you transfer just a few values. But the method has at least two disadvantages:
|
||||
|
||||
* When you have more parameters, things will get messy.
|
||||
* When you have free text input fields, the user might enter characters that are not allowed in URL.
|
||||
|
||||
For those cases it is better to pack the data into a json container and transfer
|
||||
this in the body of the request message. The changes are simple. Lets start with
|
||||
the javascript:
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<script>
|
||||
async function example_select(){
|
||||
const layer_number = parseInt(document.getElementById("layer_number").value)
|
||||
const size = document.getElementById("string").value
|
||||
const cmd_url="{% url 'plugin:examplepanel:transfer' %}";
|
||||
data = {
|
||||
layer_number: layer_number,
|
||||
size: size
|
||||
}
|
||||
response = inventreeFormDataUpload(url=cmd_url, data=JSON.stringify(data))
|
||||
}
|
||||
</script>
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
Here we create a json container (data). The function stringify converts this to the
|
||||
proper string format for transfer. That's all. The function inventreeFormDataUpload
|
||||
does the rest of the work.
|
||||
|
||||
The python code in the plugin also needs minor changes:
|
||||
|
||||
```python
|
||||
from django.urls import re_path
|
||||
import json
|
||||
|
||||
...
|
||||
|
||||
def setup_urls(self):
|
||||
return [
|
||||
re_path(r'example(?:\.(?P<format>json))?$', self.do_something, name='transfer'),
|
||||
]
|
||||
|
||||
# Define the function that will be called.
|
||||
def do_something(self, request):
|
||||
|
||||
data=json.loads(request.body)
|
||||
print('Data received:', data)
|
||||
```
|
||||
|
||||
The URL and the called function have no parameter names any longer. All data is in the
|
||||
request message and can be extracted from this using json.loads. If more data is needed
|
||||
just add it to the json container. No further changes are needed. It's really simple :-)
|
||||
|
||||
#### Populate a drop down field
|
||||
|
||||
Now we add a dropdown menu and fill it with values from the InvenTree database.
|
||||
|
||||
{% with id="panel_with_dropwdown", url="plugin/panel_with_dropdown.png", description="Panel with dropdown menu" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
|
||||
```python
|
||||
from company.models import Company
|
||||
|
||||
...
|
||||
|
||||
def get_custom_panels(self, view, request):
|
||||
panels = []
|
||||
if isinstance(view, BuildDetail):
|
||||
self.build=view.get_object()
|
||||
self.companies=Company.objects.filter(is_supplier=True)
|
||||
panels.append({
|
||||
...
|
||||
```
|
||||
Here we create self.companies and fill it with all companies that have the is_supplier flag
|
||||
set to true. This is available in the context of the template. A drop down menu can be created
|
||||
by looping.
|
||||
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<select id="ems">
|
||||
{% for company in plugin.companies %}
|
||||
<option value="{{ company.id }}"> {{ company.name }} </option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
The value of the select is the pk of the company. It can simply be added to the
|
||||
json container and transferred to the plugin.
|
||||
|
||||
#### Store the Data
|
||||
I case you plugin needs to store data permanently, InvenTree has a nice feature called
|
||||
[metadata](metadata.md). You can easily store your values by adding a few lines
|
||||
to the do_something function.
|
||||
code:
|
||||
|
||||
```python
|
||||
def do_something(self, request):
|
||||
|
||||
data=json.loads(request.body)
|
||||
print('Data received:', data)
|
||||
for key in data:
|
||||
self.build.metadata[key]=data[key]
|
||||
self.build.save()
|
||||
```
|
174
docs/docs/extend/plugins/test.md
Normal file
@ -0,0 +1,174 @@
|
||||
---
|
||||
Title: Unit Tests
|
||||
---
|
||||
|
||||
## Unit Tests
|
||||
For complicated plugins it makes sense to add unit tests the code to ensure
|
||||
that plugins work correctly and are compatible with future versions too.
|
||||
You can run these tests as part of your ci against the current stable and
|
||||
latest tag to get notified when something breaks before it gets released as
|
||||
part of stable. InvenTree offers a framework for testing. Please refer
|
||||
to [Unit Tests](../../develop/contributing.md) for more information.
|
||||
|
||||
### Prerequisites
|
||||
For plugin testing the following environment variables must be set to True:
|
||||
|
||||
| Name | Function | Value |
|
||||
| --- | --- | --- |
|
||||
| INVENTREE_PLUGINS_ENABLED | Enables the use of 3rd party plugins | True |
|
||||
| INVENTREE_PLUGIN_TESTING | Enables enables all plugins no matter of their active state in the db or built-in flag | True |
|
||||
| INVENTREE_PLUGIN_TESTING_SETUP | Enables the url mixin | True |
|
||||
|
||||
### Test program
|
||||
|
||||
A file called test_plugin_name.py should be added to the plugin directory. It can have the
|
||||
following structure:
|
||||
|
||||
```
|
||||
# Basic unit tests for the plugin
|
||||
from InvenTree.unit_test import InvenTreeTestCase
|
||||
|
||||
class TestMyPlugin(InvenTreeTestCase):
|
||||
def test_my_function(self):
|
||||
do some work here...
|
||||
```
|
||||
|
||||
The test can be executed using invoke:
|
||||
|
||||
```
|
||||
invoke dev.test -r module.file.class
|
||||
```
|
||||
|
||||
Plugins are usually installed outside of the InventTree directory, e.g. in .local/lib/...
|
||||
I that case module must be omitted.
|
||||
|
||||
```
|
||||
invoke dev.test -r plugin_directory.test_plugin_name.TestMyPlugin
|
||||
```
|
||||
|
||||
### do some work here... A simple Example
|
||||
A simple example is shown here. Assume the plugin has a function that converts a price string
|
||||
that comes from a supplier API to a float value. The price might have the form "1.456,34 €".
|
||||
It can be different based on country and local settings.
|
||||
The function in the plugin will convert it to a float 1456.34. It is in the class MySupplier
|
||||
and has the following structure:
|
||||
|
||||
```
|
||||
class MySupplier():
|
||||
|
||||
def reformat_price(self, string_price):
|
||||
|
||||
...
|
||||
return float_price
|
||||
```
|
||||
|
||||
This function needs to be tested. The test can look like this:
|
||||
|
||||
```
|
||||
from .myplugin import MySupplier
|
||||
|
||||
def test_reformat_price(self):
|
||||
|
||||
self.assertEqual(MySupplier.reformat_price(self, '1.456,34 €'), 1456.34)
|
||||
self.assertEqual(MySupplier.reformat_price(self, '1,45645 €'), 1.45645)
|
||||
self.assertEqual(MySupplier.reformat_price(self, '1,56 $'), 1.56)
|
||||
self.assertEqual(MySupplier.reformat_price(self, ''), 0)
|
||||
self.assertEqual(MySupplier.reformat_price(self, 'Mumpitz'), 0)
|
||||
```
|
||||
|
||||
The function assertEqual flags an error in case the two arguments are not equal. In equal case
|
||||
no error is flagged and the test passes. The test function tests five different
|
||||
input variations. More might be added based on the requirements.
|
||||
|
||||
### Involve the database
|
||||
Now we test a function that uses InvenTree database objects. The function checks if a part
|
||||
should be updated with latest data from a supplier. Parts that are not purchasable or inactive
|
||||
should not be updated. The function in the plugin has the following form:
|
||||
|
||||
```
|
||||
class MySupplier():
|
||||
|
||||
def should_be_updated(self, my_part):
|
||||
|
||||
...
|
||||
return True/False
|
||||
```
|
||||
|
||||
To test this function, parts are needed in the database. The test framework creates
|
||||
a dummy database for each run which is empty. Parts for testing need to be added.
|
||||
This is done in the test function which looks like:
|
||||
|
||||
```
|
||||
from part.models import Part, PartCategory
|
||||
|
||||
|
||||
def test_should_be_updated(self):
|
||||
test_cat = PartCategory.objects.create(name='test_cat')
|
||||
active_part = Part.objects.create(
|
||||
name='Part1',
|
||||
IPN='IPN1',
|
||||
category=test_cat,
|
||||
active=True,
|
||||
purchaseable=True,
|
||||
component=True,
|
||||
virtual=False)
|
||||
inactive_part = Part.objects.create(
|
||||
name='Part2',
|
||||
IPN='IPN2',
|
||||
category=test_cat,
|
||||
active=False,
|
||||
purchaseable=True,
|
||||
component=True,
|
||||
virtual=False)
|
||||
non_purchasable_part = Part.objects.create(
|
||||
name='Part3',
|
||||
IPN='IPN3',
|
||||
category=test_cat,
|
||||
active=True,
|
||||
purchaseable=False,
|
||||
component=True,
|
||||
virtual=False)
|
||||
|
||||
self.assertEqual(MySupplier.should_be_updated(self, active_part, True, 'Active part')
|
||||
self.assertEqual(MySupplier.should_be_updated(self, inactive_part, False, 'Inactive part')
|
||||
self.assertEqual(MySupplier.should_be_updated(self, non_purchasable_part, False, 'Non purchasable part')
|
||||
```
|
||||
|
||||
A category and three parts are created. One part is active, one is inactive and one is not
|
||||
purchasable. The function should_be_updated is tested with all
|
||||
three parts. The first test should return True, the others False. A message was added to the assert
|
||||
function for better clarity of test results.
|
||||
|
||||
The dummy database is completely separate from the one that you might use for development
|
||||
and it is deleted after the test. There is no danger for your development database.
|
||||
|
||||
In case everything is OK, the result looks like:
|
||||
|
||||
```
|
||||
----------------------------------------------------------------------
|
||||
Ran 1 tests in 0.809s
|
||||
|
||||
OK
|
||||
Destroying test database for alias 'default'...
|
||||
```
|
||||
|
||||
In case of a problem you will see something like:
|
||||
|
||||
```
|
||||
======================================================================
|
||||
FAIL: test_should_be_updated (inventree_supplier_sync.test_supplier_sync.TestSyncPlugin)
|
||||
----------------------------------------------------------------------
|
||||
Traceback (most recent call last):
|
||||
File "/home/michael/.local/lib/python3.10/site-packages/inventree_supplier_sync/test_supplier_sync.py", line 73, in test_should_be_updated
|
||||
self.assertEqual(SupplierSyncPlugin.should_be_updated(self, non_purchasable_part,), False, 'Non purchasable part')
|
||||
AssertionError: True != False : Non purchasable part
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 3 tests in 0.679s
|
||||
|
||||
FAILED (failures=1)
|
||||
Destroying test database for alias 'default'...
|
||||
|
||||
```
|
||||
|
||||
In the AssertionError the message appears that was added to the assertEqual function.
|
219
docs/docs/extend/plugins/ui.md
Normal file
@ -0,0 +1,219 @@
|
||||
---
|
||||
title: User Interface Mixin
|
||||
---
|
||||
|
||||
## User Interface Mixin
|
||||
|
||||
The `UserInterfaceMixin` class provides a set of methods to implement custom functionality for the InvenTree web interface.
|
||||
|
||||
### Enable User Interface Mixin
|
||||
|
||||
To enable user interface plugins, the global setting `ENABLE_PLUGINS_INTERFACE` must be enabled, in the [plugin settings](../../settings/global.md#plugin-settings).
|
||||
|
||||
## Custom UI Features
|
||||
|
||||
The InvenTree user interface functionality can be extended in various ways using plugins. Multiple types of user interface *features* can be added to the InvenTree user interface.
|
||||
|
||||
The entrypoint for user interface plugins is the `UserInterfaceMixin` class, which provides a number of methods which can be overridden to provide custom functionality. The `get_ui_features` method is used to extract available user interface features from the plugin:
|
||||
|
||||
::: plugin.base.ui.mixins.UserInterfaceMixin.get_ui_features
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
Note here that the `get_ui_features` calls other methods to extract the available features from the plugin, based on the requested feature type. These methods can be overridden to provide custom functionality.
|
||||
|
||||
!!! info "Implementation"
|
||||
Your custom plugin does not need to override the `get_ui_features` method. Instead, override one of the other methods to provide custom functionality.
|
||||
|
||||
### UIFeature Return Type
|
||||
|
||||
The `get_ui_features` method should return a list of `UIFeature` objects, which define the available user interface features for the plugin. The `UIFeature` class is defined as follows:
|
||||
|
||||
::: plugin.base.ui.mixins.UIFeature
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
Note that the *options* field contains fields which may be specific to a particular feature type - read the documentation below on each feature type for more information.
|
||||
|
||||
### Dynamic Feature Loading
|
||||
|
||||
Each of the provided feature types can be loaded dynamically by the plugin, based on the information provided in the API request. For example, the plugin can choose to show or hide a particular feature based on the user permissions, or the current state of the system.
|
||||
|
||||
For examples of this dynamic feature loading, refer to the [sample plugin](#sample-plugin) implementation which demonstrates how to dynamically load custom panels based on the provided context.
|
||||
|
||||
### Javascript Source Files
|
||||
|
||||
The rendering function for the custom user interface features expect that the plugin provides a Javascript source file which contains the necessary code to render the custom content. The path to this file should be provided in the `source` field of the `UIFeature` object.
|
||||
|
||||
Note that the `source` field can include the name of the function to be called (if this differs from the expected default function name).
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
"source": "/static/plugins/my_plugin/my_plugin.js:my_custom_function"
|
||||
```
|
||||
|
||||
## Available UI Feature Types
|
||||
|
||||
The following user interface feature types are available:
|
||||
|
||||
### Dashboard Items
|
||||
|
||||
The InvenTree dashboard is a collection of "items" which are displayed on the main dashboard page. Custom dashboard items can be added to the dashboard by implementing the `get_ui_dashboard_items` method:
|
||||
|
||||
::: plugin.base.ui.mixins.UserInterfaceMixin.get_ui_dashboard_items
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
#### Dashboard Item Options
|
||||
|
||||
The *options* field in the returned `UIFeature` object can contain the following properties:
|
||||
|
||||
::: plugin.base.ui.mixins.CustomDashboardItemOptions
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
#### Source Function
|
||||
|
||||
The frontend code expects a path to a javascript file containing a function named `renderDashboardItem` which will be called to render the custom dashboard item. Note that this function name can be overridden by appending the function name in the `source` field of the `UIFeature` object.
|
||||
|
||||
#### Example
|
||||
|
||||
Refer to the [sample plugin](#sample-plugin) for an example of how to implement server side rendering for custom panels.
|
||||
|
||||
### Panels
|
||||
|
||||
Many of the pages in the InvenTree web interface are built using a series of "panels" which are displayed on the page. Custom panels can be added to these pages, by implementing the `get_ui_panels` method:
|
||||
|
||||
::: plugin.base.ui.mixins.UserInterfaceMixin.get_ui_panels
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
#### Panel Options
|
||||
|
||||
The *options* field in the returned `UIFeature` object can contain the following properties:
|
||||
|
||||
::: plugin.base.ui.mixins.CustomPanelOptions
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
#### Source Function
|
||||
|
||||
The frontend code expects a path to a javascript file containing a function named `renderPanel` which will be called to render the custom panel. Note that this function name can be overridden by appending the function name in the `source` field of the `UIFeature` object.
|
||||
|
||||
#### Example
|
||||
|
||||
Refer to the [sample plugin](#sample-plugin) for an example of how to implement server side rendering for custom panels.
|
||||
|
||||
### Template Editors
|
||||
|
||||
The `get_ui_template_editors` feature type can be used to provide custom template editors.
|
||||
|
||||
::: plugin.base.ui.mixins.UserInterfaceMixin.get_ui_template_editors
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Template previews
|
||||
|
||||
The `get_ui_template_previews` feature type can be used to provide custom template previews:
|
||||
|
||||
::: plugin.base.ui.mixins.UserInterfaceMixin.get_ui_template_previews
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
## Plugin Context
|
||||
|
||||
When rendering certain content in the user interface, the rendering functions are passed a `context` object which contains information about the current page being rendered. The type of the `context` object is defined in the `PluginContext` file:
|
||||
|
||||
{{ includefile("src/frontend/src/components/plugins/PluginContext.tsx", title="Plugin Context", fmt="javascript") }}
|
||||
|
||||
This context data can be used to provide additional information to the rendering functions, and can be used to dynamically render content based on the current state of the system.
|
||||
|
||||
### Additional Context
|
||||
|
||||
Note that additional context can be passed to the rendering functions by adding additional key-value pairs to the `context` field in the `UIFeature` return type (provided by the backend via the API). This field is optional, and can be used at the discretion of the plugin developer.
|
||||
|
||||
## File Distribution
|
||||
|
||||
When distributing a custom UI plugin, the plugin should include the necessary frontend code to render the custom content. This frontend code should be included in the plugin package, and should be made available to the InvenTree frontend when the plugin is installed.
|
||||
|
||||
The simplest (and recommended) way to achieve this is to distribute the compiled javascript files with the plugin package, in a top-level `static` directory. This directory will be automatically collected by InvenTree when the plugin is installed, and the files will be copied to the appropriate location.
|
||||
|
||||
Read more about [static plugin files](../plugins.md#static-files) for more information.
|
||||
|
||||
## Sample Plugin
|
||||
|
||||
A (very simple) sample plugin which implements custom user interface functionality is provided in the InvenTree source code, which provides a full working example of how to implement custom user interface functionality.
|
||||
|
||||
::: plugin.samples.integration.user_interface_sample.SampleUserInterfacePlugin
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
### More Examples
|
||||
|
||||
Some more complex examples of user interface plugins can be found on the InvenTree GitHub repository:
|
||||
|
||||
- [inventree-test-statistics](https://github.com/inventree/inventree-test-statistics)
|
||||
- [inventree-order-history](https://github.com/inventree/inventree-order-history)
|
||||
|
||||
## Consistent Theming
|
||||
|
||||
When developing a custom UI plugin for InvenTree, the plugin should aim to match the existing InvenTree theme as closely as possible. This will help to ensure that the custom content fits seamlessly into the existing user interface.
|
||||
|
||||
To achieve this, we strongly recommend that you use the same framework as the InvenTree frontend - which is built using [React](https://react.dev) on top of the [Mantine](https://mantine.dev) UI component library.
|
||||
|
||||
### Mantine
|
||||
|
||||
The Mantine UI component library is used throughout the InvenTree frontend, and provides a consistent look and feel to the user interface. By using Mantine components in your custom UI plugin, you can ensure that your custom content fits seamlessly into the existing InvenTree theme.
|
||||
|
||||
### InvenTree Component Library
|
||||
|
||||
We are working to develop and distribute a library of custom InvenTree components which can be used to build custom UI plugins. This library will be made available to plugin developers in the near future.
|
||||
|
||||
### Examples
|
||||
|
||||
Refer to some of the existing InvenTree plugins linked above for examples of building custom UI plugins using the Mantine component library for seamless integration.
|
@ -43,40 +43,6 @@ def setup_urls(self):
|
||||
]
|
||||
```
|
||||
|
||||
### Implementing the Page Base
|
||||
Some plugins require a page with a navbar, sidebar, and content similar to other InvenTree pages.
|
||||
This can be done within a templated HTML file by extending the file "page_base.html". To do this, place the following line at the top of your template file.
|
||||
``` HTML
|
||||
{% raw %}
|
||||
{% extends "page_base.html" %}
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
Additionally, add the following imports after the extended line.
|
||||
``` HTML
|
||||
{% raw %}
|
||||
{% load static %}
|
||||
{% load inventree_extras %}
|
||||
{% load plugin_extras %}
|
||||
{% load i18n %}
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
#### 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]({{ 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.
|
||||
``` HTML
|
||||
{% raw %}
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
enableSidebar('label');
|
||||
{% endblock js_ready %}
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
#### Panels
|
||||
InvenTree uses bootstrap panels to display the page's content. These panels are locate inside the block `page_content`.
|
||||
|
@ -108,22 +108,71 @@ By default, part names are not subject to any particular naming conventions or r
|
||||
|
||||
If the custom method determines that the part name is *objectionable*, it should throw a `ValidationError` which will be handled upstream by parent calling methods.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_name
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Part IPN
|
||||
|
||||
Validation of the Part IPN (Internal Part Number) field is exposed to custom plugins via the `validate_part_IPN` method. Any plugins which extend the `ValidationMixin` class can implement this method, and raise a `ValidationError` if the IPN value does not match a required convention.
|
||||
Validation of the Part IPN (Internal Part Number) field is exposed to custom plugins via the `validate_part_ipn` method. Any plugins which extend the `ValidationMixin` class can implement this method, and raise a `ValidationError` if the IPN value does not match a required convention.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_ipn
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Part Parameter Values
|
||||
|
||||
[Part parameters](../../part/parameter.md) can also have custom validation rules applied, by implementing the `validate_part_parameter` method. A plugin which implements this method should raise a `ValidationError` with an appropriate message if the part parameter value does not match a required convention.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_parameter
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Batch Codes
|
||||
|
||||
[Batch codes](../../stock/tracking.md#batch-codes) can be generated and/or validated by custom plugins.
|
||||
|
||||
#### Validate Batch Code
|
||||
|
||||
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.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_batch_code
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
#### Generate Batch Code
|
||||
|
||||
The `generate_batch_code` method can be implemented to generate a new batch code, based on a set of provided information.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.generate_batch_code
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Serial Numbers
|
||||
|
||||
Requirements for serial numbers can vary greatly depending on the application. Rather than attempting to provide a "one size fits all" serial number implementation, InvenTree allows custom serial number schemes to be implemented via plugins.
|
||||
@ -134,17 +183,30 @@ The default InvenTree [serial numbering system](../../stock/tracking.md#serial-n
|
||||
|
||||
Custom serial number validation can be implemented using the `validate_serial_number` method. A *proposed* serial number is passed to this method, which then has the opportunity to raise a `ValidationError` to indicate that the serial number is not valid.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_serial_number
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
!!! info "Stock Item"
|
||||
If the `stock_item` argument is provided, then this stock item has already been assigned with the provided serial number. This stock item should be excluded from any subsequent checks for *uniqueness*. The `stock_item` parameter is optional, and may be `None` if the serial number is being validated in a context where no stock item is available.
|
||||
|
||||
##### Example
|
||||
|
||||
A plugin which requires all serial numbers to be valid hexadecimal values may implement this method as follows:
|
||||
|
||||
```python
|
||||
def validate_serial_number(self, serial: str, part: Part):
|
||||
def validate_serial_number(self, serial: str, part: Part, stock_item: StockItem = None):
|
||||
"""Validate the supplied serial number
|
||||
|
||||
Arguments:
|
||||
serial: The proposed serial number (string)
|
||||
part: The Part instance for which this serial number is being validated
|
||||
stock_item: The StockItem instance for which this serial number is being validated
|
||||
"""
|
||||
|
||||
try:
|
||||
@ -160,6 +222,15 @@ While InvenTree supports arbitrary text values in the serial number fields, behi
|
||||
|
||||
A custom plugin can implement the `convert_serial_to_int` method to determine how a particular serial number is converted to an integer representation.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.convert_serial_to_int
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
!!! info "Not Required"
|
||||
If this method is not implemented, or the serial number cannot be converted to an integer, then the sorting algorithm falls back to the text (string) value
|
||||
|
||||
@ -169,6 +240,15 @@ A core component of the InvenTree serial number system is the ability to *increm
|
||||
|
||||
For custom serial number schemes, it is important to provide a method to generate the *next* serial number given a current value. The `increment_serial_number` method can be implemented by a plugin to achieve this.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.increment_serial_number
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
!!! info "Invalid Increment"
|
||||
If the provided number cannot be incremented (or an error occurs) the method should return `None`
|
||||
|
||||
|
@ -14,37 +14,17 @@ InvenTree installation is not officially supported natively on Windows. However
|
||||
|
||||
### Command 'invoke' not found
|
||||
|
||||
If the `invoke` command does not work, it means that the [invoke](https://pypi.org/project/invoke/) python library has not been correctly installed.
|
||||
If the `invoke` command does not work, it means that the invoke tool has not been correctly installed.
|
||||
|
||||
Update the installed python packages with PIP:
|
||||
Refer to the [invoke installation guide](./start/invoke.md#installation) for more information.
|
||||
|
||||
```
|
||||
pip3 install -U --require-hashes -r requirements.txt
|
||||
```
|
||||
### Can't find any collection named tasks
|
||||
|
||||
Refer to the [invoke guide](./start/invoke.md#cant-find-any-collection-named-tasks) for more information.
|
||||
|
||||
### Invoke Version
|
||||
|
||||
If the installed version of invoke is too old, users may see error messages during the installation procedure, such as:
|
||||
|
||||
- *'update' did not receive all required positional arguments!*
|
||||
- *Function has keyword-only arguments or annotations*
|
||||
|
||||
As per the [invoke guide](./start/intro.md#invoke), the minimum required version of Invoke is `{{ config.extra.min_invoke_version }}`.
|
||||
|
||||
To determine the version of invoke you have installed, run either:
|
||||
|
||||
```
|
||||
invoke --version
|
||||
```
|
||||
```
|
||||
python -m invoke --version
|
||||
```
|
||||
|
||||
If you are running an older version of invoke, ensure it is updated to the latest version:
|
||||
|
||||
```
|
||||
pip install -U invoke
|
||||
```
|
||||
If the installed version of invoke is too old, users may see error messages during the installation procedure. Refer to the [invoke guide](./start/invoke.md#minimum-version) for more information.
|
||||
|
||||
### No module named 'django'
|
||||
|
||||
@ -83,6 +63,26 @@ For more information, refer to the installation guides:
|
||||
!!! warning "Invoke Update"
|
||||
You must ensure that the `invoke update` command is performed *every time* you update InvenTree
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
Before performing an update, check the release notes! Any *breaking changes* (changes which require user intervention) will be clearly noted.
|
||||
|
||||
### Cannot import name get_storage_class
|
||||
|
||||
When running an install or update, you may see an error similar to:
|
||||
|
||||
```python
|
||||
ImportError: cannot import name 'get_storage_class' from 'django.core.files.storage'
|
||||
```
|
||||
|
||||
In such a situation, it is likely that the automatic backup procedure is unable to run, as the required python packages are not yet installed or are unavailable.
|
||||
|
||||
To proceed in this case, you can skip the backup procedure by running the `invoke update` command with the `--skip-backup` flag:
|
||||
|
||||
```bash
|
||||
invoke update --skip-backup
|
||||
```
|
||||
|
||||
### Feature *x* does not work after update
|
||||
|
||||
If a particular menu / item is not visible after updating InvenTree, or a certain function no longer seems to work, it may be due to your internet browser caching old versions of CSS and JavaScript files.
|
||||
|
@ -39,7 +39,7 @@ InvenTree allows you to upload simple BOM files in multiple formats, and downloa
|
||||
|
||||
## Build Parts
|
||||
|
||||
Inventree features a build management system to help you track the progress of your builds.
|
||||
InvenTree features a build management system to help you track the progress of your builds.
|
||||
Builds consume stock items to make new parts, you can decide to automatically or manually allocate parts from your current inventory.
|
||||
|
||||
[Read more...](./build/build.md)
|
||||
|
@ -57,7 +57,7 @@ def fetch_rtd_versions():
|
||||
versions = sorted(versions, key=lambda x: StrictVersion(x['version']), reverse=True)
|
||||
|
||||
# Add "latest" version first
|
||||
if not any((x['title'] == 'latest' for x in versions)):
|
||||
if not any(x['title'] == 'latest' for x in versions):
|
||||
versions.insert(
|
||||
0,
|
||||
{
|
||||
@ -70,7 +70,7 @@ def fetch_rtd_versions():
|
||||
# Ensure we have the 'latest' version
|
||||
current_version = os.environ.get('READTHEDOCS_VERSION', None)
|
||||
|
||||
if current_version and not any((x['title'] == current_version for x in versions)):
|
||||
if current_version and not any(x['title'] == current_version for x in versions):
|
||||
versions.append({
|
||||
'version': current_version,
|
||||
'title': current_version,
|
||||
@ -82,7 +82,7 @@ def fetch_rtd_versions():
|
||||
print('Discovered the following versions:')
|
||||
print(versions)
|
||||
|
||||
with open(output_filename, 'w') as file:
|
||||
with open(output_filename, 'w', encoding='utf-8') as file:
|
||||
json.dump(versions, file, indent=2)
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@ def get_release_data():
|
||||
# Release information has been cached to file
|
||||
|
||||
print("Loading release information from 'releases.json'")
|
||||
with open(json_file) as f:
|
||||
with open(json_file, encoding='utf-8') as f:
|
||||
return json.loads(f.read())
|
||||
|
||||
# Download release information via the GitHub API
|
||||
@ -127,7 +127,7 @@ def get_release_data():
|
||||
page += 1
|
||||
|
||||
# Cache these results to file
|
||||
with open(json_file, 'w') as f:
|
||||
with open(json_file, 'w', encoding='utf-8') as f:
|
||||
print("Saving release information to 'releases.json'")
|
||||
f.write(json.dumps(releases))
|
||||
|
||||
@ -173,7 +173,7 @@ def on_config(config, *args, **kwargs):
|
||||
# Add *all* readthedocs related keys
|
||||
readthedocs = {}
|
||||
|
||||
for key in os.environ.keys():
|
||||
for key in os.environ:
|
||||
if key.startswith('READTHEDOCS_'):
|
||||
k = key.replace('READTHEDOCS_', '').lower()
|
||||
readthedocs[k] = os.environ[key]
|
||||
|
@ -61,7 +61,7 @@ A *contact* can be assigned to orders, (such as [purchase orders](./purchase_ord
|
||||
|
||||
A company can have multiple registered addresses for use with all types of orders.
|
||||
An address is broken down to internationally recognised elements that are designed to allow for formatting an address according to user needs.
|
||||
Addresses are composed differently across the world, and Inventree reflects this by splitting addresses into components:
|
||||
Addresses are composed differently across the world, and InvenTree reflects this by splitting addresses into components:
|
||||
- Line 1: Main street address
|
||||
- Line 2: Extra street address line
|
||||
- Postal Code: Also known as ZIP code, this is normally a number 3-5 digits in length
|
||||
|
@ -20,6 +20,7 @@ Each Purchase Order has a specific status code which indicates the current state
|
||||
| --- | --- |
|
||||
| Pending | The purchase order has been created, but has not been submitted to the supplier |
|
||||
| In Progress | The purchase order has been issued to the supplier, and is in progress |
|
||||
| On Hold | The purchase order has been placed on hold, but is still active |
|
||||
| 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 |
|
||||
@ -37,6 +38,8 @@ Refer to the source code for the Purchase Order status codes:
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
Purchase Order Status supports [custom states](../concepts/custom_states.md).
|
||||
|
||||
### Purchase Order Currency
|
||||
|
||||
The currency code can be specified for an individual purchase order. If not specified, the default currency specified against the [supplier](./company.md#suppliers) will be used.
|
||||
@ -90,6 +93,14 @@ There are two options to mark items as "received":
|
||||
!!! note "Permissions"
|
||||
Marking line items as received requires the "Purchase order" ADD permission.
|
||||
|
||||
### Item Location
|
||||
|
||||
When receiving items from a purchase order, the location of the items must be specified. There are multiple ways to specify the location:
|
||||
|
||||
* **Order Destination**: The *destination* field of the purchase order can be set to a specific location. When receiving items, the location will default to the destination location.
|
||||
|
||||
* **Line Item Location**: Each line item can have a specific location set. When receiving items, the location will default to the line item location. *Note: A destination specified at the line item level will override the destination specified at the order level.*
|
||||
|
||||
### Received Items
|
||||
|
||||
Each item marked as "received" is automatically converted into a stock item.
|
||||
@ -138,3 +149,14 @@ This view can be accessed externally as an ICS calendar using a URL like the fol
|
||||
`http://inventree.example.org/api/order/calendar/purchase-order/calendar.ics`
|
||||
|
||||
by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
|
||||
|
||||
## Purchase Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for purchase orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("PURCHASEORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("PURCHASEORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("PURCHASEORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
{{ globalsetting("PURCHASEORDER_AUTO_COMPLETE") }}
|
||||
|
@ -45,6 +45,7 @@ Each Return Order has a specific status code, as follows:
|
||||
| --- | --- |
|
||||
| Pending | The return order has been created, but not sent to the customer |
|
||||
| In Progress | The return order has been issued to the customer |
|
||||
| On Hold | The return order has been placed on hold, but is still active |
|
||||
| Complete | The return order was marked as complete, and is now closed |
|
||||
| Cancelled | The return order was cancelled, and is now closed |
|
||||
|
||||
@ -60,6 +61,8 @@ Refer to the source code for the Return Order status codes:
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
Return Order Status supports [custom states](../concepts/custom_states.md).
|
||||
|
||||
## 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.
|
||||
@ -120,3 +123,14 @@ This view can be accessed externally as an ICS calendar using a URL like the fol
|
||||
`http://inventree.example.org/api/order/calendar/return-order/calendar.ics`
|
||||
|
||||
by default, completed orders are not exported. These can be included by appending `?include_completed=True` to the URL.
|
||||
|
||||
## Return Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for return orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("RETURNORDER_ENABLED") }}
|
||||
{{ globalsetting("RETURNORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("RETURNORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("RETURNORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
|
@ -20,6 +20,7 @@ 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 |
|
||||
| On Hold | The sales order has been placed on hold, but is still active |
|
||||
| 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 |
|
||||
@ -38,6 +39,8 @@ Refer to the source code for the Sales Order status codes:
|
||||
show_source: True
|
||||
members: []
|
||||
|
||||
Sales Order Status supports [custom states](../concepts/custom_states.md).
|
||||
|
||||
### Sales Order Currency
|
||||
|
||||
The currency code can be specified for an individual sales order. If not specified, the default currency specified against the [customer](./company.md#customers) will be used.
|
||||
@ -182,3 +185,15 @@ All these fields can be edited by the user:
|
||||
{% with id="edit-shipment", url="order/edit_shipment.png", description="Edit shipment" %}
|
||||
{% include "img.html" %}
|
||||
{% endwith %}
|
||||
|
||||
## Sales Order Settings
|
||||
|
||||
The following [global settings](../settings/global.md) are available for sales orders:
|
||||
|
||||
| Name | Description | Default | Units |
|
||||
| ---- | ----------- | ------- | ----- |
|
||||
{{ globalsetting("SALESORDER_REFERENCE_PATTERN") }}
|
||||
{{ globalsetting("SALESORDER_REQUIRE_RESPONSIBLE") }}
|
||||
{{ globalsetting("SALESORDER_DEFAULT_SHIPMENT") }}
|
||||
{{ globalsetting("SALESORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
{{ globalsetting("SALESORDER_SHIP_COMPLETE") }}
|
||||
|
@ -26,6 +26,7 @@ Parameter templates are used to define the different types of parameters which a
|
||||
| Units | Optional units field (*must be a valid [physical unit](#parameter-units)*) |
|
||||
| Choices | A comma-separated list of valid choices for parameter values linked to this template. |
|
||||
| Checkbox | If set, parameters linked to this template can only be assigned values *true* or *false* |
|
||||
| Selection List | If set, parameters linked to this template can only be assigned values from the linked [selection list](#selection-lists) |
|
||||
|
||||
### Create Template
|
||||
|
||||
@ -105,3 +106,12 @@ Parameter sorting takes unit conversion into account, meaning that values provid
|
||||
{% with id="sort_by_param_units", url="part/part_sorting_units.png", description="Sort by Parameter Units" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
### Selection Lists
|
||||
|
||||
Selection Lists can be used to add a large number of predefined values to a parameter template. This can be useful for parameters which must be selected from a large predefined list of values (e.g. a list of standardised colo codes). Choices on templates are limited to 5000 characters, selection lists can be used to overcome this limitation.
|
||||
|
||||
It is possible that plugins lock selection lists to ensure a known state.
|
||||
|
||||
|
||||
Administration of lists can be done through the Part Parameter section in the Admin Center or via the API.
|
||||
|
@ -47,6 +47,10 @@ If a part is designated as an *Assembly* it can be created (or built) from other
|
||||
|
||||
If a part is designated as a *Component* it can be used as a sub-component of an *Assembly*. [Read further information about BOM management here](../build/bom.md)
|
||||
|
||||
### Testable
|
||||
|
||||
Testable parts can have test templates defined against the part, allowing test results to be recorded against any stock items for that part. For more information on testing, refer to the [testing documentation](./test.md).
|
||||
|
||||
### Trackable
|
||||
|
||||
Trackable parts can be assigned batch numbers or serial numbers which uniquely identify a particular stock item. Trackable parts also provide other features (and restrictions) in the InvenTree ecosystem.
|
||||
@ -73,7 +77,15 @@ A [Purchase Order](../order/purchase_order.md) allows parts to be ordered from a
|
||||
|
||||
If a part is designated as *Salable* it can be sold to external customers. Setting this flag allows parts to be added to sales orders.
|
||||
|
||||
### Active
|
||||
## Locked Parts
|
||||
|
||||
Parts can be locked to prevent them from being modified. This is useful for parts which are in production and should not be changed. The following restrictions apply to parts which are locked:
|
||||
|
||||
- Locked parts cannot be deleted
|
||||
- BOM items cannot be created, edited, or deleted when they are part of a locked assembly
|
||||
- Part parameters linked to a locked part cannot be created, edited or deleted
|
||||
|
||||
## Active Parts
|
||||
|
||||
By default, all parts are *Active*. Marking a part as inactive means it is not available for many actions, but the part remains in the database. If a part becomes obsolete, it is recommended that it is marked as inactive, rather than deleting it from the database.
|
||||
|
||||
|
@ -46,7 +46,7 @@ Additionally, the following information is stored for each part, in relation to
|
||||
|
||||
InvenTree supports pricing data in multiple currencies, allowing integration with suppliers and customers using different currency systems.
|
||||
|
||||
Supported currencies must be configured as part of [the InvenTree setup process](../start/config.md#supported-currencies).
|
||||
Supported currencies can be configured in the [InvenTree settings](../settings/currency.md).
|
||||
|
||||
!!! info "Currency Support"
|
||||
InvenTree provides multi-currency pricing support via the [django-money](https://django-money.readthedocs.io/en/latest/) library.
|
||||
|
78
docs/docs/part/revision.md
Normal file
@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Part Revisions
|
||||
---
|
||||
|
||||
## Part Revisions
|
||||
|
||||
When creating a complex part (such as an assembly comprised of other parts), it is often necessary to track changes to the part over time. For example, throughout the lifetime of an assembly, it may be necessary to adjust the bill of materials, or update the design of the part.
|
||||
|
||||
Rather than overwrite the existing part data, InvenTree allows you to create a new *revision* of the part. This allows you to track changes to the part over time, and maintain a history of the part design.
|
||||
|
||||
Crucially, creating a new *revision* ensures that any related data entries which refer to the original part (such as stock items, build orders, purchase orders, etc) are not affected by the change.
|
||||
|
||||
### Revisions are Parts
|
||||
|
||||
A *revision* of a part is itself a part. This means that each revision of a part has its own part number, stock items, parameters, bill of materials, etc. The only thing that differentiates a *revision* from any other part is that the *revision* is linked to the original part.
|
||||
|
||||
### Revision Fields
|
||||
|
||||
Each part has two fields which are used to track the revision of the part:
|
||||
|
||||
* **Revision**: The revision number of the part. This is a user-defined field, and can be any string value.
|
||||
* **Revision Of**: A reference to the part of which *this* part is a revision. This field is used to keep track of the available revisions for any particular part.
|
||||
|
||||
### Revision Restrictions
|
||||
|
||||
When creating a new revision of a part, there are some restrictions which must be adhered to:
|
||||
|
||||
* **Circular References**: A part cannot be a revision of itself. This would create a circular reference which is not allowed.
|
||||
* **Unique Revisions**: A part cannot have two revisions with the same revision number. Each revision (of a given part) must have a unique revision code.
|
||||
* **Revisions of Revisions**: A single part can have multiple revisions, but a revision cannot have its own revision. This restriction is in place to prevent overly complex part relationships.
|
||||
* **Template Revisions**: A part which is a [template part](./template.md) cannot have revisions. This is because the template part is used to create variants, and allowing revisions of templates would create disallowed relationship states in the database. However, variant parts are allowed to have revisions.
|
||||
* **Template References**: A part which is a revision of a variant part must point to the same template as the original part. This is to ensure that the revision is correctly linked to the original part.
|
||||
|
||||
## Revision Settings
|
||||
|
||||
The following options are available to control the behavior of part revisions.
|
||||
|
||||
Note that these options can be changed in the InvenTree settings:
|
||||
|
||||
{% with id="part_revision_settings", url="part/part_revision_settings.png", description="Part revision settings" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
* **Enable Revisions**: If this setting is enabled, parts can have revisions. If this setting is disabled, parts cannot have revisions.
|
||||
* **Assembly Revisions Only**: If this setting is enabled, only assembly parts can have revisions. This is useful if you only want to track revisions of assemblies, and not individual parts.
|
||||
|
||||
## Create a Revision
|
||||
|
||||
To create a new revision for a given part, navigate to the part detail page, and click on the "Revisions" tab.
|
||||
|
||||
Select the "Duplicate Part" action, to create a new copy of the selected part. This will open the "Duplicate Part" form:
|
||||
|
||||
{% with id="part_create_revision", url="part/part_create_revision.png", description="Create part revision" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
In this form, make the following updates:
|
||||
|
||||
1. Set the *Revision Of* field to the original part (the one that you are duplicating)
|
||||
2. Set the *Revision* field to a unique revision number for the new part revision
|
||||
|
||||
Once these changes (and any other required changes) are made, press *Submit* to create the new part.
|
||||
|
||||
Once the form is submitted (without any errors), you will be redirected to the new part revision. Here you can see that it is linked to the original part:
|
||||
|
||||
{% with id="part_revision_b", url="part/part_revision_b.png", description="Revision B" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
## Revision Navigation
|
||||
|
||||
When multiple revisions exist for a particular part, you can navigate between revisions using the *Select Part Revision* drop-down which renders at the top of the part page:
|
||||
|
||||
{% with id="part_revision_select", url="part/part_revision_select.png", description="Select part revision" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
|
||||
Note that this revision selector is only visible when multiple revisions exist for the part.
|
@ -4,7 +4,7 @@ title: Part Test Templates
|
||||
|
||||
## Part Test Templates
|
||||
|
||||
Parts which are designated as *trackable* (meaning they can be uniquely serialized) can define templates for tests which are to be performed against individual stock items corresponding to the part.
|
||||
Parts which are designated as [testable](./part.md#testable) can define templates for tests which are to be performed against individual stock items corresponding to the part.
|
||||
|
||||
A test template defines the parameters of the test; the individual stock items can then have associated test results which correspond to a test template.
|
||||
|
||||
|
@ -28,7 +28,6 @@ Details provides information about the particular part. Parts details can be dis
|
||||
{% with id="part_overview", url="part/part_overview.png", description="Part details" %}
|
||||
{% include 'img.html' %}
|
||||
{% endwith %}
|
||||
<p></p>
|
||||
|
||||
A Part is defined in the system by the following parameters:
|
||||
|
||||
@ -38,7 +37,7 @@ A Part is defined in the system by the following parameters:
|
||||
|
||||
**Description** - Longer form text field describing the Part
|
||||
|
||||
**Revision** - An optional revision code denoting the particular version for the part. Used when there are multiple revisions of the same master part object.
|
||||
**Revision** - An optional revision code denoting the particular version for the part. Used when there are multiple revisions of the same master part object. Read [more about part revisions here](./revision.md).
|
||||
|
||||
**Keywords** - Optional few words to describe the part and make the part search more efficient.
|
||||
|
||||
@ -62,7 +61,7 @@ Parts can have multiple defined parameters.
|
||||
|
||||
If a part is a *Template Part* then the *Variants* tab will be visible.
|
||||
|
||||
[Read about Part templates](./template.md)
|
||||
[Read about Part templates and variants](./template.md)
|
||||
|
||||
### Stock
|
||||
|
||||
@ -132,9 +131,9 @@ The *Scheduling* tab provides an overview of the *predicted* future availability
|
||||
|
||||
The *Stocktake* tab provide historical stock level information, based on user-provided stocktake data. Refer to the [stocktake documentation](./stocktake.md) for further information.
|
||||
|
||||
### Tests
|
||||
### Test Templates
|
||||
|
||||
If a part is marked as *trackable*, the user can define tests which must be performed on any stock items which are instances of this part. [Read more about testing](./test.md).
|
||||
If a part is marked as *testable*, the user can define tests which must be performed on any stock items which are instances of this part. [Read more about testing](./test.md).
|
||||
|
||||
### Related Parts
|
||||
|
||||
|
@ -81,7 +81,7 @@ Multiple improvements have been made to the docker installation process, most no
|
||||
|
||||
### QR code scanner
|
||||
|
||||
[#2779](https://github.com/inventree/InvenTree/pull/2779) provides a QR code scanner which can be used to quickly scan Inventree generated QR codes using webcams or mobile devices. This feature requires secure (HTTPS) connection to the server.
|
||||
[#2779](https://github.com/inventree/InvenTree/pull/2779) provides a QR code scanner which can be used to quickly scan InvenTree generated QR codes using webcams or mobile devices. This feature requires secure (HTTPS) connection to the server.
|
||||
|
||||
### Order, Order
|
||||
|
||||
@ -89,7 +89,7 @@ Multiple improvements have been made to the docker installation process, most no
|
||||
|
||||
### Panel Plugins
|
||||
|
||||
[#2937](https://github.com/inventree/InvenTree/pull/2937) adds a new type of plugin mixin, which allows rendering of custom "panels" on certain pages. This is a powerful new plugin feature which allows custom UI elements to be generated with ease. Read more about this new mixin [here](../extend/plugins/panel.md).
|
||||
[#2937](https://github.com/inventree/InvenTree/pull/2937) adds a new type of plugin mixin, which allows rendering of custom "panels" on certain pages. This is a powerful new plugin feature which allows custom UI elements to be generated with ease. REMOVED AFTER 0.17.0
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
|
@ -41,6 +41,13 @@ A number of helper functions are available for accessing data contained in a par
|
||||
|
||||
To return the element at a given index in a container which supports indexed access (such as a [list](https://www.w3schools.com/python/python_lists.asp)), use the `getindex` function:
|
||||
|
||||
::: report.templatetags.report.getindex
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
#### Example
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% getindex my_list 1 as value %}
|
||||
@ -53,6 +60,13 @@ Item: {{ value }}
|
||||
To return an element corresponding to a certain key in a container which supports key access (such as a [dictionary](https://www.w3schools.com/python/python_dictionaries.asp)), use the `getkey` function:
|
||||
|
||||
|
||||
::: report.templatetags.report.getkey
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
#### Example
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<ul>
|
||||
@ -64,10 +78,91 @@ To return an element corresponding to a certain key in a container which support
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
## Database Helpers
|
||||
|
||||
A number of helper functions are available for accessing database objects:
|
||||
|
||||
### filter_queryset
|
||||
|
||||
The `filter_queryset` function allows for arbitrary filtering of the provided querysert. It takes a queryset and a list of filter arguments, and returns a filtered queryset.
|
||||
|
||||
|
||||
|
||||
::: report.templatetags.report.filter_queryset
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
!!! info "Provided QuerySet"
|
||||
The provided queryset must be a valid Django queryset object, which is already available in the template context.
|
||||
|
||||
!!! warning "Advanced Users"
|
||||
The `filter_queryset` function is a powerful tool, but it is also easy to misuse. It assumes that the user has a good understanding of Django querysets and the underlying database structure.
|
||||
|
||||
#### Example
|
||||
|
||||
In a report template which has a `PurchaseOrder` object available in its context, fetch any line items which have a received quantity greater than zero:
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% load report %}
|
||||
|
||||
{% filter_queryset order.lines.all received__gt=0 as received_lines %}
|
||||
|
||||
<ul>
|
||||
{% for line in received_lines %}
|
||||
<li>{{ line.part.part.full_name }} - {{ line.received }} / {{ line.quantity }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
### filter_db_model
|
||||
|
||||
The `filter_db_model` function allows for filtering of a database model based on a set of filter arguments. It takes a model class and a list of filter arguments, and returns a filtered queryset.
|
||||
|
||||
::: report.templatetags.report.filter_db_model
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
#### Example
|
||||
|
||||
Generate a list of all active customers:
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% load report %}
|
||||
|
||||
{% filter_db_model company.company is_customer=True active=True as active_customers %}
|
||||
|
||||
<ul>
|
||||
{% for customer in active_customers %}
|
||||
<li>{{ customer.name }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
### Advanced Database Queries
|
||||
|
||||
More advanced database filtering should be achieved using a [report plugin](../extend/plugins/report.md), and adding custom context data to the report template.
|
||||
|
||||
## Number Formatting
|
||||
|
||||
### format_number
|
||||
|
||||
The helper function `format_number` allows for some common number formatting options. It takes a number (or a number-like string) as an input, as well as some formatting arguments. It returns a *string* containing the formatted number:
|
||||
|
||||
::: report.templatetags.report.format_number
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
#### Example
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% load report %}
|
||||
@ -82,15 +177,24 @@ The helper function `format_number` allows for some common number formatting opt
|
||||
|
||||
For rendering date and datetime information, the following helper functions are available:
|
||||
|
||||
- `format_date`: Format a date object
|
||||
- `format_datetime`: Format a datetime object
|
||||
### format_date
|
||||
|
||||
Each of these helper functions takes a date or datetime object as an input, and returns a *string* containing the formatted date or datetime. The following additional arguments are available:
|
||||
::: report.templatetags.report.format_date
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### format_datetime
|
||||
|
||||
::: report.templatetags.report.format_datetime
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### Date Formatting
|
||||
|
||||
If not specified, these methods return a result which uses ISO formatting. Refer to the [datetime format codes](https://docs.python.org/3/library/datetime.html#format-codes) for more information! |
|
||||
|
||||
| Argument | Description |
|
||||
| --- | --- |
|
||||
| timezone | Specify the timezone to render the date in. If not specified, uses the InvenTree server timezone |
|
||||
| format | Specify the format string to use for rendering the date. If not specified, uses ISO formatting. Refer to the [datetime format codes](https://docs.python.org/3/library/datetime.html#format-codes) for more information! |
|
||||
|
||||
### Example
|
||||
|
||||
@ -106,8 +210,18 @@ Datetime: {% format_datetime my_datetime format="%d-%m-%Y %H:%M%S" %}
|
||||
|
||||
## Currency Formatting
|
||||
|
||||
### render_currency
|
||||
|
||||
The helper function `render_currency` allows for simple rendering of currency data. This function can also convert the specified amount of currency into a different target currency:
|
||||
|
||||
::: InvenTree.helpers_model.render_currency
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
|
||||
#### Example
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% load report %}
|
||||
@ -124,20 +238,40 @@ Total Price: {% render_currency order.total_price currency='NZD' decimal_places=
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
The following keyword arguments are available to the `render_currency` function:
|
||||
|
||||
| Argument | Description |
|
||||
| --- | --- |
|
||||
| currency | Specify the currency code to render in (will attempt conversion if different to provided currency) |
|
||||
| decimal_places | Specify the number of decimal places to render |
|
||||
| min_decimal_places | Specify the minimum number of decimal places to render (ignored if *decimal_places* is specified) |
|
||||
| max_decimal_places | Specify the maximum number of decimal places to render (ignored if *decimal_places* is specified) |
|
||||
| include_symbol | Include currency symbol in rendered value (default = True) |
|
||||
|
||||
## Maths Operations
|
||||
|
||||
Simple mathematical operators are available, as demonstrated in the example template below:
|
||||
|
||||
### add
|
||||
|
||||
::: report.templatetags.report.add
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### subtract
|
||||
|
||||
::: report.templatetags.report.subtract
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### multiply
|
||||
|
||||
::: report.templatetags.report.multiply
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### divide
|
||||
|
||||
::: report.templatetags.report.divide
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### Example
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<!-- Load the report helper functions -->
|
||||
@ -170,10 +304,15 @@ Total: {% multiply line.purchase_price line.quantity %}<br>
|
||||
|
||||
*Media files* are any files uploaded to the InvenTree server by the user. These are stored under the `/media/` directory and can be accessed for use in custom reports or labels.
|
||||
|
||||
### Uploaded Images
|
||||
### uploaded_image
|
||||
|
||||
You can access an uploaded image file if you know the *path* of the image, relative to the top-level `/media/` directory. To load the image into a report, use the `{% raw %}{% uploaded_image ... %}{% endraw %}` tag:
|
||||
|
||||
::: report.templatetags.report.uploaded_image
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<!-- Load the report helper functions -->
|
||||
@ -199,7 +338,12 @@ The `{% raw %}{% uploaded_image %}{% endraw %}` tag supports some optional param
|
||||
{% endraw %}```
|
||||
|
||||
|
||||
### SVG Images
|
||||
### encode_svg_image
|
||||
|
||||
::: report.templatetags.report.encode_svg_image
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
SVG images need to be handled in a slightly different manner. When embedding an uploaded SVG image, use the `{% raw %}{% encode_svg_image ... %}{% endraw %}` tag:
|
||||
|
||||
@ -211,10 +355,15 @@ SVG images need to be handled in a slightly different manner. When embedding an
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
### Part images
|
||||
### part_image
|
||||
|
||||
A shortcut function is provided for rendering an image associated with a Part instance. You can render the image of the part using the `{% raw %}{% part_image ... %}{% endraw %}` template tag:
|
||||
|
||||
::: report.templatetags.report.part_image
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<!-- Load the report helper functions -->
|
||||
@ -225,7 +374,7 @@ A shortcut function is provided for rendering an image associated with a Part in
|
||||
|
||||
#### Image Arguments
|
||||
|
||||
Any optional arguments which can be used in the [uploaded_image tag](#uploaded-images) can be used here too.
|
||||
Any optional arguments which can be used in the [uploaded_image tag](#uploaded_image) can be used here too.
|
||||
|
||||
#### Image Variations
|
||||
|
||||
@ -243,10 +392,15 @@ The *Part* model supports *preview* (256 x 256) and *thumbnail* (128 x 128) vers
|
||||
```
|
||||
|
||||
|
||||
### Company Images
|
||||
### company_image
|
||||
|
||||
A shortcut function is provided for rendering an image associated with a Company instance. You can render the image of the company using the `{% raw %}{% company_image ... %}{% endraw %}` template tag:
|
||||
|
||||
::: report.templatetags.report.company_image
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
<!-- Load the report helper functions -->
|
||||
@ -259,6 +413,31 @@ A shortcut function is provided for rendering an image associated with a Company
|
||||
|
||||
*Preview* and *thumbnail* image variations can be rendered for the `company_image` tag, in a similar manner to [part image variations](#image-variations)
|
||||
|
||||
## Icons
|
||||
|
||||
Some models (e.g. part categories and locations) allow to specify a custom icon. To render these icons in a report, there is a `{% raw %}{% icon location.icon %}{% endraw %}` template tag from the report template library available.
|
||||
|
||||
This tag renders the required html for the icon.
|
||||
|
||||
!!! info "Loading fonts"
|
||||
Additionally the icon fonts need to be loaded into the template. This can be done using the `{% raw %}{% include_icon_fonts %}{% endraw %}` template tag inside of a style block
|
||||
|
||||
!!! tip "Custom classes for styling the icon further"
|
||||
The icon template tag accepts an optional `class` argument which can be used to apply a custom class to the rendered icon used to style the icon further e.g. positioning it, changing it's size, ... `{% raw %}{% icon location.icon class="my-class" %}{% endraw %}`.
|
||||
|
||||
```html
|
||||
{% raw %}
|
||||
{% load report %}
|
||||
|
||||
{% block style %}
|
||||
{% include_icon_fonts %}
|
||||
{% endblock style %}
|
||||
|
||||
{% icon location.icon %}
|
||||
|
||||
{% endraw %}
|
||||
```
|
||||
|
||||
## InvenTree Logo
|
||||
|
||||
A template tag is provided to load the InvenTree logo image into a report. You can render the logo using the `{% raw %}{% logo_image %}{% endraw %}` tag:
|
||||
@ -301,7 +480,14 @@ You can add asset images to the reports and labels by using the `{% raw %}{% ass
|
||||
|
||||
## Part Parameters
|
||||
|
||||
If you need to load a part parameter for a particular Part, within the context of your template, you can use the `part_parameter` template tag.
|
||||
If you need to load a part parameter for a particular Part, within the context of your template, you can use the `part_parameter` template tag:
|
||||
|
||||
::: report.templatetags.report.part_parameter
|
||||
options:
|
||||
show_docstring_description: false
|
||||
show_source: False
|
||||
|
||||
### Example
|
||||
|
||||
The following example assumes that you have a report or label which contains a valid [Part](../part/part.md) instance:
|
||||
|
||||
|
@ -19,6 +19,7 @@ The following report templates are provided "out of the box" and can be used as
|
||||
| [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 |
|
||||
| [Sales Order Shipment](#sales-order-shipment) | [SalesOrderShipment](../order/sales_order.md) | Sales Order Shipment 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 |
|
||||
|
||||
@ -42,6 +43,10 @@ The following report templates are provided "out of the box" and can be used as
|
||||
|
||||
{{ templatefile("report/inventree_sales_order_report.html") }}
|
||||
|
||||
### Sales Order Shipment
|
||||
|
||||
{{ templatefile("report/inventree_sales_order_shipment_report.html") }}
|
||||
|
||||
### Stock Location
|
||||
|
||||
{{ templatefile("report/inventree_stock_location_report.html") }}
|
||||
|
@ -177,7 +177,7 @@ Asset files can be rendered directly into the template as follows
|
||||
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.
|
||||
Upload new assets via the [admin interface](../settings/admin.md) to ensure they are uploaded to the correct location on the server.
|
||||
|
||||
|
||||
## Report Snippets
|
||||
|
@ -4,13 +4,13 @@ title: InvenTree Single Sign On
|
||||
|
||||
## Single Sign On
|
||||
|
||||
InvenTree provides the possibility to use 3rd party services to authenticate users. This functionality makes use of [django-allauth](https://django-allauth.readthedocs.io/en/latest/) and supports a wide array of OpenID and OAuth [providers](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html).
|
||||
InvenTree provides the possibility to use 3rd party services to authenticate users. This functionality makes use of [django-allauth](https://docs.allauth.org/en/latest/) and supports a wide array of OpenID and OAuth [providers](https://docs.allauth.org/en/latest/socialaccount/providers/index.html).
|
||||
|
||||
!!! tip "Provider Documentation"
|
||||
There are a lot of technical considerations when configuring a particular SSO provider. A good starting point is the [django-allauth documentation](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html)
|
||||
There are a lot of technical considerations when configuring a particular SSO provider. A good starting point is the [django-allauth documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html)
|
||||
|
||||
!!! warning "Advanced Users"
|
||||
The SSO functionality provided by django-allauth is powerful, but can prove challenging to configure. Please ensure that you understand the implications of enabling SSO for your InvenTree instance. Specific technical details of each available SSO provider are beyond the scope of this documentation - please refer to the [django-allauth documentation](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html) for more information.
|
||||
The SSO functionality provided by django-allauth is powerful, but can prove challenging to configure. Please ensure that you understand the implications of enabling SSO for your InvenTree instance. Specific technical details of each available SSO provider are beyond the scope of this documentation - please refer to the [django-allauth documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) for more information.
|
||||
|
||||
## SSO Configuration
|
||||
|
||||
@ -31,8 +31,8 @@ There are two variables in the configuration file which define the operation of
|
||||
|
||||
| Environment Variable |Configuration File | Description | More Info |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_SOCIAL_BACKENDS | `social_backends` | A *list* of provider backends enabled for the InvenTree instance | [django-allauth docs](https://django-allauth.readthedocs.io/en/latest/installation/quickstart.html) |
|
||||
| INVENTREE_SOCIAL_PROVIDERS | `social_providers` | A *dict* of settings specific to the installed providers | [provider documentation](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html) |
|
||||
| INVENTREE_SOCIAL_BACKENDS | `social_backends` | A *list* of provider backends enabled for the InvenTree instance | [django-allauth docs](https://docs.allauth.org/en/latest/installation/quickstart.html) |
|
||||
| INVENTREE_SOCIAL_PROVIDERS | `social_providers` | A *dict* of settings specific to the installed providers | [provider documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) |
|
||||
|
||||
In the example below, SSO provider modules are activated for *google*, *github* and *microsoft*. Specific configuration options are specified for the *microsoft* provider module:
|
||||
|
||||
@ -44,7 +44,7 @@ In the example below, SSO provider modules are activated for *google*, *github*
|
||||
Note that the provider modules specified in `social_backends` must be prefixed with `allauth.socialaccounts.providers`
|
||||
|
||||
!!! warning "Provider Documentation"
|
||||
We do not provide any specific documentation for each provider module. Please refer to the [django-allauth documentation](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html) for more information.
|
||||
We do not provide any specific documentation for each provider module. Please refer to the [django-allauth documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) for more information.
|
||||
|
||||
!!! tip "Restart Server"
|
||||
As the [configuration file](../start/config.md) is only read when the server is launched, ensure you restart the server after editing the file.
|
||||
@ -57,7 +57,7 @@ The next step is to create an external authentication app with your provider of
|
||||
The provider application will be created as part of your SSO provider setup. This is *not* the same as the *SocialApp* entry in the InvenTree admin interface.
|
||||
|
||||
!!! info "Read the Documentation"
|
||||
The [django-allauth documentation](https://django-allauth.readthedocs.io/en/latest/socialaccount/providers/index.html) is a good starting point here. There are also a number of good tutorials online (at least for the major supported SSO providers).
|
||||
The [django-allauth documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) is a good starting point here. There are also a number of good tutorials online (at least for the major supported SSO providers).
|
||||
|
||||
In general, the external app will generate a *key* and *secret* pair - although different terminology may be used, depending on the provider.
|
||||
|
||||
@ -132,6 +132,31 @@ In the [settings screen](./global.md), navigate to the *Login Settings* panel. H
|
||||
|
||||
Note that [email settings](./email.md) must be correctly configured before SSO will be activated. Ensure that your email setup is correctly configured and operational.
|
||||
|
||||
## SSO Group Sync Configuration
|
||||
|
||||
InvenTree has the ability to synchronize groups assigned to each user directly from the IdP. To enable this feature, navigate to the *Login Settings* panel in the [settings screen](./global.md) first. Here, the following options are available:
|
||||
|
||||
| Setting | Description |
|
||||
| --- | --- |
|
||||
| Enable SSO group sync | Enable synchronizing InvenTree groups with groups provided by the IdP |
|
||||
| SSO group key | The name of the claim containing all groups, e.g. `groups` or `roles` |
|
||||
| SSO group map | A mapping from SSO groups to InvenTree groups as JSON, e.g. `{"/inventree/admins": "admin"}`. If the mapped group does not exist once a user signs up, a new group without assigned permissions will be created. |
|
||||
| Remove groups outside of SSO | Whether groups should be removed from the user if they are not present in the IdP data |
|
||||
|
||||
!!! warning "Remove groups outside of SSO"
|
||||
Disabling this feature might cause security issues as groups that are removed in the IdP will stay assigned in InvenTree
|
||||
|
||||
### Keycloak OIDC example configuration
|
||||
|
||||
!!! tip "Configuration for different IdPs"
|
||||
The main challenge in enabling the SSO group sync feature is for the SSO admin to configure the IdP such that the groups are correctly represented in in the Django allauth `extra_data` attribute. The SSO group sync feature has been developed and tested using integrated Keycloak users/groups and OIDC. If you are utilizing this feature using another IdP, kindly consider documenting your configuration steps as well.
|
||||
|
||||
Keycloak groups are not sent to the OIDC client by default. To enable such functionality, create a new client scope named `groups` in the Keycloak admin console. For this scope, add a new mapper ('By Configuration') and select 'Group Membership'. Give it a descriptive name and set the token claim name to `groups`.
|
||||
|
||||
For each OIDC client that relies on those group, explicitly add the `groups` scope to client scopes. The groups will now be sent to client upon request.
|
||||
|
||||
**Note:** A group named `foo` will be displayed as `/foo`. For this reason, the example above recommends using group names like `appname/rolename` which will be sent to the client as `/appname/rolename`.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
You should use SSL for your website if you want to use this feature. Also set your callback-endpoints to `https://` addresses to reduce the risk of leaking user's tokens.
|
||||
|
@ -24,12 +24,7 @@ If a different currency exchange backend is needed, or a custom implementation i
|
||||
|
||||
### Currency Settings
|
||||
|
||||
In the [settings screen](./global.md), under the *Pricing* section, the following currency settings are available:
|
||||
|
||||
| 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 |
|
||||
Refer to the [global settings](./global.md#pricing-and-currency) documentation for more information on available currency settings.
|
||||
|
||||
#### Supported Currencies
|
||||
|
||||
|