InvenTree

How to catch and analyze Inventree Python API HTTPErrors

This example shows how to differentiate between a Part with this Part Number already exists error and other errors for inventree Part.create(). Any other error is simply re-raised. See Inventree Python API: How to create a new part

try:
    part = Part.create(api, {
        'name': product.mpn,
        'description': product.description,
        'category': get_part_category_by_pathstring(api, "Elektronik-Komponenten").pk,
    })
except requests.exceptions.HTTPError as ex:
    errmsg, = ex.args
    # NOTE: errmsg is a dictionary with the following keys
    # errmsg["detail"] == "Error occurred during API request"
    # errmsg["data"] == {'name': 'VHR-4N-BK', 'description': '...', ...}
    # errmsg["params"] == {'format': 'json'}
    # errmsg["body"] == "{'non_field_erro   rs': ['Part with this Part Number already exists.']}""
    
    body = json.loads(errmsg.get("body", []))
    non_field_errors = body.get("non_field_errors", [])
    # If there is a non field error and it contains "Part with this ... already exists",
    if len(non_field_errors) > 0 and re.match(r"Part with this .* already exists", non_field_errors[0]):
        print("This part already exists")
    else:
        raise ex

 

Posted by Uli Köhler in InvenTree, Python

InvenTree Traefik reverse proxy container labels

This post is based on How to install InvenTree using docker in just 5 minutes and uses the auto-generated docker-compose.yml from there. However it should be useable for almost any setup.

The setup is pretty standard since the inventree proxy container runs the webserver on port 80. Therefore, you don’t even have to explicitly specify a load balancer port

In your docker-compose.yml, add the following labels to the inventree-proxy container:

Wildcard certificate setup:

For more details on the base Traefik setup, see Simple Traefik docker-compose setup with Lets Encrypt Cloudflare DNS-01 & TLS-ALPN-01 & HTTP-01 challenges

labels:
    - "traefik.enable=true"
    - "traefik.http.routers.inventree-mydomain.rule=Host(`inventree.mydomain.com`)"
    - "traefik.http.routers.inventree-mydomain.entrypoints=websecure"
    - "traefik.http.routers.inventree-mydomain.tls.certresolver=cloudflare"
    - "traefik.http.routers.inventree-mydomain.tls.domains[0].main=mydomain.com"
    - "traefik.http.routers.inventree-mydomain.tls.domains[0].sans=*.mydomain.com"

 

Posted by Uli Köhler in InvenTree, Traefik

How to install InvenTree using docker in just 5 minutes

The following script is an automated installation script for InvenTree that fetches the current docker-compose.yml and other configs from GitHub, modifies them so that only local directories are used for storage and then setups InvenTree.

First, create a directory such as /opt/inventree-mydomain. I recommend to choose a unique directory name and not just inventree to keep instae

 

#!/bin/sh
wget -O nginx.prod.conf https://github.com/inventree/InvenTree/raw/master/docker/production/nginx.prod.conf
wget -O docker-compose.yml https://github.com/inventree/InvenTree/raw/master/docker/production/docker-compose.yml
wget -O .env https://github.com/inventree/InvenTree/raw/master/docker/production/.env

sed -i -e 's/#INVENTREE_DB_USER=pguser/INVENTREE_DB_USER=inventree/g' .env
sed -i -e "s/#INVENTREE_DB_PASSWORD=pgpassword/INVENTREE_DB_PASSWORD=$(pwgen 30 1)/g" .env
sed -i -e "s/INVENTREE_WEB_PORT=1337/INVENTREE_WEB_PORT=$(shuf -i 1024-65535 -n 1)/g" .env
sed -i -e "s/#INVENTREE_ADMIN_USER=/INVENTREE_ADMIN_USER=admin/g" .env
sed -i -e "s/#INVENTREE_ADMIN_PASSWORD=/INVENTREE_ADMIN_PASSWORD=$(pwgen 30 1)/g" .env
sed -i -e "s/#INVENTREE_ADMIN_EMAIL=/[email protected]/g" .env
sed -i -e 's/COMPOSE_PROJECT_NAME=inventree-production//g' .env
# Enable cache
sed -i -e "s/#INVENTREE_CACHE_HOST=inventree-cache/INVENTREE_CACHE_HOST=inventree-cache/g" .env
sed -i -e "s/#INVENTREE_CACHE_PORT=6379/INVENTREE_CACHE_PORT=6379/g" .env
# Use direct directory mapping to avoid mounting issues
sed -i -e "s%- inventree_data:%- $(pwd)/inventree_data:%g" docker-compose.yml
# ... now we can remove the volume declarations from docker-compose.yml
sed -i -e '/^volumes:/,$d' docker-compose.yml

