sevdesk 0.1.0__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.
- sevdesk-0.1.0/LICENSE +33 -0
- sevdesk-0.1.0/PKG-INFO +189 -0
- sevdesk-0.1.0/README.md +160 -0
- sevdesk-0.1.0/pyproject.toml +47 -0
- sevdesk-0.1.0/setup.cfg +4 -0
- sevdesk-0.1.0/sevdesk/__init__.py +3 -0
- sevdesk-0.1.0/sevdesk/base/basecontroller.py +102 -0
- sevdesk-0.1.0/sevdesk/client.py +81 -0
- sevdesk-0.1.0/sevdesk/controllers/accountingcontact_controller.py +33 -0
- sevdesk-0.1.0/sevdesk/controllers/basics_controller.py +9 -0
- sevdesk-0.1.0/sevdesk/controllers/checkaccount_controller.py +45 -0
- sevdesk-0.1.0/sevdesk/controllers/checkaccounttransaction_controller.py +38 -0
- sevdesk-0.1.0/sevdesk/controllers/communicationway_controller.py +38 -0
- sevdesk-0.1.0/sevdesk/controllers/contact_controller.py +53 -0
- sevdesk-0.1.0/sevdesk/controllers/contactaddress_controller.py +32 -0
- sevdesk-0.1.0/sevdesk/controllers/contactfield_controller.py +72 -0
- sevdesk-0.1.0/sevdesk/controllers/creditnote_controller.py +86 -0
- sevdesk-0.1.0/sevdesk/controllers/creditnotepos_controller.py +11 -0
- sevdesk-0.1.0/sevdesk/controllers/export_controller.py +77 -0
- sevdesk-0.1.0/sevdesk/controllers/invoice_controller.py +96 -0
- sevdesk-0.1.0/sevdesk/controllers/invoicepos_controller.py +11 -0
- sevdesk-0.1.0/sevdesk/controllers/layout_controller.py +32 -0
- sevdesk-0.1.0/sevdesk/controllers/order_controller.py +78 -0
- sevdesk-0.1.0/sevdesk/controllers/orderpos_controller.py +27 -0
- sevdesk-0.1.0/sevdesk/controllers/part_controller.py +32 -0
- sevdesk-0.1.0/sevdesk/controllers/report_controller.py +25 -0
- sevdesk-0.1.0/sevdesk/controllers/tag_controller.py +37 -0
- sevdesk-0.1.0/sevdesk/controllers/voucher_controller.py +80 -0
- sevdesk-0.1.0/sevdesk/controllers/voucherpos_controller.py +11 -0
- sevdesk-0.1.0/sevdesk/converters/accountdatev.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/accountingtype.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/addresscountry.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/category.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/checkaccount.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/contact.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/contactcustomfieldsetting.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/contactperson.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/costcentre.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/country.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/createuser.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/creditnote.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/discountdelete.py +8 -0
- sevdesk-0.1.0/sevdesk/converters/document.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/error.py +8 -0
- sevdesk-0.1.0/sevdesk/converters/estimatedaccountingtype.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/invoice.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/key.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/metadaten.py +9 -0
- sevdesk-0.1.0/sevdesk/converters/object.py +6 -0
- sevdesk-0.1.0/sevdesk/converters/object_.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/order.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/origin.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/parent.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/part.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/paymentmethod.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/sevclient.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/sourcetransaction.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/supplier.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/tag.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/targettransaction.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/taxrule.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/taxset.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/unity.py +7 -0
- sevdesk-0.1.0/sevdesk/converters/voucher.py +7 -0
- sevdesk-0.1.0/sevdesk/models/accountingcontact.py +11 -0
- sevdesk-0.1.0/sevdesk/models/accountingcontactresponse.py +17 -0
- sevdesk-0.1.0/sevdesk/models/accountingcontactupdate.py +11 -0
- sevdesk-0.1.0/sevdesk/models/changelayout.py +8 -0
- sevdesk-0.1.0/sevdesk/models/changelayoutresponse.py +10 -0
- sevdesk-0.1.0/sevdesk/models/checkaccountresponse.py +28 -0
- sevdesk-0.1.0/sevdesk/models/checkaccounttransaction.py +27 -0
- sevdesk-0.1.0/sevdesk/models/checkaccounttransactionresponse.py +31 -0
- sevdesk-0.1.0/sevdesk/models/checkaccounttransactionupdate.py +19 -0
- sevdesk-0.1.0/sevdesk/models/checkaccountupdate.py +15 -0
- sevdesk-0.1.0/sevdesk/models/communicationway.py +20 -0
- sevdesk-0.1.0/sevdesk/models/communicationwayresponse.py +20 -0
- sevdesk-0.1.0/sevdesk/models/communicationwayupdate.py +14 -0
- sevdesk-0.1.0/sevdesk/models/contact.py +35 -0
- sevdesk-0.1.0/sevdesk/models/contactaddress.py +26 -0
- sevdesk-0.1.0/sevdesk/models/contactaddressresponse.py +26 -0
- sevdesk-0.1.0/sevdesk/models/contactaddressupdate.py +20 -0
- sevdesk-0.1.0/sevdesk/models/contactcustomfield.py +12 -0
- sevdesk-0.1.0/sevdesk/models/contactcustomfieldresponse.py +17 -0
- sevdesk-0.1.0/sevdesk/models/contactcustomfieldsetting.py +10 -0
- sevdesk-0.1.0/sevdesk/models/contactcustomfieldsettingresponse.py +16 -0
- sevdesk-0.1.0/sevdesk/models/contactcustomfieldsettingupdate.py +10 -0
- sevdesk-0.1.0/sevdesk/models/contactcustomfieldupdate.py +13 -0
- sevdesk-0.1.0/sevdesk/models/contactresponse.py +42 -0
- sevdesk-0.1.0/sevdesk/models/contactupdate.py +35 -0
- sevdesk-0.1.0/sevdesk/models/createclearingaccount.py +9 -0
- sevdesk-0.1.0/sevdesk/models/createclearingaccountresponse.py +19 -0
- sevdesk-0.1.0/sevdesk/models/createfileimportaccount.py +11 -0
- sevdesk-0.1.0/sevdesk/models/createfileimportaccountresponse.py +22 -0
- sevdesk-0.1.0/sevdesk/models/createinvoicefromorder.py +12 -0
- sevdesk-0.1.0/sevdesk/models/createpackinglistfromorder.py +8 -0
- sevdesk-0.1.0/sevdesk/models/creditnote.py +51 -0
- sevdesk-0.1.0/sevdesk/models/creditnote_mailresponse.py +18 -0
- sevdesk-0.1.0/sevdesk/models/creditnote_sendbywithrender.py +11 -0
- sevdesk-0.1.0/sevdesk/models/creditnotepos.py +32 -0
- sevdesk-0.1.0/sevdesk/models/creditnoteposresponse.py +31 -0
- sevdesk-0.1.0/sevdesk/models/creditnoteresponse.py +50 -0
- sevdesk-0.1.0/sevdesk/models/creditnoteupdate.py +50 -0
- sevdesk-0.1.0/sevdesk/models/discount.py +18 -0
- sevdesk-0.1.0/sevdesk/models/email.py +21 -0
- sevdesk-0.1.0/sevdesk/models/emailorder.py +21 -0
- sevdesk-0.1.0/sevdesk/models/export_job_download_info.py +10 -0
- sevdesk-0.1.0/sevdesk/models/export_progress_data.py +9 -0
- sevdesk-0.1.0/sevdesk/models/invoice.py +69 -0
- sevdesk-0.1.0/sevdesk/models/invoicepos.py +34 -0
- sevdesk-0.1.0/sevdesk/models/invoiceposresponse.py +33 -0
- sevdesk-0.1.0/sevdesk/models/invoiceresponse.py +76 -0
- sevdesk-0.1.0/sevdesk/models/iscountsresponse.py +17 -0
- sevdesk-0.1.0/sevdesk/models/order.py +44 -0
- sevdesk-0.1.0/sevdesk/models/orderpos.py +31 -0
- sevdesk-0.1.0/sevdesk/models/orderposresponse.py +31 -0
- sevdesk-0.1.0/sevdesk/models/orderposupdate.py +31 -0
- sevdesk-0.1.0/sevdesk/models/orderresponse.py +55 -0
- sevdesk-0.1.0/sevdesk/models/orderupdate.py +55 -0
- sevdesk-0.1.0/sevdesk/models/part.py +29 -0
- sevdesk-0.1.0/sevdesk/models/partupdate.py +29 -0
- sevdesk-0.1.0/sevdesk/models/receiptguidedto.py +13 -0
- sevdesk-0.1.0/sevdesk/models/savecreditnote.py +12 -0
- sevdesk-0.1.0/sevdesk/models/savecreditnoteresponse.py +9 -0
- sevdesk-0.1.0/sevdesk/models/saveinvoice.py +14 -0
- sevdesk-0.1.0/sevdesk/models/saveinvoiceresponse.py +10 -0
- sevdesk-0.1.0/sevdesk/models/saveorder.py +10 -0
- sevdesk-0.1.0/sevdesk/models/saveorderresponse.py +9 -0
- sevdesk-0.1.0/sevdesk/models/savevoucher.py +11 -0
- sevdesk-0.1.0/sevdesk/models/savevoucherresponse.py +10 -0
- sevdesk-0.1.0/sevdesk/models/tagcreateresponse.py +17 -0
- sevdesk-0.1.0/sevdesk/models/tagresponse.py +14 -0
- sevdesk-0.1.0/sevdesk/models/textparser_fetchdictionaryentriesbytype_response.py +9 -0
- sevdesk-0.1.0/sevdesk/models/validationerror.py +9 -0
- sevdesk-0.1.0/sevdesk/models/voucher.py +55 -0
- sevdesk-0.1.0/sevdesk/models/voucherpos.py +32 -0
- sevdesk-0.1.0/sevdesk/models/voucherposresponse.py +31 -0
- sevdesk-0.1.0/sevdesk/models/voucherresponse.py +55 -0
- sevdesk-0.1.0/sevdesk/models/voucherupdate.py +32 -0
- sevdesk-0.1.0/sevdesk.egg-info/PKG-INFO +189 -0
- sevdesk-0.1.0/sevdesk.egg-info/SOURCES.txt +141 -0
- sevdesk-0.1.0/sevdesk.egg-info/dependency_links.txt +1 -0
- sevdesk-0.1.0/sevdesk.egg-info/requires.txt +4 -0
- sevdesk-0.1.0/sevdesk.egg-info/top_level.txt +1 -0
sevdesk-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Maximilian Clemens
|
|
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.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
ÜBERSETZUNG, MAßGEBLICH BLEIBT ABER DIE ENGLISCHE ORIGINAL FASSUNG
|
|
26
|
+
|
|
27
|
+
Copyright (c) 2025 Maximilian Clemens
|
|
28
|
+
|
|
29
|
+
Jedem, der eine Kopie dieser Software und der zugehörigen Dokumentationsdateien (die „Software“) erhält, wird hiermit kostenlos die Erlaubnis erteilt, ohne Einschränkung mit der Software zu handeln, einschließlich und ohne Einschränkung der Rechte zur Nutzung, zum Kopieren, Ändern, Zusammenführen, Veröffentlichen, Verteilen, Unterlizenzieren und/oder Verkaufen von Kopien der Software, und Personen, denen die Software zur Verfügung gestellt wird, dies unter den folgenden Bedingungen zu gestatten:
|
|
30
|
+
|
|
31
|
+
Der obige Urheberrechtshinweis und dieser Genehmigungshinweis müssen in allen Kopien oder wesentlichen Teilen der Software enthalten sein.
|
|
32
|
+
|
|
33
|
+
DIE SOFTWARE WIRD OHNE MÄNGELGEWÄHR UND OHNE JEGLICHE AUSDRÜCKLICHE ODER STILLSCHWEIGENDE GEWÄHRLEISTUNG, EINSCHLIEẞLICH, ABER NICHT BESCHRÄNKT AUF DIE GEWÄHRLEISTUNG DER MARKTGÄNGIGKEIT, DER EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND DER NICHTVERLETZUNG VON RECHTEN DRITTER, ZUR VERFÜGUNG GESTELLT. DIE AUTOREN ODER URHEBERRECHTSINHABER SIND IN KEINEM FALL HAFTBAR FÜR ANSPRÜCHE, SCHÄDEN ODER ANDERE VERPFLICHTUNGEN, OB IN EINER VERTRAGS- ODER HAFTUNGSKLAGE, EINER UNERLAUBTEN HANDLUNG ODER ANDERWEITIG, DIE SICH AUS ODER IN VERBINDUNG MIT DER SOFTWARE ODER DER NUTZUNG ODER ANDEREN GESCHÄFTEN MIT DER SOFTWARE ERGEBEN.
|
sevdesk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sevdesk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python client for the sevDesk API, automatically generated from the OpenAPI specification
|
|
5
|
+
Author: Claude, ChatGPT, Copilot
|
|
6
|
+
Author-email: Maximilian Clemens <maximilian.clemens@gamma.red>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/MaximilianClemens/python-sevdesk
|
|
9
|
+
Project-URL: Repository, https://github.com/MaximilianClemens/python-sevdesk
|
|
10
|
+
Project-URL: Issues, https://github.com/MaximilianClemens/python-sevdesk/issues
|
|
11
|
+
Keywords: sevdesk,api,client,accounting
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: pydantic
|
|
25
|
+
Requires-Dist: requests
|
|
26
|
+
Requires-Dist: PyYAML
|
|
27
|
+
Requires-Dist: jinja2
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# sevDesk API Client (Inoffiziell)
|
|
31
|
+
|
|
32
|
+
Ein Python-Client für die sevDesk API, automatisch generiert aus der OpenAPI-Spezifikation. Dies ist notwendig, da sich die sevdesk-Spezifikation mit dem Standard OpenApi-Generator nicht parsen lässt.
|
|
33
|
+
|
|
34
|
+
## ⚠️ Wichtige Hinweise
|
|
35
|
+
|
|
36
|
+
- **Dies ist KEINE offizielle sevDesk Library**
|
|
37
|
+
- Ich arbeite nicht für sevDesk und bin nicht mit sevDesk verbunden
|
|
38
|
+
- **Nutzung auf eigene Gefahr**
|
|
39
|
+
- Keine Garantie für Funktionalität oder Vollständigkeit
|
|
40
|
+
- Bei Problemen mit Markenrechten oder dem Library-Namen kontaktieren Sie mich bitte
|
|
41
|
+
- Falls sich offizielle Vertreter von sevDesk am Namen der Library stören, bin ich bereit, diesen zu übertragen oder die Library unter einem anderen Namen zu veröffentlichen
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install sevdesk
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Anforderungen
|
|
50
|
+
|
|
51
|
+
- Python 3.8+
|
|
52
|
+
- pydantic
|
|
53
|
+
- requests
|
|
54
|
+
- pyyaml
|
|
55
|
+
- jinja2
|
|
56
|
+
|
|
57
|
+
## Verwendung
|
|
58
|
+
|
|
59
|
+
### Client initialisieren
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from sevdesk import Client
|
|
63
|
+
|
|
64
|
+
# API Token von sevDesk Dashboard holen
|
|
65
|
+
client = Client('your-api-token-here')
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Kontakte abrufen
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# Alle Kontakte abrufen
|
|
72
|
+
contacts = client.contact.getContacts()
|
|
73
|
+
|
|
74
|
+
for contact in contacts:
|
|
75
|
+
print(f"ID: {contact.id_}")
|
|
76
|
+
print(f"Name: {contact.name or f'{contact.surename} {contact.familyname}'}")
|
|
77
|
+
print(f"Kundennummer: {contact.customerNumber}")
|
|
78
|
+
print(f"Status: {contact.status}")
|
|
79
|
+
print("-" * 40)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Neuen Kontakt erstellen
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from sevdesk.models.contact import Contact
|
|
86
|
+
from sevdesk.converters.category import Category
|
|
87
|
+
|
|
88
|
+
# Organisation erstellen
|
|
89
|
+
company = Contact(
|
|
90
|
+
name="Acme Corporation GmbH",
|
|
91
|
+
category=Category(id_=3, objectName="Category")
|
|
92
|
+
)
|
|
93
|
+
new_company = client.contact.createContact(body=company)
|
|
94
|
+
print(f"Organisation erstellt mit ID: {new_company.id_}")
|
|
95
|
+
|
|
96
|
+
# Person erstellen
|
|
97
|
+
person = Contact(
|
|
98
|
+
surename="Max",
|
|
99
|
+
familyname="Mustermann",
|
|
100
|
+
gender="m",
|
|
101
|
+
category=Category(id_=3, objectName="Category")
|
|
102
|
+
)
|
|
103
|
+
new_person = client.contact.createContact(body=person)
|
|
104
|
+
print(f"Person erstellt mit ID: {new_person.id_}")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Rechnungen abrufen
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
# Alle Rechnungen abrufen
|
|
111
|
+
invoices = client.invoice.getInvoices()
|
|
112
|
+
|
|
113
|
+
for invoice in invoices:
|
|
114
|
+
print(f"Rechnungsnummer: {invoice.invoiceNumber}")
|
|
115
|
+
print(f"Datum: {invoice.invoiceDate}")
|
|
116
|
+
print(f"Betrag: {invoice.sumGross} {invoice.currency}")
|
|
117
|
+
print(f"Status: {invoice.status}")
|
|
118
|
+
print("-" * 40)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Angebote abrufen
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
# Alle Angebote abrufen
|
|
125
|
+
orders = client.order.getOrders()
|
|
126
|
+
|
|
127
|
+
for order in orders:
|
|
128
|
+
print(f"Angebotsnummer: {order.orderNumber}")
|
|
129
|
+
print(f"Datum: {order.orderDate}")
|
|
130
|
+
print(f"Betrag: {order.sumGross} {order.currency}")
|
|
131
|
+
print(f"Status: {order.status}")
|
|
132
|
+
print("-" * 40)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Kontakt aktualisieren
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from sevdesk.models.contactupdate import ContactUpdate
|
|
139
|
+
|
|
140
|
+
# Kontakt aktualisieren
|
|
141
|
+
update_data = ContactUpdate(
|
|
142
|
+
name="Neue Firma GmbH"
|
|
143
|
+
)
|
|
144
|
+
updated = client.contact.updateContact(contactId=123456, body=update_data)
|
|
145
|
+
print(f"Kontakt aktualisiert: {updated.name}")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Kontakt löschen
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Kontakt löschen
|
|
152
|
+
client.contact.deleteContact(contactId=123456)
|
|
153
|
+
print("Kontakt gelöscht")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Verfügbare Controller
|
|
157
|
+
|
|
158
|
+
Der Client lädt automatisch alle verfügbaren Controller aus der OpenAPI-Spezifikation:
|
|
159
|
+
|
|
160
|
+
- `client.contact` - Kontaktverwaltung
|
|
161
|
+
- `client.invoice` - Rechnungsverwaltung
|
|
162
|
+
- `client.order` - Angebotsverwaltung
|
|
163
|
+
- `client.voucher` - Belegverwaltung
|
|
164
|
+
- `client.part` - Artikelverwaltung
|
|
165
|
+
- ... und viele mehr
|
|
166
|
+
|
|
167
|
+
Alle Endpoints sind als Methoden verfügbar und vollständig typisiert für IDE-Unterstützung.
|
|
168
|
+
|
|
169
|
+
## Code neu generieren
|
|
170
|
+
|
|
171
|
+
Falls sich die sevDesk API ändert:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Neue openapi.yaml herunterladen
|
|
175
|
+
# Dann Generator ausführen:
|
|
176
|
+
python -m generator
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Dies generiert automatisch:
|
|
180
|
+
- Models in `sevdeskapi/models/`
|
|
181
|
+
- Converter in `sevdeskapi/converters/`
|
|
182
|
+
- Controller in `sevdeskapi/controllers/`
|
|
183
|
+
|
|
184
|
+
Das wird zeitnah über Github Actions abgebildet!
|
|
185
|
+
|
|
186
|
+
## Lizenz / Haftungsausschluss
|
|
187
|
+
|
|
188
|
+
MIT License - Siehe LICENSE Datei
|
|
189
|
+
|
sevdesk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# sevDesk API Client (Inoffiziell)
|
|
2
|
+
|
|
3
|
+
Ein Python-Client für die sevDesk API, automatisch generiert aus der OpenAPI-Spezifikation. Dies ist notwendig, da sich die sevdesk-Spezifikation mit dem Standard OpenApi-Generator nicht parsen lässt.
|
|
4
|
+
|
|
5
|
+
## ⚠️ Wichtige Hinweise
|
|
6
|
+
|
|
7
|
+
- **Dies ist KEINE offizielle sevDesk Library**
|
|
8
|
+
- Ich arbeite nicht für sevDesk und bin nicht mit sevDesk verbunden
|
|
9
|
+
- **Nutzung auf eigene Gefahr**
|
|
10
|
+
- Keine Garantie für Funktionalität oder Vollständigkeit
|
|
11
|
+
- Bei Problemen mit Markenrechten oder dem Library-Namen kontaktieren Sie mich bitte
|
|
12
|
+
- Falls sich offizielle Vertreter von sevDesk am Namen der Library stören, bin ich bereit, diesen zu übertragen oder die Library unter einem anderen Namen zu veröffentlichen
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install sevdesk
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Anforderungen
|
|
21
|
+
|
|
22
|
+
- Python 3.8+
|
|
23
|
+
- pydantic
|
|
24
|
+
- requests
|
|
25
|
+
- pyyaml
|
|
26
|
+
- jinja2
|
|
27
|
+
|
|
28
|
+
## Verwendung
|
|
29
|
+
|
|
30
|
+
### Client initialisieren
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from sevdesk import Client
|
|
34
|
+
|
|
35
|
+
# API Token von sevDesk Dashboard holen
|
|
36
|
+
client = Client('your-api-token-here')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Kontakte abrufen
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# Alle Kontakte abrufen
|
|
43
|
+
contacts = client.contact.getContacts()
|
|
44
|
+
|
|
45
|
+
for contact in contacts:
|
|
46
|
+
print(f"ID: {contact.id_}")
|
|
47
|
+
print(f"Name: {contact.name or f'{contact.surename} {contact.familyname}'}")
|
|
48
|
+
print(f"Kundennummer: {contact.customerNumber}")
|
|
49
|
+
print(f"Status: {contact.status}")
|
|
50
|
+
print("-" * 40)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Neuen Kontakt erstellen
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from sevdesk.models.contact import Contact
|
|
57
|
+
from sevdesk.converters.category import Category
|
|
58
|
+
|
|
59
|
+
# Organisation erstellen
|
|
60
|
+
company = Contact(
|
|
61
|
+
name="Acme Corporation GmbH",
|
|
62
|
+
category=Category(id_=3, objectName="Category")
|
|
63
|
+
)
|
|
64
|
+
new_company = client.contact.createContact(body=company)
|
|
65
|
+
print(f"Organisation erstellt mit ID: {new_company.id_}")
|
|
66
|
+
|
|
67
|
+
# Person erstellen
|
|
68
|
+
person = Contact(
|
|
69
|
+
surename="Max",
|
|
70
|
+
familyname="Mustermann",
|
|
71
|
+
gender="m",
|
|
72
|
+
category=Category(id_=3, objectName="Category")
|
|
73
|
+
)
|
|
74
|
+
new_person = client.contact.createContact(body=person)
|
|
75
|
+
print(f"Person erstellt mit ID: {new_person.id_}")
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Rechnungen abrufen
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# Alle Rechnungen abrufen
|
|
82
|
+
invoices = client.invoice.getInvoices()
|
|
83
|
+
|
|
84
|
+
for invoice in invoices:
|
|
85
|
+
print(f"Rechnungsnummer: {invoice.invoiceNumber}")
|
|
86
|
+
print(f"Datum: {invoice.invoiceDate}")
|
|
87
|
+
print(f"Betrag: {invoice.sumGross} {invoice.currency}")
|
|
88
|
+
print(f"Status: {invoice.status}")
|
|
89
|
+
print("-" * 40)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Angebote abrufen
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
# Alle Angebote abrufen
|
|
96
|
+
orders = client.order.getOrders()
|
|
97
|
+
|
|
98
|
+
for order in orders:
|
|
99
|
+
print(f"Angebotsnummer: {order.orderNumber}")
|
|
100
|
+
print(f"Datum: {order.orderDate}")
|
|
101
|
+
print(f"Betrag: {order.sumGross} {order.currency}")
|
|
102
|
+
print(f"Status: {order.status}")
|
|
103
|
+
print("-" * 40)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Kontakt aktualisieren
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from sevdesk.models.contactupdate import ContactUpdate
|
|
110
|
+
|
|
111
|
+
# Kontakt aktualisieren
|
|
112
|
+
update_data = ContactUpdate(
|
|
113
|
+
name="Neue Firma GmbH"
|
|
114
|
+
)
|
|
115
|
+
updated = client.contact.updateContact(contactId=123456, body=update_data)
|
|
116
|
+
print(f"Kontakt aktualisiert: {updated.name}")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Kontakt löschen
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# Kontakt löschen
|
|
123
|
+
client.contact.deleteContact(contactId=123456)
|
|
124
|
+
print("Kontakt gelöscht")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Verfügbare Controller
|
|
128
|
+
|
|
129
|
+
Der Client lädt automatisch alle verfügbaren Controller aus der OpenAPI-Spezifikation:
|
|
130
|
+
|
|
131
|
+
- `client.contact` - Kontaktverwaltung
|
|
132
|
+
- `client.invoice` - Rechnungsverwaltung
|
|
133
|
+
- `client.order` - Angebotsverwaltung
|
|
134
|
+
- `client.voucher` - Belegverwaltung
|
|
135
|
+
- `client.part` - Artikelverwaltung
|
|
136
|
+
- ... und viele mehr
|
|
137
|
+
|
|
138
|
+
Alle Endpoints sind als Methoden verfügbar und vollständig typisiert für IDE-Unterstützung.
|
|
139
|
+
|
|
140
|
+
## Code neu generieren
|
|
141
|
+
|
|
142
|
+
Falls sich die sevDesk API ändert:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Neue openapi.yaml herunterladen
|
|
146
|
+
# Dann Generator ausführen:
|
|
147
|
+
python -m generator
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Dies generiert automatisch:
|
|
151
|
+
- Models in `sevdeskapi/models/`
|
|
152
|
+
- Converter in `sevdeskapi/converters/`
|
|
153
|
+
- Controller in `sevdeskapi/controllers/`
|
|
154
|
+
|
|
155
|
+
Das wird zeitnah über Github Actions abgebildet!
|
|
156
|
+
|
|
157
|
+
## Lizenz / Haftungsausschluss
|
|
158
|
+
|
|
159
|
+
MIT License - Siehe LICENSE Datei
|
|
160
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sevdesk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "A Python client for the sevDesk API, automatically generated from the OpenAPI specification"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Maximilian Clemens", email = "maximilian.clemens@gamma.red" },
|
|
14
|
+
{ name = "Claude" },
|
|
15
|
+
{ name = "ChatGPT" },
|
|
16
|
+
{ name = "Copilot" },
|
|
17
|
+
]
|
|
18
|
+
keywords = ["sevdesk", "api", "client", "accounting"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.8",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
]
|
|
30
|
+
dependencies = [
|
|
31
|
+
"pydantic",
|
|
32
|
+
"requests",
|
|
33
|
+
"PyYAML",
|
|
34
|
+
"jinja2",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/MaximilianClemens/python-sevdesk"
|
|
39
|
+
Repository = "https://github.com/MaximilianClemens/python-sevdesk"
|
|
40
|
+
Issues = "https://github.com/MaximilianClemens/python-sevdesk/issues"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.dynamic]
|
|
43
|
+
version = {attr = "sevdesk.VERSION"}
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
where = ["."]
|
|
47
|
+
include = ["sevdesk*"]
|
sevdesk-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from typing import get_type_hints, get_origin, get_args
|
|
3
|
+
|
|
4
|
+
class BaseController:
|
|
5
|
+
def __init__(self, client):
|
|
6
|
+
self.client = client
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
def parse_response(response, return_type):
|
|
10
|
+
"""Wandelt die Response in das entsprechende Model um"""
|
|
11
|
+
if return_type is None:
|
|
12
|
+
return response
|
|
13
|
+
|
|
14
|
+
# Hole den ursprünglichen Typ (ohne list[] wrapper)
|
|
15
|
+
origin = get_origin(return_type)
|
|
16
|
+
|
|
17
|
+
# Wenn es eine Liste ist
|
|
18
|
+
if origin is list:
|
|
19
|
+
args = get_args(return_type)
|
|
20
|
+
if args:
|
|
21
|
+
model_class = args[0]
|
|
22
|
+
# Response sollte eine Liste von Dicts sein
|
|
23
|
+
if isinstance(response, dict) and 'objects' in response:
|
|
24
|
+
# sevDesk API struktur: {"objects": [...]}
|
|
25
|
+
return [model_class(**item) if isinstance(item, dict) else item
|
|
26
|
+
for item in response['objects']]
|
|
27
|
+
elif isinstance(response, list):
|
|
28
|
+
return [model_class(**item) if isinstance(item, dict) else item
|
|
29
|
+
for item in response]
|
|
30
|
+
|
|
31
|
+
# Einzelnes Model
|
|
32
|
+
elif hasattr(return_type, '__bases__'): # Check if it's a class
|
|
33
|
+
if isinstance(response, dict):
|
|
34
|
+
# sevDesk API struktur kann auch {"objects": {...}} sein
|
|
35
|
+
if 'objects' in response and isinstance(response['objects'], dict):
|
|
36
|
+
return return_type(**response['objects'])
|
|
37
|
+
else:
|
|
38
|
+
return return_type(**response)
|
|
39
|
+
|
|
40
|
+
return response
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def request(method, path):
|
|
44
|
+
def decorator(func):
|
|
45
|
+
def wrapper(self, *args, **kwargs):
|
|
46
|
+
# Args in kwargs umwandeln
|
|
47
|
+
sig = inspect.signature(func)
|
|
48
|
+
bound_args = sig.bind(self, *args, **kwargs)
|
|
49
|
+
bound_args.apply_defaults()
|
|
50
|
+
|
|
51
|
+
# Entferne 'self' aus den arguments
|
|
52
|
+
all_kwargs = dict(bound_args.arguments)
|
|
53
|
+
all_kwargs.pop('self', None)
|
|
54
|
+
|
|
55
|
+
gen = func(self, **all_kwargs)
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
# Pre-request: bis zum yield ausführen
|
|
59
|
+
next(gen)
|
|
60
|
+
# veränderte parameter übernehmen
|
|
61
|
+
for arg in all_kwargs.copy():
|
|
62
|
+
# gi_frame HACK
|
|
63
|
+
if gen.gi_frame:
|
|
64
|
+
all_kwargs[arg] = gen.gi_frame.f_locals[arg]
|
|
65
|
+
|
|
66
|
+
response = self.client.request(method, path, all_kwargs)
|
|
67
|
+
|
|
68
|
+
# Return Type aus der Funktion auslesen
|
|
69
|
+
try:
|
|
70
|
+
type_hints = get_type_hints(func)
|
|
71
|
+
return_type = type_hints.get('return', None)
|
|
72
|
+
except Exception:
|
|
73
|
+
return_type = None
|
|
74
|
+
|
|
75
|
+
# Response in Model umwandeln
|
|
76
|
+
parsed_response = BaseController.parse_response(response, return_type)
|
|
77
|
+
|
|
78
|
+
# Gebe das Ergebnis direkt zurück
|
|
79
|
+
return parsed_response
|
|
80
|
+
|
|
81
|
+
except StopIteration:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
wrapper.__name__ = func.__name__
|
|
85
|
+
return wrapper
|
|
86
|
+
return decorator
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def get(path):
|
|
90
|
+
return BaseController.request('get', path)
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def post(path):
|
|
94
|
+
return BaseController.request('post', path)
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def put(path):
|
|
98
|
+
return BaseController.request('put', path)
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def delete(path):
|
|
102
|
+
return BaseController.request('delete', path)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import re
|
|
3
|
+
import importlib
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
class Client:
|
|
7
|
+
|
|
8
|
+
def __init__(self, api_token, api_base='https://my.sevdesk.de/api/v1', session=None):
|
|
9
|
+
self.api_token = api_token
|
|
10
|
+
self.api_base = api_base
|
|
11
|
+
self.session = session
|
|
12
|
+
if not self.session:
|
|
13
|
+
self.session = requests.Session()
|
|
14
|
+
|
|
15
|
+
# Automatisch alle Controller laden
|
|
16
|
+
self._load_controllers()
|
|
17
|
+
|
|
18
|
+
def _load_controllers(self):
|
|
19
|
+
"""Lädt automatisch alle Controller aus dem controllers Verzeichnis"""
|
|
20
|
+
controllers_dir = Path(__file__).parent / "controllers"
|
|
21
|
+
|
|
22
|
+
if not controllers_dir.exists():
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
for controller_file in controllers_dir.glob("*_controller.py"):
|
|
26
|
+
# z.B. "contact_controller.py" -> "contact"
|
|
27
|
+
controller_name = controller_file.stem.replace("_controller", "")
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Dynamisch importieren
|
|
31
|
+
module = importlib.import_module(f"sevdesk.controllers.{controller_file.stem}")
|
|
32
|
+
|
|
33
|
+
# Finde die Controller-Klasse im Modul (endet mit "Controller")
|
|
34
|
+
controller_class = None
|
|
35
|
+
for attr_name in dir(module):
|
|
36
|
+
attr = getattr(module, attr_name)
|
|
37
|
+
if (isinstance(attr, type) and
|
|
38
|
+
attr_name.endswith("Controller") and
|
|
39
|
+
attr_name != "BaseController"):
|
|
40
|
+
controller_class = attr
|
|
41
|
+
break
|
|
42
|
+
|
|
43
|
+
if controller_class:
|
|
44
|
+
# Als Attribut setzen: self.contact = ContactController(self)
|
|
45
|
+
setattr(self, controller_name, controller_class(self))
|
|
46
|
+
else:
|
|
47
|
+
print(f"Warning: No controller class found in {controller_file.stem}")
|
|
48
|
+
|
|
49
|
+
except (ImportError, AttributeError) as e:
|
|
50
|
+
print(f"Warning: Could not load controller {controller_name}: {e}")
|
|
51
|
+
|
|
52
|
+
def request(self, method, path, params):
|
|
53
|
+
url_params = re.findall(r"{(\w+)}", path)
|
|
54
|
+
request_path = path.format(**params)
|
|
55
|
+
|
|
56
|
+
request_params = {
|
|
57
|
+
k: v for k, v in params.items()
|
|
58
|
+
if k not in url_params and
|
|
59
|
+
k != 'body' and
|
|
60
|
+
v} # v not None
|
|
61
|
+
request_url = f'{self.api_base}{request_path}'
|
|
62
|
+
request_body = params.get('body', None)
|
|
63
|
+
if request_body:
|
|
64
|
+
request_body = request_body.model_dump(by_alias=True, exclude_none=True)
|
|
65
|
+
|
|
66
|
+
# print('request_params', request_params)
|
|
67
|
+
# print('request_url', request_url)
|
|
68
|
+
# print('request_body', request_body)
|
|
69
|
+
|
|
70
|
+
response = self.session.request(
|
|
71
|
+
method=method,
|
|
72
|
+
url=request_url,
|
|
73
|
+
params=request_params,
|
|
74
|
+
json=request_body,
|
|
75
|
+
headers={
|
|
76
|
+
'Authorization': self.api_token
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
# print(response.status_code)
|
|
80
|
+
# print(response.text)
|
|
81
|
+
return response.json()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from sevdesk.base.basecontroller import BaseController
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from sevdesk.models.accountingcontact import AccountingContact
|
|
4
|
+
from sevdesk.models.accountingcontactresponse import AccountingContactResponse
|
|
5
|
+
from sevdesk.models.accountingcontactupdate import AccountingContactUpdate
|
|
6
|
+
|
|
7
|
+
class AccountingContactController(BaseController):
|
|
8
|
+
|
|
9
|
+
@BaseController.get("/AccountingContact")
|
|
10
|
+
def getAccountingContact(self, contact_id: Optional[str] = None, contact_objectName: Optional[str] = None) -> list[AccountingContactResponse]:
|
|
11
|
+
"""Retrieve accounting contact"""
|
|
12
|
+
return (yield)
|
|
13
|
+
|
|
14
|
+
@BaseController.post("/AccountingContact")
|
|
15
|
+
def createAccountingContact(self, body: AccountingContact) -> AccountingContactResponse:
|
|
16
|
+
"""Create a new accounting contact"""
|
|
17
|
+
return (yield)
|
|
18
|
+
|
|
19
|
+
@BaseController.get("/AccountingContact/{accountingContactId}")
|
|
20
|
+
def getAccountingContactById(self, accountingContactId: int) -> list[AccountingContactResponse]:
|
|
21
|
+
"""Find accounting contact by ID"""
|
|
22
|
+
return (yield)
|
|
23
|
+
|
|
24
|
+
@BaseController.put("/AccountingContact/{accountingContactId}")
|
|
25
|
+
def updateAccountingContact(self, accountingContactId: int, body: AccountingContactUpdate) -> AccountingContactResponse:
|
|
26
|
+
"""Update an existing accounting contact"""
|
|
27
|
+
return (yield)
|
|
28
|
+
|
|
29
|
+
@BaseController.delete("/AccountingContact/{accountingContactId}")
|
|
30
|
+
def deleteAccountingContact(self, accountingContactId: int):
|
|
31
|
+
"""Deletes an accounting contact"""
|
|
32
|
+
return (yield)
|
|
33
|
+
|