mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'master' of https://github.com/inventree/InvenTree into add-changelog
This commit is contained in:
commit
01b7ba30c9
@ -1,16 +1,11 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/python-3
|
||||
{
|
||||
"name": "InvenTree",
|
||||
"build": {
|
||||
"dockerfile": "../Dockerfile",
|
||||
"context": "..",
|
||||
"target": "devcontainer",
|
||||
"args": {
|
||||
"base_image": "mcr.microsoft.com/vscode/devcontainers/base:alpine-3.18",
|
||||
"workspace": "${containerWorkspaceFolder}"
|
||||
}
|
||||
},
|
||||
"name": "InvenTree devcontainer",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "inventree",
|
||||
"overrideCommand": true,
|
||||
"workspaceFolder": "/home/inventree/",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
@ -42,37 +37,27 @@
|
||||
},
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [5173, 8000],
|
||||
"forwardPorts": [5173, 8000, 8080],
|
||||
"portsAttributes": {
|
||||
"5173": {
|
||||
"label": "Vite server"
|
||||
"label": "Vite Server"
|
||||
},
|
||||
"8000": {
|
||||
"label": "InvenTree server"
|
||||
"label": "InvenTree Server"
|
||||
},
|
||||
"8080": {
|
||||
"label": "mkdocs server"
|
||||
}
|
||||
},
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "./.devcontainer/postCreateCommand.sh ${containerWorkspaceFolder}",
|
||||
"postCreateCommand": ".devcontainer/postCreateCommand.sh",
|
||||
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode",
|
||||
"containerUser": "vscode",
|
||||
|
||||
"remoteEnv": {
|
||||
// InvenTree config
|
||||
"INVENTREE_DEBUG": "True",
|
||||
"INVENTREE_LOG_LEVEL": "INFO",
|
||||
"INVENTREE_DB_ENGINE": "sqlite3",
|
||||
"INVENTREE_DB_NAME": "${containerWorkspaceFolder}/dev/database.sqlite3",
|
||||
"INVENTREE_MEDIA_ROOT": "${containerWorkspaceFolder}/dev/media",
|
||||
"INVENTREE_STATIC_ROOT": "${containerWorkspaceFolder}/dev/static",
|
||||
"INVENTREE_BACKUP_DIR": "${containerWorkspaceFolder}/dev/backup",
|
||||
"INVENTREE_CONFIG_FILE": "${containerWorkspaceFolder}/dev/config.yaml",
|
||||
"INVENTREE_SECRET_KEY_FILE": "${containerWorkspaceFolder}/dev/secret_key.txt",
|
||||
"INVENTREE_PLUGINS_ENABLED": "True",
|
||||
"INVENTREE_PLUGIN_DIR": "${containerWorkspaceFolder}/dev/plugins",
|
||||
"INVENTREE_PLUGIN_FILE": "${containerWorkspaceFolder}/dev/plugins.txt",
|
||||
|
||||
// Python config
|
||||
"PIP_USER": "no",
|
||||
|
38
.devcontainer/docker-compose.yml
Normal file
38
.devcontainer/docker-compose.yml
Normal file
@ -0,0 +1,38 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:13
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 5432/tcp
|
||||
volumes:
|
||||
- ../dev:/var/lib/postgresql/data:z
|
||||
environment:
|
||||
POSTGRES_DB: inventree
|
||||
POSTGRES_USER: inventree_user
|
||||
POSTGRES_PASSWORD: inventree_password
|
||||
|
||||
inventree:
|
||||
build:
|
||||
context: ..
|
||||
target: dev
|
||||
args:
|
||||
base_image: "mcr.microsoft.com/vscode/devcontainers/base:alpine-3.18"
|
||||
workspace: "${containerWorkspaceFolder}"
|
||||
data_dir: "dev"
|
||||
volumes:
|
||||
- ../:/home/inventree:z
|
||||
|
||||
environment:
|
||||
INVENTREE_DEBUG: True
|
||||
INVENTREE_DB_ENGINE: postgresql
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_DB_HOST: db
|
||||
INVENTREE_DB_USER: inventree_user
|
||||
INVENTREE_DB_PASSWORD: inventree_password
|
||||
INVENTREE_PLUGINS_ENABLED: True
|
||||
INVENTREE_PY_ENV: /home/inventree/dev/venv
|
||||
|
||||
depends_on:
|
||||
- db
|
@ -1,20 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Avoiding Dubious Ownership in Dev Containers for setup commands that use git
|
||||
# Note that the local workspace directory is passed through as the first argument $1
|
||||
git config --global --add safe.directory $1
|
||||
|
||||
# create folders
|
||||
mkdir -p $1/dev/{commandhistory,plugins}
|
||||
cd $1
|
||||
git config --global --add safe.directory /home/inventree
|
||||
|
||||
# create venv
|
||||
python3 -m venv $1/dev/venv
|
||||
. $1/dev/venv/bin/activate
|
||||
python3 -m venv /home/inventree/dev/venv --system-site-packages --upgrade-deps
|
||||
. /home/inventree/dev/venv/bin/activate
|
||||
|
||||
# setup InvenTree server
|
||||
pip install invoke
|
||||
invoke update
|
||||
invoke update -s
|
||||
invoke setup-dev
|
||||
invoke frontend-install
|
||||
|
||||
|
4
.github/actions/setup/action.yaml
vendored
4
.github/actions/setup/action.yaml
vendored
@ -86,8 +86,8 @@ runs:
|
||||
- name: Run invoke install
|
||||
if: ${{ inputs.install == 'true' }}
|
||||
shell: bash
|
||||
run: invoke install
|
||||
run: invoke install --uv
|
||||
- name: Run invoke update
|
||||
if: ${{ inputs.update == 'true' }}
|
||||
shell: bash
|
||||
run: invoke update
|
||||
run: invoke update --uv
|
||||
|
2
.github/workflows/docker.yaml
vendored
2
.github/workflows/docker.yaml
vendored
@ -156,7 +156,7 @@ jobs:
|
||||
sbom: true
|
||||
provenance: false
|
||||
target: production
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
tags: ${{ env.docker_tags }}
|
||||
build-args: |
|
||||
commit_hash=${{ env.git_commit_hash }}
|
||||
commit_date=${{ env.git_commit_date }}
|
||||
|
9
.github/workflows/qc_checks.yaml
vendored
9
.github/workflows/qc_checks.yaml
vendored
@ -111,9 +111,12 @@ jobs:
|
||||
pip install -r docs/requirements.txt
|
||||
python docs/ci/check_mkdocs_config.py
|
||||
- name: Check Links
|
||||
run: |
|
||||
pip install linkcheckmd requests
|
||||
python -m linkcheckmd docs --recurse
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
folder-path: docs
|
||||
config-file: docs/mlc_config.json
|
||||
check-modified-files-only: 'yes'
|
||||
use-quiet-mode: 'yes'
|
||||
|
||||
schema:
|
||||
name: Tests - API Schema Documentation
|
||||
|
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -14,7 +14,7 @@
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "Python: Django - 3rd party",
|
||||
"name": "InvenTree Server - 3rd party",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/InvenTree/manage.py",
|
||||
|
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
@ -45,7 +45,7 @@
|
||||
{
|
||||
"label": "setup-test",
|
||||
"type": "shell",
|
||||
"command": "inv setup-test --path dev/inventree-demo-dataset",
|
||||
"command": "inv setup-test -i --path dev/inventree-demo-dataset",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
|
265
CONTRIBUTING.md
265
CONTRIBUTING.md
@ -1,263 +1,6 @@
|
||||
### Contributing to InvenTree
|
||||
|
||||
Hi there, thank you for your interest in contributing!
|
||||
Please read the contribution guidelines below, before submitting your first pull request to the InvenTree codebase.
|
||||
Please read our contribution guidelines, before submitting your first pull request to the InvenTree codebase.
|
||||
|
||||
## Quickstart
|
||||
|
||||
The following commands will get you quickly configure and run a development server, complete with a demo dataset to work with:
|
||||
|
||||
### Bare Metal
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
git clone https://github.com/inventree/InvenTree.git && cd InvenTree
|
||||
docker compose run inventree-dev-server invoke install
|
||||
docker compose run inventree-dev-server invoke setup-test --dev
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Read the [InvenTree setup documentation](https://docs.inventree.org/en/latest/start/intro/) for a complete installation reference guide.
|
||||
|
||||
### Setup Devtools
|
||||
|
||||
Run the following command to set up all toolsets for development.
|
||||
|
||||
```bash
|
||||
invoke 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.
|
||||
|
||||
### Version Numbering
|
||||
|
||||
InvenTree version numbering follows the [semantic versioning](https://semver.org/) specification.
|
||||
|
||||
### Master Branch
|
||||
|
||||
The HEAD of the "main" or "master" branch of InvenTree represents the current "latest" state of code development.
|
||||
|
||||
- All feature branches are merged into master
|
||||
- All bug fixes are merged into master
|
||||
|
||||
**No pushing to master:** New features must be submitted as a pull request from a separate branch (one branch per feature).
|
||||
|
||||
### Feature Branches
|
||||
|
||||
Feature branches should be branched *from* the *master* branch.
|
||||
|
||||
- One major feature per branch / pull request
|
||||
- Feature pull requests are merged back *into* the master branch
|
||||
- Features *may* also be merged into a release candidate branch
|
||||
|
||||
### Stable Branch
|
||||
|
||||
The HEAD of the "stable" branch represents the latest stable release code.
|
||||
|
||||
- Versioned releases are merged into the "stable" branch
|
||||
- Bug fix branches are made *from* the "stable" branch
|
||||
|
||||
#### Release Candidate Branches
|
||||
|
||||
- Release candidate branches are made from master, and merged into stable.
|
||||
- RC branches are targeted at a major/minor version e.g. "0.5"
|
||||
- When a release candidate branch is merged into *stable*, the release is tagged
|
||||
|
||||
#### Bugfix Branches
|
||||
|
||||
- If a bug is discovered in a tagged release version of InvenTree, a "bugfix" or "hotfix" branch should be made *from* that tagged release
|
||||
- When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2)
|
||||
- The bugfix *must* also be cherry picked into the *master* branch.
|
||||
|
||||
## API versioning
|
||||
|
||||
The [API version](https://github.com/inventree/InvenTree/blob/master/InvenTree/InvenTree/api_version.py) needs to be bumped every time when the API is changed.
|
||||
|
||||
## Environment
|
||||
### Target version
|
||||
We are currently targeting:
|
||||
| Name | Minimum version | Note |
|
||||
|---|---| --- |
|
||||
| Python | 3.9 | |
|
||||
| Django | 3.2 | |
|
||||
| Node | 18 | Only needed for frontend development |
|
||||
|
||||
### Auto creating updates
|
||||
The following tools can be used to auto-upgrade syntax that was depreciated in new versions:
|
||||
```bash
|
||||
pip install pyupgrade
|
||||
pip install django-upgrade
|
||||
```
|
||||
|
||||
To update the codebase run the following script.
|
||||
```bash
|
||||
pyupgrade `find . -name "*.py"`
|
||||
django-upgrade --target-version 3.2 `find . -name "*.py"`
|
||||
```
|
||||
|
||||
## Credits
|
||||
If you add any new dependencies / libraries, they need to be added to [the docs](https://github.com/inventree/inventree/blob/master/docs/docs/credits.md). Please try to do that as timely as possible.
|
||||
|
||||
|
||||
## Migration Files
|
||||
|
||||
Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR.
|
||||
|
||||
*Note: A github action checks for unstaged migration files and will reject the PR if it finds any!*
|
||||
|
||||
## Unit Testing
|
||||
|
||||
Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage for any new features is insufficient, or the overall code coverage is decreased.
|
||||
|
||||
The InvenTree code base makes use of [GitHub actions](https://github.com/features/actions) to run a suite of automated tests against the code base every time a new pull request is received. These actions include (but are not limited to):
|
||||
|
||||
- Checking Python and Javascript code against standard style guides
|
||||
- Running unit test suite
|
||||
- Automated building and pushing of docker images
|
||||
- Generating translation files
|
||||
|
||||
The various github actions can be found in the `./github/workflows` directory
|
||||
|
||||
### Run tests locally
|
||||
|
||||
To run test locally, use:
|
||||
```
|
||||
invoke test
|
||||
```
|
||||
|
||||
To run only partial tests, for example for a module use:
|
||||
```
|
||||
invoke test --runtest order
|
||||
```
|
||||
|
||||
To see all the available options:
|
||||
|
||||
```
|
||||
invoke test --help
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### Backend Code
|
||||
|
||||
Backend code (Python) is checked against the [PEP style guidelines](https://peps.python.org/pep-0008/). Please write docstrings for each function and class - we follow the [google doc-style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) for python.
|
||||
|
||||
### Frontend Code
|
||||
|
||||
Frontend code (Javascript) is checked using [eslint](https://eslint.org/). While docstrings are not enforced for front-end code, good code documentation is encouraged!
|
||||
|
||||
### Running Checks Locally
|
||||
|
||||
If you have followed the setup devtools procedure, then code style checking is performend automatically whenever you commit changes to the code.
|
||||
|
||||
### Django templates
|
||||
|
||||
Django are checked by [djlint](https://github.com/Riverside-Healthcare/djlint) through pre-commit.
|
||||
|
||||
The following rules out of the [default set](https://djlint.com/docs/linter/) are not applied:
|
||||
```bash
|
||||
D018: (Django) Internal links should use the { % url ... % } pattern
|
||||
H006: Img tag should have height and width attributes
|
||||
H008: Attributes should be double quoted
|
||||
H021: Inline styles should be avoided
|
||||
H023: Do not use entity references
|
||||
H025: Tag seems to be an orphan
|
||||
H030: Consider adding a meta description
|
||||
H031: Consider adding meta keywords
|
||||
T002: Double quotes should be used in tags
|
||||
```
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
New features or updates to existing features should be accompanied by user documentation.
|
||||
|
||||
## Translations
|
||||
|
||||
Any user-facing strings *must* be passed through the translation engine.
|
||||
|
||||
- InvenTree code is written in English
|
||||
- User translatable strings are provided in English as the primary language
|
||||
- Secondary language translations are provided [via Crowdin](https://crowdin.com/project/inventree)
|
||||
|
||||
*Note: Translation files are updated via GitHub actions - you do not need to compile translations files before submitting a pull request!*
|
||||
|
||||
### Python Code
|
||||
|
||||
For strings exposed via Python code, use the following format:
|
||||
|
||||
```python
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
user_facing_string = _('This string will be exposed to the translation engine!')
|
||||
```
|
||||
|
||||
### Templated Strings
|
||||
|
||||
HTML and javascript files are passed through the django templating engine. Translatable strings are implemented as follows:
|
||||
|
||||
```html
|
||||
{ % load i18n % }
|
||||
|
||||
<span>{ % trans "This string will be translated" % } - this string will not!</span>
|
||||
```
|
||||
|
||||
## Github use
|
||||
|
||||
### Tags
|
||||
|
||||
The tags describe issues and PRs in multiple areas:
|
||||
|
||||
| Area | Name | Description |
|
||||
| --- | --- | --- |
|
||||
| Triage Labels | | |
|
||||
| | triage:not-checked | Item was not checked by the core team |
|
||||
| | triage:not-approved | Item is not green-light by maintainer |
|
||||
| Type Labels | | |
|
||||
| | breaking | Indicates a major update or change which breaks compatibility |
|
||||
| | bug | Identifies a bug which needs to be addressed |
|
||||
| | dependency | Relates to a project dependency |
|
||||
| | duplicate | Duplicate of another issue or PR |
|
||||
| | enhancement | This is an suggested enhancement, extending the functionality of an existing feature |
|
||||
| | experimental | This is a new *experimental* feature which needs to be enabled manually |
|
||||
| | feature | This is a new feature, introducing novel functionality |
|
||||
| | help wanted | Assistance required |
|
||||
| | invalid | This issue or PR is considered invalid |
|
||||
| | inactive | Indicates lack of activity |
|
||||
| | migration | Database migration, requires special attention |
|
||||
| | question | This is a question |
|
||||
| | roadmap | This is a roadmap feature with no immediate plans for implementation |
|
||||
| | security | Relates to a security issue |
|
||||
| | starter | Good issue for a developer new to the project |
|
||||
| | wontfix | No work will be done against this issue or PR |
|
||||
| Feature Labels | | |
|
||||
| | API | Relates to the API |
|
||||
| | barcode | Barcode scanning and integration |
|
||||
| | build | Build orders |
|
||||
| | importer | Data importing and processing |
|
||||
| | order | Purchase order and sales orders |
|
||||
| | part | Parts |
|
||||
| | plugin | Plugin ecosystem |
|
||||
| | pricing | Pricing functionality |
|
||||
| | report | Report generation |
|
||||
| | stock | Stock item management |
|
||||
| | user interface | User interface |
|
||||
| Ecosystem Labels | | |
|
||||
| | backport | Tags that the issue will be backported to a stable branch as a bug-fix |
|
||||
| | demo | Relates to the InvenTree demo server or dataset |
|
||||
| | docker | Docker / docker-compose |
|
||||
| | CI | CI / unit testing ecosystem |
|
||||
| | refactor | Refactoring existing code |
|
||||
| | setup | Relates to the InvenTree setup / installation process |
|
||||
Refer to our [contribution guidelines](https://docs.inventree.org/en/latest/develop/contributing/) for more information!
|
||||
|
19
Dockerfile
19
Dockerfile
@ -17,6 +17,8 @@ ARG commit_tag=""
|
||||
ARG commit_hash=""
|
||||
ARG commit_date=""
|
||||
|
||||
ARG data_dir="data"
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV PIP_DISABLE_PIP_VERSION_CHECK 1
|
||||
ENV INVOKE_RUN_SHELL="/bin/ash"
|
||||
@ -27,7 +29,7 @@ ENV INVENTREE_DOCKER="true"
|
||||
# InvenTree paths
|
||||
ENV INVENTREE_HOME="/home/inventree"
|
||||
ENV INVENTREE_MNG_DIR="${INVENTREE_HOME}/InvenTree"
|
||||
ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data"
|
||||
ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/${data_dir}"
|
||||
ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static"
|
||||
ENV INVENTREE_MEDIA_ROOT="${INVENTREE_DATA_DIR}/media"
|
||||
ENV INVENTREE_BACKUP_DIR="${INVENTREE_DATA_DIR}/backup"
|
||||
@ -62,7 +64,9 @@ RUN apk add --no-cache \
|
||||
# Image format support
|
||||
libjpeg libwebp zlib \
|
||||
# Weasyprint requirements : https://doc.courtbouillon.org/weasyprint/stable/first_steps.html#alpine-3-12
|
||||
py3-pip py3-pillow py3-cffi py3-brotli pango poppler-utils openldap && \
|
||||
py3-pip py3-pillow py3-cffi py3-brotli pango poppler-utils openldap \
|
||||
# Core database packages
|
||||
postgresql13-client && \
|
||||
# fonts
|
||||
apk --update --upgrade --no-cache add fontconfig ttf-freefont font-noto terminus-font && fc-cache -f
|
||||
|
||||
@ -92,7 +96,7 @@ FROM inventree_base as prebuild
|
||||
|
||||
ENV PATH=/root/.local/bin:$PATH
|
||||
RUN ./install_build_packages.sh --no-cache --virtual .build-deps && \
|
||||
pip install --user uv --no-cache-dir && uv pip install -r base_requirements.txt -r requirements.txt --no-cache && \
|
||||
pip install --user uv --no-cache-dir && pip install -r base_requirements.txt -r requirements.txt --no-cache && \
|
||||
apk --purge del .build-deps
|
||||
|
||||
# Frontend builder image:
|
||||
@ -137,7 +141,7 @@ EXPOSE 5173
|
||||
# Install packages required for building python packages
|
||||
RUN ./install_build_packages.sh
|
||||
|
||||
RUN pip install uv --no-cache-dir && uv pip install -r base_requirements.txt --no-cache
|
||||
RUN pip install uv --no-cache-dir && pip install -r base_requirements.txt --no-cache
|
||||
|
||||
# Install nodejs / npm / yarn
|
||||
|
||||
@ -160,10 +164,3 @@ ENTRYPOINT ["/bin/ash", "./docker/init.sh"]
|
||||
|
||||
# Launch the development server
|
||||
CMD ["invoke", "server", "-a", "${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT}"]
|
||||
|
||||
# Image target for devcontainer
|
||||
FROM dev as devcontainer
|
||||
|
||||
ARG workspace="/workspaces/InvenTree"
|
||||
|
||||
WORKDIR ${WORKSPACE}
|
||||
|
@ -1,11 +1,17 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 177
|
||||
INVENTREE_API_VERSION = 178
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v178 - 2024-02-29 : https://github.com/inventree/InvenTree/pull/6604
|
||||
- Adds "external_stock" field to the Part API endpoint
|
||||
- Adds "external_stock" field to the BomItem API endpoint
|
||||
- Adds "external_stock" field to the BuildLine API endpoint
|
||||
- Stock quantites represented in the BuildLine API endpoint are now filtered by Build.source_location
|
||||
|
||||
v177 - 2024-02-27 : https://github.com/inventree/InvenTree/pull/6581
|
||||
- Adds "subcategoies" count to PartCategoryTree serializer
|
||||
- Adds "sublocations" count to StockLocationTree serializer
|
||||
|
@ -10,6 +10,9 @@ import string
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import Storage
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
CONFIG_DATA = None
|
||||
CONFIG_LOOKUPS = {}
|
||||
@ -69,11 +72,16 @@ def get_base_dir() -> Path:
|
||||
return Path(__file__).parent.parent.resolve()
|
||||
|
||||
|
||||
def ensure_dir(path: Path) -> None:
|
||||
def ensure_dir(path: Path, storage=None) -> None:
|
||||
"""Ensure that a directory exists.
|
||||
|
||||
If it does not exist, create it.
|
||||
"""
|
||||
if storage and isinstance(storage, Storage):
|
||||
if not storage.exists(str(path)):
|
||||
storage.save(str(path / '.empty'), ContentFile(''))
|
||||
return
|
||||
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
@ -136,6 +136,11 @@ STATICFILES_DIRS = []
|
||||
STATICFILES_I18_PREFIX = 'i18n'
|
||||
STATICFILES_I18_SRC = BASE_DIR.joinpath('templates', 'js', 'translated')
|
||||
STATICFILES_I18_TRG = BASE_DIR.joinpath('InvenTree', 'static_i18n')
|
||||
|
||||
# Create the target directory if it does not exist
|
||||
if not STATICFILES_I18_TRG.exists():
|
||||
STATICFILES_I18_TRG.mkdir(parents=True)
|
||||
|
||||
STATICFILES_DIRS.append(STATICFILES_I18_TRG)
|
||||
STATICFILES_I18_TRG = STATICFILES_I18_TRG.joinpath(STATICFILES_I18_PREFIX)
|
||||
|
||||
@ -989,6 +994,9 @@ ALLOWED_HOSTS = get_setting(
|
||||
typecast=list,
|
||||
)
|
||||
|
||||
if SITE_URL and SITE_URL not in ALLOWED_HOSTS:
|
||||
ALLOWED_HOSTS.append(SITE_URL)
|
||||
|
||||
# List of trusted origins for unsafe requests
|
||||
# Ref: https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins
|
||||
CSRF_TRUSTED_ORIGINS = get_setting(
|
||||
@ -999,7 +1007,7 @@ CSRF_TRUSTED_ORIGINS = get_setting(
|
||||
)
|
||||
|
||||
# If a list of trusted is not specified, but a site URL has been specified, use that
|
||||
if SITE_URL and len(CSRF_TRUSTED_ORIGINS) == 0:
|
||||
if SITE_URL and SITE_URL not in CSRF_TRUSTED_ORIGINS:
|
||||
CSRF_TRUSTED_ORIGINS.append(SITE_URL)
|
||||
|
||||
USE_X_FORWARDED_HOST = get_boolean_setting(
|
||||
@ -1040,7 +1048,7 @@ CORS_ALLOWED_ORIGINS = get_setting(
|
||||
)
|
||||
|
||||
# If no CORS origins are specified, but a site URL has been specified, use that
|
||||
if SITE_URL and len(CORS_ALLOWED_ORIGINS) == 0:
|
||||
if SITE_URL and SITE_URL not in CORS_ALLOWED_ORIGINS:
|
||||
CORS_ALLOWED_ORIGINS.append(SITE_URL)
|
||||
|
||||
for app in SOCIAL_BACKENDS:
|
||||
|
@ -314,11 +314,21 @@ class BuildLineEndpoint:
|
||||
queryset = BuildLine.objects.all()
|
||||
serializer_class = build.serializers.BuildLineSerializer
|
||||
|
||||
def get_source_build(self) -> Build:
|
||||
"""Return the source Build object for the BuildLine queryset.
|
||||
|
||||
This source build is used to filter the available stock for each BuildLine.
|
||||
|
||||
- If this is a "detail" view, use the build associated with the line
|
||||
- If this is a "list" view, use the build associated with the request
|
||||
"""
|
||||
raise NotImplementedError("get_source_build must be implemented in the child class")
|
||||
|
||||
def get_queryset(self):
|
||||
"""Override queryset to select-related and annotate"""
|
||||
queryset = super().get_queryset()
|
||||
|
||||
queryset = build.serializers.BuildLineSerializer.annotate_queryset(queryset)
|
||||
source_build = self.get_source_build()
|
||||
queryset = build.serializers.BuildLineSerializer.annotate_queryset(queryset, build=source_build)
|
||||
|
||||
return queryset
|
||||
|
||||
@ -353,10 +363,26 @@ class BuildLineList(BuildLineEndpoint, ListCreateAPI):
|
||||
'bom_item__reference',
|
||||
]
|
||||
|
||||
def get_source_build(self) -> Build:
|
||||
"""Return the target build for the BuildLine queryset."""
|
||||
|
||||
try:
|
||||
build_id = self.request.query_params.get('build', None)
|
||||
if build_id:
|
||||
build = Build.objects.get(pk=build_id)
|
||||
return build
|
||||
except (Build.DoesNotExist, AttributeError, ValueError):
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
class BuildLineDetail(BuildLineEndpoint, RetrieveUpdateDestroyAPI):
|
||||
"""API endpoint for detail view of a BuildLine object."""
|
||||
pass
|
||||
|
||||
def get_source_build(self) -> Build:
|
||||
"""Return the target source location for the BuildLine queryset."""
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class BuildOrderContextMixin:
|
||||
|
@ -1083,6 +1083,7 @@ class BuildLineSerializer(InvenTreeModelSerializer):
|
||||
'available_substitute_stock',
|
||||
'available_variant_stock',
|
||||
'total_available_stock',
|
||||
'external_stock',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
@ -1124,15 +1125,23 @@ class BuildLineSerializer(InvenTreeModelSerializer):
|
||||
available_substitute_stock = serializers.FloatField(read_only=True)
|
||||
available_variant_stock = serializers.FloatField(read_only=True)
|
||||
total_available_stock = serializers.FloatField(read_only=True)
|
||||
external_stock = serializers.FloatField(read_only=True)
|
||||
|
||||
@staticmethod
|
||||
def annotate_queryset(queryset):
|
||||
def annotate_queryset(queryset, build=None):
|
||||
"""Add extra annotations to the queryset:
|
||||
|
||||
- allocated: Total stock quantity allocated against this build line
|
||||
- available: Total stock available for allocation against this build line
|
||||
- on_order: Total stock on order for this build line
|
||||
- in_production: Total stock currently in production for this build line
|
||||
|
||||
Arguments:
|
||||
queryset: The queryset to annotate
|
||||
build: The build order to filter against (optional)
|
||||
|
||||
Note: If the 'build' is provided, we can use it to filter available stock, depending on the specified location for the build
|
||||
|
||||
"""
|
||||
queryset = queryset.select_related(
|
||||
'build', 'bom_item',
|
||||
@ -1169,6 +1178,18 @@ class BuildLineSerializer(InvenTreeModelSerializer):
|
||||
|
||||
ref = 'bom_item__sub_part__'
|
||||
|
||||
stock_filter = None
|
||||
|
||||
if build is not None and build.take_from is not None:
|
||||
location = build.take_from
|
||||
# Filter by locations below the specified location
|
||||
stock_filter = Q(
|
||||
location__tree_id=location.tree_id,
|
||||
location__lft__gte=location.lft,
|
||||
location__rght__lte=location.rght,
|
||||
location__level__gte=location.level,
|
||||
)
|
||||
|
||||
# Annotate the "in_production" quantity
|
||||
queryset = queryset.annotate(
|
||||
in_production=part.filters.annotate_in_production_quantity(reference=ref)
|
||||
@ -1181,10 +1202,8 @@ class BuildLineSerializer(InvenTreeModelSerializer):
|
||||
)
|
||||
|
||||
# Annotate the "available" quantity
|
||||
# TODO: In the future, this should be refactored.
|
||||
# TODO: Note that part.serializers.BomItemSerializer also has a similar annotation
|
||||
queryset = queryset.alias(
|
||||
total_stock=part.filters.annotate_total_stock(reference=ref),
|
||||
total_stock=part.filters.annotate_total_stock(reference=ref, filter=stock_filter),
|
||||
allocated_to_sales_orders=part.filters.annotate_sales_order_allocations(reference=ref),
|
||||
allocated_to_build_orders=part.filters.annotate_build_order_allocations(reference=ref),
|
||||
)
|
||||
@ -1197,11 +1216,21 @@ class BuildLineSerializer(InvenTreeModelSerializer):
|
||||
)
|
||||
)
|
||||
|
||||
external_stock_filter = Q(location__external=True)
|
||||
|
||||
if stock_filter:
|
||||
external_stock_filter &= stock_filter
|
||||
|
||||
# Add 'external stock' annotations
|
||||
queryset = queryset.annotate(
|
||||
external_stock=part.filters.annotate_total_stock(reference=ref, filter=external_stock_filter)
|
||||
)
|
||||
|
||||
ref = 'bom_item__substitutes__part__'
|
||||
|
||||
# Extract similar information for any 'substitute' parts
|
||||
queryset = queryset.alias(
|
||||
substitute_stock=part.filters.annotate_total_stock(reference=ref),
|
||||
substitute_stock=part.filters.annotate_total_stock(reference=ref, filter=stock_filter),
|
||||
substitute_build_allocations=part.filters.annotate_build_order_allocations(reference=ref),
|
||||
substitute_sales_allocations=part.filters.annotate_sales_order_allocations(reference=ref)
|
||||
)
|
||||
@ -1215,7 +1244,7 @@ class BuildLineSerializer(InvenTreeModelSerializer):
|
||||
)
|
||||
|
||||
# Annotate the queryset with 'available variant stock' information
|
||||
variant_stock_query = part.filters.variant_stock_query(reference='bom_item__sub_part__')
|
||||
variant_stock_query = part.filters.variant_stock_query(reference='bom_item__sub_part__', filter=stock_filter)
|
||||
|
||||
queryset = queryset.alias(
|
||||
variant_stock_total=part.filters.annotate_variant_quantity(variant_stock_query, reference='quantity'),
|
||||
|
@ -200,6 +200,11 @@
|
||||
<div id='build-lines-toolbar'>
|
||||
{% include "filter_list.html" with id='buildlines' %}
|
||||
</div>
|
||||
{% if build.take_from %}
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Available stock has been filtered based on specified source location for this build order" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<table class='table table-striped table-condensed' id='build-lines-table' data-toolbar='#build-lines-toolbar'></table>
|
||||
</div>
|
||||
</div>
|
||||
@ -374,6 +379,9 @@ onPanelLoad('allocate', function() {
|
||||
"#build-lines-table",
|
||||
{{ build.pk }},
|
||||
{
|
||||
{% if build.take_from %}
|
||||
location: {{ build.take_from.pk }},
|
||||
{% endif %}
|
||||
{% if build.project_code %}
|
||||
project_code: {{ build.project_code.pk }},
|
||||
{% endif %}
|
||||
|
@ -675,12 +675,14 @@ class BaseInvenTreeSetting(models.Model):
|
||||
}
|
||||
|
||||
try:
|
||||
setting = cls.objects.get(**filters)
|
||||
except cls.DoesNotExist:
|
||||
if create:
|
||||
setting = cls(key=key, **kwargs)
|
||||
else:
|
||||
return
|
||||
setting = cls.objects.filter(**filters).first()
|
||||
|
||||
if not setting:
|
||||
if create:
|
||||
setting = cls(key=key, **kwargs)
|
||||
else:
|
||||
return
|
||||
|
||||
except (OperationalError, ProgrammingError):
|
||||
if not key.startswith('_'):
|
||||
logger.warning("Database is locked, cannot set setting '%s'", key)
|
||||
|
@ -215,7 +215,7 @@ login_default_protocol: http
|
||||
|
||||
# Remote / proxy login
|
||||
# These settings can introduce security problems if configured incorrectly. Please read
|
||||
# https://docs.djangoproject.com/en/4.0/howto/auth-remote-user/ for more details
|
||||
# https://docs.djangoproject.com/en/4.2/howto/auth-remote-user/ for more details
|
||||
# The header name should be prefixed by `HTTP`. Please read the docs for more details
|
||||
# https://docs.djangoproject.com/en/stable/ref/request-response/#django.http.HttpRequest.META
|
||||
remote_login_enabled: False
|
||||
|
0
InvenTree/generic/templating/__init__.py
Normal file
0
InvenTree/generic/templating/__init__.py
Normal file
140
InvenTree/generic/templating/apps.py
Normal file
140
InvenTree/generic/templating/apps.py
Normal file
@ -0,0 +1,140 @@
|
||||
"""Shared templating code."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import AppRegistryNotReady
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
|
||||
|
||||
from maintenance_mode.core import maintenance_mode_on, set_maintenance_mode
|
||||
|
||||
import InvenTree.helpers
|
||||
from InvenTree.config import ensure_dir
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
MEDIA_STORAGE_DIR = Path(settings.MEDIA_ROOT)
|
||||
|
||||
|
||||
class TemplatingMixin:
|
||||
"""Mixin that contains shared templating code."""
|
||||
|
||||
name: str = ''
|
||||
db: str = ''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Ensure that the required properties are set."""
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.name == '':
|
||||
raise NotImplementedError('ref must be set')
|
||||
if self.db == '':
|
||||
raise NotImplementedError('db must be set')
|
||||
|
||||
def create_defaults(self):
|
||||
"""Function that creates all default templates for the app."""
|
||||
raise NotImplementedError('create_defaults must be implemented')
|
||||
|
||||
def get_src_dir(self, ref_name):
|
||||
"""Get the source directory for the default templates."""
|
||||
raise NotImplementedError('get_src_dir must be implemented')
|
||||
|
||||
def get_new_obj_data(self, data, filename):
|
||||
"""Get the data for a new template db object."""
|
||||
raise NotImplementedError('get_new_obj_data must be implemented')
|
||||
|
||||
# Standardized code
|
||||
def ready(self):
|
||||
"""This function is called whenever the app is loaded."""
|
||||
import InvenTree.ready
|
||||
|
||||
# skip loading if plugin registry is not loaded or we run in a background thread
|
||||
if (
|
||||
not InvenTree.ready.isPluginRegistryLoaded()
|
||||
or not InvenTree.ready.isInMainThread()
|
||||
):
|
||||
return
|
||||
|
||||
if not InvenTree.ready.canAppAccessDatabase(allow_test=False):
|
||||
return # pragma: no cover
|
||||
|
||||
with maintenance_mode_on():
|
||||
try:
|
||||
self.create_defaults()
|
||||
except (
|
||||
AppRegistryNotReady,
|
||||
IntegrityError,
|
||||
OperationalError,
|
||||
ProgrammingError,
|
||||
):
|
||||
# Database might not yet be ready
|
||||
warnings.warn(
|
||||
f'Database was not ready for creating {self.name}s', stacklevel=2
|
||||
)
|
||||
|
||||
set_maintenance_mode(False)
|
||||
|
||||
def create_template_dir(self, model, data):
|
||||
"""Create folder and database entries for the default templates, if they do not already exist."""
|
||||
ref_name = model.getSubdir()
|
||||
|
||||
# Create root dir for templates
|
||||
src_dir = self.get_src_dir(ref_name)
|
||||
dst_dir = MEDIA_STORAGE_DIR.joinpath(self.name, 'inventree', ref_name)
|
||||
ensure_dir(dst_dir, default_storage)
|
||||
|
||||
# Copy each template across (if required)
|
||||
for entry in data:
|
||||
self.create_template_file(model, src_dir, entry, ref_name)
|
||||
|
||||
def create_template_file(self, model, src_dir, data, ref_name):
|
||||
"""Ensure a label template is in place."""
|
||||
# Destination filename
|
||||
filename = os.path.join(self.name, 'inventree', ref_name, data['file'])
|
||||
|
||||
src_file = src_dir.joinpath(data['file'])
|
||||
dst_file = MEDIA_STORAGE_DIR.joinpath(filename)
|
||||
|
||||
do_copy = False
|
||||
|
||||
if not dst_file.exists():
|
||||
logger.info("%s template '%s' is not present", self.name, filename)
|
||||
do_copy = True
|
||||
else:
|
||||
# Check if the file contents are different
|
||||
src_hash = InvenTree.helpers.hash_file(src_file)
|
||||
dst_hash = InvenTree.helpers.hash_file(dst_file)
|
||||
|
||||
if src_hash != dst_hash:
|
||||
logger.info("Hash differs for '%s'", filename)
|
||||
do_copy = True
|
||||
|
||||
if do_copy:
|
||||
logger.info("Copying %s template '%s'", self.name, dst_file)
|
||||
# Ensure destination dir exists
|
||||
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy file
|
||||
default_storage.save(filename, src_file.open('rb'))
|
||||
|
||||
# Check if a file matching the template already exists
|
||||
try:
|
||||
if model.objects.filter(**{self.db: filename}).exists():
|
||||
return # pragma: no cover
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to query %s for '%s' - you should run 'invoke update' first!",
|
||||
self.name,
|
||||
filename,
|
||||
)
|
||||
|
||||
logger.info("Creating entry for %s '%s'", model, data.get('name'))
|
||||
|
||||
try:
|
||||
model.objects.create(**self.get_new_obj_data(data, filename))
|
||||
except Exception:
|
||||
logger.warning("Failed to create %s '%s'", self.name, data['name'])
|
@ -1,69 +1,31 @@
|
||||
"""label app specification."""
|
||||
"""Config options for the label app."""
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import AppRegistryNotReady
|
||||
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
|
||||
|
||||
from maintenance_mode.core import maintenance_mode_on, set_maintenance_mode
|
||||
|
||||
import InvenTree.helpers
|
||||
import InvenTree.ready
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
from generic.templating.apps import TemplatingMixin
|
||||
|
||||
|
||||
class LabelConfig(AppConfig):
|
||||
"""App configuration class for the 'label' app."""
|
||||
class LabelConfig(TemplatingMixin, AppConfig):
|
||||
"""Configuration class for the "label" app."""
|
||||
|
||||
name = 'label'
|
||||
db = 'label'
|
||||
|
||||
def ready(self):
|
||||
"""This function is called whenever the label app is loaded."""
|
||||
# skip loading if plugin registry is not loaded or we run in a background thread
|
||||
if (
|
||||
not InvenTree.ready.isPluginRegistryLoaded()
|
||||
or not InvenTree.ready.isInMainThread()
|
||||
):
|
||||
return
|
||||
|
||||
if not InvenTree.ready.canAppAccessDatabase(allow_test=False):
|
||||
return # pragma: no cover
|
||||
|
||||
with maintenance_mode_on():
|
||||
try:
|
||||
self.create_labels() # pragma: no cover
|
||||
except (
|
||||
AppRegistryNotReady,
|
||||
IntegrityError,
|
||||
OperationalError,
|
||||
ProgrammingError,
|
||||
):
|
||||
# Database might not yet be ready
|
||||
warnings.warn(
|
||||
'Database was not ready for creating labels', stacklevel=2
|
||||
)
|
||||
|
||||
set_maintenance_mode(False)
|
||||
|
||||
def create_labels(self):
|
||||
def create_defaults(self):
|
||||
"""Create all default templates."""
|
||||
# Test if models are ready
|
||||
import label.models
|
||||
|
||||
try:
|
||||
import label.models
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
assert bool(label.models.StockLocationLabel is not None)
|
||||
|
||||
# Create the categories
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.StockItemLabel,
|
||||
'stockitem',
|
||||
[
|
||||
{
|
||||
'file': 'qr.html',
|
||||
@ -75,9 +37,8 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.StockLocationLabel,
|
||||
'stocklocation',
|
||||
[
|
||||
{
|
||||
'file': 'qr.html',
|
||||
@ -96,9 +57,8 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.PartLabel,
|
||||
'part',
|
||||
[
|
||||
{
|
||||
'file': 'part_label.html',
|
||||
@ -117,9 +77,8 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.BuildLineLabel,
|
||||
'buildline',
|
||||
[
|
||||
{
|
||||
'file': 'buildline_label.html',
|
||||
@ -131,72 +90,18 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
def create_labels_category(self, model, ref_name, labels):
|
||||
"""Create folder and database entries for the default templates, if they do not already exist."""
|
||||
# Create root dir for templates
|
||||
src_dir = Path(__file__).parent.joinpath('templates', 'label', ref_name)
|
||||
def get_src_dir(self, ref_name):
|
||||
"""Get the source directory."""
|
||||
return Path(__file__).parent.joinpath('templates', self.name, ref_name)
|
||||
|
||||
dst_dir = settings.MEDIA_ROOT.joinpath('label', 'inventree', ref_name)
|
||||
|
||||
if not dst_dir.exists():
|
||||
logger.info("Creating required directory: '%s'", dst_dir)
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create labels
|
||||
for label in labels:
|
||||
self.create_template_label(model, src_dir, ref_name, label)
|
||||
|
||||
def create_template_label(self, model, src_dir, ref_name, label):
|
||||
"""Ensure a label template is in place."""
|
||||
filename = os.path.join('label', 'inventree', ref_name, label['file'])
|
||||
|
||||
src_file = src_dir.joinpath(label['file'])
|
||||
dst_file = settings.MEDIA_ROOT.joinpath(filename)
|
||||
|
||||
to_copy = False
|
||||
|
||||
if dst_file.exists():
|
||||
# File already exists - let's see if it is the "same"
|
||||
|
||||
if InvenTree.helpers.hash_file(dst_file) != InvenTree.helpers.hash_file(
|
||||
src_file
|
||||
): # pragma: no cover
|
||||
logger.info("Hash differs for '%s'", filename)
|
||||
to_copy = True
|
||||
|
||||
else:
|
||||
logger.info("Label template '%s' is not present", filename)
|
||||
to_copy = True
|
||||
|
||||
if to_copy:
|
||||
logger.info("Copying label template '%s'", dst_file)
|
||||
# Ensure destination dir exists
|
||||
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy file
|
||||
shutil.copyfile(src_file, dst_file)
|
||||
|
||||
# Check if a label matching the template already exists
|
||||
try:
|
||||
if model.objects.filter(label=filename).exists():
|
||||
return # pragma: no cover
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to query label for '%s' - you should run 'invoke update' first!",
|
||||
filename,
|
||||
)
|
||||
|
||||
logger.info("Creating entry for %s '%s'", model, label['name'])
|
||||
|
||||
try:
|
||||
model.objects.create(
|
||||
name=label['name'],
|
||||
description=label['description'],
|
||||
label=filename,
|
||||
filters='',
|
||||
enabled=True,
|
||||
width=label['width'],
|
||||
height=label['height'],
|
||||
)
|
||||
except Exception:
|
||||
logger.warning("Failed to create label '%s'", label['name'])
|
||||
def get_new_obj_data(self, data, filename):
|
||||
"""Get the data for a new template db object."""
|
||||
return {
|
||||
'name': data['name'],
|
||||
'description': data['description'],
|
||||
'label': filename,
|
||||
'filters': '',
|
||||
'enabled': True,
|
||||
'width': data['width'],
|
||||
'height': data['height'],
|
||||
}
|
||||
|
@ -96,8 +96,13 @@ class LabelTemplate(InvenTree.models.InvenTreeMetadataModel):
|
||||
|
||||
abstract = True
|
||||
|
||||
@classmethod
|
||||
def getSubdir(cls) -> str:
|
||||
"""Return the subdirectory for this label."""
|
||||
return cls.SUBDIR
|
||||
|
||||
# Each class of label files will be stored in a separate subdirectory
|
||||
SUBDIR = 'label'
|
||||
SUBDIR: str = 'label'
|
||||
|
||||
# Object we will be printing against (will be filled out later)
|
||||
object_to_print = None
|
||||
|
@ -30,7 +30,7 @@ class LabelTest(InvenTreeAPITestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Ensure that some label instances exist as part of init routine."""
|
||||
super().setUpTestData()
|
||||
apps.get_app_config('label').create_labels()
|
||||
apps.get_app_config('label').create_defaults()
|
||||
|
||||
def test_default_labels(self):
|
||||
"""Test that the default label templates are copied across."""
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -247,7 +247,7 @@ class TestLabelPrinterMachineType(TestMachineRegistryMixin, InvenTreeAPITestCase
|
||||
plugin_ref = 'inventreelabelmachine'
|
||||
|
||||
# setup the label app
|
||||
apps.get_app_config('label').create_labels() # type: ignore
|
||||
apps.get_app_config('label').create_defaults() # type: ignore
|
||||
plg_registry.reload_plugins()
|
||||
config = cast(PluginConfig, plg_registry.get_plugin(plugin_ref).plugin_config()) # type: ignore
|
||||
config.active = True
|
||||
|
@ -1767,6 +1767,7 @@ class BomFilter(rest_filters.FilterSet):
|
||||
part_active = rest_filters.BooleanFilter(
|
||||
label='Master part is active', field_name='part__active'
|
||||
)
|
||||
|
||||
part_trackable = rest_filters.BooleanFilter(
|
||||
label='Master part is trackable', field_name='part__trackable'
|
||||
)
|
||||
@ -1775,6 +1776,7 @@ class BomFilter(rest_filters.FilterSet):
|
||||
sub_part_trackable = rest_filters.BooleanFilter(
|
||||
label='Sub part is trackable', field_name='sub_part__trackable'
|
||||
)
|
||||
|
||||
sub_part_assembly = rest_filters.BooleanFilter(
|
||||
label='Sub part is an assembly', field_name='sub_part__assembly'
|
||||
)
|
||||
@ -1814,6 +1816,22 @@ class BomFilter(rest_filters.FilterSet):
|
||||
|
||||
return queryset.filter(q_a | q_b).distinct()
|
||||
|
||||
part = rest_filters.ModelChoiceFilter(
|
||||
queryset=Part.objects.all(), method='filter_part', label=_('Part')
|
||||
)
|
||||
|
||||
def filter_part(self, queryset, name, part):
|
||||
"""Filter the queryset based on the specified part."""
|
||||
return queryset.filter(part.get_bom_item_filter())
|
||||
|
||||
uses = rest_filters.ModelChoiceFilter(
|
||||
queryset=Part.objects.all(), method='filter_uses', label=_('Uses')
|
||||
)
|
||||
|
||||
def filter_uses(self, queryset, name, part):
|
||||
"""Filter the queryset based on the specified part."""
|
||||
return queryset.filter(part.get_used_in_bom_item_filter())
|
||||
|
||||
|
||||
class BomMixin:
|
||||
"""Mixin class for BomItem API endpoints."""
|
||||
@ -1889,62 +1907,6 @@ class BomList(BomMixin, ListCreateDestroyAPIView):
|
||||
return JsonResponse(data, safe=False)
|
||||
return Response(data)
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
"""Custom query filtering for the BomItem list API."""
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
params = self.request.query_params
|
||||
|
||||
# Filter by part?
|
||||
part = params.get('part', None)
|
||||
|
||||
if part is not None:
|
||||
"""
|
||||
If we are filtering by "part", there are two cases to consider:
|
||||
|
||||
a) Bom items which are defined for *this* part
|
||||
b) Inherited parts which are defined for a *parent* part
|
||||
|
||||
So we need to construct two queries!
|
||||
"""
|
||||
|
||||
# First, check that the part is actually valid!
|
||||
try:
|
||||
part = Part.objects.get(pk=part)
|
||||
|
||||
queryset = queryset.filter(part.get_bom_item_filter())
|
||||
|
||||
except (ValueError, Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
"""
|
||||
Filter by 'uses'?
|
||||
|
||||
Here we pass a part ID and return BOM items for any assemblies which "use" (or "require") that part.
|
||||
|
||||
There are multiple ways that an assembly can "use" a sub-part:
|
||||
|
||||
A) Directly specifying the sub_part in a BomItem field
|
||||
B) Specifying a "template" part with inherited=True
|
||||
C) Allowing variant parts to be substituted
|
||||
D) Allowing direct substitute parts to be specified
|
||||
|
||||
- BOM items which are "inherited" by parts which are variants of the master BomItem
|
||||
"""
|
||||
uses = params.get('uses', None)
|
||||
|
||||
if uses is not None:
|
||||
try:
|
||||
# Extract the part we are interested in
|
||||
uses_part = Part.objects.get(pk=uses)
|
||||
|
||||
queryset = queryset.filter(uses_part.get_used_in_bom_item_filter())
|
||||
|
||||
except (ValueError, Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
return queryset
|
||||
|
||||
filter_backends = SEARCH_ORDER_FILTER_ALIAS
|
||||
|
||||
search_fields = [
|
||||
|
@ -107,7 +107,7 @@ def annotate_on_order_quantity(reference: str = ''):
|
||||
)
|
||||
|
||||
|
||||
def annotate_total_stock(reference: str = ''):
|
||||
def annotate_total_stock(reference: str = '', filter: Q = None):
|
||||
"""Annotate 'total stock' quantity against a queryset.
|
||||
|
||||
- This function calculates the 'total stock' for a given part
|
||||
@ -121,6 +121,9 @@ def annotate_total_stock(reference: str = ''):
|
||||
# Stock filter only returns 'in stock' items
|
||||
stock_filter = stock.models.StockItem.IN_STOCK_FILTER
|
||||
|
||||
if filter is not None:
|
||||
stock_filter &= filter
|
||||
|
||||
return Coalesce(
|
||||
SubquerySum(f'{reference}stock_items__quantity', filter=stock_filter),
|
||||
Decimal(0),
|
||||
@ -216,9 +219,7 @@ def annotate_sales_order_allocations(reference: str = ''):
|
||||
)
|
||||
|
||||
|
||||
def variant_stock_query(
|
||||
reference: str = '', filter: Q = stock.models.StockItem.IN_STOCK_FILTER
|
||||
):
|
||||
def variant_stock_query(reference: str = '', filter: Q = None):
|
||||
"""Create a queryset to retrieve all stock items for variant parts under the specified part.
|
||||
|
||||
- Useful for annotating a queryset with aggregated information about variant parts
|
||||
@ -227,11 +228,16 @@ def variant_stock_query(
|
||||
reference: The relationship reference of the part from the current model
|
||||
filter: Q object which defines how to filter the returned StockItem instances
|
||||
"""
|
||||
stock_filter = stock.models.StockItem.IN_STOCK_FILTER
|
||||
|
||||
if filter:
|
||||
stock_filter &= filter
|
||||
|
||||
return stock.models.StockItem.objects.filter(
|
||||
part__tree_id=OuterRef(f'{reference}tree_id'),
|
||||
part__lft__gt=OuterRef(f'{reference}lft'),
|
||||
part__rght__lt=OuterRef(f'{reference}rght'),
|
||||
).filter(filter)
|
||||
).filter(stock_filter)
|
||||
|
||||
|
||||
def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'):
|
||||
|
@ -610,6 +610,7 @@ class PartSerializer(
|
||||
'stock_item_count',
|
||||
'suppliers',
|
||||
'total_in_stock',
|
||||
'external_stock',
|
||||
'unallocated_stock',
|
||||
'variant_stock',
|
||||
# Fields only used for Part creation
|
||||
@ -734,6 +735,12 @@ class PartSerializer(
|
||||
)
|
||||
)
|
||||
|
||||
queryset = queryset.annotate(
|
||||
external_stock=part.filters.annotate_total_stock(
|
||||
filter=Q(location__external=True)
|
||||
)
|
||||
)
|
||||
|
||||
# Annotate with the total 'available stock' quantity
|
||||
# This is the current stock, minus any allocations
|
||||
queryset = queryset.annotate(
|
||||
@ -780,14 +787,17 @@ class PartSerializer(
|
||||
allocated_to_sales_orders = serializers.FloatField(read_only=True)
|
||||
building = serializers.FloatField(read_only=True)
|
||||
in_stock = serializers.FloatField(read_only=True)
|
||||
ordering = serializers.FloatField(read_only=True)
|
||||
ordering = serializers.FloatField(read_only=True, label=_('On Order'))
|
||||
required_for_build_orders = serializers.IntegerField(read_only=True)
|
||||
required_for_sales_orders = serializers.IntegerField(read_only=True)
|
||||
stock_item_count = serializers.IntegerField(read_only=True)
|
||||
suppliers = serializers.IntegerField(read_only=True)
|
||||
total_in_stock = serializers.FloatField(read_only=True)
|
||||
unallocated_stock = serializers.FloatField(read_only=True)
|
||||
variant_stock = serializers.FloatField(read_only=True)
|
||||
stock_item_count = serializers.IntegerField(read_only=True, label=_('Stock Items'))
|
||||
suppliers = serializers.IntegerField(read_only=True, label=_('Suppliers'))
|
||||
total_in_stock = serializers.FloatField(read_only=True, label=_('Total Stock'))
|
||||
external_stock = serializers.FloatField(read_only=True, label=_('External Stock'))
|
||||
unallocated_stock = serializers.FloatField(
|
||||
read_only=True, label=_('Unallocated Stock')
|
||||
)
|
||||
variant_stock = serializers.FloatField(read_only=True, label=_('Variant Stock'))
|
||||
|
||||
minimum_stock = serializers.FloatField()
|
||||
|
||||
@ -1387,6 +1397,7 @@ class BomItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
'available_stock',
|
||||
'available_substitute_stock',
|
||||
'available_variant_stock',
|
||||
'external_stock',
|
||||
# Annotated field describing quantity on order
|
||||
'on_order',
|
||||
# Annotated field describing quantity being built
|
||||
@ -1456,6 +1467,8 @@ class BomItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
available_substitute_stock = serializers.FloatField(read_only=True)
|
||||
available_variant_stock = serializers.FloatField(read_only=True)
|
||||
|
||||
external_stock = serializers.FloatField(read_only=True)
|
||||
|
||||
@staticmethod
|
||||
def setup_eager_loading(queryset):
|
||||
"""Prefetch against the provided queryset to speed up database access."""
|
||||
@ -1534,6 +1547,13 @@ class BomItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
)
|
||||
)
|
||||
|
||||
# Calculate 'external_stock'
|
||||
queryset = queryset.annotate(
|
||||
external_stock=part.filters.annotate_total_stock(
|
||||
reference=ref, filter=Q(location__external=True)
|
||||
)
|
||||
)
|
||||
|
||||
ref = 'substitutes__part__'
|
||||
|
||||
# Extract similar information for any 'substitute' parts
|
||||
|
@ -289,13 +289,13 @@ def check_plugin(plugin_slug: str, plugin_pk: int) -> InvenTreePlugin:
|
||||
|
||||
# Check that the 'plugin' specified is valid
|
||||
try:
|
||||
plugin_cgf = PluginConfig.objects.get(**filter)
|
||||
plugin_cgf = PluginConfig.objects.filter(**filter).first()
|
||||
except PluginConfig.DoesNotExist:
|
||||
raise NotFound(detail=f"Plugin '{ref}' not installed")
|
||||
|
||||
if plugin_cgf is None:
|
||||
# This only occurs if the plugin mechanism broke
|
||||
raise NotFound(detail=f"Plugin '{ref}' not found") # pragma: no cover
|
||||
raise NotFound(detail=f"Plugin '{ref}' not installed") # pragma: no cover
|
||||
|
||||
# Check that the plugin is activated
|
||||
if not plugin_cgf.active:
|
||||
|
@ -121,7 +121,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
def test_printing_process(self):
|
||||
"""Test that a label can be printed."""
|
||||
# Ensure the labels were created
|
||||
apps.get_app_config('label').create_labels()
|
||||
apps.get_app_config('label').create_defaults()
|
||||
|
||||
# Lookup references
|
||||
part = Part.objects.first()
|
||||
@ -183,7 +183,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
def test_printing_options(self):
|
||||
"""Test printing options."""
|
||||
# Ensure the labels were created
|
||||
apps.get_app_config('label').create_labels()
|
||||
apps.get_app_config('label').create_defaults()
|
||||
|
||||
# Lookup references
|
||||
parts = Part.objects.all()[:2]
|
||||
@ -224,7 +224,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
plugin_ref = 'samplelabelprinter'
|
||||
|
||||
# Activate the label components
|
||||
apps.get_app_config('label').create_labels()
|
||||
apps.get_app_config('label').create_defaults()
|
||||
self.do_activate_plugin()
|
||||
|
||||
def run_print_test(label, qs, url_name, url_single):
|
||||
|
@ -115,7 +115,11 @@ class PluginsRegistry:
|
||||
return None
|
||||
|
||||
try:
|
||||
cfg, _created = PluginConfig.objects.get_or_create(key=slug)
|
||||
cfg = PluginConfig.objects.filter(key=slug).first()
|
||||
|
||||
if not cfg:
|
||||
cfg = PluginConfig.objects.create(key=slug)
|
||||
|
||||
except PluginConfig.DoesNotExist:
|
||||
return None
|
||||
except (IntegrityError, OperationalError, ProgrammingError): # pragma: no cover
|
||||
|
@ -1,256 +1,124 @@
|
||||
"""Config options for the 'report' app."""
|
||||
"""Config options for the report app."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import AppRegistryNotReady
|
||||
from django.db.utils import IntegrityError, OperationalError, ProgrammingError
|
||||
|
||||
from maintenance_mode.core import maintenance_mode_on, set_maintenance_mode
|
||||
|
||||
import InvenTree.helpers
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
from generic.templating.apps import TemplatingMixin
|
||||
|
||||
|
||||
class ReportConfig(AppConfig):
|
||||
"""Configuration class for the 'report' app."""
|
||||
class ReportConfig(TemplatingMixin, AppConfig):
|
||||
"""Configuration class for the "report" app."""
|
||||
|
||||
name = 'report'
|
||||
db = 'template'
|
||||
|
||||
def ready(self):
|
||||
"""This function is called whenever the report app is loaded."""
|
||||
import InvenTree.ready
|
||||
|
||||
# skip loading if plugin registry is not loaded or we run in a background thread
|
||||
if (
|
||||
not InvenTree.ready.isPluginRegistryLoaded()
|
||||
or not InvenTree.ready.isInMainThread()
|
||||
):
|
||||
return
|
||||
|
||||
if not InvenTree.ready.canAppAccessDatabase(allow_test=False):
|
||||
return # pragma: no cover
|
||||
|
||||
"""This function is called whenever the app is loaded."""
|
||||
# Configure logging for PDF generation (disable "info" messages)
|
||||
logging.getLogger('fontTools').setLevel(logging.WARNING)
|
||||
logging.getLogger('weasyprint').setLevel(logging.WARNING)
|
||||
|
||||
with maintenance_mode_on():
|
||||
self.create_reports()
|
||||
super().ready()
|
||||
|
||||
set_maintenance_mode(False)
|
||||
|
||||
def create_reports(self):
|
||||
"""Create default report templates."""
|
||||
def create_defaults(self):
|
||||
"""Create all default templates."""
|
||||
# Test if models are ready
|
||||
try:
|
||||
self.create_default_test_reports()
|
||||
self.create_default_build_reports()
|
||||
self.create_default_bill_of_materials_reports()
|
||||
self.create_default_purchase_order_reports()
|
||||
self.create_default_sales_order_reports()
|
||||
self.create_default_return_order_reports()
|
||||
self.create_default_stock_location_reports()
|
||||
except (
|
||||
AppRegistryNotReady,
|
||||
IntegrityError,
|
||||
OperationalError,
|
||||
ProgrammingError,
|
||||
):
|
||||
# Database might not yet be ready
|
||||
warnings.warn('Database was not ready for creating reports', stacklevel=2)
|
||||
|
||||
def create_default_reports(self, model, reports):
|
||||
"""Copy default report files across to the media directory."""
|
||||
# Source directory for report templates
|
||||
src_dir = Path(__file__).parent.joinpath('templates', 'report')
|
||||
|
||||
# Destination directory
|
||||
dst_dir = settings.MEDIA_ROOT.joinpath('report', 'inventree', model.getSubdir())
|
||||
|
||||
if not dst_dir.exists():
|
||||
logger.info("Creating missing directory: '%s'", dst_dir)
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy each report template across (if required)
|
||||
for report in reports:
|
||||
# Destination filename
|
||||
filename = os.path.join(
|
||||
'report', 'inventree', model.getSubdir(), report['file']
|
||||
)
|
||||
|
||||
src_file = src_dir.joinpath(report['file'])
|
||||
dst_file = settings.MEDIA_ROOT.joinpath(filename)
|
||||
|
||||
do_copy = False
|
||||
|
||||
if not dst_file.exists():
|
||||
logger.info("Report template '%s' is not present", filename)
|
||||
do_copy = True
|
||||
else:
|
||||
# Check if the file contents are different
|
||||
src_hash = InvenTree.helpers.hash_file(src_file)
|
||||
dst_hash = InvenTree.helpers.hash_file(dst_file)
|
||||
|
||||
if src_hash != dst_hash:
|
||||
logger.info("Hash differs for '%s'", filename)
|
||||
do_copy = True
|
||||
|
||||
if do_copy:
|
||||
logger.info("Copying test report template '%s'", dst_file)
|
||||
shutil.copyfile(src_file, dst_file)
|
||||
|
||||
try:
|
||||
# Check if a report matching the template already exists
|
||||
if model.objects.filter(template=filename).exists():
|
||||
continue
|
||||
|
||||
logger.info("Creating new TestReport for '%s'", report.get('name'))
|
||||
|
||||
model.objects.create(
|
||||
name=report['name'],
|
||||
description=report['description'],
|
||||
template=filename,
|
||||
enabled=True,
|
||||
)
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def create_default_test_reports(self):
|
||||
"""Create database entries for the default TestReport templates, if they do not already exist."""
|
||||
try:
|
||||
from .models import TestReport
|
||||
import report.models
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
assert bool(report.models.TestReport is not None)
|
||||
|
||||
# List of test reports to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_test_report.html',
|
||||
'name': 'InvenTree Test Report',
|
||||
'description': 'Stock item test report',
|
||||
}
|
||||
]
|
||||
# Create the categories
|
||||
self.create_template_dir(
|
||||
report.models.TestReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_test_report.html',
|
||||
'name': 'InvenTree Test Report',
|
||||
'description': 'Stock item test report',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
self.create_default_reports(TestReport, reports)
|
||||
self.create_template_dir(
|
||||
report.models.BuildReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_build_order.html',
|
||||
'name': 'InvenTree Build Order',
|
||||
'description': 'Build Order job sheet',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def create_default_bill_of_materials_reports(self):
|
||||
"""Create database entries for the default Bill of Material templates (if they do not already exist)."""
|
||||
try:
|
||||
from .models import BillOfMaterialsReport
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
self.create_template_dir(
|
||||
report.models.BillOfMaterialsReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_bill_of_materials_report.html',
|
||||
'name': 'Bill of Materials',
|
||||
'description': 'Bill of Materials report',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
# List of Build reports to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_bill_of_materials_report.html',
|
||||
'name': 'Bill of Materials',
|
||||
'description': 'Bill of Materials report',
|
||||
}
|
||||
]
|
||||
self.create_template_dir(
|
||||
report.models.PurchaseOrderReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_po_report.html',
|
||||
'name': 'InvenTree Purchase Order',
|
||||
'description': 'Purchase Order example report',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
self.create_default_reports(BillOfMaterialsReport, reports)
|
||||
self.create_template_dir(
|
||||
report.models.SalesOrderReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_so_report.html',
|
||||
'name': 'InvenTree Sales Order',
|
||||
'description': 'Sales Order example report',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
def create_default_build_reports(self):
|
||||
"""Create database entries for the default BuildReport templates (if they do not already exist)."""
|
||||
try:
|
||||
from .models import BuildReport
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
self.create_template_dir(
|
||||
report.models.ReturnOrderReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_return_order_report.html',
|
||||
'name': 'InvenTree Return Order',
|
||||
'description': 'Return Order example report',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
# List of Build reports to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_build_order.html',
|
||||
'name': 'InvenTree Build Order',
|
||||
'description': 'Build Order job sheet',
|
||||
}
|
||||
]
|
||||
self.create_template_dir(
|
||||
report.models.StockLocationReport,
|
||||
[
|
||||
{
|
||||
'file': 'inventree_slr_report.html',
|
||||
'name': 'InvenTree Stock Location',
|
||||
'description': 'Stock Location example report',
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
self.create_default_reports(BuildReport, reports)
|
||||
def get_src_dir(self, ref_name):
|
||||
"""Get the source directory."""
|
||||
return Path(__file__).parent.joinpath('templates', self.name)
|
||||
|
||||
def create_default_purchase_order_reports(self):
|
||||
"""Create database entries for the default SalesOrderReport templates (if they do not already exist)."""
|
||||
try:
|
||||
from .models import PurchaseOrderReport
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
|
||||
# List of Build reports to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_po_report.html',
|
||||
'name': 'InvenTree Purchase Order',
|
||||
'description': 'Purchase Order example report',
|
||||
}
|
||||
]
|
||||
|
||||
self.create_default_reports(PurchaseOrderReport, reports)
|
||||
|
||||
def create_default_sales_order_reports(self):
|
||||
"""Create database entries for the default Sales Order report templates (if they do not already exist)."""
|
||||
try:
|
||||
from .models import SalesOrderReport
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
|
||||
# List of Build reports to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_so_report.html',
|
||||
'name': 'InvenTree Sales Order',
|
||||
'description': 'Sales Order example report',
|
||||
}
|
||||
]
|
||||
|
||||
self.create_default_reports(SalesOrderReport, reports)
|
||||
|
||||
def create_default_return_order_reports(self):
|
||||
"""Create database entries for the default ReturnOrderReport templates."""
|
||||
try:
|
||||
from report.models import ReturnOrderReport
|
||||
except Exception: # pragma: no cover
|
||||
# Database not yet ready
|
||||
return
|
||||
|
||||
# List of templates to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_return_order_report.html',
|
||||
'name': 'InvenTree Return Order',
|
||||
'description': 'Return Order example report',
|
||||
}
|
||||
]
|
||||
|
||||
self.create_default_reports(ReturnOrderReport, reports)
|
||||
|
||||
def create_default_stock_location_reports(self):
|
||||
"""Create database entries for the default StockLocationReport templates."""
|
||||
try:
|
||||
from report.models import StockLocationReport
|
||||
except Exception: # pragma: no cover
|
||||
# Database not yet ready
|
||||
return
|
||||
|
||||
# List of templates to copy across
|
||||
reports = [
|
||||
{
|
||||
'file': 'inventree_slr_report.html',
|
||||
'name': 'InvenTree Stock Location',
|
||||
'description': 'Stock Location example report',
|
||||
}
|
||||
]
|
||||
|
||||
self.create_default_reports(StockLocationReport, reports)
|
||||
def get_new_obj_data(self, data, filename):
|
||||
"""Get the data for a new template db object."""
|
||||
return {
|
||||
'name': data['name'],
|
||||
'description': data['description'],
|
||||
'template': filename,
|
||||
'enabled': True,
|
||||
}
|
||||
|
@ -1172,12 +1172,18 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
var available_stock = availableQuantity(row);
|
||||
|
||||
var external_stock = row.external_stock ?? 0;
|
||||
|
||||
var text = renderLink(`${available_stock}`, url);
|
||||
|
||||
if (row.sub_part_detail && row.sub_part_detail.units) {
|
||||
text += ` <small>${row.sub_part_detail.units}</small>`;
|
||||
}
|
||||
|
||||
if (external_stock > 0) {
|
||||
text += makeIconBadge('fa-sitemap', `{% trans "External stock" %}: ${external_stock}`);
|
||||
}
|
||||
|
||||
if (available_stock <= 0) {
|
||||
text += makeIconBadge('fa-times-circle icon-red', '{% trans "No Stock Available" %}');
|
||||
} else {
|
||||
|
@ -2618,6 +2618,10 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
icons += makeIconBadge('fa-tools icon-blue', `{% trans "In Production" %}: ${formatDecimal(row.in_production)}`);
|
||||
}
|
||||
|
||||
if (row.external_stock > 0) {
|
||||
icons += makeIconBadge('fa-sitemap', `{% trans "External stock" %}: ${row.external_stock}`);
|
||||
}
|
||||
|
||||
return renderLink(text, url) + icons;
|
||||
}
|
||||
},
|
||||
@ -2730,6 +2734,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
|
||||
allocateStockToBuild(build_id, [row], {
|
||||
output: options.output,
|
||||
source_location: options.location,
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
|
@ -2804,6 +2804,15 @@ function loadPartCategoryTable(table, options) {
|
||||
title: '{% trans "Parts" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'structural',
|
||||
title: '{% trans "Structural" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -8,9 +8,7 @@
|
||||
<h3>{% trans "Sign Up" %}</h3>
|
||||
|
||||
<p>
|
||||
{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}
|
||||
You are about to use your {{provider_name}} account to login to {{site_name}}.
|
||||
{% endblocktrans %}
|
||||
{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to {{site_name}}.{% endblocktrans %}
|
||||
<br>
|
||||
{% trans "As a final step, please complete the following form" %}:
|
||||
</p>
|
||||
|
@ -1,16 +1,8 @@
|
||||
# InvenTree environment variables for a postgresql production setup
|
||||
# InvenTree environment variables for docker compose deployment
|
||||
|
||||
# Location of persistent database data (stored external to the docker containers)
|
||||
# Note: You *must* un-comment this line, and point it to a path on your local machine
|
||||
|
||||
# e.g. Linux
|
||||
#INVENTREE_EXT_VOLUME=/home/inventree/data
|
||||
|
||||
# e.g. Windows (docker desktop)
|
||||
#INVENTREE_EXT_VOLUME=c:/Users/inventree/data
|
||||
|
||||
# Default web port for the InvenTree server
|
||||
INVENTREE_WEB_PORT=1337
|
||||
# Specify the location of the external data volume
|
||||
# By default, placed in local directory 'inventree-data'
|
||||
INVENTREE_EXT_VOLUME=./inventree-data
|
||||
|
||||
# Ensure debug is false for a production setup
|
||||
INVENTREE_DEBUG=False
|
||||
@ -23,16 +15,14 @@ INVENTREE_LOG_LEVEL=WARNING
|
||||
#INVENTREE_ADMIN_EMAIL=
|
||||
|
||||
# Database configuration options
|
||||
# Note: The example setup is for a PostgreSQL database
|
||||
INVENTREE_DB_ENGINE=postgresql
|
||||
INVENTREE_DB_NAME=inventree
|
||||
INVENTREE_DB_HOST=inventree-db
|
||||
INVENTREE_DB_PORT=5432
|
||||
|
||||
# Database credentials - These must be configured before running
|
||||
# Uncomment the lines below, and change from the default values!
|
||||
#INVENTREE_DB_USER=pguser
|
||||
#INVENTREE_DB_PASSWORD=pgpassword
|
||||
# Database credentials - These should be changed from the default values!
|
||||
INVENTREE_DB_USER=pguser
|
||||
INVENTREE_DB_PASSWORD=pgpassword
|
||||
|
||||
# Redis cache setup (disabled by default)
|
||||
# Un-comment the following lines to enable Redis cache
|
||||
@ -45,12 +35,15 @@ INVENTREE_DB_PORT=5432
|
||||
INVENTREE_GUNICORN_TIMEOUT=90
|
||||
|
||||
# Enable custom plugins?
|
||||
INVENTREE_PLUGINS_ENABLED=False
|
||||
INVENTREE_PLUGINS_ENABLED=True
|
||||
|
||||
# Run migrations automatically?
|
||||
INVENTREE_AUTO_UPDATE=False
|
||||
INVENTREE_AUTO_UPDATE=True
|
||||
|
||||
# Image tag that should be used
|
||||
INVENTREE_TAG=stable
|
||||
|
||||
COMPOSE_PROJECT_NAME=inventree-production
|
||||
# Site URL - update this to match your host (and update the Caddyfile too!)
|
||||
INVENTREE_SITE_URL="http://inventree.localhost"
|
||||
|
||||
COMPOSE_PROJECT_NAME=inventree
|
36
docker/Caddyfile
Normal file
36
docker/Caddyfile
Normal file
@ -0,0 +1,36 @@
|
||||
# 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)
|
||||
|
||||
(log_common) {
|
||||
log {
|
||||
output file /var/log/caddy/{args.0}.access.log
|
||||
}
|
||||
}
|
||||
|
||||
# Change the host to your domain (this will serve at inventree.localhost)
|
||||
{$INVENTREE_SITE_URL:inventree.localhost} {
|
||||
import log_common inventree
|
||||
|
||||
encode gzip
|
||||
|
||||
request_body {
|
||||
max_size 100MB
|
||||
}
|
||||
|
||||
handle_path /static/* {
|
||||
root * /var/www/static
|
||||
file_server
|
||||
}
|
||||
|
||||
handle_path /media/* {
|
||||
forward_auth {$INVENTREE_SERVER:"http://inventree-server:8000"} {
|
||||
uri /auth/
|
||||
}
|
||||
root * /var/www/media
|
||||
file_server
|
||||
}
|
||||
|
||||
reverse_proxy {$INVENTREE_SERVER:"http://inventree-server:8000"}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
# Docker compose recipe for InvenTree development server
|
||||
# - Runs sqlite database
|
||||
# - Uses built-in django webserver
|
||||
# - Runs the InvenTree background worker process
|
||||
# - Serves media and static content directly from Django webserver
|
||||
|
||||
# IMPORTANT NOTE:
|
||||
# The InvenTree docker image does not clone source code from git.
|
||||
# Instead, you must specify *where* the source code is located,
|
||||
# (on your local machine).
|
||||
# The django server will auto-detect any code changes and reload the server.
|
||||
|
||||
services:
|
||||
|
||||
# InvenTree web server services
|
||||
# Uses gunicorn as the web server
|
||||
inventree-dev-server:
|
||||
container_name: inventree-dev-server
|
||||
build:
|
||||
context: .
|
||||
target: dev
|
||||
# Cache the built image to be used by the inventree-dev-worker process
|
||||
image: inventree-dev-image
|
||||
ports:
|
||||
# Expose web server on port 8000
|
||||
- 8000:8000
|
||||
volumes:
|
||||
# Ensure you specify the location of the 'src' directory at the end of this file
|
||||
- src:/home/inventree
|
||||
environment:
|
||||
- INVENTREE_DEBUG=True
|
||||
- INVENTREE_DB_ENGINE=sqlite
|
||||
- INVENTREE_DB_NAME=/home/inventree/db.sqlite3
|
||||
restart: unless-stopped
|
||||
|
||||
# Background worker process handles long-running or periodic tasks
|
||||
inventree-dev-worker:
|
||||
container_name: inventree-dev-worker
|
||||
image: inventree-dev-image
|
||||
command: invoke worker
|
||||
depends_on:
|
||||
- inventree-dev-server
|
||||
volumes:
|
||||
# Ensure you specify the location of the 'src' directory at the end of this file
|
||||
- src:/home/inventree
|
||||
environment:
|
||||
- INVENTREE_DEBUG=True
|
||||
- INVENTREE_DB_ENGINE=sqlite
|
||||
- INVENTREE_DB_NAME=/home/inventree/db.sqlite3
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
# NOTE: Change "../" to a directory on your local machine, where the InvenTree source code is located
|
||||
# Persistent data, stored external to the container(s)
|
||||
src:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
# This directory specified where InvenTree source code is stored "outside" the docker containers
|
||||
# By default, this directory is one level above the "docker" directory
|
||||
device: ${INVENTREE_EXT_VOLUME:-../}
|
@ -39,6 +39,7 @@ services:
|
||||
# Use PostgreSQL as the database backend
|
||||
inventree-db:
|
||||
image: postgres:13
|
||||
container_name: inventree-db
|
||||
expose:
|
||||
- ${INVENTREE_DB_PORT:-5432}/tcp
|
||||
environment:
|
||||
@ -48,13 +49,14 @@ services:
|
||||
- POSTGRES_DB=${INVENTREE_DB_NAME:?You must provide the 'INVENTREE_DB_NAME' variable in the .env file}
|
||||
volumes:
|
||||
# Map 'data' volume such that postgres database is stored externally
|
||||
- inventree_data:/var/lib/postgresql/data/:z
|
||||
- ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}:/var/lib/postgresql/data/:z
|
||||
restart: unless-stopped
|
||||
|
||||
# redis acts as database cache manager
|
||||
# only runs under the "redis" profile : https://docs.docker.com/compose/profiles/
|
||||
inventree-cache:
|
||||
image: redis:7.0
|
||||
container_name: inventree-cache
|
||||
depends_on:
|
||||
- inventree-db
|
||||
profiles:
|
||||
@ -70,10 +72,8 @@ services:
|
||||
inventree-server:
|
||||
# If you wish to specify a particular InvenTree version, do so here
|
||||
image: inventree/inventree:${INVENTREE_TAG:-stable}
|
||||
container_name: inventree-server
|
||||
# Only change this port if you understand the stack.
|
||||
# If you change this you have to change:
|
||||
# - the proxy settings (on two lines)
|
||||
# - only change the exposed port - eg `1338:8000` if you want to expose the server on port 1338
|
||||
expose:
|
||||
- 8000
|
||||
depends_on:
|
||||
@ -82,13 +82,14 @@ services:
|
||||
- .env
|
||||
volumes:
|
||||
# Data volume must map to /home/inventree/data
|
||||
- inventree_data:/home/inventree/data:z
|
||||
- ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z
|
||||
restart: unless-stopped
|
||||
|
||||
# Background worker process handles long-running or periodic tasks
|
||||
inventree-worker:
|
||||
# If you wish to specify a particular InvenTree version, do so here
|
||||
image: inventree/inventree:${INVENTREE_TAG:-stable}
|
||||
container_name: inventree-worker
|
||||
command: invoke worker
|
||||
depends_on:
|
||||
- inventree-server
|
||||
@ -96,37 +97,26 @@ services:
|
||||
- .env
|
||||
volumes:
|
||||
# Data volume must map to /home/inventree/data
|
||||
- inventree_data:/home/inventree/data:z
|
||||
- ${INVENTREE_EXT_VOLUME}:/home/inventree/data:z
|
||||
restart: unless-stopped
|
||||
|
||||
# nginx acts as a reverse proxy
|
||||
# static files are served directly by nginx
|
||||
# media files are served by nginx, although authentication is redirected to inventree-server
|
||||
# web requests are redirected to gunicorn
|
||||
# NOTE: You will need to provide a working nginx.conf file!
|
||||
# caddy acts as reverse proxy and static file server
|
||||
# https://hub.docker.com/_/caddy
|
||||
inventree-proxy:
|
||||
image: nginx:stable
|
||||
container_name: inventree-proxy
|
||||
image: caddy:alpine
|
||||
restart: always
|
||||
depends_on:
|
||||
- inventree-server
|
||||
ports:
|
||||
- ${INVENTREE_WEB_PORT:-80}:80
|
||||
- 443:443
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
# Default web port is 1337 (can be changed in the .env file)
|
||||
- ${INVENTREE_WEB_PORT:-1337}:80
|
||||
volumes:
|
||||
# Provide nginx configuration file to the container
|
||||
# Refer to the provided example file as a starting point
|
||||
- ./nginx.prod.conf:/etc/nginx/conf.d/default.conf:ro,z
|
||||
# nginx proxy needs access to static and media files
|
||||
- inventree_data:/var/www:z
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
# Persistent data, stored external to the container(s)
|
||||
inventree_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
# This directory specified where InvenTree data are stored "outside" the docker containers
|
||||
device: ${INVENTREE_EXT_VOLUME:?You must specify the 'INVENTREE_EXT_VOLUME' variable in the .env file!}
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- ${INVENTREE_EXT_VOLUME}/static:/var/www/static:z
|
||||
- ${INVENTREE_EXT_VOLUME}/media:/var/www/media:z
|
||||
- ${INVENTREE_EXT_VOLUME}:/var/log:z
|
||||
- ${INVENTREE_EXT_VOLUME}:/data:z
|
||||
- ${INVENTREE_EXT_VOLUME}:/config:z
|
@ -7,5 +7,5 @@ apk add gcc g++ musl-dev openssl-dev libffi-dev cargo python3-dev openldap-dev \
|
||||
jpeg-dev openjpeg-dev libwebp-dev zlib-dev \
|
||||
sqlite sqlite-dev \
|
||||
mariadb-connector-c-dev mariadb-client mariadb-dev \
|
||||
postgresql13-dev postgresql-libs postgresql13-client \
|
||||
postgresql13-dev postgresql-libs \
|
||||
$@
|
||||
|
@ -1,66 +0,0 @@
|
||||
|
||||
server {
|
||||
|
||||
# Listen for connection on (internal) port 80
|
||||
listen 80;
|
||||
|
||||
real_ip_header proxy_protocol;
|
||||
|
||||
location / {
|
||||
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header CLIENT_IP $remote_addr;
|
||||
|
||||
proxy_pass_request_headers on;
|
||||
|
||||
proxy_redirect off;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
# Change 'inventree-dev-server' to the name of the inventree server container,
|
||||
# and '8000' to the INVENTREE_WEB_PORT (if not default)
|
||||
proxy_pass http://inventree-dev-server:8000;
|
||||
|
||||
}
|
||||
|
||||
# Redirect any requests for static files
|
||||
location /static/ {
|
||||
alias /var/www/dev/static/;
|
||||
autoindex on;
|
||||
|
||||
# Caching settings
|
||||
expires 30d;
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# Redirect any requests for media files
|
||||
location /media/ {
|
||||
alias /var/www/dev/media/;
|
||||
|
||||
# Media files require user authentication
|
||||
auth_request /auth;
|
||||
|
||||
# Content header to force download
|
||||
add_header Content-disposition "attachment";
|
||||
}
|
||||
|
||||
# Use the 'user' API endpoint for auth
|
||||
location /auth {
|
||||
internal;
|
||||
|
||||
proxy_pass http://inventree-dev-server:8000/auth/;
|
||||
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
}
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
|
||||
server {
|
||||
|
||||
# Listen for connection on (internal) port 80
|
||||
# If you are exposing this server to the internet, you should use HTTPS!
|
||||
# In which case, you should also set up a redirect from HTTP to HTTPS, and listen on port 443
|
||||
# See the Nginx documentation for more details
|
||||
listen 80;
|
||||
|
||||
real_ip_header proxy_protocol;
|
||||
|
||||
location / {
|
||||
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-By $server_addr:$server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header CLIENT_IP $remote_addr;
|
||||
|
||||
proxy_pass_request_headers on;
|
||||
|
||||
proxy_redirect off;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
# Do not touch this unless you have a specific reason - this and the docker-compose need to match
|
||||
proxy_pass http://inventree-server:8000;
|
||||
}
|
||||
|
||||
# Redirect any requests for static files
|
||||
location /static/ {
|
||||
alias /var/www/static/;
|
||||
autoindex on;
|
||||
|
||||
# Caching settings
|
||||
expires 30d;
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# Redirect any requests for media files
|
||||
location /media/ {
|
||||
alias /var/www/media/;
|
||||
|
||||
# Media files require user authentication
|
||||
auth_request /auth;
|
||||
|
||||
# Content header to force download
|
||||
add_header Content-disposition "attachment";
|
||||
}
|
||||
|
||||
# Use the 'user' API endpoint for auth
|
||||
location /auth {
|
||||
internal;
|
||||
|
||||
proxy_pass http://inventree-server:8000/auth/;
|
||||
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
}
|
||||
|
||||
}
|
@ -17,3 +17,6 @@ gunicorn>=21.2.0
|
||||
# LDAP required packages
|
||||
django-auth-ldap # Django integration for ldap auth
|
||||
python-ldap # LDAP auth support
|
||||
|
||||
# Upgraded python package installer
|
||||
uv
|
||||
|
1
docs/_includes/django.html
Normal file
1
docs/_includes/django.html
Normal file
@ -0,0 +1 @@
|
||||
https://docs.djangoproject.com/en/{{ config.extra.django_version }}
|
@ -1,3 +1,285 @@
|
||||
{!
|
||||
include-markdown "../../../CONTRIBUTING.md"
|
||||
!}
|
||||
---
|
||||
title: Contribution Guide
|
||||
---
|
||||
|
||||
|
||||
Please read the contribution guidelines below, before submitting your first pull request to the InvenTree codebase.
|
||||
|
||||
## Quickstart
|
||||
|
||||
The following commands will get you quickly configure and run a development server, complete with a demo dataset to work with:
|
||||
|
||||
### Devcontainer
|
||||
|
||||
The recommended method for getting up and running with an InvenTree development environment is to use our [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers) setup in [vscode](https://code.visualstudio.com/).
|
||||
|
||||
!!! success "Devcontainer Guide"
|
||||
Refer to the [devcontainer guide](./devcontainer.md) for more information!
|
||||
|
||||
### Docker
|
||||
|
||||
To setup a development environment using [docker](../start/docker.md), run the following instructions:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/inventree/InvenTree.git && cd InvenTree
|
||||
docker compose run inventree-dev-server invoke install
|
||||
docker compose run inventree-dev-server invoke setup-test --dev
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Bare Metal
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Read the [InvenTree setup documentation](../start/intro.md) for a complete installation reference guide.
|
||||
|
||||
### Setup Devtools
|
||||
|
||||
Run the following command to set up all toolsets for development.
|
||||
|
||||
```bash
|
||||
invoke 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.
|
||||
|
||||
### Version Numbering
|
||||
|
||||
InvenTree version numbering follows the [semantic versioning](https://semver.org/) specification.
|
||||
|
||||
### Master Branch
|
||||
|
||||
The HEAD of the "main" or "master" branch of InvenTree represents the current "latest" state of code development.
|
||||
|
||||
- All feature branches are merged into master
|
||||
- All bug fixes are merged into master
|
||||
|
||||
**No pushing to master:** New features must be submitted as a pull request from a separate branch (one branch per feature).
|
||||
|
||||
### Feature Branches
|
||||
|
||||
Feature branches should be branched *from* the *master* branch.
|
||||
|
||||
- One major feature per branch / pull request
|
||||
- Feature pull requests are merged back *into* the master branch
|
||||
- Features *may* also be merged into a release candidate branch
|
||||
|
||||
### Stable Branch
|
||||
|
||||
The HEAD of the "stable" branch represents the latest stable release code.
|
||||
|
||||
- Versioned releases are merged into the "stable" branch
|
||||
- Bug fix branches are made *from* the "stable" branch
|
||||
|
||||
#### Release Candidate Branches
|
||||
|
||||
- Release candidate branches are made from master, and merged into stable.
|
||||
- RC branches are targeted at a major/minor version e.g. "0.5"
|
||||
- When a release candidate branch is merged into *stable*, the release is tagged
|
||||
|
||||
#### Bugfix Branches
|
||||
|
||||
- If a bug is discovered in a tagged release version of InvenTree, a "bugfix" or "hotfix" branch should be made *from* that tagged release
|
||||
- When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2)
|
||||
- The bugfix *must* also be cherry picked into the *master* branch.
|
||||
|
||||
## API versioning
|
||||
|
||||
The [API version](https://github.com/inventree/InvenTree/blob/master/InvenTree/InvenTree/api_version.py) needs to be bumped every time when the API is changed.
|
||||
|
||||
## Environment
|
||||
|
||||
### Software Versions
|
||||
|
||||
The core software modules are targeting the following versions:
|
||||
|
||||
| Name | Minimum version | Note |
|
||||
|---|---| --- |
|
||||
| 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 |
|
||||
|
||||
Any other software dependencies are handled by the project package config.
|
||||
|
||||
### Auto creating updates
|
||||
|
||||
The following tools can be used to auto-upgrade syntax that was depreciated in new versions:
|
||||
```bash
|
||||
pip install pyupgrade
|
||||
pip install django-upgrade
|
||||
```
|
||||
|
||||
To update the codebase run the following script.
|
||||
```bash
|
||||
pyupgrade `find . -name "*.py"`
|
||||
django-upgrade --target-version {{ config.extra.django_version }} `find . -name "*.py"`
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
If you add any new dependencies / libraries, they should be added to [the credits page](../credits.md).
|
||||
|
||||
## Migration Files
|
||||
|
||||
Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR.
|
||||
|
||||
*Note: A github action checks for unstaged migration files and will reject the PR if it finds any!*
|
||||
|
||||
## Unit Testing
|
||||
|
||||
Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage for any new features is insufficient, or the overall code coverage is decreased.
|
||||
|
||||
The InvenTree code base makes use of [GitHub actions](https://github.com/features/actions) to run a suite of automated tests against the code base every time a new pull request is received. These actions include (but are not limited to):
|
||||
|
||||
- Checking Python and Javascript code against standard style guides
|
||||
- Running unit test suite
|
||||
- Automated building and pushing of docker images
|
||||
- Generating translation files
|
||||
|
||||
The various github actions can be found in the `./github/workflows` directory
|
||||
|
||||
### Run tests locally
|
||||
|
||||
To run test locally, use:
|
||||
```
|
||||
invoke test
|
||||
```
|
||||
|
||||
To run only partial tests, for example for a module use:
|
||||
```
|
||||
invoke test --runtest order
|
||||
```
|
||||
|
||||
To see all the available options:
|
||||
|
||||
```
|
||||
invoke test --help
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
### Backend Code
|
||||
|
||||
Backend code (Python) is checked against the [PEP style guidelines](https://peps.python.org/pep-0008/). Please write docstrings for each function and class - we follow the [google doc-style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) for python.
|
||||
|
||||
### Frontend Code
|
||||
|
||||
Frontend code (Javascript) is checked using [eslint](https://eslint.org/). While docstrings are not enforced for front-end code, good code documentation is encouraged!
|
||||
|
||||
### Running Checks Locally
|
||||
|
||||
If you have followed the setup devtools procedure, then code style checking is performend automatically whenever you commit changes to the code.
|
||||
|
||||
### Django templates
|
||||
|
||||
Django are checked by [djlint](https://github.com/Riverside-Healthcare/djlint) through pre-commit.
|
||||
|
||||
The following rules out of the [default set](https://djlint.com/docs/linter/) are not applied:
|
||||
```bash
|
||||
D018: (Django) Internal links should use the { % url ... % } pattern
|
||||
H006: Img tag should have height and width attributes
|
||||
H008: Attributes should be double quoted
|
||||
H021: Inline styles should be avoided
|
||||
H023: Do not use entity references
|
||||
H025: Tag seems to be an orphan
|
||||
H030: Consider adding a meta description
|
||||
H031: Consider adding meta keywords
|
||||
T002: Double quotes should be used in tags
|
||||
```
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
New features or updates to existing features should be accompanied by user documentation.
|
||||
|
||||
## Translations
|
||||
|
||||
Any user-facing strings *must* be passed through the translation engine.
|
||||
|
||||
- InvenTree code is written in English
|
||||
- User translatable strings are provided in English as the primary language
|
||||
- Secondary language translations are provided [via Crowdin](https://crowdin.com/project/inventree)
|
||||
|
||||
*Note: Translation files are updated via GitHub actions - you do not need to compile translations files before submitting a pull request!*
|
||||
|
||||
### Python Code
|
||||
|
||||
For strings exposed via Python code, use the following format:
|
||||
|
||||
```python
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
user_facing_string = _('This string will be exposed to the translation engine!')
|
||||
```
|
||||
|
||||
### Templated Strings
|
||||
|
||||
HTML and javascript files are passed through the django templating engine. Translatable strings are implemented as follows:
|
||||
|
||||
```html
|
||||
{ % load i18n % }
|
||||
|
||||
<span>{ % trans "This string will be translated" % } - this string will not!</span>
|
||||
```
|
||||
|
||||
## Github use
|
||||
|
||||
### Tags
|
||||
|
||||
The tags describe issues and PRs in multiple areas:
|
||||
|
||||
| Area | Name | Description |
|
||||
| --- | --- | --- |
|
||||
| Triage Labels | | |
|
||||
| | triage:not-checked | Item was not checked by the core team |
|
||||
| | triage:not-approved | Item is not green-light by maintainer |
|
||||
| Type Labels | | |
|
||||
| | breaking | Indicates a major update or change which breaks compatibility |
|
||||
| | bug | Identifies a bug which needs to be addressed |
|
||||
| | dependency | Relates to a project dependency |
|
||||
| | duplicate | Duplicate of another issue or PR |
|
||||
| | enhancement | This is an suggested enhancement, extending the functionality of an existing feature |
|
||||
| | experimental | This is a new *experimental* feature which needs to be enabled manually |
|
||||
| | feature | This is a new feature, introducing novel functionality |
|
||||
| | help wanted | Assistance required |
|
||||
| | invalid | This issue or PR is considered invalid |
|
||||
| | inactive | Indicates lack of activity |
|
||||
| | migration | Database migration, requires special attention |
|
||||
| | question | This is a question |
|
||||
| | roadmap | This is a roadmap feature with no immediate plans for implementation |
|
||||
| | security | Relates to a security issue |
|
||||
| | starter | Good issue for a developer new to the project |
|
||||
| | wontfix | No work will be done against this issue or PR |
|
||||
| Feature Labels | | |
|
||||
| | API | Relates to the API |
|
||||
| | barcode | Barcode scanning and integration |
|
||||
| | build | Build orders |
|
||||
| | importer | Data importing and processing |
|
||||
| | order | Purchase order and sales orders |
|
||||
| | part | Parts |
|
||||
| | plugin | Plugin ecosystem |
|
||||
| | pricing | Pricing functionality |
|
||||
| | report | Report generation |
|
||||
| | stock | Stock item management |
|
||||
| | user interface | User interface |
|
||||
| Ecosystem Labels | | |
|
||||
| | backport | Tags that the issue will be backported to a stable branch as a bug-fix |
|
||||
| | demo | Relates to the InvenTree demo server or dataset |
|
||||
| | docker | Docker / docker-compose |
|
||||
| | CI | CI / unit testing ecosystem |
|
||||
| | refactor | Refactoring existing code |
|
||||
| | setup | Relates to the InvenTree setup / installation process |
|
||||
|
@ -16,22 +16,29 @@ You need to make sure that you have the following tools installed before continu
|
||||
- [docker](https://www.docker.com/products/docker-desktop/) is needed to run the devcontainer
|
||||
- [vscode](https://code.visualstudio.com/Download) is needed to edit and debug code
|
||||
|
||||
#### Docker Containers
|
||||
|
||||
The InvenTree devcontainer setup will install two docker containers:
|
||||
|
||||
| Container | Description |
|
||||
| --- | --- |
|
||||
| db | InvenTree database (postgresql) |
|
||||
| inventree | InvenTree server |
|
||||
|
||||
#### Setup/Installation
|
||||
|
||||
1. Clone the repository (If you want to submit changes fork it and use the url to your fork in the next step)
|
||||
```bash
|
||||
git clone https://github.com/inventree/InvenTree.git
|
||||
```
|
||||
2. open vscode, navigate to the extensions sidebar and search for `ms-vscode-remote.remote-containers`. Click on install.
|
||||
3. open the cloned folder from above by clicking on `file > open folder`
|
||||
2. Open vscode, navigate to the extensions sidebar and search for `ms-vscode-remote.remote-containers`. Click on install.
|
||||
3. Open the cloned folder from above by clicking on `file > open folder`
|
||||
4. vscode should now ask you if you'd like to reopen this folder in a devcontainer. Click `Reopen in Container`. If it does not ask you, open the command palette (<kbd>CTRL/CMD</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>) and search for `Reopen in Container`. This can take a few minutes until the image is downloaded, build and setup with all dependencies.
|
||||
5. Open a new terminal from the top menu by clicking `Terminal > New Terminal`
|
||||
6. The last line in your terminal should now show the text `(venv)` at the start of the line
|
||||
7. From here' we need to setup the InvenTree specific development environment
|
||||
8. From the newly opened terminal, run: `invoke install`
|
||||
9. If you want test data on your server, run: `invoke setup-test --dev`. If not, run `invoke setup-dev`
|
||||
7. You are done! You now should have a functioning InvenTree development installation
|
||||
|
||||
### Setup in codespaces
|
||||
### Setup in Codespaces
|
||||
|
||||
Open [inventree/InvenTree](https://github.com/inventree/InvenTree) with your browser and click on `Code`, select the `codespaces` tab and click on create codespace on current branch. This may can take a few minutes until your inventree development environment is setup.
|
||||
|
||||
@ -52,14 +59,14 @@ If you only need a superuser, run the `superuser` task. It should prompt you for
|
||||
|
||||
#### Run background workers
|
||||
|
||||
If you need to process your queue with background workers, run the `worker` task.
|
||||
If you need to process your queue with background workers, run the `worker` task. This is a foreground task which will execute in the terminal.
|
||||
|
||||
### Running InvenTree
|
||||
|
||||
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 `python: Django - 3rd party`
|
||||
Sometimes you need to debug also some 3rd party packages. Just select `InvenTree Servre - 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.
|
||||
|
||||
@ -94,12 +101,15 @@ Your plugin should now be activateable from the InvenTree settings. You can also
|
||||
### Troubleshooting
|
||||
|
||||
#### Your ssh-keys are not available in the devcontainer but are loaded to the active `ssh-agent` on macOS
|
||||
|
||||
Make sure you enabled full disk access on macOS for vscode, otherwise your ssh-keys are not available inside the container (Ref: [Automatically add SSH keys to ssh-agent [comment]](https://github.com/microsoft/vscode-remote-release/issues/4024#issuecomment-831671081)).
|
||||
|
||||
#### You're not able to use your gpg-keys inside the devcontainer to sign commits on macOS
|
||||
|
||||
Make sure you have `gnupg` and `pinentry-mac` installed and set up correctly. Read this [medium post](https://medium.com/@jma/setup-gpg-for-git-on-macos-4ad69e8d3733) for more info on how to set it up correctly.
|
||||
|
||||
#### Where are the database, media files, ... stored?
|
||||
|
||||
Backups, Commandhistory, media/static files, venv, plugin.txt, secret_key.txt, ... are stored in the `dev` folder. If you want to start with a clean setup, you can remove that folder, but be aware that this will delete everything you already setup in InvenTree.
|
||||
|
||||
### Performance Improvements
|
||||
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: Getting started
|
||||
---
|
||||
|
||||
InvenTree consists of a Django-based backend server, and a HTML / vanilla JS based frontend that uses Bootstrap. The main languages used are Python and Javascript.
|
||||
As part of the larger project other languages/techniques are used, such as docker (dev/deployment), bash (installer) and markdown (documentation).
|
||||
|
||||
### Getting started
|
||||
#### Getting to know the basics
|
||||
|
||||
The Django framework is a powerful tool for creating web applications. It is well documented and has a large community. The [Django documentation](https://docs.djangoproject.com/en/stable/) is a good place to start.
|
||||
|
||||
In particular the [tutorial](https://docs.djangoproject.com/en/stable/intro/tutorial01/) is a good way to get to know the basics of Django.
|
||||
InvenTree follows the best practies for Django so most of the contents should be applicable to InvenTree as well. The REST API is based on the [Django REST framework](https://www.django-rest-framework.org/).
|
||||
|
||||
#### Setting up a development environment
|
||||
|
||||
The recommended way to set up a development environment is to use VSCode devcontainers. The required files are provided with the repo, the docs are on a [dedicated page](./devcontainer.md).
|
||||
|
||||
It is also possible to use [docker development](../start/docker_dev.md) or [bare metal development](../start/bare_dev.md). With these you need to install the required dependencies manually with a dedicated task.
|
||||
```bash
|
||||
invoke setup-dev
|
||||
```
|
||||
|
||||
#### Following standards
|
||||
|
||||
Before contributing to the project, please read the [contributing guidelines](contributing.md). Pull requests that do not follow the guidelines, do not pass QC checks or lower the test coverage will not be accepted.
|
@ -85,7 +85,7 @@ The configuration entries must be enabled via the [InvenTree admin interface](..
|
||||
|
||||
### Plugin Mixins
|
||||
|
||||
Common use cases are covered by pre-supplied modules in the form of *mixins* (similar to how [Django](https://docs.djangoproject.com/en/stable/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.
|
||||
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.
|
||||
|
||||
Supported mixin classes are:
|
||||
|
||||
|
@ -7,4 +7,4 @@ title: App Mixin
|
||||
If this mixin is added to a plugin the directory the plugin class is defined in is added to the list of `INSTALLED_APPS` in the InvenTree server configuration.
|
||||
|
||||
!!! warning "Danger Zone"
|
||||
Only use this mixin if you have an understanding of djangos [app system](https://docs.djangoproject.com/en/stable/ref/applications). Plugins with this mixin are deeply integrated into InvenTree and can cause difficult to reproduce or long-running errors. Use the built-in testing functions of django to make sure your code does not cause unwanted behaviour in InvenTree before releasing.
|
||||
Only use this mixin if you have an understanding of djangos [app system]({% include "django.html" %}/ref/applications). Plugins with this mixin are deeply integrated into InvenTree and can cause difficult to reproduce or long-running errors. Use the built-in testing functions of django to make sure your code does not cause unwanted behaviour in InvenTree before releasing.
|
||||
|
@ -12,7 +12,7 @@ A much simpler and more accessible method of recording custom information agains
|
||||
|
||||
### MetadataMixin Class
|
||||
|
||||
*Most* of the models in the InvenTree database inherit from the `MetadataMixin` class, which adds the `metadata` field to each inheriting model. The `metadata` field is a [JSONField](https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.JSONField) which allows for storing arbitrary JSON data against the model instance.
|
||||
*Most* of the models in the InvenTree database inherit from the `MetadataMixin` class, which adds the `metadata` field to each inheriting model. The `metadata` field is a [JSONField]({% include "django.html" %}/ref/models/fields/#django.db.models.JSONField) which allows for storing arbitrary JSON data against the model instance.
|
||||
|
||||
This field is provided to allow any plugins to store and retrieve arbitrary data against any item in the database.
|
||||
|
||||
|
@ -6,7 +6,7 @@ title: URLs Mixin
|
||||
|
||||
Use the class constant `URLS` for a array of URLs that should be added to InvenTrees URL paths or override the `plugin.setup_urls` function.
|
||||
|
||||
The array has to contain valid URL patterns as defined in the [django documentation](https://docs.djangoproject.com/en/stable/topics/http/urls/).
|
||||
The array has to contain valid URL patterns as defined in the [django documentation]({% include "django.html" %}/topics/http/urls/).
|
||||
|
||||
``` python
|
||||
class MyUrlsPlugin(UrlsMixin, InvenTreePlugin):
|
||||
@ -19,14 +19,14 @@ class MyUrlsPlugin(UrlsMixin, InvenTreePlugin):
|
||||
```
|
||||
|
||||
|
||||
The URLs get exposed under `/plugin/{plugin.slug}/*` and get exposed to the template engine with the prefix `plugin:{plugin.slug}:` (for usage with the [url tag](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#url)).
|
||||
The URLs get exposed under `/plugin/{plugin.slug}/*` and get exposed to the template engine with the prefix `plugin:{plugin.slug}:` (for usage with the [url tag]({% include "django.html" %}/ref/templates/builtins/#url)).
|
||||
|
||||
!!! info "Note"
|
||||
In this example, when an HTTP request is made to `/plugin/{plugin.slug}/increase/.../...` the function `self.view_increase` is called and returns the view to be displayed (step 4 in the Django documentation)
|
||||
|
||||
### Views
|
||||
If your plugin will implement and host another webpage, familiarize yourself with Django views. Implementation is exactly the same.
|
||||
A good place to start is the [django documentation](https://docs.djangoproject.com/en/4.2/topics/http/views/). Additional InvenTree-specific information is below.
|
||||
A good place to start is the [django documentation]({% include "django.html" %}/topics/http/views/). Additional InvenTree-specific information is below.
|
||||
|
||||
### Rendering Views
|
||||
Rendering templated views is also supported. Templated HTML files should be placed inside your plugin folder in a sub folder called `templates`.
|
||||
|
@ -29,7 +29,7 @@ If the installed version of invoke is too old, users may see error messages duri
|
||||
- *'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 `2.0.0`.
|
||||
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:
|
||||
|
||||
@ -60,7 +60,7 @@ Always activate the virtual environment before running server commands!
|
||||
|
||||
### 'str' object has no attribute 'removeSuffix'
|
||||
|
||||
This error occurs because your installed python version is not up to date. We [require Python v3.9 or newer](./start/intro.md#python-requirements)
|
||||
This error occurs because your installed python version is not up to date. We [require Python {{ config.extra.min_python_version }} or newer](./start/intro.md#python-requirements)
|
||||
|
||||
You (or your system administrator) needs to update python to meet the minimum requirements for InvenTree.
|
||||
|
||||
@ -77,7 +77,7 @@ The most common problem here is that the correct sequenct of steps has not been
|
||||
|
||||
For more information, refer to the installation guides:
|
||||
|
||||
- [Docker Installation](./start/docker_prod.md#updating-inventree)
|
||||
- [Docker Installation](./start/docker_install.md#updating-inventree)
|
||||
- [Bare Metal Installation](./start/install.md#updating-inventree)
|
||||
|
||||
!!! warning "Invoke Update"
|
||||
@ -117,7 +117,7 @@ invoke worker
|
||||
|
||||
### File Sync Issues - Docker
|
||||
|
||||
When installing under [Docker](./start/docker.md), sometimes issues may arise keeping [persistent data](./start/docker.md#persistent-data) in sync. Refer to the [common issues](./start/docker_prod.md#common-issues) section in the docker setup guide for further details.
|
||||
When installing under [Docker](./start/docker.md), sometimes issues may arise keeping [persistent data](./start/docker.md#persistent-data) in sync. Refer to the [common issues](./start/docker.md#common-issues) section in the docker setup guide for further details.
|
||||
|
||||
### Permission denied for mkdir: /home/inventree
|
||||
|
||||
|
@ -25,7 +25,7 @@ Templates are rendered using standard HTML / CSS - if you are familiar with web
|
||||
|
||||
### Template Language
|
||||
|
||||
Uploaded report template files are passed through the [django template rendering framework](https://docs.djangoproject.com/en/dev/topics/templates/), and as such accept the same variable template strings as any other django template file. Different variables are passed to the report template (based on the context of the report) and can be used to customize the contents of the generated PDF.
|
||||
Uploaded report template files are passed through the [django template rendering framework]({% include "django.html" %}/topics/templates/), and as such accept the same variable template strings as any other django template file. Different variables are passed to the report template (based on the context of the report) and can be used to customize the contents of the generated PDF.
|
||||
|
||||
### Variables
|
||||
|
||||
@ -77,7 +77,7 @@ Item: {{ item }}
|
||||
```
|
||||
|
||||
!!! info "Conditionals"
|
||||
Refer to the [django template language documentation](https://docs.djangoproject.com/en/dev/ref/templates/language/) for more information.
|
||||
Refer to the [django template language documentation]({% include "django.html" %}/ref/templates/language/) for more information.
|
||||
|
||||
### Localization Issues
|
||||
|
||||
@ -278,7 +278,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/docker_dev.md/#data-directory). Upload new assets via the [admin interface](../settings/admin.md) to ensure they are uploaded to the correct location on the server.
|
||||
You need to ensure your asset images to the report/assets directory in the [data directory](../start/intro.md#file-storage). Upload new assets via the [admin interface](../settings/admin.md) to ensure they are uploaded to the correct location on the server.
|
||||
|
||||
|
||||
## Report Snippets
|
||||
|
@ -94,7 +94,7 @@ This can be used to track usage and performance of the InvenTree backend and con
|
||||
If your InvenTree instance is used in a multi-site environment, you can enable multi-site support. Note that supporting multiple sites is well outside the scope of most InvenTree installations. If you know what you are doing, and have a good reason to enable multi-site support, you can do so by setting the `INVENTREE_SITE_MULTI` environment variable to `True`.
|
||||
|
||||
!!! tip "Django Documentation"
|
||||
For more information on multi-site support, refer to the [Django documentation](https://docs.djangoproject.com/en/3.2/ref/contrib/sites/).
|
||||
For more information on multi-site support, refer to the [Django documentation]({% include "django.html" %}/ref/contrib/sites/).
|
||||
|
||||
| Environment Variable | Config Key | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
|
@ -9,7 +9,7 @@ title: Bare Metal Production Server
|
||||
|
||||
The following instructions provide a reasonably performant server, using [gunicorn](https://gunicorn.org/) as a webserver, and [supervisor](http://supervisord.org/) as a process manager.
|
||||
|
||||
For alternative deployment methods, django apps provide multiple deployment methods - see the [Django documentation](https://docs.djangoproject.com/en/2.2/howto/deployment/).
|
||||
For alternative deployment methods, django apps provide multiple deployment methods - see the [Django documentation]({% include "django.html" %}/howto/deployment/).
|
||||
|
||||
There are also numerous online tutorials describing how to deploy a Django application either locally or on an online platform.
|
||||
|
||||
@ -102,10 +102,9 @@ In addition to the InvenTree server, you will need a method of delivering static
|
||||
|
||||
### Next Steps
|
||||
|
||||
You (or your system administrator) may wish to perform further steps such as placing the InvenTree server behind a reverse-proxy such as [nginx](https://www.nginx.com/).
|
||||
|
||||
You (or your system administrator) may wish to perform further steps such as placing the InvenTree server behind a reverse-proxy such as [caddy](https://caddyserver.com/), or [nginx](https://www.nginx.com/).
|
||||
As production environment options are many and varied, such tasks are outside the scope of this documentation.
|
||||
|
||||
There are many great online tutorials about running django applications in production!
|
||||
|
||||
As a starting point, you can refer to the [docker production example](./docker_prod.md) for a demonstration of running InvenTree behind a nginx proxy.
|
||||
As a starting point, you can refer to the [docker guide](./docker.md) for a demonstration of running InvenTree behind a Caddy proxy.
|
||||
|
@ -1,28 +1,13 @@
|
||||
---
|
||||
title: Database Configuration
|
||||
title: InvenTree Configuration
|
||||
---
|
||||
|
||||
## Database Configuration
|
||||
## InvenTree Configuration
|
||||
|
||||
While many InvenTree options can be configured at "run time", there are a number of system configuration parameters which need to be set *before* running InvenTree. Admin users will need to adjust the InvenTree installation to meet the particular needs of their setup. For example, pointing to the correct database backend, or specifying a list of allowed hosts.
|
||||
|
||||
InvenTree system settings can be specified either via environment variables, or in a configuration file.
|
||||
|
||||
!!! info "Environment Variables"
|
||||
Settings specified using environment variables take priority. Values provided in the configuration file are ignored if a matching environment variable is present.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
In addition to specifying InvenTree options via the `config.yaml` file, these options can also be specified via environment variables. This can be usful for system administrators who want the flexibility of altering settings without editing the configuration file.
|
||||
|
||||
Environment variable settings generally use the `INVENTREE_` prefix, and are all uppercase.
|
||||
|
||||
!!! info "Configuration Priority"
|
||||
Configuration options set via environment variables will take priority over the values set in the `config.yaml` file.
|
||||
|
||||
!!! warning "Available Variables"
|
||||
Some configuration options cannot be set via environment variables. Refer to the documentation below.
|
||||
|
||||
### Configuration File
|
||||
|
||||
To support install specific settings, a simple configuration file `config.yaml` is provided. This configuration file is loaded by the InvenTree server at runtime. Settings specific to a given install should be adjusted in `config.yaml`.
|
||||
@ -43,7 +28,19 @@ The configuration file *template* can be found on [GitHub](https://github.com/in
|
||||
The default configuration file (as defined by the template linked above) will be copied to the specified configuration file location on first run, if a configuration file is not found in that location.
|
||||
|
||||
!!! tip "Restart Server"
|
||||
The contents of the configuration file are read when the InevnTree server first launches. If any changes are made to the configuration file, ensure that the server is restarted, so that the changes can be made operational.
|
||||
The contents of the configuration file are read when the InvenTree server first launches. If any changes are made to the configuration file, ensure that the server is restarted, so that the changes can be made operational.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
In addition to specifying InvenTree options via the `config.yaml` file, these options can also be specified via environment variables. This can be useful for system administrators who want the flexibility of altering settings without editing the configuration file.
|
||||
|
||||
Environment variable settings generally use the `INVENTREE_` prefix, and are all uppercase.
|
||||
|
||||
!!! info "Configuration Priority"
|
||||
Configuration options set via environment variables will take priority over the values set in the `config.yaml` file. This can be useful for overriding specific settings without needing to edit the configuration file.
|
||||
|
||||
!!! warning "Available Variables"
|
||||
Some configuration options cannot be set via environment variables. Refer to the documentation below.
|
||||
|
||||
## Basic Options
|
||||
|
||||
@ -56,31 +53,52 @@ The following basic options are available:
|
||||
| INVENTREE_DB_LOGGING | db_logging | Enable logging of database messages | False |
|
||||
| INVENTREE_TIMEZONE | timezone | Server timezone | UTC |
|
||||
| INVENTREE_SITE_URL | site_url | Specify a fixed site URL | *Not specified* |
|
||||
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/) | True |
|
||||
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the [django administrator interface]({% include "django.html" %}/ref/contrib/admin/) | True |
|
||||
| INVENTREE_ADMIN_URL | admin_url | URL for accessing [admin interface](../settings/admin.md) | admin |
|
||||
| INVENTREE_LANGUAGE | language | Default language | en-us |
|
||||
| INVENTREE_BASE_URL | base_url | Server base URL | *Not specified* |
|
||||
| INVENTREE_AUTO_UPDATE | auto_update | Database migrations will be run automatically | False |
|
||||
|
||||
### Admin Site
|
||||
## Server Access
|
||||
|
||||
Django provides a powerful [administrator interface](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/) which can be used to manage the InvenTree database. This interface is enabled by default, but can be disabled by setting `INVENTREE_ADMIN_ENABLED` to `False`.
|
||||
Depending on how your InvenTree installation is configured, you will need to pay careful attention to the following settings. If you are running your server behind a proxy, or want to adjust support for CORS requests, one or more of the following settings may need to be adjusted.
|
||||
|
||||
#### Custom Admin URL
|
||||
!!! warning "Advanced Users"
|
||||
The following settings require a certain assumed level of knowledge. You should also refer to the [django documentation]({% include "django.html" %}/ref/settings/) for more information.
|
||||
|
||||
By default, the admin interface is available at the `/admin/` URL. This can be changed by setting the `INVENTREE_ADMIN_URL` environment variable.
|
||||
!!! danger "Not Secure"
|
||||
Allowing access from any host is not secure, and should be adjusted for your installation.
|
||||
|
||||
!!! info "Environment Variables"
|
||||
Note that a provided environment variable will override the value provided in the configuration file.
|
||||
|
||||
!!! success "INVENTREE_SITE_URL"
|
||||
If you have specified the `INVENTREE_SITE_URL`, this will automatically be used as a trusted CSRF and CORS host (see below).
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_ALLOWED_HOSTS | allowed_hosts | List of allowed hosts | `*` |
|
||||
| INVENTREE_TRUSTED_ORIGINS | trusted_origins | List of trusted origins. Refer to the [django documentation]({% include "django.html" %}/ref/settings/#csrf-trusted-origins) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
|
||||
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | False |
|
||||
| INVENTREE_CORS_ORIGIN_WHITELIST | cors.whitelist | List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
|
||||
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | False |
|
||||
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | False |
|
||||
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | True |
|
||||
|
||||
## Admin Site
|
||||
|
||||
Django provides a powerful [administrator interface]({% include "django.html" %}/ref/contrib/admin/) which can be used to manage the InvenTree database. This interface is enabled by default, and available at the `/admin/` URL.
|
||||
|
||||
The following admin site configuration options are available:
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_ADMIN_ENABLED | admin_enabled | Enable the django administrator interface | True |
|
||||
| INVENTREE_ADMIN_URL | admin_url | URL for accessing the admin interface | admin |
|
||||
|
||||
!!! warning "Security"
|
||||
Changing the admin URL is a simple way to improve security, but it is not a substitute for proper security practices.
|
||||
|
||||
### Base URL Configuration
|
||||
|
||||
The base URL of the InvenTree site is required for constructing absolute URLs in a number of circumstances. To construct a URL, the InvenTree iterates through the following options in decreasing order of importance:
|
||||
|
||||
1. Static configuration (i.e. set using environment variable or configuration file as above)
|
||||
2. Global settings (i.e. configured at run-time in the [global settings](../settings/global.md))
|
||||
3. Using the hostname supplied by the user request
|
||||
|
||||
## Administrator Account
|
||||
|
||||
An administrator account can be specified using the following environment variables:
|
||||
@ -196,32 +214,6 @@ A list of currency codes (e.g. *AUD*, *CAD*, *JPY*, *USD*) can be specified usin
|
||||
!!! tip "More Info"
|
||||
Read the [currencies documentation](../settings/currency.md) for more information on currency support in InvenTree
|
||||
|
||||
## Server Access
|
||||
|
||||
Depending on how your InvenTree installation is configured, you will need to pay careful attention to the following settings. If you are running your server behind a proxy, or want to adjust support for CORS requests, one or more of the following settings may need to be adjusted.
|
||||
|
||||
!!! warning "Advanced Users"
|
||||
The following settings require a certain assumed level of knowledge. You should also refer to the [django documentation](https://docs.djangoproject.com/en/4.2/ref/settings/) for more information.
|
||||
|
||||
!!! danger "Not Secure"
|
||||
Allowing access from any host is not secure, and should be adjusted for your installation.
|
||||
|
||||
!!! info "Environment Variables"
|
||||
Note that a provided environment variable will override the value provided in the configuration file.
|
||||
|
||||
!!! success "INVENTREE_SITE_URL"
|
||||
If you have specified the `INVENTREE_SITE_URL`, this will automatically be used as a trusted CSRF and CORS host (see below).
|
||||
|
||||
| Environment Variable | Configuration File | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_ALLOWED_HOSTS | allowed_hosts | List of allowed hosts | `*` |
|
||||
| INVENTREE_TRUSTED_ORIGINS | trusted_origins | List of trusted origins. Refer to the [django documentation](https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
|
||||
| INVENTREE_CORS_ORIGIN_ALLOW_ALL | cors.allow_all | Allow all remote URLS for CORS checks | False |
|
||||
| INVENTREE_CORS_ORIGIN_WHITELIST | cors.whitelist | List of whitelisted CORS URLs. Refer to the [django-cors-headers documentation](https://github.com/adamchainz/django-cors-headers#cors_allowed_origins-sequencestr) | Uses the *INVENTREE_SITE_URL* parameter, if set. Otherwise, an empty list. |
|
||||
| INVENTREE_USE_X_FORWARDED_HOST | use_x_forwarded_host | Use forwarded host header | False |
|
||||
| INVENTREE_USE_X_FORWARDED_PORT | use_x_forwarded_port | Use forwarded port header | False |
|
||||
| INVENTREE_CORS_ALLOW_CREDENTIALS | cors.allow_credentials | Allow cookies in cross-site requests | True |
|
||||
|
||||
## File Storage Locations
|
||||
|
||||
InvenTree requires some external directories for storing files:
|
||||
@ -230,6 +222,7 @@ InvenTree requires some external directories for storing files:
|
||||
| --- | --- | --- | --- |
|
||||
| INVENTREE_STATIC_ROOT | static_root | [Static files](./serving_files.md#static-files) directory | *Not specified* |
|
||||
| INVENTREE_MEDIA_ROOT | media_root | [Media files](./serving_files.md#media-files) directory | *Not specified* |
|
||||
| INVENTREE_BACKUP_DIR | backup_dir | Backup files directory | *Not specified* |
|
||||
|
||||
!!! tip "Serving Files"
|
||||
Read the [Serving Files](./serving_files.md) section for more information on hosting *static* and *media* files
|
||||
@ -298,7 +291,7 @@ The InvenTree server can be integrated with the [sentry.io](https://sentry.io) m
|
||||
!!! info "Default DSN"
|
||||
If enabled with the default DSN, server errors will be logged to a sentry.io account monitored by the InvenTree developers.
|
||||
|
||||
### Customisation Options
|
||||
### Customization Options
|
||||
|
||||
The logo and custom messages can be changed/set:
|
||||
|
||||
@ -327,9 +320,3 @@ The following [plugin](../extend/plugins.md) configuration options are available
|
||||
| INVENTREE_PLUGIN_NOINSTALL | plugin_noinstall | Disable Plugin installation via API - only use plugins.txt file | False |
|
||||
| INVENTREE_PLUGIN_FILE | plugins_plugin_file | Location of plugin installation file | *Not specified* |
|
||||
| INVENTREE_PLUGIN_DIR | plugins_plugin_dir | Location of external plugin directory | *Not specified* |
|
||||
|
||||
## Other Options
|
||||
|
||||
### Middleware
|
||||
|
||||
Custom middleware layers can be specified here.
|
||||
|
@ -2,18 +2,24 @@
|
||||
title: Docker Setup
|
||||
---
|
||||
|
||||
## Docker Image
|
||||
## Docker Installation Guide
|
||||
|
||||
The information on this page serves as useful theory and background information for setting up InvenTree using docker.
|
||||
|
||||
!!! tip "Docker Install"
|
||||
To jump right into the installation process, refer to the [docker installation guide](./docker_install.md)
|
||||
|
||||
## Docker Theory
|
||||
|
||||
The most convenient method of installing and running InvenTree is to use the official [docker image](https://hub.docker.com/r/inventree/inventree), available from docker-hub.
|
||||
|
||||
The InvenTree docker image contains all the required system packages, python modules, and configuration files for running a containerised InvenTree web server.
|
||||
|
||||
!!! tip "Compose Yourself"
|
||||
The InvenTree container requires linking with other docker containers (such as a database backend) for complete operation. Sample [docker compose](#docker-compose) scripts are provided to get you up and running
|
||||
The InvenTree docker image contains all the required system packages, python modules, and configuration files for running a containerized InvenTree production installation.
|
||||
|
||||
!!! tip "Docker Compose"
|
||||
The InvenTree container requires linking with other docker containers (such as a database backend, and a file server) for complete operation. Refer to the [docker compose](#docker-compose) instructions to get up and running
|
||||
|
||||
!!! warning "Check the version"
|
||||
Please make sure you are reading the [STABLE](https://docs.inventree.org/en/stable/start/docker_prod/) documentation when using the stable docker image tags.
|
||||
Please make sure you are reading the [STABLE](https://docs.inventree.org/en/stable/start/docker/) documentation when using the stable docker image tags.
|
||||
|
||||
!!! warning "Assumed Knowledge"
|
||||
A very basic understanding of [Docker](https://www.docker.com/) and [docker compose](https://docs.docker.com/compose/) is assumed, for the following setup guides.
|
||||
@ -26,16 +32,11 @@ Pre-built Docker images are available from [dockerhub](https://hub.docker.com/r/
|
||||
| --- | --- | --- |
|
||||
| **inventree:stable** | The most recent *stable* release version of InvenTree | [stable docs](https://docs.inventree.org/en/stable/start/docker/) |
|
||||
| **inventree:latest** | The most up-to-date *development* version of InvenTree. | [latest docs](https://docs.inventree.org/en/latest/start/docker/) |
|
||||
| **inventree:_tag_** | Specific tagged images are built for each tagged release of InvenTree, e.g. `inventree:0.7.3`| https://docs.inventree.org/en/INSERT_YOUR_TAG_HERE/start/docker/ |
|
||||
| **inventree:_tag_** | Specific tagged images are built for each tagged release of InvenTree, e.g. `inventree:0.7.3`| *Refer to specific InvenTree version* |
|
||||
|
||||
### Docker Compose
|
||||
|
||||
The InvenTree docker image provides a containerized webserver, however it *must* be connected with other containers (at the very least, a database backend).
|
||||
|
||||
InvenTree provides sample docker compose files to get you up and running:
|
||||
|
||||
- A [development](#development-server) compose file provides a simple way to spin up a development environment
|
||||
- A [production](#production-server) compose file is intended to be used in a production environment, running the web server behind a nginx proxy.
|
||||
The InvenTree docker image provides a containerized webserver, however it *must* be connected with other containers to function.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
@ -77,14 +78,82 @@ Plugins are supported natively when running under docker. There are two ways to
|
||||
- Install via the `plugins.txt` file provided in the external data directory
|
||||
- Install into the `plugins/` subdirectory in the external data directory
|
||||
|
||||
## Docker Setup Guides
|
||||
## Docker Compose
|
||||
|
||||
With these basics in mind, refer to the following installation guides:
|
||||
[docker compose](https://docs.docker.com/compose/) is used to sequence all the required containerized processes.
|
||||
|
||||
### Production Server
|
||||
### Static and Media Files
|
||||
|
||||
Refer to the [docker production server setup guide](./docker_prod.md) for instructions on configuring a production server using docker.
|
||||
The production docker compose configuration outlined on this page uses [Caddy](https://caddyserver.com/) to serve static files and media files. If you change this configuration, you will need to ensure that static and media files are served correctly.
|
||||
|
||||
### Development Server
|
||||
!!! info "Read More"
|
||||
Refer to the [Serving Files](./serving_files.md) section for more details
|
||||
|
||||
Refer to the [docker development server setup guide](./docker_dev.md) for instructions on configuring a development server using docker.
|
||||
### SSL Certificates
|
||||
|
||||
The provided `Caddyfile` configuration file is setup to enable [Automatic HTTPS](https://caddyserver.com/docs/automatic-https) by default! All you have to do is specify a `https://` URL in the `INVENTREE_SITE_URL` variable.
|
||||
|
||||
### Containers
|
||||
|
||||
The example docker compose file launches the following containers:
|
||||
|
||||
| Container | Description |
|
||||
| --- | --- |
|
||||
| inventree-db | PostgreSQL database |
|
||||
| inventree-server | Gunicorn web server |
|
||||
| inventree-worker | django-q background worker |
|
||||
| inventree-proxy | Caddy file server and reverse proxy |
|
||||
| *inventree-cache* | *redis cache (optional)* |
|
||||
|
||||
#### PostgreSQL Database
|
||||
|
||||
A PostgreSQL database container which requires a username:password combination (which can be changed). This uses the official [PostgreSQL image](https://hub.docker.com/_/postgres).
|
||||
|
||||
#### Web Server
|
||||
|
||||
Runs an InvenTree web server instance, powered by a Gunicorn web server.
|
||||
|
||||
#### Background Worker
|
||||
|
||||
Runs the InvenTree background worker process. This spins up a second instance of the *inventree* container, with a different entrypoint command.
|
||||
|
||||
#### File Server
|
||||
|
||||
Caddy working as a reverse proxy, separating requests for static and media files, and directing everything else to Gunicorn.
|
||||
|
||||
This container uses the official [caddy image](https://hub.docker.com/_/caddy).
|
||||
|
||||
#### Redis Cache
|
||||
|
||||
Redis is used as cache storage for the InvenTree server. This provides a more performant caching system which can useful in larger installations.
|
||||
|
||||
This container uses the official [redis image](https://hub.docker.com/_/redis).
|
||||
|
||||
!!! info "Redis on Docker"
|
||||
Docker adds an additional network layer - that might lead to lower performance than bare metal.
|
||||
To optimize and configure your redis deployment follow the [official docker guide](https://redis.io/docs/getting-started/install-stack/docker/#configuration).
|
||||
|
||||
!!! warning "Disabled by default"
|
||||
The *redis* container is not enabled in the default configuration. This is provided as an example for users wishing to use redis.
|
||||
To enable the *redis* container, run any `docker compose` commands with the `--profile redis` flag.
|
||||
You will also need to un-comment the `INVENTREE_CACHE_<...>` variables in the `.env` file.
|
||||
|
||||
### Data Volume
|
||||
|
||||
InvenTree stores any persistent data (e.g. uploaded media files, database data, etc) in a [volume](https://docs.docker.com/storage/volumes/) which is mapped to a local system directory. The location of this directory must be configured in the `.env` file, specified using the `INVENTREE_EXT_VOLUME` variable.
|
||||
|
||||
!!! info "Data Directory"
|
||||
Make sure you change the path to the local directory where you want persistent data to be stored.
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Volume Mapping
|
||||
|
||||
When configuring a docker install, sometimes a misconfiguration can cause peculiar issues where it seems that the installation is functioning correctly, but uploaded files and plugins do not "persist" across sessions. In such cases, the "mounted" volume has not mapped to a directory on your local filesystem. This may occur if you have tried multiple setup options without clearing existing volume bindings.
|
||||
|
||||
!!! tip "Start with a clean slate"
|
||||
To prevent such issues, it is recommended that you start with a "clean slate" if you have previously configured an InvenTree installation under docker.
|
||||
|
||||
If you have previously setup InvenTree, remove existing volume bindings using the following command:
|
||||
|
||||
```docker volume rm -f inventree-production_inventree_data```
|
||||
|
@ -1,241 +0,0 @@
|
||||
---
|
||||
title: Docker Development Server
|
||||
---
|
||||
|
||||
## Docker Development Server
|
||||
|
||||
You can use docker to launch and manage a development server, in a similar fashion to managing a production server.
|
||||
|
||||
The InvenTree dockerfile (`./Dockerfile`) uses a [multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/) process to allow both production and development setups from the same image.
|
||||
|
||||
There are some key differences compared to the [docker production setup](./docker_prod.md):
|
||||
|
||||
- The docker image is built locally, rather than being downloaded from DockerHub
|
||||
- The docker image points to the source code on your local machine (mounted as a 'volume' in the docker container)
|
||||
- The django webserver is used, instead of running behind Gunicorn
|
||||
- The server will automatically reload when code changes are detected
|
||||
|
||||
!!! info "Static and Media Files"
|
||||
The development server runs in DEBUG mode, and serves static and media files natively.
|
||||
|
||||
!!! info "Hacker Mode"
|
||||
The following setup guide starts a development server which will reload "on the fly" as changes are made to the source code. This is designed for programmers and developers who wish to add and test new InvenTree features.
|
||||
|
||||
### Data Directory
|
||||
|
||||
Persistent data (such as the stored database, media files, configuration files, etc) will be stored in the `./data` directory (relative to the InvenTree source code directory).
|
||||
|
||||
- This directory is automatically created when you launch InvenTree via docker
|
||||
- This directory is excluded from git version tracking
|
||||
|
||||
## Quickstart Guide
|
||||
|
||||
To get "up and running" with a development environment, complete with a set of [demo data](https://github.com/inventree/demo-dataset) to work with, run the following commands:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/inventree/InvenTree.git && cd InvenTree
|
||||
docker compose run inventree-dev-server invoke update
|
||||
docker compose run inventree-dev-server invoke setup-test --dev
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
!!! tip "Development Server"
|
||||
You can then view the development server at [http://localhost:8000](http://localhost:8000)
|
||||
|
||||
!!! info "Details, details"
|
||||
For a more in-depth setup guide, continue reading below!
|
||||
|
||||
## Development Setup Guide
|
||||
|
||||
To get started with an InvenTree development setup, follow the simple steps outlined below. Before continuing, ensure that you have completed the following steps:
|
||||
|
||||
- Downloaded the InvenTree source code to your local machine
|
||||
- Installed docker on your local machine (install *Docker Desktop* on Windows)
|
||||
- Have a terminal open to the root directory of the InvenTree source code
|
||||
|
||||
### Edit Environment Variables (Optional)
|
||||
|
||||
If desired, the user may edit the environment variables, located in the `.env` file.
|
||||
|
||||
!!! success "This step is optional"
|
||||
This step can be skipped, as the default variables will work just fine!
|
||||
|
||||
!!! info "Database Credentials"
|
||||
You may also wish to change the database username (`INVENTREE_DB_USER`) and password (`INVENTREE_DB_PASSWORD`) from their default values
|
||||
|
||||
### Perform Initial Setup
|
||||
|
||||
Perform the initial database setup by running the following command:
|
||||
|
||||
```bash
|
||||
docker compose run inventree-dev-server invoke update
|
||||
```
|
||||
|
||||
If this is the first time you are configuring the development server, this command will build a development version of the inventree docker image.
|
||||
|
||||
This command also performs the following steps:
|
||||
|
||||
- Ensure required python packages are installed
|
||||
- Perform the required schema updates to create the required database tables
|
||||
- Update translation files
|
||||
- Collect all required static files into a directory where they can be served by nginx
|
||||
|
||||
!!! info "Grab a coffee"
|
||||
This initial build process may take a few minutes!
|
||||
|
||||
### Import Demo Data
|
||||
|
||||
To fill the database with a demo dataset, run the following command:
|
||||
|
||||
```bash
|
||||
docker compose run inventree-dev-server invoke setup-test --dev
|
||||
```
|
||||
|
||||
### Start Docker Containers
|
||||
|
||||
Now that the database has been created, and migrations applied, we are ready to launch the InvenTree containers:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Create Admin Account
|
||||
|
||||
If you are creating the initial database, you need to create an admin (superuser) account for the database. Run the command below, and follow the prompts:
|
||||
|
||||
!!! info "Containers must be running"
|
||||
For the `invoke superuser` command to execute properly, ensure you have run the `docker compose up -d` command.
|
||||
|
||||
```bash
|
||||
docker compose run inventree-dev-server invoke superuser
|
||||
```
|
||||
|
||||
This command launches the remaining containers:
|
||||
|
||||
- `inventree-dev-server` - InvenTree web server
|
||||
- `inventree-dev-worker` - Background worker
|
||||
|
||||
!!! success "Check Connection"
|
||||
Check that the server is running at [http://localhost:8000](http://localhost:8000). The server may take a few minutes to be ready.
|
||||
|
||||
## Running commands in the container
|
||||
|
||||
Using `docker compose run [...]` commands creates a new container to run this specific command.
|
||||
This will eventually clutter your docker with many dead containers that take up space on the system.
|
||||
|
||||
You can access the running containers directly with the following:
|
||||
```bash
|
||||
docker exec -it inventree-dev-server /bin/bash
|
||||
```
|
||||
|
||||
You then run the following to access the virtualenv:
|
||||
```bash
|
||||
source data/env/bin/activate
|
||||
```
|
||||
|
||||
This sets up a bash terminal where you can run `invoke` commands directly.
|
||||
|
||||
!!! warning "Tests"
|
||||
Running `invoke test` in your currently active inventree-dev-server container may result in tests taking longer than usual.
|
||||
|
||||
### Cleaning up old containers
|
||||
|
||||
If you have Docker Desktop installed, you will be able to remove containers directly in the GUI.
|
||||
Your active containers are grouped under "inventree" in Docker Desktop.
|
||||
The main dev-server, dev-db, and dev-worker containers are all listed without the "inventree" prefix.
|
||||
One time run containers, like those executed via `docker compose run [...]` are suffixed with `run-1a2b3c4d5e6f` where the hex string varies.
|
||||
|
||||
To remove such containers, either click the garbage bin on the end of the line, or mark the containers, and click the delete button that shows up.
|
||||
This is the recommended procedure for container cleanup.
|
||||
|
||||
#### Advanced cleanup
|
||||
!!! warning "Advanced users only"
|
||||
This section requires good knowledge of Docker and how it operates.
|
||||
Never perform these commands if you do not understand what they do
|
||||
|
||||
If you're running a container with the general boilerplate commands used with invoke (invoke test, invoke update, etc) and no custom parameters or execution, you can add the `--rm` flag to `docker compose run`, and the container will delete itself when it goes down.
|
||||
Do note that any data not stored in a volume, i.e. only in the container, will be lost when the container stops.
|
||||
|
||||
To clean out old containers using the command line, follow this guide:
|
||||
|
||||
Run the following command:
|
||||
```bash
|
||||
docker ps -a --filter status=exited
|
||||
```
|
||||
|
||||
This gives you a list of all stopped containers.
|
||||
Find the containers you wish to delete, copy the container IDs and add them to this command:
|
||||
|
||||
```bash
|
||||
docker rm [ID1] [ID2] [IDn]
|
||||
```
|
||||
When executed, this removes all containers whose IDs were pasted.
|
||||
|
||||
!!! warning "Execute at own risk"
|
||||
The command below does not forgive errors.
|
||||
Execute this only if you know what you're doing
|
||||
|
||||
Running this command will remove **all** stopped one-time run InvenTree containers matching parameters:
|
||||
```bash
|
||||
docker container prune --filter label="com.docker.compose.oneoff=True" --filter label="com.docker.compose.service=inventree-dev-server"
|
||||
```
|
||||
|
||||
The following output will appear:
|
||||
```
|
||||
WARNING! This will remove all stopped containers.
|
||||
Are you sure you want to continue? [y/N] y
|
||||
Deleted Containers:
|
||||
[IDs of any container that was deleted, one per line]
|
||||
```
|
||||
|
||||
## Restarting Services
|
||||
|
||||
Once initial setup is complete, stopping and restarting the services is much simpler:
|
||||
|
||||
### Stop InvenTree Services
|
||||
|
||||
To stop the InvenTree development server, simply run the following command:
|
||||
|
||||
```
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Start InvenTree Services
|
||||
|
||||
To start the InvenTree development server, simply run the following command:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Restart InvenTree Services
|
||||
|
||||
A restart cycle is as simple as:
|
||||
|
||||
```
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
## Editing InvenTree Source
|
||||
|
||||
Any changes made to the InvenTree source code are automatically detected by the services running under docker.
|
||||
|
||||
Thus, you can freely edit the InvenTree source files in your editor of choice.
|
||||
|
||||
### Database Updates
|
||||
|
||||
Any updates which require a database schema change must be reflected in the database itself.
|
||||
|
||||
To run database migrations inside the docker container, run the following command:
|
||||
|
||||
```
|
||||
docker compose run inventree-dev-server invoke update
|
||||
```
|
||||
|
||||
### Docker Image Updates
|
||||
|
||||
Occasionally, the docker image itself may receive some updates. In these cases, it may be required that the image is rebuilt. To perform a complete rebuild of the InvenTree development image from local source, run the following command:
|
||||
|
||||
```
|
||||
docker compose build --no-cache
|
||||
```
|
211
docs/docs/start/docker_install.md
Normal file
211
docs/docs/start/docker_install.md
Normal file
@ -0,0 +1,211 @@
|
||||
---
|
||||
title: Docker Production Server
|
||||
---
|
||||
|
||||
## Docker Production Server
|
||||
|
||||
The following guide provides a streamlined production InvenTree installation, with minimal configuration required.
|
||||
|
||||
!!! tip "Docker Installation"
|
||||
This guide assumes that you have already installed [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/). If you have not yet installed Docker, please refer to the [official installation guide](https://docs.docker.com/get-docker/).
|
||||
|
||||
!!! info "Starting Point"
|
||||
This setup guide should be considered a *starting point* - your particular production requirements vary from the example shown here.
|
||||
|
||||
!!! tip "Docker Theory"
|
||||
Refer to the [docker theory section](./docker.md) for more information about how our docker installation works under the hood.
|
||||
|
||||
### Before You Start
|
||||
|
||||
!!! warning "Check the version"
|
||||
Please make sure you are reading the [STABLE](https://docs.inventree.org/en/stable/start/docker/) documentation when using the stable docker image tags.
|
||||
|
||||
!!! warning "Docker Knowledge Required"
|
||||
This guide assumes that you are reasonably comfortable with the basic concepts of docker and docker compose.
|
||||
|
||||
## Docker Installation
|
||||
|
||||
### Required Files
|
||||
|
||||
The following files required for this setup are provided with the InvenTree source, located in the `./docker/` directory of the [InvenTree source code](https://github.com/inventree/InvenTree/tree/master/docker/production):
|
||||
|
||||
| Filename | Description |
|
||||
| --- | --- |
|
||||
| [docker-compose.yml](https://github.com/inventree/InvenTree/blob/master/docker/docker-compose.yml) | The docker compose script |
|
||||
| [.env](https://github.com/inventree/InvenTree/blob/master/docker/.env) | Environment variables |
|
||||
| [Caddyfile](https://github.com/inventree/InvenTree/blob/master/docker/Caddyfile) | Caddy configuration file |
|
||||
|
||||
Download these files to a directory on your local machine.
|
||||
|
||||
!!! success "Working Directory"
|
||||
This tutorial assumes you are working from a direction where all of these files are located.
|
||||
|
||||
!!! tip "No Source Required"
|
||||
For a production setup you do not need the InvenTree source code. Simply download the three required files from the links above!
|
||||
|
||||
### Edit Environment Variables
|
||||
|
||||
The first step is to edit the environment variables, located in the `.env` file.
|
||||
|
||||
!!! warning "External Volume"
|
||||
You must define the `INVENTREE_EXT_VOLUME` variable - this must point to a directory *on your local machine* where persistent data is to be stored.
|
||||
|
||||
!!! warning "Database Credentials"
|
||||
You must also define the database username (`INVENTREE_DB_USER`) and password (`INVENTREE_DB_PASSWORD`). You should ensure they are changed from the default values for added security
|
||||
|
||||
!!! info "Other Variables"
|
||||
There are a number of other environment variables which can be set to customize the InvenTree installation. Refer to the [environment variables](./config.md) documentation for more information.
|
||||
|
||||
### Initial Database Setup
|
||||
|
||||
Perform the initial database setup by running the following command:
|
||||
|
||||
```bash
|
||||
docker compose run --rm inventree-server invoke update
|
||||
```
|
||||
|
||||
This command performs the following steps:
|
||||
|
||||
- Ensure required python packages are installed
|
||||
- Create a new (empty) database
|
||||
- Perform the required schema updates to create the required database tables
|
||||
- Update translation files
|
||||
- Update required static files
|
||||
|
||||
### Create Administrator Account
|
||||
|
||||
If you are creating the initial database, you need to create an admin (superuser) account for the database. Run the command below, and follow the prompts:
|
||||
|
||||
```
|
||||
docker compose run inventree-server invoke superuser
|
||||
```
|
||||
|
||||
Alternatively, admin account details can be specified in the `.env` file, removing the need for this manual step:
|
||||
|
||||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| INVENTREE_ADMIN_USER | Admin account username |
|
||||
| INVENTREE_ADMIN_PASSWORD | Admin account password |
|
||||
| INVENTREE_ADMIN_EMAIL | Admin account email address |
|
||||
|
||||
!!! warning "Scrub Account Data"
|
||||
Ensure that the admin account credentials are removed from the `.env` file after the first run, for security.
|
||||
|
||||
### Start Docker Containers
|
||||
|
||||
Now that the database has been created, migrations applied, and you have created an admin account, we are ready to launch the InvenTree containers:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This command launches the following containers:
|
||||
|
||||
- `inventree-db` - PostgreSQL database
|
||||
- `inventree-server` - InvenTree web server
|
||||
- `inventree-worker` - Background worker
|
||||
- `inventree-proxy` - Caddy reverse proxy
|
||||
|
||||
!!! success "Up and Running!"
|
||||
You should now be able to view the InvenTree login screen at [http://inventree.localhost](http://inventree.localhost) (or whatever custom domain you have configured in the `.env` file).
|
||||
|
||||
## Updating InvenTree
|
||||
|
||||
To update your InvenTree installation to the latest version, follow these steps:
|
||||
|
||||
### Stop Containers
|
||||
|
||||
Stop all running containers as below:
|
||||
|
||||
```
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Update Images
|
||||
|
||||
Pull down the latest version of the InvenTree docker image
|
||||
|
||||
```
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
This ensures that the InvenTree containers will be running the latest version of the InvenTree source code.
|
||||
|
||||
!!! tip "Docker Directory"
|
||||
All `docker compose` commands must be performed in the same directory as the [docker-compose.yml file](#required-files)
|
||||
|
||||
!!! info "Tagged Version"
|
||||
If you are targeting a particular "tagged" version of InvenTree, you may wish to edit the `INVENTREE_TAG` variable in the `.env` file before issuing the `docker compose pull` command
|
||||
|
||||
### Update Database
|
||||
|
||||
Run the following command to ensure that the InvenTree database is updated:
|
||||
|
||||
```
|
||||
docker compose run --rm inventree-server invoke update
|
||||
```
|
||||
|
||||
!!! info "Skip Backup"
|
||||
By default, the `invoke update` command performs a database backup. To skip this step, add the `--skip-backup` flag
|
||||
|
||||
### Start Containers
|
||||
|
||||
Now restart the docker containers:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Data Backup
|
||||
|
||||
Database and media files are stored external to the container, in the volume location specified in the `docker-compose.yml` file. It is strongly recommended that a backup of the files in this volume is performed on a regular basis.
|
||||
|
||||
Read more about [data backup](./backup.md).
|
||||
|
||||
### Exporting Database as JSON
|
||||
|
||||
To export the database to an agnostic JSON file, perform the following command:
|
||||
|
||||
```
|
||||
docker compose run --rm inventree-server invoke export-records -f /home/inventree/data/data.json
|
||||
```
|
||||
|
||||
This will export database records to the file `data.json` in your mounted volume directory.
|
||||
|
||||
## Further Configuration
|
||||
|
||||
### Custom Domain
|
||||
|
||||
By default, the InvenTree server is accessible at [http://inventree.localhost](http://inventree.localhost). If you wish to use a custom domain, you can edit the `.env` environment file to specify the domain name.
|
||||
|
||||
Look for the `INVENTREE_SITE_URL` variable, and set it to the desired domain name.
|
||||
|
||||
!!! tip "Configuration Options"
|
||||
There are a number of other environment variables which can be set to customize the InvenTree installation. Refer to the [configuration documentation](./config.md) for more information.
|
||||
|
||||
### SSL Configuration
|
||||
|
||||
The provided `Caddyfile` configuration file is setup to enable [Automatic HTTPS](https://caddyserver.com/docs/automatic-https) "out of the box". All you have to do is specify a `https://` URL in the `INVENTREE_SITE_URL` variable.
|
||||
|
||||
|
||||
The [Caddy](./docker.md#ssl-certificates) container will automatically generate SSL certificates for your domain.
|
||||
|
||||
#### Persistent Files
|
||||
|
||||
Any persistent files generated by the Caddy container (such as certificates, etc) will be stored in the `caddy` directory within the external volume.
|
||||
|
||||
### Demo Dataset
|
||||
|
||||
To quickly get started with a demo dataset, you can run the following command:
|
||||
|
||||
```
|
||||
docker compose run --rm inventree-server invoke setup-test -i
|
||||
```
|
||||
|
||||
This will install the InvenTree demo dataset into your instance.
|
||||
|
||||
To start afresh (and completely remove the existing database), run the following command:
|
||||
|
||||
```
|
||||
docker compose run --rm inventree-server invoke delete-data
|
||||
```
|
@ -1,247 +0,0 @@
|
||||
---
|
||||
title: Docker Production Server
|
||||
---
|
||||
|
||||
## Docker Production Server
|
||||
|
||||
The following guide provides a streamlined production InvenTree installation, with minimal configuration required.
|
||||
|
||||
!!! info "Starting Point"
|
||||
This setup guide should be considered a *starting point*. It is likely that your particular production requirements will vary from the example shown here.
|
||||
|
||||
### Before You Start
|
||||
|
||||
!!! warning "Check the version"
|
||||
Please make sure you are reading the [STABLE](https://docs.inventree.org/en/stable/start/docker_prod/) documentation when using the stable docker image tags.
|
||||
|
||||
!!! warning "Docker Skills Required"
|
||||
This guide assumes that you are reasonably comfortable with the basic concepts of docker and docker compose.
|
||||
|
||||
#### Docker Image
|
||||
|
||||
This production setup guide uses the official InvenTree docker image, available from dockerhub.
|
||||
|
||||
!!! info "Stable Version"
|
||||
The provided docker compose file targets `inventree:stable` by default.
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
A sample [docker compose file](https://github.com/inventree/InvenTree/blob/master/docker/production/docker-compose.yml) is provided to sequence all the required processes.
|
||||
|
||||
!!! tip "Starting Point"
|
||||
If you require a different configuration, use this docker compose file as a starting point.
|
||||
|
||||
#### Static and Media Files
|
||||
|
||||
The sample docker compose configuration outlined on this page uses nginx to serve static files and media files. If you change this configuration, you will need to ensure that static and media files are served correctly.
|
||||
|
||||
!!! info "Read More"
|
||||
Refer to the [Serving Files](./serving_files.md) section for more details
|
||||
|
||||
#### Required Files
|
||||
|
||||
The following files required for this setup are provided with the InvenTree source, located in the `./docker/production` directory of the [InvenTree source code](https://github.com/inventree/InvenTree/tree/master/docker/production):
|
||||
|
||||
| Filename | Description |
|
||||
| --- | --- |
|
||||
| [docker-compose.yml](https://github.com/inventree/InvenTree/blob/master/docker/production/docker-compose.yml) | The docker compose script |
|
||||
| [.env](https://github.com/inventree/InvenTree/blob/master/docker/production/.env) | Environment variables |
|
||||
| [nginx.prod.conf](https://github.com/inventree/InvenTree/blob/master/docker/production/nginx.prod.conf) | nginx proxy configuration file |
|
||||
|
||||
This tutorial assumes you are working from the `./docker/production` directory. If this is not the case, ensure that these required files are all located in your working directory.
|
||||
|
||||
!!! tip "No Source Required"
|
||||
For a production setup you do not need the InvenTree source code. Simply download the three required files from the links above!
|
||||
|
||||
### Containers
|
||||
|
||||
The example docker compose file launches the following containers:
|
||||
|
||||
| Container | Description |
|
||||
| --- | --- |
|
||||
| inventree-db | PostgreSQL database |
|
||||
| inventree-server | Gunicorn web server |
|
||||
| inventree-worker | django-q background worker |
|
||||
| inventree-proxy | nginx proxy server |
|
||||
| *inventree-cache* | *redis cache (optional)* |
|
||||
|
||||
#### PostgreSQL Database
|
||||
|
||||
A PostgreSQL database container which requires a username:password combination (which can be changed). This uses the official [PostgreSQL image](https://hub.docker.com/_/postgres).
|
||||
|
||||
#### Web Server
|
||||
|
||||
Runs an InvenTree web server instance, powered by a Gunicorn web server.
|
||||
|
||||
#### Background Worker
|
||||
|
||||
Runs the InvenTree background worker process. This spins up a second instance of the *inventree* container, with a different entrypoint command.
|
||||
|
||||
#### Nginx Proxy
|
||||
|
||||
Nginx working as a reverse proxy, separating requests for static and media files, and directing everything else to Gunicorn.
|
||||
|
||||
This container uses the official [nginx image](https://hub.docker.com/_/nginx).
|
||||
|
||||
#### Redis Cache
|
||||
|
||||
Redis is used as cache storage for the InvenTree server. This provides a more performant caching system which can useful in larger installations.
|
||||
|
||||
This container uses the official [redis image](https://hub.docker.com/_/redis).
|
||||
|
||||
!!! info "Redis on Docker"
|
||||
Docker adds an additional network layer - that might lead to lower performance than bare metal.
|
||||
To optimize and configure your redis deployment follow the [official docker guide](https://redis.io/docs/getting-started/install-stack/docker/#configuration).
|
||||
|
||||
!!! warning "Disabled by default"
|
||||
The *redis* container is not enabled in the default configuration. This is provided as an example for users wishing to use redis.
|
||||
To enable the *redis* container, run any `docker compose` commands with the `--profile redis` flag.
|
||||
You will also need to un-comment the `INVENTREE_CACHE_<...>` variables in the `.env` file.
|
||||
|
||||
### Data Volume
|
||||
|
||||
InvenTree stores any persistent data (e.g. uploaded media files, database data, etc) in a [volume](https://docs.docker.com/storage/volumes/) which is mapped to a local system directory. The location of this directory must be configured in the `.env` file, specified using the `INVENTREE_EXT_VOLUME` variable.
|
||||
|
||||
!!! info "Data Directory"
|
||||
Make sure you change the path to the local directory where you want persistent data to be stored.
|
||||
|
||||
#### Common Issues
|
||||
|
||||
When configuring a docker install, sometimes a misconfiguration can cause peculiar issues where it seems that the installation is functioning correctly, but uploaded files and plugins do not "persist" across sessions. In such cases, the "mounted" volume has not mapped to a directory on your local filesystem. This may occur if you have tried multiple setup options without clearing existing volume bindings.
|
||||
|
||||
!!! tip "Start with a clean slate"
|
||||
To prevent such issues, it is recommended that you start with a "clean slate" if you have previously configured an InvenTree installation under docker.
|
||||
|
||||
If you have previously setup InvenTree, remove existing volume bindings using the following command:
|
||||
|
||||
```docker volume rm -f inventree-production_inventree_data```
|
||||
|
||||
|
||||
## Production Setup Guide
|
||||
|
||||
### Edit Environment Variables
|
||||
|
||||
The first step is to edit the environment variables, located in the `.env` file.
|
||||
|
||||
!!! warning "External Volume"
|
||||
You must define the `INVENTREE_EXT_VOLUME` variable - this must point to a directory *on your local machine* where persistent data is to be stored.
|
||||
|
||||
!!! warning "Database Credentials"
|
||||
You must also define the database username (`INVENTREE_DB_USER`) and password (`INVENTREE_DB_PASSWORD`). You should ensure they are changed from the default values for added security
|
||||
|
||||
|
||||
### Initial Database Setup
|
||||
|
||||
Perform the initial database setup by running the following command:
|
||||
|
||||
```bash
|
||||
docker compose run inventree-server invoke update
|
||||
```
|
||||
|
||||
This command performs the following steps:
|
||||
|
||||
- Ensure required python packages are installed
|
||||
- Create a new (empty) database
|
||||
- Perform the required schema updates to create the required database tables
|
||||
- Update translation files
|
||||
- Collect all required static files into a directory where they can be served by nginx
|
||||
|
||||
### Create Administrator Account
|
||||
|
||||
If you are creating the initial database, you need to create an admin (superuser) account for the database. Run the command below, and follow the prompts:
|
||||
|
||||
```
|
||||
docker compose run inventree-server invoke superuser
|
||||
```
|
||||
|
||||
Alternatively, admin account details can be specified in the `.env` file, removing the need for this manual step:
|
||||
|
||||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| INVENTREE_ADMIN_USER | Admin account username |
|
||||
| INVENTREE_ADMIN_PASSWORD | Admin account password |
|
||||
| INVENTREE_ADMIN_EMAIL | Admin account email address |
|
||||
|
||||
!!! warning "Scrub Account Data"
|
||||
Ensure that the admin account credentials are removed from the `.env` file after the first run, for security.
|
||||
|
||||
### Start Docker Containers
|
||||
|
||||
Now that the database has been created, migrations applied, and you have created an admin account, we are ready to launch the InvenTree containers:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This command launches the following containers:
|
||||
|
||||
- `inventree-db` - PostgreSQL database
|
||||
- `inventree-server` - InvenTree web server
|
||||
- `inventree-worker` - Background worker
|
||||
- `inventree-nginx` - Nginx reverse proxy
|
||||
|
||||
!!! success "Up and Running!"
|
||||
You should now be able to view the InvenTree login screen at [http://localhost:1337](http://localhost:1337)
|
||||
|
||||
## Updating InvenTree
|
||||
|
||||
To update your InvenTree installation to the latest version, follow these steps:
|
||||
|
||||
### Stop Containers
|
||||
|
||||
Stop all running containers as below:
|
||||
|
||||
```
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Update Images
|
||||
|
||||
Pull down the latest version of the InvenTree docker image
|
||||
|
||||
```
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
This ensures that the InvenTree containers will be running the latest version of the InvenTree source code.
|
||||
|
||||
!!! tip "Docker Directory"
|
||||
All `docker compose` commands must be performed in the same directory as the [docker-compose.yml file](#required-files)
|
||||
|
||||
!!! info "Tagged Version"
|
||||
If you are targeting a particular "tagged" version of InvenTree, you may wish to edit the `INVENTREE_TAG` variable in the `.env` file before issuing the `docker compose pull` command
|
||||
|
||||
### Update Database
|
||||
|
||||
Run the following command to ensure that the InvenTree database is updated:
|
||||
|
||||
```
|
||||
docker compose run inventree-server invoke update
|
||||
```
|
||||
|
||||
!!! info "Skip Backup"
|
||||
By default, the `invoke update` command performs a database backup. To skip this step, add the `--skip-backup` flag
|
||||
|
||||
### Start Containers
|
||||
|
||||
Now restart the docker containers:
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Data Backup
|
||||
|
||||
Database and media files are stored external to the container, in the volume location specified in the `docker-compose.yml` file. It is strongly recommended that a backup of the files in this volume is performed on a regular basis.
|
||||
|
||||
Read more about [data backup](./backup.md).
|
||||
|
||||
### Exporting Database as JSON
|
||||
|
||||
To export the database to an agnostic JSON file, perform the following command:
|
||||
|
||||
```
|
||||
docker compose run inventree-server invoke export-records -f /home/inventree/data/data.json
|
||||
```
|
||||
|
||||
This will export database records to the file `data.json` in your mounted volume directory.
|
@ -2,71 +2,78 @@
|
||||
title: Setup Introduction
|
||||
---
|
||||
|
||||
!!! info "Fast install"
|
||||
A quick-and-easy install can be done done with the following one-liner.
|
||||
```bash
|
||||
wget -qO install.sh https://get.inventree.org && bash install.sh
|
||||
```
|
||||
Read more about the [installer](./installer.md).
|
||||
|
||||
## Introduction
|
||||
|
||||
InvenTree can be self-hosted with minimal system requirements. Multiple database back-ends are supported, allowing for flexibility where required.
|
||||
A functional InvenTree server can be hosted with minimal setup requirements. Multiple installation methods and database back-ends are supported, allowing for flexibility where required.
|
||||
|
||||
!!! info "Production Ready"
|
||||
InvenTree is designed to be a production-ready application, and can be deployed in a variety of environments. The following instructions are designed to help you get started with a *production* setup. For a development setup, refer to the [devcontainer setup guide](../develop/devcontainer.md).
|
||||
|
||||
## Installation Methods
|
||||
|
||||
To quickly jump to a specific installation method, refer to the following links:
|
||||
|
||||
- [**Docker**](./docker.md)
|
||||
- [**Package Installer**](./installer.md)
|
||||
- [**Bare Metal**](./install.md)
|
||||
|
||||
!!! success "Docker Recommended"
|
||||
The recommended method of installing InvenTree is to follow our [docker setup guide](./docker.md). InvenTree provides out-of-the-box support for docker and docker compose, which provides a simple, reliable and repeatable pipeline for integration into your production environment.
|
||||
|
||||
!!! info "Further Reading"
|
||||
For more information on the InvenTree tech stack, continue reading below!
|
||||
|
||||
### Configuration Options
|
||||
|
||||
Independent of the preferred installation method, InvenTree provides a number of [configuration options](./config.md) which can be used to customize the server environment.
|
||||
|
||||
## System Components
|
||||
|
||||
The InvenTree server ecosystem consists of the following components:
|
||||
|
||||
### Database
|
||||
|
||||
A persistent database is required for data storage. InvenTree can be used with any of the following database backends:
|
||||
A persistent database is required for data storage. By default, InvenTree is configured to use [PostgreSQL](https://www.postgresql.org/) - and this is the recommended database backend to use. However, InvenTree can also be configured to connect to any database backend [supported by Django]({% include "django.html" %}/ref/databases/)
|
||||
|
||||
* PostgreSQL
|
||||
* MySQL / MariaDB
|
||||
* SQLite
|
||||
|
||||
!!! warning "SQLite"
|
||||
While SQLite provides a simpler setup and is useful for a development environment, we strongly recommend against using it for a production environment. Use PostgreSQL or MySQL instead
|
||||
|
||||
Database selection should be determined by your particular installation requirements.
|
||||
|
||||
### Media Files
|
||||
|
||||
Uploaded media files (images, attachments, reports, etc) are stored to a persistent storage volume.
|
||||
|
||||
### Web Server
|
||||
|
||||
The bulk of the InvenTree code base supports the custom web server application. The web server application services user requests and facilitates database access.
|
||||
The bulk of the InvenTree code base supports the custom web server application. The web server application services user requests and facilitates database access. The webserver provides access to the [API](../api/api.md) for performing database query actions.
|
||||
|
||||
The webserver code also provides a first-party API for performing database query actions.
|
||||
|
||||
Once a database is setup, you need a way of accessing the data. InvenTree provides a "server" application out of the box, but this may not scale particularly well with multiple users. Instead, InvenTree can be served using a webserver such as [Gunicorn](https://gunicorn.org/). For more information see the [deployment documentation](./bare_prod.md).
|
||||
InvenTree uses [Gunicorn](https://gunicorn.org/) as the web server - a Python WSGI HTTP server.
|
||||
|
||||
### Background Tasks
|
||||
|
||||
A separate application handles management of [background tasks](../settings/tasks.md), separate to user-facing web requests.
|
||||
A separate application handles management of [background tasks](../settings/tasks.md), separate to user-facing web requests. The background task manager is required to perform asynchronous tasks, such as sending emails, generating reports, and other long-running tasks.
|
||||
|
||||
InvenTree uses [django-q2](https://django-q2.readthedocs.io/en/master/) as the background task manager.
|
||||
|
||||
### File Storage
|
||||
|
||||
Uploaded *media* files (images, attachments, reports, etc) and *static* files (javascript, html) are stored to a persistent storage volume. A *file server* is required to serve these files to the user.
|
||||
|
||||
InvenTree uses [Caddy](https://caddyserver.com/) as a file server, which is configured to serve both *static* and *media* files. Additionally, Caddy provides SSL termination and reverse proxy services.
|
||||
|
||||
## OS Requirements
|
||||
|
||||
The InvenTree documentation assumes that the operating system is a debian based Linux OS. Some installation steps may differ for different systems.
|
||||
The InvenTree documentation *assumes* that the operating system is a debian based Linux OS. Some installation steps may differ for different systems.
|
||||
|
||||
!!! warning "Installing on Windows"
|
||||
Installation on Windows is *not guaranteed* to work (at all). To install on a Windows system, it is highly recommended that you [install WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps), and then follow installation procedure from within the WSL environment.
|
||||
To install on a Windows system, you should [install WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps), and then follow installation procedure from within the WSL environment.
|
||||
|
||||
!!! success "Docker"
|
||||
Installation on any OS is simplified by following the [docker setup guide](../docker).
|
||||
Installation on any OS is simplified by following the [docker setup guide](./docker.md).
|
||||
|
||||
## Python Requirements
|
||||
|
||||
InvenTree runs on [Python](https://python.org).
|
||||
|
||||
!!! warning "Python Version"
|
||||
InvenTree requires Python 3.9 (or newer). If your system has an older version of Python installed, you will need to follow the update instructions for your OS.
|
||||
InvenTree requires a minimum Python version of {{ config.extra.min_python_version}}. If your system has an older version of Python installed, you will need to follow the update instructions for your OS.
|
||||
|
||||
### Invoke
|
||||
|
||||
InvenTree makes use of the [invoke](https://www.pyinvoke.org/) python toolkit for performing various administrative actions.
|
||||
|
||||
!!! warning "Invoke Version"
|
||||
InvenTree requires invoke version 2.0.0 or newer. Some platforms may be shipped with older versions of invoke!
|
||||
InvenTree requires invoke version {{ config.extra.min_invoke_version }} or newer. Some platforms may be shipped with older versions of invoke!
|
||||
|
||||
!!! tip "Updating Invoke"
|
||||
To update your invoke version, run `pip install -U invoke`
|
||||
@ -102,15 +109,11 @@ To configure Inventree inside a virtual environment, ``cd`` into the inventree b
|
||||
source env/bin/activate
|
||||
```
|
||||
|
||||
!!! info "Activate Virtual Environment"
|
||||
if
|
||||
```
|
||||
source env/bin/activate
|
||||
```
|
||||
is not working try
|
||||
```
|
||||
. env/bin/activate
|
||||
```
|
||||
Or, if that does not work, try:
|
||||
|
||||
```
|
||||
. env/bin/activate
|
||||
```
|
||||
|
||||
This will place the current shell session inside a virtual environment - the terminal should display the ``(env)`` prefix.
|
||||
|
||||
@ -127,31 +130,10 @@ Alternatively, the source can be downloaded as a [.zip archive](https://github.c
|
||||
!!! info "Updating via Git"
|
||||
Downloading the source code using Git is recommended, as it allows for simple updates when a new version of InvenTree is released.
|
||||
|
||||
## Installation Guides
|
||||
|
||||
There are multiple ways to get an InvenTree server up and running, of various complexity (and robustness)!
|
||||
|
||||
### Docker
|
||||
|
||||
The recommended method of installing InvenTree is to use [docker](https://www.docker.com). InvenTree provides out-of-the-box support for docker and docker compose, which provides a simple, reliable and repeatable pipeline for integration into your production environment.
|
||||
|
||||
Refer to the following guides for further instructions:
|
||||
|
||||
- [**Docker development server setup guide**](./docker_dev.md)
|
||||
- [**Docker production server setup guide**](./docker.md)
|
||||
|
||||
### Bare Metal
|
||||
|
||||
If you do not wish to use the docker container, you will need to manually install the required packages and follow through the installation guide.
|
||||
|
||||
Refer to the following guides for further instructions:
|
||||
|
||||
- [**Bare metal development server setup guide**](./bare_dev.md)
|
||||
- [**Bare metal production server setup guide**](./install.md)
|
||||
|
||||
## Debug Mode
|
||||
|
||||
By default, the InvenTree web server is configured to run in [DEBUG mode](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-DEBUG).
|
||||
By default, a production InvenTree installation is configured to run with [DEBUG mode]({% include "django.html" %}/ref/settings/#std:setting-DEBUG) *disabled*.
|
||||
|
||||
Running in DEBUG mode provides many handy development features, however it is strongly recommended *NOT* to run in DEBUG mode in a production environment. This recommendation is made because DEBUG mode leaks a lot of information about your installation and may pose a security risk.
|
||||
|
||||
|
@ -4,25 +4,26 @@ title: Serving Static and Media Files
|
||||
|
||||
## Serving Files
|
||||
|
||||
In production, the InvenTree web server software *does not* provide hosting of static files, or user-uploaded (media) files.
|
||||
In a production installation, the InvenTree web server application *does not* provide hosting of static files, or user-uploaded (media) files. Instead, these files should be served by a separate web server, such as [Caddy](https://caddyserver.com/), [Nginx](https://www.nginx.com/), or [Apache](https://httpd.apache.org/).
|
||||
|
||||
When running in [production mode](./bare_prod.md) (i.e. the `INVENTREE_DEBUG` flag is disabled), a separate web server is required for serving *static* and *media* files. In `DEBUG` mode, the django webserver facilitates delivery of *static* and *media* files, but this is explicitly not suitable for a production environment.
|
||||
!!! info "Debug Mode"
|
||||
When running in [production mode](./bare_prod.md) (i.e. the `INVENTREE_DEBUG` flag is disabled), a separate web server is required for serving *static* and *media* files. In `DEBUG` mode, the django webserver facilitates delivery of *static* and *media* files, but this is explicitly not suitable for a production environment.
|
||||
|
||||
!!! into "Read More"
|
||||
You can find further information in the [django documentation](https://docs.djangoproject.com/en/dev/howto/static-files/deployment/).
|
||||
!!! tip "Read More"
|
||||
You can find further information in the [django documentation]({% include "django.html" %}/howto/static-files/deployment/).
|
||||
|
||||
There are *many* different ways that a sysadmin might wish to handle this - and it depends on your particular installation requirements.
|
||||
|
||||
The [docker production example](./docker_prod.md) provides an example using [Nginx](https://www.nginx.com/) to serve *static* and *media* files, and redirecting other requests to the InvenTree web server itself.
|
||||
|
||||
You may use this as a jumping off point, or use an entirely different server setup.
|
||||
|
||||
#### Static Files
|
||||
### Static Files
|
||||
|
||||
Static files can be served without any need for authentication. In fact, they must be accessible *without* authentication, otherwise the unauthenticated views (such as the login screen) will not function correctly.
|
||||
|
||||
#### Media Files
|
||||
### Media Files
|
||||
|
||||
It is highly recommended that the *media* files are served in such a way that user authentication is required.
|
||||
It is highly recommended that the *media* files are served behind an authentication layer. This is because the media files are user-uploaded, and may contain sensitive information. Most modern web servers provide a way to serve files behind an authentication layer.
|
||||
|
||||
Refer to the [docker production example](./docker_prod.md) for a demonstration of using nginx to serve media files only to authenticated users, and forward authentication requests to the InvenTree web server.
|
||||
### Example Configuration
|
||||
|
||||
The [docker production example](./docker.md) provides an example using [Caddy](https://caddyserver.com) to serve *static* and *media* files, and redirecting other requests to the InvenTree web server itself.
|
||||
|
||||
Caddy is a modern web server which is easy to configure and provides a number of useful features, including automatic SSL certificate generation.
|
||||
|
@ -8,6 +8,7 @@ site_author: InvenTree
|
||||
repo_url: https://github.com/inventree/inventree
|
||||
repo_name: inventree/inventree
|
||||
|
||||
|
||||
# Theme
|
||||
theme:
|
||||
name: material
|
||||
@ -34,6 +35,7 @@ theme:
|
||||
icon:
|
||||
repo: fontawesome/brands/github
|
||||
features:
|
||||
- content.code.copy
|
||||
- header.autohide
|
||||
- navigation.expand
|
||||
- navigation.footer
|
||||
@ -75,10 +77,9 @@ nav:
|
||||
- Terminology: concepts/terminology.md
|
||||
- Physical Units: concepts/units.md
|
||||
- Development:
|
||||
- Getting started: develop/starting.md
|
||||
- Contributing: develop/contributing.md
|
||||
- Devcontainer: develop/devcontainer.md
|
||||
- Platform UI: develop/react-frontend.md
|
||||
- React Frontend: develop/react-frontend.md
|
||||
- Credits: credits.md
|
||||
- Privacy: privacy.md
|
||||
- Release Notes: releases/release_notes.md
|
||||
@ -87,8 +88,7 @@ nav:
|
||||
- Configuration: start/config.md
|
||||
- Docker:
|
||||
- Introduction: start/docker.md
|
||||
- Production: start/docker_prod.md
|
||||
- Development: start/docker_dev.md
|
||||
- Installation: start/docker_install.md
|
||||
- Bare Metal:
|
||||
- Introduction: start/install.md
|
||||
- Installer: start/installer.md
|
||||
@ -274,6 +274,10 @@ extra:
|
||||
# provider: google
|
||||
# property: UA-143467500-1
|
||||
|
||||
min_python_version: 3.9
|
||||
min_invoke_version: 2.0.0
|
||||
django_version: 4.2
|
||||
|
||||
version:
|
||||
default: stable
|
||||
provider: mike
|
||||
|
13
docs/mlc_config.json
Normal file
13
docs/mlc_config.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"ignorePatterns": [
|
||||
{
|
||||
"pattern": "http://inventree.localhost"
|
||||
},
|
||||
{
|
||||
"pattern": "http://localhost"
|
||||
},
|
||||
{
|
||||
"pattern": "http://127.0.0.1"
|
||||
}
|
||||
]
|
||||
}
|
@ -45,7 +45,7 @@ python-barcode[images] # Barcode generator
|
||||
python-dotenv # Environment variable management
|
||||
pyyaml>=6.0.1 # YAML parsing
|
||||
qrcode[pil] # QR code generator
|
||||
rapidfuzz==0.7.6 # Fuzzy string matching
|
||||
rapidfuzz # Fuzzy string matching
|
||||
regex # Advanced regular expressions
|
||||
sentry-sdk # Error reporting (optional)
|
||||
setuptools # Standard dependency
|
||||
|
@ -274,7 +274,7 @@ pyyaml==6.0.1
|
||||
# tablib
|
||||
qrcode==7.4.2
|
||||
# via django-allauth-2fa
|
||||
rapidfuzz==0.7.6
|
||||
rapidfuzz==3.6.1
|
||||
redis==5.0.1
|
||||
# via django-redis
|
||||
referencing==0.33.0
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Trans, t } from '@lingui/macro';
|
||||
import {
|
||||
AspectRatio,
|
||||
Button,
|
||||
Group,
|
||||
Image,
|
||||
Modal,
|
||||
Overlay,
|
||||
Paper,
|
||||
Text,
|
||||
rem,
|
||||
@ -20,6 +22,7 @@ import { InvenTreeIcon } from '../../functions/icons';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import { PartThumbTable } from '../../tables/part/PartThumbTable';
|
||||
import { ActionButton } from '../buttons/ActionButton';
|
||||
import { StylishText } from '../items/StylishText';
|
||||
import { ApiImage } from './ApiImage';
|
||||
|
||||
/**
|
||||
@ -58,9 +61,9 @@ const backup_image = '/static/img/blank_image.png';
|
||||
*/
|
||||
const removeModal = (apiPath: string, setImage: (image: string) => void) =>
|
||||
modals.openConfirmModal({
|
||||
title: t`Remove Image`,
|
||||
title: <StylishText size="xl">{t`Remove Image`}</StylishText>,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
<Text>
|
||||
<Trans>Remove the associated image from this item?</Trans>
|
||||
</Text>
|
||||
),
|
||||
@ -245,13 +248,8 @@ function ImageActionButtons({
|
||||
pk: string;
|
||||
setImage: (image: string) => void;
|
||||
}) {
|
||||
const [opened, { open, close }] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal opened={opened} onClose={close} title={t`Select image`} size="70%">
|
||||
<PartThumbTable pk={pk} close={close} setImage={setImage} />
|
||||
</Modal>
|
||||
{visible && (
|
||||
<Group
|
||||
spacing="xs"
|
||||
@ -259,24 +257,37 @@ function ImageActionButtons({
|
||||
>
|
||||
{actions.selectExisting && (
|
||||
<ActionButton
|
||||
icon={<InvenTreeIcon icon="select_image" />}
|
||||
icon={
|
||||
<InvenTreeIcon
|
||||
icon="select_image"
|
||||
iconProps={{ color: 'white' }}
|
||||
/>
|
||||
}
|
||||
tooltip={t`Select from existing images`}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
tooltipAlignment="top"
|
||||
onClick={open}
|
||||
onClick={() => {
|
||||
modals.open({
|
||||
title: <StylishText size="xl">{t`Select Image`}</StylishText>,
|
||||
size: 'xxl',
|
||||
children: <PartThumbTable pk={pk} setImage={setImage} />
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{actions.uploadFile && (
|
||||
<ActionButton
|
||||
icon={<InvenTreeIcon icon="upload" />}
|
||||
icon={
|
||||
<InvenTreeIcon icon="upload" iconProps={{ color: 'white' }} />
|
||||
}
|
||||
tooltip={t`Upload new image`}
|
||||
variant="outline"
|
||||
size="lg"
|
||||
tooltipAlignment="top"
|
||||
onClick={() => {
|
||||
modals.open({
|
||||
title: t`Upload Image`,
|
||||
title: <StylishText size="xl">{t`Upload Image`}</StylishText>,
|
||||
children: (
|
||||
<UploadModal apiPath={apiPath} setImage={setImage} />
|
||||
)
|
||||
@ -320,39 +331,33 @@ export function DetailsImage(props: DetailImageProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper
|
||||
ref={ref}
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: `${IMAGE_DIMENSION}px`,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<ApiImage
|
||||
src={img}
|
||||
style={{ zIndex: 1 }}
|
||||
height={IMAGE_DIMENSION}
|
||||
width={IMAGE_DIMENSION}
|
||||
onClick={() => {
|
||||
modals.open({
|
||||
children: <ApiImage src={img} />,
|
||||
withCloseButton: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{permissions.hasChangeRole(props.appRole) && (
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
apiPath={props.apiPath}
|
||||
hasImage={props.src ? true : false}
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
<AspectRatio ref={ref} maw={IMAGE_DIMENSION} ratio={1}>
|
||||
<>
|
||||
<ApiImage
|
||||
src={img}
|
||||
height={IMAGE_DIMENSION}
|
||||
width={IMAGE_DIMENSION}
|
||||
onClick={() => {
|
||||
modals.open({
|
||||
children: <ApiImage src={img} />,
|
||||
withCloseButton: false
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
{permissions.hasChangeRole(props.appRole) && hovered && (
|
||||
<Overlay color="black" opacity={0.8}>
|
||||
<ImageActionButtons
|
||||
visible={hovered}
|
||||
actions={props.imageActions}
|
||||
apiPath={props.apiPath}
|
||||
hasImage={props.src ? true : false}
|
||||
pk={props.pk}
|
||||
setImage={setAndRefresh}
|
||||
/>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
</AspectRatio>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export enum UserRoles {
|
||||
return_order = 'return_order',
|
||||
sales_order = 'sales_order',
|
||||
stock = 'stock',
|
||||
stock_location = 'stocklocation',
|
||||
stock_location = 'stock_location',
|
||||
stocktake = 'stocktake'
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user