sed -z -i -e 's#profiles:\s*- redis\s*##g' docker-compose.yml # Make redis start always, even without docker-compose --profile redis
# Use standard docker-compose directory naming to facilitate multiple parallel installations
sed -z -i -e 's#container_name:\s*[a-zA-Z0-9_-]*\s*##g' docker-compose.yml # Remove container_name: ... statements
# Create data directory which is bound to the docker volume
mkdir -p inventree_data
# Initialize database
docker-compose up -d inventree-cache inventree-db # database initialization needs cache
docker-compose run inventree-server invoke update

After that, you can check .env for the randomly generated  INVENTREE_ADMIN_PASSWORD and INVENTREE_WEB_PORT.

Now you can enable autostart & start the service using systemd, for more details see our post Create a systemd service for your docker-compose project in 10 seconds:

curl -fsSL https://techoverflow.net/scripts/create-docker-compose-service.sh | sudo bash /dev/stdin

Don’t forget to configure your reverse proxy to point to InvenTree.

Posted by Uli Köhler in Docker, InvenTree

How to fix InvenTree API remote_image: Server responded with invalid status code: 403

Problem:

While trying to create a Company using the InvenTree API you see an error message like

requests.exceptions.HTTPError: {'detail': 'Error occurred during API request', 'url': 'https://inventree.techoverflow.net/api/company/', 'method': 'POST', 'status_code': 400, 'body': '{"remote_image":["Server responded with invalid status code: 403"]}', 'headers': {'AUTHORIZATION': 'Token 340fdf063d5433b83bc37c50a4b52ee2f246021b'}, 'params': {'format': 'json'}, 'data': {'name': 'DigiKey', 'website': 'https://www.digikey.de/', 'remote_image': 'https://www.digikey.de/-/media/Images/Header/logo_dk.png', 'description': 'Test', 'is_manufacturer': False, 'is_supplier': True, 'is_customer': False, 'currency': 'EUR'}}

Solution:

The core issue here is:

{"remote_image":["Server responded with invalid status code: 403"]}

InvenTree tries to download the remote_image URL – but that URL can’t be downloaded and the upstream server (digikey.de in this case) generates a 403 response. This is often the case when an URL can only be downloaded with correct Referer headers or other headers set to a specific value.

In order to fix this issue, change the remote_image URL to an image URL which can be fetched correctly.

Sometimes this issue can also arise if the IP address of the InvenTree server is blocked by the server serving the image. This can easily found out by trying to download the given image URL from the server running inventree using wget [URL].

Posted by Uli Köhler in InvenTree

Inventree Python API: How to list all stock locations

See our previous post Inventree Python API: Minimal API connect example using YAML config for our method of creating the api object using a YAML config file!

from inventree.stock import StockLocation

all_stock_locations = StockLocation.list(api)

# Dict of part categories by name
# (e.g. 'OpAmps')
stock_locations_by_name = {
    category["name"]: category
    for category in all_stock_locations
}
# Dict of part categories by public key (e.g. 7)
part_locations_by_pk = {
    category.pk: category
    for category in all_stock_locations
}
# Dict of part categories by hierarchical path
# (e.g. 'Office/Spart parts box')
stock_locations_by_pathstring = {
    category.pathstring: category
    for category in all_stock_locations
}

 

Posted by Uli Köhler in InvenTree, Python

How to manually run InvenTree backup

By default, InvenTree creates a daily backup of both its data files and the database.

You can manually run the backup by using the following command

invoke backup

In a docker-compose-based setup, for example, you can run the backup using

docker-compose run inventree-server invoke backup

Example output:

Loading config file : /home/inventree/data/config.yaml
InvenTree translation coverage: 22%
Restoring InvenTree database...
Finding latest backup
Restoring backup for database 'default' and server 'None'
Restoring: default-de1837564ff0-2023-01-20-004732.psql.bin.gz
Restore tempfile created: 391.4 KiB
Restoring InvenTree media files...
Restoring backup for media files
Finding latest backup
Restoring: de1837564ff0-2023-01-20-004736.tar.gz

 

Posted by Uli Köhler in InvenTree

Which PostgreSQL version to use for InvenTree?

At the time of writing this post (2022-01-20), InvenTree only support PostgreSQL version 11, 12 & 13.

Newer versions of PostgreSQL such as version 14 and 15 are not supported due to dependencies in the backup workflow.

Source: This reddit post

Posted by Uli Köhler in InvenTree

How to access InvenTree backup settings?

You can ccessing it by opening Admin, then Scheduled tasks and then searching for backup and clicking on the

