InvenTree: Lagerorte in Massen über die Python-API anlegen
Dieses Skript legt die Lagerorte A1..19, B1..19 und C1..19 in Massen an.
Pass es entsprechend deinen Anforderungen an.
Zusätzlich benötigt es eine config.yaml mit den Daten deiner InvenTree-Instanz.
inventree_create_drawers.py
#!/usr/bin/env python3
"""Schublada-Unterorte unter einem Apothekerschrank-Lagerort in InvenTree anlegen.
Dieses Skript verbindet sich über die REST-API mit einer InvenTree-Instanz
und legt einen Satz von Schubladen-Unterorten („Schublada") unter einem
übergeordneten Lagerort namens „Apothekerschrank" an. Für jede der drei
Reihen A, B und C werden Schubladen mit den Nummern 1 bis 19 erstellt,
insgesamt also 57 Unterorte. Jeder Unterort erhält den Lagerorttyp
„Schublada".
Das Skript ist idempotent: Existiert ein Unterort bereits unter dem
übergeordneten Lagerort, wird er übersprungen statt dupliziert.
Die Konfiguration wird aus ``config.yaml`` im selben Verzeichnis gelesen:
.. code-block:: yaml
inventree:
server: https://inventree.example.com
token: your-api-token-here
Voraussetzungen:
- Python 3.8+
- requests
- PyYAML
Aufruf::
python3 create_locations.py
"""
import sys
import requests
import yaml
from pathlib import Path
# Pfad zur YAML-Konfigurationsdatei, erwartet im selben Verzeichnis wie dieses Skript.
CONFIG_PATH = Path(__file__).parent / "config.yaml"
def load_config():
"""InvenTree-Verbindungseinstellungen aus ``config.yaml`` laden.
Gibt ein Dict mit den Schlüsseln ``server`` und ``token`` zurück.
"""
with open(CONFIG_PATH, "r") as f:
return yaml.safe_load(f)["inventree"]
class InvenTreeAPI:
"""Minimaler REST-API-Client für InvenTree.
Kapselt :mod:`requests` mit tokenbasierter Authentifizierung und
Hilfsmethoden für GET- und POST-Aufrufe gegen die InvenTree-API.
"""
def __init__(self, server, token):
"""API-Client initialisieren.
:param server: Basis-URL der InvenTree-Instanz, z. B.
``https://inventree.example.com``.
:param token: API-Authentifizierungs-Token (in der InvenTree-Weboberfläche
unter *Settings > API Keys* generieren).
"""
self.server = server.rstrip("/")
self.token = token
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Token {token}",
"Content-Type": "application/json",
})
def get(self, path, params=None):
"""GET-Anfrage ausführen und die geparste JSON-Antwort zurückgeben."""
r = self.session.get(f"{self.server}{path}", params=params)
r.raise_for_status()
return r.json()
def post(self, path, data):
"""POST-Anfrage mit JSON-Body ausführen und die geparste Antwort zurückgeben."""
r = self.session.post(f"{self.server}{path}", json=data)
if r.status_code == 400:
print(f" ERROR 400: {r.json()}")
r.raise_for_status()
return r.json()
def find_location_type(api, name):
"""Lagerorttyp anhand des Namens finden.
Iteriert über alle Lagerorttypen via paginierter API-Aufrufe.
Gibt den Primärschlüssel (``pk``) zurück, falls gefunden, sonst ``None``.
"""
offset = 0
while True:
data = api.get("/api/stock/location-type/", params={"limit": 100, "offset": offset})
results = data if isinstance(data, list) else data.get("results", [])
for t in results:
if t["name"] == name:
return t["pk"]
if isinstance(data, list) or not data.get("next"):
break
offset += 100
return None
def find_location(api, name):
"""Lagerort anhand des Namens über alle Lagerorte hinweg finden.
Iteriert über alle Lagerorte via paginierter API-Aufrufe.
Gibt den Primärschlüssel (``pk``) zurück, falls gefunden, sonst ``None``.
"""
offset = 0
while True:
data = api.get("/api/stock/location/", params={"limit": 100, "offset": offset})
for loc in data["results"]:
if loc["name"] == name:
return loc["pk"]
if not data["next"]:
break
offset += 100
return None
def find_child_location(api, parent_pk, name):
"""Unterort anhand des Namens unter einem gegebenen übergeordneten Lagerort finden.
Fragt die API nach allen Lagerorten ab, deren ``parent`` mit
``parent_pk`` übereinstimmt, und gibt den ``pk`` des ersten Treffers
zurück, sonst ``None``.
"""
results = api.get("/api/stock/location/", params={
"parent": parent_pk, "limit": 1000
})
loc_list = results if isinstance(results, list) else results.get("results", [])
for loc in loc_list:
if loc["name"] == name:
return loc["pk"]
return None
def main():
"""Haupteinstiegspunkt: übergeordneten Lagerort finden, Lagerorttyp sicherstellen, Unterorte anlegen."""
config = load_config()
api = InvenTreeAPI(config["server"], config["token"])
# --- Lagerorttyp „Schublada" finden oder anlegen ---
# InvenTree unterstützt benutzerdefinierte Lagerorttypen, die einzelnen
# Lagerorten zugewiesen werden können. Existiert ein Typ bereits, wird er wiederverwendet.
schublade_type_pk = find_location_type(api, "Schublade")
if schublade_type_pk:
print(f"Location type 'Schublade' found (pk={schublade_type_pk})")
else:
result = api.post("/api/stock/location-type/", {
"name": "Schublade",
"description": "Schublade im Apothekerschrank",
"icon": "ti:box:outline",
})
schublade_type_pk = result["pk"]
print(f"Location type 'Schublade' created (pk={schublade_type_pk})")
# --- Übergeordneten Lagerort „Apothekerschrank" finden ---
# Dieser Lagerort muss bereits in InvenTree existieren. Das Skript
# bricht mit einem Fehler ab, falls er nicht gefunden wird.
apotheker_pk = find_location(api, "Apothekerschrank")
if not apotheker_pk:
print("ERROR: Location 'Apothekerschrank' not found")
sys.exit(1)
print(f"Location 'Apothekerschrank' found (pk={apotheker_pk})")
# --- Unterorte anlegen: Schublada A1..A19, B1..B19, C1..C19 ---
# Jeder Unterort wird als Kind des Apothekerschrank-Lagerorts angelegt
# und erhält den Lagerorttyp „Schublada". Bereits existierende Lagerorte
# werden übersprungen, um das Skript idempotent zu halten.
created = 0
skipped = 0
for letter in ["A", "B", "C"]:
for number in range(1, 20):
name = f"Schublade {letter}{number}"
existing = find_child_location(api, apotheker_pk, name)
if existing:
print(f" Skipped (exists): {name} (pk={existing})")
skipped += 1
continue
result = api.post("/api/stock/location/", {
"name": name,
"parent": apotheker_pk,
"location_type": schublade_type_pk,
})
print(f" Created: {name} (pk={result['pk']})")
created += 1
print(f"\nDone: {created} created, {skipped} skipped")
if __name__ == "__main__":
main()Ähnliche Beiträge nach Kategorie:
InvenTree
Wenn dieser Beitrag dir geholfen hat, erwäge, mir einen Kaffee zu spendieren oder per PayPal zu spenden, um die Recherche und Veröffentlichung neuer Beiträge auf TechOverflow zu unterstützen