Python gateway for the Hanel automated warehouse SOAP interface
Project description
Hanel Warehouse Gateway
Python module for communicating with the Hanel automatic warehouse via SOAP. Exposes a fully typed Python interface and hides all SOAP details from the caller.
Requirements
- Python ≥ 3.10
uv(install withbrew install uvorpip install uv)- Dependencies:
requests,python-dotenv
Installation
# Install all dependencies including dev tools (recommended for development)
uv sync
# Production only (no dev dependencies)
uv sync --no-dev
Configuration
Copy .env.example to .env and fill in the values:
HANEL_ENDPOINT_URL=http://192.168.1.100:8080/HanelService
HANEL_TEST_MODE=false
HANEL_TEST_PREFIX=TEST_
| Variable | Description |
|---|---|
HANEL_ENDPOINT_URL |
URL of the Hanel t-Server SOAP endpoint |
HANEL_TEST_MODE |
Set to true to prefix order numbers, making them identifiable by warehouse operators |
HANEL_TEST_PREFIX |
Prefix applied to order numbers when HANEL_TEST_MODE=true (default: TEST_) |
Load the configuration in code with GatewayConfig.from_env():
from hanel_warehouse_gateway import HanelWarehouseGateway, GatewayConfig
# Reads from .env or environment variables
config = GatewayConfig.from_env()
# Optional overrides (useful in tests)
config = GatewayConfig.from_env({"endpoint_url": "http://localhost:8080/HanelService", "test_mode": True})
Quick start
from hanel_warehouse_gateway import (
HanelWarehouseGateway,
GatewayConfig,
MovementLine,
)
config = GatewayConfig.from_env()
gateway = HanelWarehouseGateway(config)
# Register an article
gateway.register_article("ART-001", "Widget A")
# Send a movement order (operation "+" = pick, "-" = load)
gateway.send_movement_order("ORD-001", [
MovementLine(article_number="ART-001", operation="+", nominal_quantity=5.0),
])
# Retrieve completed orders
results = gateway.get_completed_movements()
for r in results:
print(r.job_number, r.job_status)
for pos in r.positions:
print(f" {pos.article_number}: {pos.actual_quantity}/{pos.nominal_quantity}")
# Get stock levels
stock = gateway.get_inventory()
for record in stock:
print(record.article_number, record.inventory_at_storage_location)
Available operations
| Method | Description |
|---|---|
register_article(article_number, article_name) |
Register or update an article in the warehouse |
send_movement_order(order_number, positions) |
Send a pick or load order |
get_completed_movements() |
Retrieve orders with status = completed |
get_all_orders() |
Retrieve all orders currently in the warehouse queue |
get_inventory() |
Get stock levels for all articles across all physical locations |
cancel_order(order_number) |
Cancel a queued order (only works if the order has not started) |
Error handling
All exceptions inherit from HanelGatewayError:
| Exception | When raised |
|---|---|
HanelGatewayNetworkError |
Network failure after all retry attempts are exhausted |
HanelGatewayHttpError |
Non-2xx HTTP response from the server |
HanelGatewaySoapFaultError |
SOAP fault element present in the response |
HanelGatewayApplicationError |
returnValue != 0 in the application response |
HanelGatewayValidationError |
Invalid input detected before sending (no HTTP call is made) |
from hanel_warehouse_gateway import (
HanelWarehouseGateway,
GatewayConfig,
HanelGatewayNetworkError,
HanelGatewayApplicationError,
HanelGatewayValidationError,
)
gateway = HanelWarehouseGateway(GatewayConfig.from_env())
try:
gateway.register_article("ART-001", "Widget A")
except HanelGatewayValidationError as e:
print(f"Invalid input for field '{e.field}': {e.value}")
except HanelGatewayNetworkError as e:
print(f"Network error on {e.operation}: {e}")
except HanelGatewayApplicationError as e:
print(f"t-Server returned error code {e.return_value}")
Release process
Releases are automated via release-please and triggered by merging commits into main.
Commit convention
All commits must follow the Conventional Commits format. The commit type determines the version bump:
| Prefix | Version bump | When to use |
|---|---|---|
fix: |
patch (0.1.0 → 0.1.1) | Bug fix |
feat: |
minor (0.1.0 → 0.2.0) | New operation or feature |
feat!: or footer BREAKING CHANGE: |
major (0.1.0 → 1.0.0) | Incompatible public API change |
chore:, docs:, test:, refactor:, ci: |
none | Everything else |
The commitlint CI check enforces this format on every PR.
How a release works
- Merge one or more conventional commits into
main. - The Release Please workflow opens (or updates) a "Release PR" automatically, with the computed version and a generated
CHANGELOG.md. - Review and optionally edit the changelog text in the PR.
- Merge the Release PR.
- Release Please creates the git tag and the GitHub Release; a second CI job builds the wheel and sdist and attaches them as release assets.
One-time repository setup
In Settings → Actions → General, enable "Allow GitHub Actions to create and approve pull requests" — required for Release Please to open its PR.
Running tests
# Unit tests (no external dependencies)
uv run pytest tests/ --ignore=tests/test_mock_server.py --tb=short -q
# Unit tests with coverage report
uv run pytest tests/ --ignore=tests/test_mock_server.py --cov=src/hanel_warehouse_gateway --cov-report=term-missing
# Integration tests against the mock server (requires: docker compose up --build)
uv run pytest tests/test_mock_server.py --tb=short -q
# Type checking
uv run mypy src/hanel_warehouse_gateway/
# Lint
uv run ruff check src/ tests/
Mock server
The mock_server/ directory contains a Flask implementation of the Hanel t-Server SOAP interface, intended for local development and integration testing without access to the physical warehouse. It supports all five SOAP operations (sendAPDReqV01, sendJobsReqV01, readAllJobsReqV01, readAllAMDReqV01, deleteJobReqV01), simulates automatic order completion after a configurable delay, and exposes additional HTTP endpoints (/admin/state, /admin/reset, /admin/complete-all) to inspect and control state during testing.
Start it with:
docker compose up --build
The server listens at http://localhost:8080/HanelService.
See mock_server/README.md for full documentation, including data formats, environment variables, and example curl requests for each operation.
License
This project is licensed under the GNU Lesser General Public License v3.0 or later
(LGPL-3.0-or-later). See the LICENSE file for the full text; the LGPL
is supplemented by the GNU GPL v3 it incorporates, provided in LICENSE.GPL.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hanel_warehouse_gateway-0.1.5.tar.gz.
File metadata
- Download URL: hanel_warehouse_gateway-0.1.5.tar.gz
- Upload date:
- Size: 31.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bcaa375984ef3454f679f0e758ace29e2ce60f6518f10c0eb0486e830636711
|
|
| MD5 |
df53248a36cc19e821b93fb28cd385e8
|
|
| BLAKE2b-256 |
94b8272a7125e3609f23aad44208e17331a1ae63369ac29dbe06fce78844afaa
|
Provenance
The following attestation bundles were made for hanel_warehouse_gateway-0.1.5.tar.gz:
Publisher:
release-please.yml on 6feetup/hanel_warehouse_gateway
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hanel_warehouse_gateway-0.1.5.tar.gz -
Subject digest:
5bcaa375984ef3454f679f0e758ace29e2ce60f6518f10c0eb0486e830636711 - Sigstore transparency entry: 1813580626
- Sigstore integration time:
-
Permalink:
6feetup/hanel_warehouse_gateway@0691d984f798708a4ba360310b7a67285e172e21 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/6feetup
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@0691d984f798708a4ba360310b7a67285e172e21 -
Trigger Event:
push
-
Statement type:
File details
Details for the file hanel_warehouse_gateway-0.1.5-py3-none-any.whl.
File metadata
- Download URL: hanel_warehouse_gateway-0.1.5-py3-none-any.whl
- Upload date:
- Size: 34.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6e5621a746f6fa38a08eb2e00ff78a650757a80ddfcbd4a0995a8d01ea94ca0
|
|
| MD5 |
3dc208e6b5f2e19ba4b58c23474bb86c
|
|
| BLAKE2b-256 |
9ca5f24b3a07bbc82e4e2361dee699eda2c67107f3e1aad56fb79ae142e3183d
|
Provenance
The following attestation bundles were made for hanel_warehouse_gateway-0.1.5-py3-none-any.whl:
Publisher:
release-please.yml on 6feetup/hanel_warehouse_gateway
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hanel_warehouse_gateway-0.1.5-py3-none-any.whl -
Subject digest:
f6e5621a746f6fa38a08eb2e00ff78a650757a80ddfcbd4a0995a8d01ea94ca0 - Sigstore transparency entry: 1813581135
- Sigstore integration time:
-
Permalink:
6feetup/hanel_warehouse_gateway@0691d984f798708a4ba360310b7a67285e172e21 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/6feetup
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@0691d984f798708a4ba360310b7a67285e172e21 -
Trigger Event:
push
-
Statement type: