fega-schmitt-client 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fega_schmitt_client-0.0.2/.github/workflows/pypi-publish.yml +202 -0
- fega_schmitt_client-0.0.2/.github/workflows/test.yml +28 -0
- fega_schmitt_client-0.0.2/.gitignore +13 -0
- fega_schmitt_client-0.0.2/.pre-commit-config.yaml +25 -0
- fega_schmitt_client-0.0.2/LICENSE +21 -0
- fega_schmitt_client-0.0.2/PKG-INFO +150 -0
- fega_schmitt_client-0.0.2/README.md +127 -0
- fega_schmitt_client-0.0.2/docs/architecture.md +190 -0
- fega_schmitt_client-0.0.2/pyproject.toml +54 -0
- fega_schmitt_client-0.0.2/renovate.json +20 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/__init__.py +15 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/_soap.py +181 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/cli.py +141 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/exceptions.py +21 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/ids/__init__.py +34 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/ids/_xml.py +331 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/ids/cart.py +68 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/ids/models.py +154 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/models.py +56 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/price_avail.py +110 -0
- fega_schmitt_client-0.0.2/src/fega_schmitt_client/py.typed +0 -0
- fega_schmitt_client-0.0.2/tests/fixtures.py +84 -0
- fega_schmitt_client-0.0.2/tests/test_cli.py +81 -0
- fega_schmitt_client-0.0.2/tests/test_client.py +90 -0
- fega_schmitt_client-0.0.2/tests/test_ids_cart.py +68 -0
- fega_schmitt_client-0.0.2/tests/test_ids_xml.py +118 -0
- fega_schmitt_client-0.0.2/tests/test_models.py +24 -0
- fega_schmitt_client-0.0.2/tests/test_soap.py +115 -0
- fega_schmitt_client-0.0.2/uv.lock +574 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
name: Create Release and Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- 'src/**'
|
|
9
|
+
- 'pyproject.toml'
|
|
10
|
+
- '.github/workflows/pypi-publish.yml'
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
inputs:
|
|
13
|
+
publish_target:
|
|
14
|
+
description: 'Publish target'
|
|
15
|
+
required: true
|
|
16
|
+
default: 'all'
|
|
17
|
+
type: choice
|
|
18
|
+
options:
|
|
19
|
+
- all
|
|
20
|
+
- pypi-only
|
|
21
|
+
- none
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
version:
|
|
25
|
+
name: Calculate Version
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
outputs:
|
|
28
|
+
version: ${{ steps.semver.outputs.version }}
|
|
29
|
+
version_tag: ${{ steps.semver.outputs.version_tag }}
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v7
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
|
|
36
|
+
- name: Calculate semantic version
|
|
37
|
+
id: semver
|
|
38
|
+
uses: paulhatch/semantic-version@v6.0.3
|
|
39
|
+
with:
|
|
40
|
+
tag_prefix: "v"
|
|
41
|
+
major_pattern: "/(feat|fix|refactor)!:/"
|
|
42
|
+
minor_pattern: "/^feat:/"
|
|
43
|
+
version_format: "${major}.${minor}.${patch}"
|
|
44
|
+
bump_each_commit: true
|
|
45
|
+
search_commit_body: true
|
|
46
|
+
|
|
47
|
+
- name: Show version
|
|
48
|
+
run: |
|
|
49
|
+
echo "Version: ${{ steps.semver.outputs.version }}"
|
|
50
|
+
echo "Version Tag: ${{ steps.semver.outputs.version_tag }}"
|
|
51
|
+
cat >> $GITHUB_STEP_SUMMARY <<EOF
|
|
52
|
+
## 📦 Neue Version berechnet
|
|
53
|
+
|
|
54
|
+
**Version:** \`${{ steps.semver.outputs.version }}\`
|
|
55
|
+
**Git-Tag:** \`${{ steps.semver.outputs.version_tag }}\`
|
|
56
|
+
EOF
|
|
57
|
+
|
|
58
|
+
create-release:
|
|
59
|
+
name: Create GitHub Release
|
|
60
|
+
needs: [version]
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
permissions:
|
|
63
|
+
contents: write
|
|
64
|
+
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v7
|
|
67
|
+
|
|
68
|
+
- name: Create Release
|
|
69
|
+
env:
|
|
70
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
run: |
|
|
72
|
+
gh release create ${{ needs.version.outputs.version_tag }} \
|
|
73
|
+
--title "fega-schmitt-client ${{ needs.version.outputs.version }}" \
|
|
74
|
+
--notes "## fega-schmitt-client ${{ needs.version.outputs.version }}
|
|
75
|
+
|
|
76
|
+
Python-Client fĂĽr die B2B-Schnittstellen von FEGA & Schmitt ElektrogroĂźhandel.
|
|
77
|
+
|
|
78
|
+
### Installation
|
|
79
|
+
|
|
80
|
+
\`\`\`bash
|
|
81
|
+
pip install fega-schmitt-client==${{ needs.version.outputs.version }}
|
|
82
|
+
# oder als CLI-Tool:
|
|
83
|
+
uv tool install fega-schmitt-client==${{ needs.version.outputs.version }}
|
|
84
|
+
\`\`\`
|
|
85
|
+
" \
|
|
86
|
+
--latest
|
|
87
|
+
|
|
88
|
+
cat >> $GITHUB_STEP_SUMMARY <<EOF
|
|
89
|
+
## 🏷️ GitHub Release erstellt
|
|
90
|
+
|
|
91
|
+
**Release:** [${{ needs.version.outputs.version_tag }}](https://github.com/${{ github.repository }}/releases/tag/${{ needs.version.outputs.version_tag }})
|
|
92
|
+
EOF
|
|
93
|
+
|
|
94
|
+
build:
|
|
95
|
+
name: Build fega-schmitt-client distribution
|
|
96
|
+
needs: [version]
|
|
97
|
+
if: |
|
|
98
|
+
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
|
|
99
|
+
(github.event_name == 'workflow_dispatch' &&
|
|
100
|
+
(github.event.inputs.publish_target == 'all' || github.event.inputs.publish_target == 'pypi-only'))
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v7
|
|
105
|
+
|
|
106
|
+
- name: Set up Python
|
|
107
|
+
uses: actions/setup-python@v7
|
|
108
|
+
with:
|
|
109
|
+
python-version: "3.11"
|
|
110
|
+
|
|
111
|
+
- name: Install build dependencies
|
|
112
|
+
run: |
|
|
113
|
+
python -m pip install --upgrade pip
|
|
114
|
+
pip install build twine
|
|
115
|
+
|
|
116
|
+
- name: Set package version
|
|
117
|
+
# GOTCHA: pyproject.toml has a plain static `version = "..."` under
|
|
118
|
+
# [project] (no hatch-vcs/dynamic versioning) - `hatch version <x>`
|
|
119
|
+
# refuses to touch it, so the version line is rewritten directly.
|
|
120
|
+
run: |
|
|
121
|
+
python3 -c "
|
|
122
|
+
import re, pathlib
|
|
123
|
+
path = pathlib.Path('pyproject.toml')
|
|
124
|
+
text = path.read_text()
|
|
125
|
+
new_text, count = re.subn(r'(?m)^version = \"[^\"]*\"', 'version = \"${{ needs.version.outputs.version }}\"', text, count=1)
|
|
126
|
+
assert count == 1, 'expected exactly one version = \"...\" line in [project]'
|
|
127
|
+
path.write_text(new_text)
|
|
128
|
+
"
|
|
129
|
+
grep '^version = ' pyproject.toml
|
|
130
|
+
|
|
131
|
+
- name: Build package
|
|
132
|
+
run: python -m build
|
|
133
|
+
|
|
134
|
+
- name: Check distribution
|
|
135
|
+
run: |
|
|
136
|
+
twine check dist/*
|
|
137
|
+
cat >> $GITHUB_STEP_SUMMARY <<EOF
|
|
138
|
+
## 🔨 fega-schmitt-client Build
|
|
139
|
+
|
|
140
|
+
**Version:** \`${{ needs.version.outputs.version }}\`
|
|
141
|
+
\`\`\`
|
|
142
|
+
$(ls -lh dist/)
|
|
143
|
+
\`\`\`
|
|
144
|
+
EOF
|
|
145
|
+
|
|
146
|
+
- name: Upload artifacts
|
|
147
|
+
uses: actions/upload-artifact@v7
|
|
148
|
+
with:
|
|
149
|
+
name: fega-schmitt-client-dist
|
|
150
|
+
path: dist/
|
|
151
|
+
|
|
152
|
+
publish-to-pypi:
|
|
153
|
+
name: Publish fega-schmitt-client to PyPI
|
|
154
|
+
needs: [version, build]
|
|
155
|
+
runs-on: ubuntu-latest
|
|
156
|
+
|
|
157
|
+
environment:
|
|
158
|
+
name: pypi
|
|
159
|
+
url: https://pypi.org/p/fega-schmitt-client
|
|
160
|
+
|
|
161
|
+
permissions:
|
|
162
|
+
id-token: write # required for OIDC Trusted Publishing - no API token/secret needed
|
|
163
|
+
|
|
164
|
+
steps:
|
|
165
|
+
- name: Download distributions
|
|
166
|
+
uses: actions/download-artifact@v8
|
|
167
|
+
with:
|
|
168
|
+
name: fega-schmitt-client-dist
|
|
169
|
+
path: dist/
|
|
170
|
+
|
|
171
|
+
- name: Publish to PyPI
|
|
172
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
173
|
+
|
|
174
|
+
- name: Add PyPI Summary
|
|
175
|
+
run: |
|
|
176
|
+
cat >> $GITHUB_STEP_SUMMARY <<EOF
|
|
177
|
+
## 🚀 fega-schmitt-client PyPI Veröffentlichung
|
|
178
|
+
|
|
179
|
+
**Package:** [fega-schmitt-client](https://pypi.org/project/fega-schmitt-client/)
|
|
180
|
+
**Version:** \`${{ needs.version.outputs.version }}\`
|
|
181
|
+
EOF
|
|
182
|
+
|
|
183
|
+
summary:
|
|
184
|
+
name: Release Summary
|
|
185
|
+
needs: [version, create-release, build, publish-to-pypi]
|
|
186
|
+
if: always()
|
|
187
|
+
runs-on: ubuntu-latest
|
|
188
|
+
|
|
189
|
+
steps:
|
|
190
|
+
- name: Create Summary
|
|
191
|
+
run: |
|
|
192
|
+
cat >> $GITHUB_STEP_SUMMARY <<EOF
|
|
193
|
+
## 🎉 Release Workflow Abgeschlossen
|
|
194
|
+
|
|
195
|
+
**Version:** \`${{ needs.version.outputs.version }}\`
|
|
196
|
+
**Git Tag:** \`${{ needs.version.outputs.version_tag }}\`
|
|
197
|
+
|
|
198
|
+
### Status
|
|
199
|
+
|
|
200
|
+
- GitHub Release: ${{ needs.create-release.result == 'success' && '✅' || '❌' }}
|
|
201
|
+
- PyPI Publish: ${{ needs.publish-to-pypi.result == 'success' && 'âś…' || needs.publish-to-pypi.result == 'skipped' && 'âŹď¸Ź' || '❌' }}
|
|
202
|
+
EOF
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- uses: astral-sh/setup-uv@v7
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- run: uv sync --all-groups
|
|
20
|
+
- run: uv run pytest --cov --cov-report=term-missing --cov-report=xml
|
|
21
|
+
- name: Coverage summary
|
|
22
|
+
run: |
|
|
23
|
+
cat >> $GITHUB_STEP_SUMMARY <<EOF
|
|
24
|
+
## đź§Ş Test coverage (Python ${{ matrix.python-version }})
|
|
25
|
+
\`\`\`
|
|
26
|
+
$(uv run coverage report)
|
|
27
|
+
\`\`\`
|
|
28
|
+
EOF
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-json
|
|
10
|
+
- id: check-added-large-files
|
|
11
|
+
- id: check-merge-conflict
|
|
12
|
+
- id: debug-statements
|
|
13
|
+
- id: check-ast
|
|
14
|
+
|
|
15
|
+
- repo: https://github.com/python-jsonschema/check-jsonschema
|
|
16
|
+
rev: 0.38.0
|
|
17
|
+
hooks:
|
|
18
|
+
- id: check-github-workflows
|
|
19
|
+
|
|
20
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
21
|
+
rev: v0.16.2
|
|
22
|
+
hooks:
|
|
23
|
+
- id: ruff
|
|
24
|
+
args: [--fix]
|
|
25
|
+
- id: ruff-format
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Glaser
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fega-schmitt-client
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Python-Client fĂĽr die B2B-Schnittstellen von FEGA & Schmitt ElektrogroĂźhandel (SOAP-Preis-/VerfĂĽgbarkeitsservice)
|
|
5
|
+
Project-URL: Homepage, https://github.com/the78mole/fega-schmitt-client
|
|
6
|
+
Project-URL: Repository, https://github.com/the78mole/fega-schmitt-client
|
|
7
|
+
Project-URL: Issues, https://github.com/the78mole/fega-schmitt-client/issues
|
|
8
|
+
Author-email: Daniel Glaser <me@the78mole.de>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: click>=8.1.0
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# fega-schmitt-client
|
|
25
|
+
|
|
26
|
+
[](https://github.com/the78mole/fega-schmitt-client/actions/workflows/test.yml)
|
|
27
|
+
[](https://github.com/the78mole/fega-schmitt-client/actions/workflows/pypi-publish.yml)
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
[](https://renovatebot.com)
|
|
30
|
+
[](https://github.com/astral-sh/uv)
|
|
31
|
+
[](https://github.com/astral-sh/ruff)
|
|
32
|
+
|
|
33
|
+
> **Status: v1 implementiert (SOAP-Preis-/Verfügbarkeitsservice) + erste IDS-Erweiterung (Warenkorb senden/empfangen), noch nicht auf PyPI veröffentlicht.** UGL4 sowie IDS-Artikeldeeplink/-suche/Heatinglabel sind bewusst noch nicht umgesetzt, siehe [Architektur](docs/architecture.md#7-bewusst-nicht-in-v1-abgedeckt). Der SOAP-Preis-/Verfügbarkeitsservice ist sowohl gegen die Spezifikation/einen Mock-Server (automatisierte Tests) als auch **live gegen den echten FEGA & Schmitt-Server** verifiziert (siehe "Offene Punkte" für Details zum manuellen Testaufruf).
|
|
34
|
+
|
|
35
|
+
Eine reine Python-Library für den Zugriff auf die B2B-Schnittstellen von **FEGA & Schmitt Elektrogroßhandel** — allen voran den Preis-/Verfügbarkeitsservice. Kein MCP-, kein KI-spezifischer Code: diese Library ist eigenständig nutzbar (Skripte, andere Services, Warenwirtschaftssysteme) und wird zusätzlich vom Schwester-Projekt `fega-schmitt-mcp` (separates Repository unter `GIT/MCP/`) als MCP-Interface verpackt.
|
|
36
|
+
|
|
37
|
+
> **Suchst du das MCP-Interface fĂĽr KI-Assistenten?**
|
|
38
|
+
> Siehe das Repository `fega-schmitt-mcp` — der dünne MCP-Wrapper um diese Library.
|
|
39
|
+
|
|
40
|
+
## Ăśber FEGA & Schmitt
|
|
41
|
+
|
|
42
|
+
FEGA & Schmitt ist ein deutscher Elektrogroßhändler. Kunden erhalten Zugriff auf mehrere, technisch sehr unterschiedliche B2B-Schnittstellen zum Datenaustausch mit ihrer Warenwirtschaft/Handwerkersoftware. Details siehe [docs/architecture.md](docs/architecture.md).
|
|
43
|
+
|
|
44
|
+
## Schnittstellen im Ăśberblick
|
|
45
|
+
|
|
46
|
+
| Schnittstelle | Typ | Zweck | Eignung fĂĽr Library v1 |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| **SOAP Preis-/Verfügbarkeitsservice** | Synchrones SOAP/XML über HTTPS | Preis & Verfügbarkeit für bis zu 1000 Artikel je Anfrage | ✅ Basis für v1 — einzige echte Request/Response-API |
|
|
49
|
+
| **IDS-Schnittstelle** (BVBS/ITEK-Branchenstandard, v2.5) | Browser-Redirect + Hook-URL, halbautomatisch | Warenkorb-Austausch, Artikelsuche, Artikel-Deeplinks, Heizungslabel (ErP) | ⚠️ Später — passt nicht zu einer reinen Funktionsaufruf-API, siehe [Architektur](docs/architecture.md#7-bewusst-nicht-in-v1-abgedeckt) |
|
|
50
|
+
| **UGL Version 4** (SHK-Branchenstandard) | Datei-basiert (FTP/Portal), ASCII Fixed-Length | Anfragen, Abrufaufträge, Auftragsbestätigungen als Batch-Dateien | ⚠️ Später — Batch/Polling statt Live-Antwort |
|
|
51
|
+
|
|
52
|
+
Die vollständigen Original-Spezifikationen liegen als PDF in [docs/specs/](docs/specs/):
|
|
53
|
+
|
|
54
|
+
- `Schnittstellenbeschreibung_SOAP.pdf` — Preis-/Verfügbarkeits-Webservice (FEGA & Schmitt, Stand März 2016)
|
|
55
|
+
- `IDS_Schnittstelle_2_5_final_NEU.pdf` — IDS-Schnittstelle für Warenkorb/Artikelsuche/Heizlabel (BVBS/ITEK, Version 2.5, Stand 02.11.2020)
|
|
56
|
+
- `ugl4neutral.pdf` — UGL Version 4, Datenaustausch Handwerk ↔ SHK-Großhandel (GC-Gruppe, Stand 16.06.2006)
|
|
57
|
+
|
|
58
|
+
## Ă–ffentliche API (v1)
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from fega_schmitt_client import FegaSchmittClient, PriceAvailRequestItem
|
|
62
|
+
|
|
63
|
+
client = FegaSchmittClient(
|
|
64
|
+
partner_purchaser="9920", # Kundennummer bei FEGA & Schmitt
|
|
65
|
+
legitimation_id="...", # Shop-Kennwort
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
results = client.get_price_availability([
|
|
69
|
+
PriceAvailRequestItem(material_number="0815", quantity=200, unit="MTR"),
|
|
70
|
+
PriceAvailRequestItem(material_number="4711", quantity=1),
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
for r in results:
|
|
74
|
+
print(r.material_number, r.availability_status, r.net_amount)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Details zu Datenmodell, Fehlerbehandlung und Architektur: siehe [docs/architecture.md](docs/architecture.md).
|
|
78
|
+
|
|
79
|
+
## IDS-Erweiterung: Warenkorb senden/empfangen
|
|
80
|
+
|
|
81
|
+
`fega_schmitt_client.ids` implementiert den "Warenkorb senden"-Teil der IDS-Schnittstelle (BVBS/ITEK v2.5, halbautomatisch — Browserfenster + manuelle Nutzerinteraktion im FEGA-Shop, siehe [`docs/specs/IDS_Schnittstelle_2_5_final_NEU.pdf`](docs/specs/IDS_Schnittstelle_2_5_final_NEU.pdf), Abschnitt 5.2). Die Library baut nur die Daten — **kein** Browser-Handling, **kein** Webhook-Server; das übernimmt der Aufrufer (z. B. `fega-schmitt-mcp`):
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from decimal import Decimal
|
|
85
|
+
from fega_schmitt_client.ids import Cart, CartItem, build_cart_request, parse_cart_callback
|
|
86
|
+
|
|
87
|
+
cart = Cart(items=[
|
|
88
|
+
CartItem(article_number="0815", quantity=Decimal(200), unit="MTR"),
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
request = build_cart_request(
|
|
92
|
+
cart,
|
|
93
|
+
shop_url="https://shop.fega.de/...", # noch nicht bekannt, siehe "Offene Punkte"
|
|
94
|
+
customer_number="9920",
|
|
95
|
+
hook_url="https://mein-server.example/fega-hook", # optional
|
|
96
|
+
)
|
|
97
|
+
# request.fields als multipart/form-data-POST an request.shop_url schicken
|
|
98
|
+
# (bzw. als auto-submitting HTML-Formular im Browser des Nutzers öffnen)
|
|
99
|
+
|
|
100
|
+
# Später, wenn der Shop das Ergebnis an die hook_url zurückschickt:
|
|
101
|
+
updated_cart = parse_cart_callback(received_xml)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Ohne `hook_url` funktioniert das Senden weiterhin — der Nutzer schließt den Vorgang dann manuell im Browser ab, es gibt nur keinen automatischen Rücklauf.
|
|
105
|
+
|
|
106
|
+
## CLI
|
|
107
|
+
|
|
108
|
+
Als Kommandozeilen-Tool `fega` installierbar, ohne dass ein eigenes venv angelegt werden muss:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
uv tool install fega-schmitt-client
|
|
112
|
+
fega --customer-number 9920 --shop-password "..." price-avail 0815:200:MTR 4711
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Alternativ per Umgebungsvariablen (`FEGA_CUSTOMER_NUMBER`, `FEGA_SHOP_PASSWORD`) statt Optionen. `-j`/`--json` liefert JSON statt Tabellenausgabe. `fega --help` bzw. `fega price-avail --help` zeigen alle Optionen.
|
|
116
|
+
|
|
117
|
+
## Installation
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install fega-schmitt-client
|
|
121
|
+
# oder
|
|
122
|
+
uv add fega-schmitt-client
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
(Noch nicht auf [PyPI](https://pypi.org/) veröffentlicht — bis dahin lokal installierbar via `uv pip install .` bzw. `uv tool install .` aus dem geklonten Repo.)
|
|
126
|
+
|
|
127
|
+
## Entwicklung
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
uv sync --all-groups # Dependencies + Dev-Tools installieren
|
|
131
|
+
uv run pytest # Tests (gegen einen respx-Mock-Server, kein echter FEGA-Zugang nötig)
|
|
132
|
+
uv run pre-commit install # Linting/Formatting-Hooks aktivieren
|
|
133
|
+
uv run pre-commit run --all-files
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Offene Punkte
|
|
137
|
+
|
|
138
|
+
- ~~Test-/Produktivzugangsdaten für den SOAP-Service~~ — **erledigt**: `get_price_availability` wurde live gegen `https://soap.fega.de/priceavail.php` verifiziert (echte Kundennummer/Shop-Kennwort, Artikel 121350 → `I720`, korrekte Preis-, Verfügbarkeits- und Kupferzuschlag-Daten). Zugangsdaten liegen als Secret in OpenBao, nicht im Repo.
|
|
139
|
+
- Wie FEGA & Schmitt eine komplett abgelehnte Anmeldung tatsächlich signalisiert (HTTP-Status, Fault oder ein bestimmter Returncode), bleibt weiterhin offen — der Live-Test lief mit gültigen Zugangsdaten, absichtlich falsche wurden nicht getestet. Beobachtet wurde allerdings: eine unbekannte Artikelnummer liefert `E999`/Fehler 216 mit dem missverständlichen Text "Bitte pruefen Sie Ihre Anmeldedaten" (HTTP 200) — nicht mit einem Auth-spezifischen HTTP-Status. `FegaAuthError` (aktuell nur bei HTTP 401/403) ist also weiterhin eine ungetestete Annahme, könnte in der Praxis aber nie greifen, wenn FEGA Ablehnungen generell per Item-Returncode statt HTTP-Status meldet
|
|
140
|
+
- Die tatsächliche IDS-Shop-URL von FEGA & Schmitt (`shop_url` in `build_cart_request`) sowie die referenzierten XSD-Schemas (`Warenkorb_senden.xsd`, `Warenkorb_empfangen_2-5.xsd` — in der vorliegenden PDF nicht enthalten, nur die tabellarische Feldbeschreibung); die Implementierung von `fega_schmitt_client.ids` ist entsprechend nur gegen die Spezifikation getestet, nicht gegen den echten Shop
|
|
141
|
+
- Das exakte Formularfeld, unter dem der Shop den Warenkorb an die Hook-URL zurückschickt — die Spec dokumentiert nur den Feldnamen `warenkorb` für die ausgehende Richtung, für den Rücklauf wird das per Analogieschluss angenommen
|
|
142
|
+
- Artikeldeeplink, Artikelsuche, Login-Informationen, Schnittstellenversion und Heatinglabel (restliche IDS-Aktionen) sind noch nicht umgesetzt
|
|
143
|
+
- **DATANORM v5 + Offline-Zugang (FTP)**: bei FEGA & Schmitt beantragt, aber Zugangsdaten und Format-Spezifikation liegen noch nicht vor — siehe [docs/architecture.md](docs/architecture.md#73-datanorm-v5-artikel-stammdatenpreislisten). DATANORM ist die von der SOAP-Spec selbst referenzierte Quelle der Artikelnummern für `get_price_availability`, daher relevanter als "nur" ein weiteres Zusatzformat
|
|
144
|
+
- Ob UGL4 fĂĽr dieses Projekt relevant ist, und falls ja: FTP-Zugangsdaten/Verzeichnis
|
|
145
|
+
- EndgĂĽltiger PyPI-/Modulname (`fega-schmitt-client` ist ein Arbeitstitel)
|
|
146
|
+
- PyPI Trusted Publishing muss einmalig manuell auf pypi.org eingerichtet werden (Workflow-Datei `pypi-publish.yml`, Environment `pypi`), bevor der Release-Workflow tatsächlich veröffentlichen kann
|
|
147
|
+
|
|
148
|
+
## Lizenz
|
|
149
|
+
|
|
150
|
+
MIT, siehe [LICENSE](LICENSE).
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# fega-schmitt-client
|
|
2
|
+
|
|
3
|
+
[](https://github.com/the78mole/fega-schmitt-client/actions/workflows/test.yml)
|
|
4
|
+
[](https://github.com/the78mole/fega-schmitt-client/actions/workflows/pypi-publish.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://renovatebot.com)
|
|
7
|
+
[](https://github.com/astral-sh/uv)
|
|
8
|
+
[](https://github.com/astral-sh/ruff)
|
|
9
|
+
|
|
10
|
+
> **Status: v1 implementiert (SOAP-Preis-/Verfügbarkeitsservice) + erste IDS-Erweiterung (Warenkorb senden/empfangen), noch nicht auf PyPI veröffentlicht.** UGL4 sowie IDS-Artikeldeeplink/-suche/Heatinglabel sind bewusst noch nicht umgesetzt, siehe [Architektur](docs/architecture.md#7-bewusst-nicht-in-v1-abgedeckt). Der SOAP-Preis-/Verfügbarkeitsservice ist sowohl gegen die Spezifikation/einen Mock-Server (automatisierte Tests) als auch **live gegen den echten FEGA & Schmitt-Server** verifiziert (siehe "Offene Punkte" für Details zum manuellen Testaufruf).
|
|
11
|
+
|
|
12
|
+
Eine reine Python-Library für den Zugriff auf die B2B-Schnittstellen von **FEGA & Schmitt Elektrogroßhandel** — allen voran den Preis-/Verfügbarkeitsservice. Kein MCP-, kein KI-spezifischer Code: diese Library ist eigenständig nutzbar (Skripte, andere Services, Warenwirtschaftssysteme) und wird zusätzlich vom Schwester-Projekt `fega-schmitt-mcp` (separates Repository unter `GIT/MCP/`) als MCP-Interface verpackt.
|
|
13
|
+
|
|
14
|
+
> **Suchst du das MCP-Interface fĂĽr KI-Assistenten?**
|
|
15
|
+
> Siehe das Repository `fega-schmitt-mcp` — der dünne MCP-Wrapper um diese Library.
|
|
16
|
+
|
|
17
|
+
## Ăśber FEGA & Schmitt
|
|
18
|
+
|
|
19
|
+
FEGA & Schmitt ist ein deutscher Elektrogroßhändler. Kunden erhalten Zugriff auf mehrere, technisch sehr unterschiedliche B2B-Schnittstellen zum Datenaustausch mit ihrer Warenwirtschaft/Handwerkersoftware. Details siehe [docs/architecture.md](docs/architecture.md).
|
|
20
|
+
|
|
21
|
+
## Schnittstellen im Ăśberblick
|
|
22
|
+
|
|
23
|
+
| Schnittstelle | Typ | Zweck | Eignung fĂĽr Library v1 |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| **SOAP Preis-/Verfügbarkeitsservice** | Synchrones SOAP/XML über HTTPS | Preis & Verfügbarkeit für bis zu 1000 Artikel je Anfrage | ✅ Basis für v1 — einzige echte Request/Response-API |
|
|
26
|
+
| **IDS-Schnittstelle** (BVBS/ITEK-Branchenstandard, v2.5) | Browser-Redirect + Hook-URL, halbautomatisch | Warenkorb-Austausch, Artikelsuche, Artikel-Deeplinks, Heizungslabel (ErP) | ⚠️ Später — passt nicht zu einer reinen Funktionsaufruf-API, siehe [Architektur](docs/architecture.md#7-bewusst-nicht-in-v1-abgedeckt) |
|
|
27
|
+
| **UGL Version 4** (SHK-Branchenstandard) | Datei-basiert (FTP/Portal), ASCII Fixed-Length | Anfragen, Abrufaufträge, Auftragsbestätigungen als Batch-Dateien | ⚠️ Später — Batch/Polling statt Live-Antwort |
|
|
28
|
+
|
|
29
|
+
Die vollständigen Original-Spezifikationen liegen als PDF in [docs/specs/](docs/specs/):
|
|
30
|
+
|
|
31
|
+
- `Schnittstellenbeschreibung_SOAP.pdf` — Preis-/Verfügbarkeits-Webservice (FEGA & Schmitt, Stand März 2016)
|
|
32
|
+
- `IDS_Schnittstelle_2_5_final_NEU.pdf` — IDS-Schnittstelle für Warenkorb/Artikelsuche/Heizlabel (BVBS/ITEK, Version 2.5, Stand 02.11.2020)
|
|
33
|
+
- `ugl4neutral.pdf` — UGL Version 4, Datenaustausch Handwerk ↔ SHK-Großhandel (GC-Gruppe, Stand 16.06.2006)
|
|
34
|
+
|
|
35
|
+
## Ă–ffentliche API (v1)
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from fega_schmitt_client import FegaSchmittClient, PriceAvailRequestItem
|
|
39
|
+
|
|
40
|
+
client = FegaSchmittClient(
|
|
41
|
+
partner_purchaser="9920", # Kundennummer bei FEGA & Schmitt
|
|
42
|
+
legitimation_id="...", # Shop-Kennwort
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
results = client.get_price_availability([
|
|
46
|
+
PriceAvailRequestItem(material_number="0815", quantity=200, unit="MTR"),
|
|
47
|
+
PriceAvailRequestItem(material_number="4711", quantity=1),
|
|
48
|
+
])
|
|
49
|
+
|
|
50
|
+
for r in results:
|
|
51
|
+
print(r.material_number, r.availability_status, r.net_amount)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Details zu Datenmodell, Fehlerbehandlung und Architektur: siehe [docs/architecture.md](docs/architecture.md).
|
|
55
|
+
|
|
56
|
+
## IDS-Erweiterung: Warenkorb senden/empfangen
|
|
57
|
+
|
|
58
|
+
`fega_schmitt_client.ids` implementiert den "Warenkorb senden"-Teil der IDS-Schnittstelle (BVBS/ITEK v2.5, halbautomatisch — Browserfenster + manuelle Nutzerinteraktion im FEGA-Shop, siehe [`docs/specs/IDS_Schnittstelle_2_5_final_NEU.pdf`](docs/specs/IDS_Schnittstelle_2_5_final_NEU.pdf), Abschnitt 5.2). Die Library baut nur die Daten — **kein** Browser-Handling, **kein** Webhook-Server; das übernimmt der Aufrufer (z. B. `fega-schmitt-mcp`):
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from decimal import Decimal
|
|
62
|
+
from fega_schmitt_client.ids import Cart, CartItem, build_cart_request, parse_cart_callback
|
|
63
|
+
|
|
64
|
+
cart = Cart(items=[
|
|
65
|
+
CartItem(article_number="0815", quantity=Decimal(200), unit="MTR"),
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
request = build_cart_request(
|
|
69
|
+
cart,
|
|
70
|
+
shop_url="https://shop.fega.de/...", # noch nicht bekannt, siehe "Offene Punkte"
|
|
71
|
+
customer_number="9920",
|
|
72
|
+
hook_url="https://mein-server.example/fega-hook", # optional
|
|
73
|
+
)
|
|
74
|
+
# request.fields als multipart/form-data-POST an request.shop_url schicken
|
|
75
|
+
# (bzw. als auto-submitting HTML-Formular im Browser des Nutzers öffnen)
|
|
76
|
+
|
|
77
|
+
# Später, wenn der Shop das Ergebnis an die hook_url zurückschickt:
|
|
78
|
+
updated_cart = parse_cart_callback(received_xml)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Ohne `hook_url` funktioniert das Senden weiterhin — der Nutzer schließt den Vorgang dann manuell im Browser ab, es gibt nur keinen automatischen Rücklauf.
|
|
82
|
+
|
|
83
|
+
## CLI
|
|
84
|
+
|
|
85
|
+
Als Kommandozeilen-Tool `fega` installierbar, ohne dass ein eigenes venv angelegt werden muss:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
uv tool install fega-schmitt-client
|
|
89
|
+
fega --customer-number 9920 --shop-password "..." price-avail 0815:200:MTR 4711
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Alternativ per Umgebungsvariablen (`FEGA_CUSTOMER_NUMBER`, `FEGA_SHOP_PASSWORD`) statt Optionen. `-j`/`--json` liefert JSON statt Tabellenausgabe. `fega --help` bzw. `fega price-avail --help` zeigen alle Optionen.
|
|
93
|
+
|
|
94
|
+
## Installation
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install fega-schmitt-client
|
|
98
|
+
# oder
|
|
99
|
+
uv add fega-schmitt-client
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
(Noch nicht auf [PyPI](https://pypi.org/) veröffentlicht — bis dahin lokal installierbar via `uv pip install .` bzw. `uv tool install .` aus dem geklonten Repo.)
|
|
103
|
+
|
|
104
|
+
## Entwicklung
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
uv sync --all-groups # Dependencies + Dev-Tools installieren
|
|
108
|
+
uv run pytest # Tests (gegen einen respx-Mock-Server, kein echter FEGA-Zugang nötig)
|
|
109
|
+
uv run pre-commit install # Linting/Formatting-Hooks aktivieren
|
|
110
|
+
uv run pre-commit run --all-files
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Offene Punkte
|
|
114
|
+
|
|
115
|
+
- ~~Test-/Produktivzugangsdaten für den SOAP-Service~~ — **erledigt**: `get_price_availability` wurde live gegen `https://soap.fega.de/priceavail.php` verifiziert (echte Kundennummer/Shop-Kennwort, Artikel 121350 → `I720`, korrekte Preis-, Verfügbarkeits- und Kupferzuschlag-Daten). Zugangsdaten liegen als Secret in OpenBao, nicht im Repo.
|
|
116
|
+
- Wie FEGA & Schmitt eine komplett abgelehnte Anmeldung tatsächlich signalisiert (HTTP-Status, Fault oder ein bestimmter Returncode), bleibt weiterhin offen — der Live-Test lief mit gültigen Zugangsdaten, absichtlich falsche wurden nicht getestet. Beobachtet wurde allerdings: eine unbekannte Artikelnummer liefert `E999`/Fehler 216 mit dem missverständlichen Text "Bitte pruefen Sie Ihre Anmeldedaten" (HTTP 200) — nicht mit einem Auth-spezifischen HTTP-Status. `FegaAuthError` (aktuell nur bei HTTP 401/403) ist also weiterhin eine ungetestete Annahme, könnte in der Praxis aber nie greifen, wenn FEGA Ablehnungen generell per Item-Returncode statt HTTP-Status meldet
|
|
117
|
+
- Die tatsächliche IDS-Shop-URL von FEGA & Schmitt (`shop_url` in `build_cart_request`) sowie die referenzierten XSD-Schemas (`Warenkorb_senden.xsd`, `Warenkorb_empfangen_2-5.xsd` — in der vorliegenden PDF nicht enthalten, nur die tabellarische Feldbeschreibung); die Implementierung von `fega_schmitt_client.ids` ist entsprechend nur gegen die Spezifikation getestet, nicht gegen den echten Shop
|
|
118
|
+
- Das exakte Formularfeld, unter dem der Shop den Warenkorb an die Hook-URL zurückschickt — die Spec dokumentiert nur den Feldnamen `warenkorb` für die ausgehende Richtung, für den Rücklauf wird das per Analogieschluss angenommen
|
|
119
|
+
- Artikeldeeplink, Artikelsuche, Login-Informationen, Schnittstellenversion und Heatinglabel (restliche IDS-Aktionen) sind noch nicht umgesetzt
|
|
120
|
+
- **DATANORM v5 + Offline-Zugang (FTP)**: bei FEGA & Schmitt beantragt, aber Zugangsdaten und Format-Spezifikation liegen noch nicht vor — siehe [docs/architecture.md](docs/architecture.md#73-datanorm-v5-artikel-stammdatenpreislisten). DATANORM ist die von der SOAP-Spec selbst referenzierte Quelle der Artikelnummern für `get_price_availability`, daher relevanter als "nur" ein weiteres Zusatzformat
|
|
121
|
+
- Ob UGL4 fĂĽr dieses Projekt relevant ist, und falls ja: FTP-Zugangsdaten/Verzeichnis
|
|
122
|
+
- EndgĂĽltiger PyPI-/Modulname (`fega-schmitt-client` ist ein Arbeitstitel)
|
|
123
|
+
- PyPI Trusted Publishing muss einmalig manuell auf pypi.org eingerichtet werden (Workflow-Datei `pypi-publish.yml`, Environment `pypi`), bevor der Release-Workflow tatsächlich veröffentlichen kann
|
|
124
|
+
|
|
125
|
+
## Lizenz
|
|
126
|
+
|
|
127
|
+
MIT, siehe [LICENSE](LICENSE).
|