InvenTree.tasks.run_backup

task.

On many InvenTree instances you can also access it using the following link (it won’t work on all instances since the ID for the backup task is not always 8 – you can verify if it worked by checking if the task name shown on the page is InvenTree.tasks.run_backup).

https://inventree.mydomain.com/admin/django_q/schedule/8

By default, InvenTree will perform a full backup daily.

 

Posted by Uli Köhler in InvenTree

Inventree Python API: How to create a new part

See our previous post Inventree Python API: Minimal API connect example using YAML config for our method of creating the api object using a YAML config file!

The following code finds a part category called ICs and creates a new part in that category.

First, we need to fetch the part categories, this is from our previous post Inventree Python API: Minimal API connect example using YAML config:

from inventree.part import PartCategory

all_categories = PartCategory.list(api)

# Dict of part categories by name
# (e.g. 'OpAmps')
part_categories_by_name = {
    category["name"]: category
    for category in all_categories
}
# Dict of part categories by public key (e.g. 7)
part_categories_by_pk = {
    category.pk: category
    for category in all_categories
}
# Dict of part categories by hierarchical path
# (e.g. 'Electronics-Components/ICs/OpAmps')
part_categories_by_pathstring = {
    category.pathstring: category
    for category in all_categories
}

Now let’s select the correct category:

ics = part_categories_by_name['ICs']

Now it’s finally time to create the part:

from inventree.part import Part

new_part = Part.create(api, {
    'name': 'L78L33ABD',
    'description': '100mA 3.3V fixed LDO regulator, SOIC-8',
    'category': ics.pk
})

You can read the primary key (pk) for the newly generated part using

new_part.pk
Posted by Uli Köhler in InvenTree, Python

Inventree Python API: How to list all part categories

See our previous post Inventree Python API: Minimal API connect example using YAML config for our method of creating the api object using a YAML config file!

from inventree.part import PartCategory

all_categories = PartCategory.list(api)

# Dict of part categories by name
# (e.g. 'OpAmps')
part_categories_by_name = {
    category["name"]: category
    for category in all_categories
}
# Dict of part categories by public key (e.g. 7)
part_categories_by_pk = {
    category.pk: category
    for category in all_categories
}
# Dict of part categories by hierarchical path
# (e.g. 'Electronics-Components/ICs/OpAmps')
part_categories_by_pathstring = {
    category.pathstring: category
    for category in all_categories
}

Note that this code by itself does not take into account the hierarchy of the categories.

Example output

part_categories_by_name:

{'Elektronik-Komponenten': <inventree.part.PartCategory at 0x7fd735125510>,
 'ICs': <inventree.part.PartCategory at 0x7fd7356d1720>,
 'OpAmps': <inventree.part.PartCategory at 0x7fd735ead4e0>}

part_categories_by_pk:

{1: <inventree.part.PartCategory at 0x7fd7346b5db0>,
 2: <inventree.part.PartCategory at 0x7fd73438aa10>,
 3: <inventree.part.PartCategory at 0x7fd727325b40>}

part_categories_by_pathstring:

{'Elektronik-Komponenten': <inventree.part.PartCategory at 0x7fd727bd3040>,
 'Elektronik-Komponenten/ICs': <inventree.part.PartCategory at 0x7fd727bd1db0>,
 'Elektronik-Komponenten/ICs/OpAmps': <inventree.part.PartCategory at 0x7fd727bd1060>}

 

Posted by Uli Köhler in InvenTree, Python

Inventree Python API: Minimal API connect example using YAML config

This example allows you to create a config file inventree.yml:

server: 'https://inventree.mydomain.com'
username: 'admin'
password: 'Chuiquu5Oqu8il5ahLoja4aecai4ee'

and then read it in Python and connect to the Inventree API:

from inventree.api import InvenTreeAPI

# Load config
import yaml

with open("inventree.yml", "r") as file:
    config = yaml.safe_load(file)
    
api = InvenTreeAPI(config["server"], username=config["username"], password=config["password"])

 

Posted by Uli Köhler in InvenTree, Python

How to configure SMTP E-Mail for InvenTree (docker/docker-compose)

You can easily configure SMTP email for InvenTree by adding the following config to your .env file (I’m using the docker production config):

INVENTREE_EMAIL_HOST=smtp.mydomain.com
[email protected]
INVENTREE_EMAIL_PASSWORD=cheen1zaiCh4yaithaecieng2jazey
INVENTREE_EMAIL_TLS=true
[email protected]

Even after setting up InvenTree, it is sufficient to just add this config to the .env file and restarting the server.

Posted by Uli Köhler in Docker, InvenTree