python-easyverein 2.1.2__tar.gz → 2.3.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.
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/PKG-INFO +21 -6
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/README.md +6 -3
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/__init__.py +1 -1
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/api.py +2 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/client.py +32 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/types.py +3 -1
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/__init__.py +8 -1
- python_easyverein-2.3.0/easyverein/models/billing_account.py +65 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/booking.py +6 -4
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/contact_details.py +1 -1
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/custom_field.py +6 -13
- python_easyverein-2.3.0/easyverein/models/custom_field_select_option.py +60 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/invoice.py +2 -2
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/invoice_item.py +4 -3
- python_easyverein-2.3.0/easyverein/models/lsb_dosb_sport.py +12 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/member.py +43 -16
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/member_custom_field.py +3 -1
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/member_group.py +6 -1
- python_easyverein-2.3.0/easyverein/modules/billing_account.py +27 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/booking.py +2 -1
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/contact_details.py +2 -1
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/custom_field.py +4 -0
- python_easyverein-2.3.0/easyverein/modules/custom_field_select_option.py +31 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/invoice.py +2 -1
- python_easyverein-2.3.0/easyverein/modules/member.py +49 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/member_custom_field.py +31 -4
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/mixins/crud.py +68 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/pyproject.toml +14 -12
- python_easyverein-2.1.2/easyverein/modules/member.py +0 -27
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/LICENSE +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/__init__.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/exceptions.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/protocol.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/responses.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/core/validators.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/base.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/member_member_group.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/mixins/__init__.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/mixins/empty_strings_mixin.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/mixins/required_attributes.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/__init__.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/invoice_item.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/member_group.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/member_member_group.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/mixins/__init__.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/mixins/helper.py +0 -0
- {python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/mixins/recycle_bin.py +0 -0
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-easyverein
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Python library to interact with the EasyVerein API
|
|
5
|
+
License: Copyright 2023 Daniel Herrmann
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
+
|
|
9
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
12
|
+
License-File: LICENSE
|
|
5
13
|
Author: Daniel Herrmann
|
|
6
14
|
Author-email: daniel.herrmann1@gmail.com
|
|
7
|
-
Requires-Python: >=3.11,<4
|
|
15
|
+
Requires-Python: >=3.11,<4
|
|
16
|
+
Classifier: License :: Other/Proprietary License
|
|
8
17
|
Classifier: Programming Language :: Python :: 3
|
|
9
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
22
|
Requires-Dist: email-validator (>=2,<3)
|
|
11
23
|
Requires-Dist: pydantic (>=2,<3)
|
|
12
24
|
Requires-Dist: requests (>=2,<3)
|
|
@@ -44,11 +56,14 @@ Not all endpoints offered by the EasyVerein API are supported. For now, only the
|
|
|
44
56
|
When saying CRUD, it means the library supports various methods to **C**reate, **R**ead, **U**pdate and
|
|
45
57
|
**D**elete objects. See the API reference for details on supported CRUD operations.
|
|
46
58
|
|
|
47
|
-
* `
|
|
59
|
+
* `booking`: CRUD, Bulk Create and Update, Soft-Delete
|
|
60
|
+
* `billing-account`: CRUD, Soft-Delete
|
|
61
|
+
* `contact-details`: CRUD, Bulk Create and Update, Soft-Delete
|
|
48
62
|
* `custom-fields`: CRUD, Soft-Delete
|
|
49
|
-
* `
|
|
63
|
+
* `custom-fields/<id>/select-options`: CRUD
|
|
64
|
+
* `invoice`: CRUD, Bulk Create and Update, Soft-Delete, plus some convenience methods
|
|
50
65
|
* `invoice-item`: CRUD
|
|
51
|
-
* `member`: CRUD, Soft-Delete
|
|
66
|
+
* `member`: CRUD, Bulk Create and Update, Soft-Delete
|
|
52
67
|
* `member-groups`: CRUD, Soft-Delete
|
|
53
68
|
* `member/<id>/custom-fields`: CRUD, plus some convenience methods
|
|
54
69
|
* `member/<id>/member-groups`: CRUD, plus some convenience methods
|
|
@@ -30,11 +30,14 @@ Not all endpoints offered by the EasyVerein API are supported. For now, only the
|
|
|
30
30
|
When saying CRUD, it means the library supports various methods to **C**reate, **R**ead, **U**pdate and
|
|
31
31
|
**D**elete objects. See the API reference for details on supported CRUD operations.
|
|
32
32
|
|
|
33
|
-
* `
|
|
33
|
+
* `booking`: CRUD, Bulk Create and Update, Soft-Delete
|
|
34
|
+
* `billing-account`: CRUD, Soft-Delete
|
|
35
|
+
* `contact-details`: CRUD, Bulk Create and Update, Soft-Delete
|
|
34
36
|
* `custom-fields`: CRUD, Soft-Delete
|
|
35
|
-
* `
|
|
37
|
+
* `custom-fields/<id>/select-options`: CRUD
|
|
38
|
+
* `invoice`: CRUD, Bulk Create and Update, Soft-Delete, plus some convenience methods
|
|
36
39
|
* `invoice-item`: CRUD
|
|
37
|
-
* `member`: CRUD, Soft-Delete
|
|
40
|
+
* `member`: CRUD, Bulk Create and Update, Soft-Delete
|
|
38
41
|
* `member-groups`: CRUD, Soft-Delete
|
|
39
42
|
* `member/<id>/custom-fields`: CRUD, plus some convenience methods
|
|
40
43
|
* `member/<id>/member-groups`: CRUD, plus some convenience methods
|
|
@@ -7,6 +7,7 @@ from typing import Callable, cast
|
|
|
7
7
|
|
|
8
8
|
from .core.client import EasyvereinClient
|
|
9
9
|
from .core.responses import BearerToken
|
|
10
|
+
from .modules.billing_account import BillingAccountMixin
|
|
10
11
|
from .modules.booking import BookingMixin
|
|
11
12
|
from .modules.contact_details import ContactDetailsMixin
|
|
12
13
|
from .modules.custom_field import CustomFieldMixin
|
|
@@ -59,6 +60,7 @@ class EasyvereinAPI:
|
|
|
59
60
|
|
|
60
61
|
# Add methods
|
|
61
62
|
self.booking = BookingMixin(self.c, self.logger)
|
|
63
|
+
self.billing_account = BillingAccountMixin(self.c, self.logger)
|
|
62
64
|
self.contact_details = ContactDetailsMixin(self.c, self.logger)
|
|
63
65
|
self.custom_field = CustomFieldMixin(self.c, self.logger)
|
|
64
66
|
self.invoice = InvoiceMixin(self.c, self.logger)
|
|
@@ -204,6 +204,38 @@ class EasyvereinClient:
|
|
|
204
204
|
expected_status_code=status_code,
|
|
205
205
|
)
|
|
206
206
|
|
|
207
|
+
def bulk_create(self, url, data: list[BaseModel], status_code: int = 201) -> ResponseSchema:
|
|
208
|
+
"""
|
|
209
|
+
Method to create multiple objects in the API
|
|
210
|
+
"""
|
|
211
|
+
return self._handle_response(
|
|
212
|
+
self._do_request(
|
|
213
|
+
"post",
|
|
214
|
+
url,
|
|
215
|
+
data={"entries": [d.model_dump(exclude_none=True, exclude_unset=True, by_alias=True) for d in data]},
|
|
216
|
+
),
|
|
217
|
+
expected_status_code=status_code,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def bulk_update(
|
|
221
|
+
self, url, data: list[BaseModel], status_code: int = 200, exclude_none: bool = True
|
|
222
|
+
) -> ResponseSchema:
|
|
223
|
+
"""
|
|
224
|
+
Method to update multiple objects in the API
|
|
225
|
+
"""
|
|
226
|
+
return self._handle_response(
|
|
227
|
+
self._do_request(
|
|
228
|
+
"patch",
|
|
229
|
+
url,
|
|
230
|
+
data={
|
|
231
|
+
"entries": [
|
|
232
|
+
d.model_dump(exclude_none=exclude_none, exclude_unset=True, by_alias=True) for d in data
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
),
|
|
236
|
+
expected_status_code=status_code,
|
|
237
|
+
)
|
|
238
|
+
|
|
207
239
|
def upload(
|
|
208
240
|
self,
|
|
209
241
|
url: str,
|
|
@@ -4,7 +4,7 @@ Custom types used for model validation
|
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
6
6
|
import json
|
|
7
|
-
from typing import Annotated
|
|
7
|
+
from typing import Annotated, Literal
|
|
8
8
|
|
|
9
9
|
from pydantic import Field, PlainSerializer, PlainValidator, UrlConstraints
|
|
10
10
|
from pydantic_core import Url
|
|
@@ -40,3 +40,5 @@ FilterStrList = Annotated[
|
|
|
40
40
|
list[str],
|
|
41
41
|
PlainSerializer(lambda x: ",".join(x), return_type=str),
|
|
42
42
|
]
|
|
43
|
+
|
|
44
|
+
Sphere = Literal[1, 2, 3, 4, 9]
|
|
@@ -12,6 +12,12 @@ from .custom_field import (
|
|
|
12
12
|
CustomFieldFilter,
|
|
13
13
|
CustomFieldUpdate,
|
|
14
14
|
)
|
|
15
|
+
from .custom_field_select_option import (
|
|
16
|
+
CustomFieldSelectOption,
|
|
17
|
+
CustomFieldSelectOptionCreate,
|
|
18
|
+
CustomFieldSelectOptionFilter,
|
|
19
|
+
CustomFieldSelectOptionUpdate,
|
|
20
|
+
)
|
|
15
21
|
from .invoice import Invoice, InvoiceCreate, InvoiceFilter, InvoiceUpdate
|
|
16
22
|
from .invoice_item import (
|
|
17
23
|
InvoiceItem,
|
|
@@ -19,7 +25,7 @@ from .invoice_item import (
|
|
|
19
25
|
InvoiceItemFilter,
|
|
20
26
|
InvoiceItemUpdate,
|
|
21
27
|
)
|
|
22
|
-
from .member import Member, MemberCreate, MemberFilter, MemberUpdate
|
|
28
|
+
from .member import Member, MemberCreate, MemberFilter, MemberSetDosb, MemberSetLsb, MemberUpdate
|
|
23
29
|
from .member_custom_field import (
|
|
24
30
|
MemberCustomField,
|
|
25
31
|
MemberCustomFieldCreate,
|
|
@@ -37,6 +43,7 @@ from .member_member_group import (
|
|
|
37
43
|
Booking.model_rebuild()
|
|
38
44
|
ContactDetails.model_rebuild()
|
|
39
45
|
CustomField.model_rebuild()
|
|
46
|
+
CustomFieldSelectOption.model_rebuild()
|
|
40
47
|
Invoice.model_rebuild()
|
|
41
48
|
InvoiceItem.model_rebuild()
|
|
42
49
|
Member.model_rebuild()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, PositiveInt
|
|
4
|
+
|
|
5
|
+
from ..core.types import EasyVereinReference, FilterIntList, FilterStrList, PositiveIntWithZero, Sphere
|
|
6
|
+
from .base import EasyVereinBase
|
|
7
|
+
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
8
|
+
from .mixins.required_attributes import required_mixin
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BillingAccountBase(EasyVereinBase):
|
|
12
|
+
"""
|
|
13
|
+
| Representative Model Class | Update Model Class | Create Model Class |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `BillingAccount` | `BillingAccountUpdate` | `BillingAccountCreate` |
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
name: str | None = None
|
|
19
|
+
excludeInEur: bool | None = None
|
|
20
|
+
number: PositiveInt | None = None
|
|
21
|
+
defaultSphere: Sphere | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BillingAccount(BillingAccountBase, EmptyStringsToNone):
|
|
25
|
+
"""
|
|
26
|
+
Pydantic model representing an BillingAccount
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
skr: str | None = None
|
|
30
|
+
accountingPlans: list[EasyVereinReference] | None = None
|
|
31
|
+
numberLength: PositiveIntWithZero
|
|
32
|
+
linkedBookings: PositiveIntWithZero
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BillingAccountCreate(
|
|
36
|
+
BillingAccountBase,
|
|
37
|
+
required_mixin(["name", "number"]), # type: ignore
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Pydantic model representing a BillingAccount
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BillingAccountUpdate(BillingAccountBase):
|
|
45
|
+
"""
|
|
46
|
+
Pydantic model used to patch an BillingAccount
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BillingAccountFilter(BaseModel):
|
|
51
|
+
"""
|
|
52
|
+
Pydantic model used to filter billing accounts
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
id__in: FilterIntList | None = None
|
|
56
|
+
name: str | None = None
|
|
57
|
+
skr: str | None = None
|
|
58
|
+
skr__in: FilterStrList | None = None
|
|
59
|
+
number__gte: PositiveInt | None = None
|
|
60
|
+
number__lte: PositiveInt | None = None
|
|
61
|
+
deleted: bool | None = None
|
|
62
|
+
accountingPlan__isnull: bool | None = None
|
|
63
|
+
showOwnBillingAccounts: bool | None = None
|
|
64
|
+
ordering: str | None = None
|
|
65
|
+
search: str | None = None
|
|
@@ -6,7 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
|
-
from ..core.types import DateTime, EasyVereinReference, FilterIntList
|
|
9
|
+
from ..core.types import DateTime, EasyVereinReference, FilterIntList, Sphere
|
|
10
10
|
from .base import EasyVereinBase
|
|
11
11
|
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
12
12
|
from .mixins.required_attributes import required_mixin
|
|
@@ -22,8 +22,7 @@ class BookingBase(EasyVereinBase):
|
|
|
22
22
|
amount: float | None = None
|
|
23
23
|
# TODO: Add reference to BankAccount once implemented
|
|
24
24
|
bankAccount: EasyVereinReference | None = None
|
|
25
|
-
|
|
26
|
-
billingAccount: EasyVereinReference | None = None
|
|
25
|
+
billingAccount: BillingAccount | EasyVereinReference | None = None
|
|
27
26
|
description: str | None = None
|
|
28
27
|
date: DateTime | None = None
|
|
29
28
|
receiver: str | None = None
|
|
@@ -34,7 +33,7 @@ class BookingBase(EasyVereinBase):
|
|
|
34
33
|
counterpartBic: str | None = None
|
|
35
34
|
twingleDonation: bool | None = None
|
|
36
35
|
bookingProject: str | None = None
|
|
37
|
-
sphere:
|
|
36
|
+
sphere: Sphere | None = None
|
|
38
37
|
relatedInvoice: list[EasyVereinReference] | None = None
|
|
39
38
|
|
|
40
39
|
|
|
@@ -98,3 +97,6 @@ class BookingFilter(BaseModel):
|
|
|
98
97
|
relatedInvoice__isnull: bool | None = None
|
|
99
98
|
relatedInvoice: int | None = None
|
|
100
99
|
search: str | None = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
from .billing_account import BillingAccount # noqa: E402
|
|
@@ -148,7 +148,7 @@ class ContactDetailsFilter(BaseModel):
|
|
|
148
148
|
|
|
149
149
|
id__in: FilterIntList | None = None
|
|
150
150
|
country: str | None = None
|
|
151
|
-
isCompany: bool = Field(default=None, serialization_alias="_isCompany")
|
|
151
|
+
isCompany: bool | None = Field(default=None, serialization_alias="_isCompany")
|
|
152
152
|
preferredCommunicationWay: str | None = None
|
|
153
153
|
contactDetailsGroups: str | None = None
|
|
154
154
|
contactDetailsGroups__not: str | None = None
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Member related models
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
1
|
from __future__ import annotations
|
|
6
2
|
|
|
7
3
|
from typing import Literal
|
|
8
4
|
|
|
9
5
|
from pydantic import BaseModel, Field
|
|
10
6
|
|
|
11
|
-
from ..core.types import
|
|
12
|
-
EasyVereinReference,
|
|
13
|
-
FilterIntList,
|
|
14
|
-
HexColor,
|
|
15
|
-
OptionsField,
|
|
16
|
-
PositiveIntWithZero,
|
|
17
|
-
)
|
|
7
|
+
from ..core.types import EasyVereinReference, FilterIntList, HexColor, PositiveIntWithZero
|
|
18
8
|
from .base import EasyVereinBase
|
|
19
9
|
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
20
10
|
from .mixins.required_attributes import required_mixin
|
|
@@ -67,7 +57,7 @@ class CustomFieldBase(EasyVereinBase):
|
|
|
67
57
|
It is not even possible to set other fields except the ones mentioned before in the portal,
|
|
68
58
|
so not sure what the other values are meant for.
|
|
69
59
|
"""
|
|
70
|
-
selectOptions:
|
|
60
|
+
selectOptions: list[CustomFieldSelectOption | EasyVereinReference] | None = None
|
|
71
61
|
description: str | None = Field(default=None, max_length=500)
|
|
72
62
|
member_show: bool | None = None
|
|
73
63
|
member_edit: bool | None = None
|
|
@@ -111,6 +101,9 @@ class CustomFieldFilter(BaseModel):
|
|
|
111
101
|
kind: str | None = None
|
|
112
102
|
member_edit: bool | None = None
|
|
113
103
|
member_show: bool | None = None
|
|
114
|
-
deletedBy__isnull: bool = Field(default=None, serialization_alias="_deletedBy__isnull")
|
|
104
|
+
deletedBy__isnull: bool | None = Field(default=None, serialization_alias="_deletedBy__isnull")
|
|
115
105
|
deleted: bool | None = None
|
|
116
106
|
ordering: str | None = None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
from .custom_field_select_option import CustomFieldSelectOption # noqa: E402
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from ..core.types import FilterIntList, FilterStrList, PositiveIntWithZero
|
|
6
|
+
from .base import EasyVereinBase
|
|
7
|
+
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
8
|
+
from .mixins.required_attributes import required_mixin
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CustomFieldSelectOptionBase(EasyVereinBase):
|
|
12
|
+
"""
|
|
13
|
+
| Representative Model Class | Update Model Class | Create Model Class |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `CustomFieldSelectOption` | `CustomFieldSelectOptionUpdate` | `CustomFieldSelectOptionCreate` |
|
|
16
|
+
|
|
17
|
+
Custom field select options are used to define the possible values for a custom field.
|
|
18
|
+
Those are only available for select (s) and multiselect (a) custom fields.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
value: str
|
|
22
|
+
orderSequence: PositiveIntWithZero | None = None
|
|
23
|
+
availableForAssignment: bool | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CustomFieldSelectOption(CustomFieldSelectOptionBase, EmptyStringsToNone):
|
|
27
|
+
"""
|
|
28
|
+
Pydantic model used to represent a custom field select option
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CustomFieldSelectOptionCreate(CustomFieldSelectOptionBase, required_mixin(["value"])): # type: ignore
|
|
35
|
+
"""
|
|
36
|
+
Pydantic model for creating a new custom field select option
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CustomFieldSelectOptionUpdate(CustomFieldSelectOptionBase):
|
|
41
|
+
"""
|
|
42
|
+
Pydantic model used to update a custom field select option
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class CustomFieldSelectOptionFilter(BaseModel):
|
|
49
|
+
"""
|
|
50
|
+
Pydantic model used to filter custom field select options
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
id__in: FilterIntList | None = None
|
|
54
|
+
value: str | None = None
|
|
55
|
+
value__in: FilterStrList | None = None
|
|
56
|
+
maxSelections__isnull: bool | None = None
|
|
57
|
+
maxSelections: PositiveIntWithZero | None = None
|
|
58
|
+
orderSequence__isnull: bool | None = None
|
|
59
|
+
orderSequence: PositiveIntWithZero | None = None
|
|
60
|
+
actuallyRemoved: bool | None = None
|
|
@@ -55,8 +55,7 @@ class InvoiceBase(EasyVereinBase):
|
|
|
55
55
|
]
|
|
56
56
|
| None
|
|
57
57
|
) = None
|
|
58
|
-
|
|
59
|
-
selectionAcc: EasyVereinReference | None = None
|
|
58
|
+
selectionAcc: BillingAccount | EasyVereinReference | None = None
|
|
60
59
|
refNumber: str | None = None
|
|
61
60
|
paymentDifference: float | None = None
|
|
62
61
|
isDraft: bool | None = None
|
|
@@ -148,6 +147,7 @@ class InvoiceFilter(BaseModel):
|
|
|
148
147
|
search: str | None = None
|
|
149
148
|
|
|
150
149
|
|
|
150
|
+
from .billing_account import BillingAccount # noqa: E402
|
|
151
151
|
from .contact_details import ContactDetails # noqa: E402
|
|
152
152
|
from .invoice_item import InvoiceItem # noqa: E402
|
|
153
153
|
from .member import Member # noqa: E402
|
|
@@ -8,7 +8,7 @@ from typing import Annotated
|
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, Field, PositiveInt
|
|
10
10
|
|
|
11
|
-
from ..core.types import EasyVereinReference, FilterIntList
|
|
11
|
+
from ..core.types import EasyVereinReference, FilterIntList, Sphere
|
|
12
12
|
from .base import EasyVereinBase
|
|
13
13
|
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
14
14
|
from .mixins.required_attributes import required_mixin
|
|
@@ -35,8 +35,8 @@ class InvoiceItemBase(EasyVereinBase):
|
|
|
35
35
|
taxRate: float | None = None
|
|
36
36
|
gross: bool | None = None
|
|
37
37
|
taxName: str | None = None
|
|
38
|
-
|
|
39
|
-
billingAccount: EasyVereinReference | None = None
|
|
38
|
+
sphere: Sphere | None = None
|
|
39
|
+
billingAccount: BillingAccount | EasyVereinReference | None = None
|
|
40
40
|
costCentre: Annotated[str, Field(max_length=8)] | None = None
|
|
41
41
|
|
|
42
42
|
|
|
@@ -85,4 +85,5 @@ class InvoiceItemFilter(BaseModel):
|
|
|
85
85
|
search: str | None = None
|
|
86
86
|
|
|
87
87
|
|
|
88
|
+
from .billing_account import BillingAccount # noqa: E402
|
|
88
89
|
from .invoice import Invoice # noqa: E402
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .base import EasyVereinBase
|
|
2
|
+
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LsbDosbSport(EasyVereinBase, EmptyStringsToNone):
|
|
6
|
+
"""
|
|
7
|
+
Pydantic Model for a LSB/DOSB Sport.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
title: str | None = None
|
|
11
|
+
sportNumber: str | None = None
|
|
12
|
+
federationNumber: str | None = None
|
|
@@ -63,7 +63,6 @@ class MemberBase(EasyVereinBase):
|
|
|
63
63
|
paymentIntervallMonths: PositiveInt | Literal[-1] = 1
|
|
64
64
|
useBalanceForMembershipFee: bool | None = None
|
|
65
65
|
bulletinBoardNewPostNotification: bool | None = None
|
|
66
|
-
integrationDosbGender: Literal["m", "w", "d"] | None = None
|
|
67
66
|
isApplication: bool | None = Field(default=None, alias="_isApplication")
|
|
68
67
|
"""
|
|
69
68
|
Alias for `_isApplication` field. See [Pydantic Models](../usage.md#pydantic-models) for details.
|
|
@@ -83,8 +82,10 @@ class MemberBase(EasyVereinBase):
|
|
|
83
82
|
Alias for `_editableByRelatedMembers` field. See [Pydantic Models](../usage.md#pydantic-models) for details.
|
|
84
83
|
"""
|
|
85
84
|
sepaMandateFile: AnyHttpURL | str | None = None
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
integrationLsbSport: list[EasyVereinReference] | list[LsbDosbSport] | None = None
|
|
86
|
+
integrationLsbGender: Literal["m", "w", "d"] | None = None
|
|
87
|
+
integrationDosbSport: list[EasyVereinReference] | list[LsbDosbSport] | None = None
|
|
88
|
+
integrationDosbGender: Literal["m", "w", "d"] | None = None
|
|
88
89
|
customFields: list[EasyVereinReference] | list[MemberCustomField] | None = None
|
|
89
90
|
memberGroups: list[EasyVereinReference] | list[MemberMemberGroup] | None = None
|
|
90
91
|
|
|
@@ -135,9 +136,9 @@ class MemberFilter(BaseModel):
|
|
|
135
136
|
contactDetails__country: str | None = None
|
|
136
137
|
membershipNumber: str | None = None
|
|
137
138
|
membershipNumber__in: FilterStrList | None = None
|
|
138
|
-
deletedBy: int = Field(default=None, serialization_alias="_deletedBy")
|
|
139
|
-
deletedBy__ne: int = Field(default=None, serialization_alias="_deletedBy__ne")
|
|
140
|
-
deletedBy__isnull: bool = Field(default=None, serialization_alias="_deletedBy__isnull")
|
|
139
|
+
deletedBy: int | None = Field(default=None, serialization_alias="_deletedBy")
|
|
140
|
+
deletedBy__ne: int | None = Field(default=None, serialization_alias="_deletedBy__ne")
|
|
141
|
+
deletedBy__isnull: bool | None = Field(default=None, serialization_alias="_deletedBy__isnull")
|
|
141
142
|
joinDate: DateTime | None = None
|
|
142
143
|
joinDate__gte: DateTime | None = None
|
|
143
144
|
joinDate__lte: DateTime | None = None
|
|
@@ -146,18 +147,22 @@ class MemberFilter(BaseModel):
|
|
|
146
147
|
resignationDate__gte: DateTime | None = None
|
|
147
148
|
resignationDate__lte: DateTime | None = None
|
|
148
149
|
resignationDate__isnull: bool | None = None
|
|
149
|
-
isApplication: bool = Field(default=None, serialization_alias="_isApplication")
|
|
150
|
-
applicationDate: Date = Field(default=None, serialization_alias="_applicationDate")
|
|
151
|
-
applicationDate__gte: Date = Field(default=None, serialization_alias="_applicationDate__gte")
|
|
152
|
-
applicationDate__lte: Date = Field(default=None, serialization_alias="_applicationDate__lte")
|
|
153
|
-
applicationDate__isnull: bool = Field(default=None, serialization_alias="_applicationDate__isnull")
|
|
154
|
-
applicationWasAcceptedAt: Date = Field(default=None, serialization_alias="_applicationWasAcceptedAt")
|
|
155
|
-
applicationWasAcceptedAt__gte: Date = Field(
|
|
156
|
-
|
|
157
|
-
|
|
150
|
+
isApplication: bool | None = Field(default=None, serialization_alias="_isApplication")
|
|
151
|
+
applicationDate: Date | None = Field(default=None, serialization_alias="_applicationDate")
|
|
152
|
+
applicationDate__gte: Date | None = Field(default=None, serialization_alias="_applicationDate__gte")
|
|
153
|
+
applicationDate__lte: Date | None = Field(default=None, serialization_alias="_applicationDate__lte")
|
|
154
|
+
applicationDate__isnull: bool | None = Field(default=None, serialization_alias="_applicationDate__isnull")
|
|
155
|
+
applicationWasAcceptedAt: Date | None = Field(default=None, serialization_alias="_applicationWasAcceptedAt")
|
|
156
|
+
applicationWasAcceptedAt__gte: Date | None = Field(
|
|
157
|
+
default=None, serialization_alias="_applicationWasAcceptedAt__gte"
|
|
158
|
+
)
|
|
159
|
+
applicationWasAcceptedAt__lte: Date | None = Field(
|
|
160
|
+
default=None, serialization_alias="_applicationWasAcceptedAt__lte"
|
|
161
|
+
)
|
|
162
|
+
applicationWasAcceptedAt__isnull: bool | None = Field(
|
|
158
163
|
default=None, serialization_alias="_applicationWasAcceptedAt__isnull"
|
|
159
164
|
)
|
|
160
|
-
isChairman: bool = Field(default=None, serialization_alias="_isChairman")
|
|
165
|
+
isChairman: bool | None = Field(default=None, serialization_alias="_isChairman")
|
|
161
166
|
memberGroups: FilterIntList | None = None
|
|
162
167
|
"""
|
|
163
168
|
Filter for members that are member of the given group(s)
|
|
@@ -185,6 +190,28 @@ class MemberFilter(BaseModel):
|
|
|
185
190
|
search: str | None = None
|
|
186
191
|
|
|
187
192
|
|
|
193
|
+
class MemberSetLsb(BaseModel):
|
|
194
|
+
"""
|
|
195
|
+
Pydantic model used to set LSB sports for a member
|
|
196
|
+
|
|
197
|
+
Does not match the documentation, but works (with v2.0 in january 2026 at least)
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
lsbSport: list[str]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class MemberSetDosb(BaseModel):
|
|
204
|
+
"""
|
|
205
|
+
Pydantic model used to set DOSB sports for a member
|
|
206
|
+
|
|
207
|
+
Does not match the documentation, but works (with v2.0 in january 2026 at least)
|
|
208
|
+
And for some reason, has another format than the set-lsb endpoint
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
dosb_sport: list[str]
|
|
212
|
+
|
|
213
|
+
|
|
188
214
|
from .contact_details import ContactDetails # noqa: E402
|
|
215
|
+
from .lsb_dosb_sport import LsbDosbSport # noqa: E402
|
|
189
216
|
from .member_custom_field import MemberCustomField # noqa: E402
|
|
190
217
|
from .member_member_group import MemberMemberGroup # noqa: E402
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/member_custom_field.py
RENAMED
|
@@ -32,6 +32,7 @@ class MemberCustomFieldBase(EasyVereinBase):
|
|
|
32
32
|
customField: EasyVereinReference | CustomField | None = None
|
|
33
33
|
value: str | None = None
|
|
34
34
|
requestedValue: str | None = None # Purpose of this field is not documented
|
|
35
|
+
selectedOptions: list[CustomFieldSelectOption | EasyVereinReference] | None = None
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
class MemberCustomField(MemberCustomFieldBase, EmptyStringsToNone):
|
|
@@ -42,7 +43,7 @@ class MemberCustomField(MemberCustomFieldBase, EmptyStringsToNone):
|
|
|
42
43
|
pass
|
|
43
44
|
|
|
44
45
|
|
|
45
|
-
class MemberCustomFieldCreate(MemberCustomFieldBase, required_mixin(["customField"
|
|
46
|
+
class MemberCustomFieldCreate(MemberCustomFieldBase, required_mixin(["customField"])): # type: ignore
|
|
46
47
|
"""
|
|
47
48
|
Pydantic model for creating a new member
|
|
48
49
|
"""
|
|
@@ -76,4 +77,5 @@ class MemberCustomFieldFilter(BaseModel):
|
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
from .custom_field import CustomField # noqa: E402
|
|
80
|
+
from .custom_field_select_option import CustomFieldSelectOption # noqa: E402
|
|
79
81
|
from .member import Member # noqa: E402
|
|
@@ -13,6 +13,7 @@ from ..core.types import (
|
|
|
13
13
|
FilterIntList,
|
|
14
14
|
HexColor,
|
|
15
15
|
PositiveIntWithZero,
|
|
16
|
+
Sphere,
|
|
16
17
|
)
|
|
17
18
|
from .base import EasyVereinBase
|
|
18
19
|
from .mixins.empty_strings_mixin import EmptyStringsToNone
|
|
@@ -48,7 +49,7 @@ class MemberGroupBase(EasyVereinBase):
|
|
|
48
49
|
agePermission: PositiveIntWithZero | None = None
|
|
49
50
|
nextGroup: EasyVereinReference | None = None
|
|
50
51
|
taxRate: float | None = None
|
|
51
|
-
billingAccount: EasyVereinReference | None = None
|
|
52
|
+
billingAccount: BillingAccount | EasyVereinReference | None = None
|
|
52
53
|
costCentre: str | None = Field(default=None, max_length=8)
|
|
53
54
|
isOnlyVisibleToAdmins: bool | None = None
|
|
54
55
|
user_shares: Literal["n", "a", "d"] | None = None
|
|
@@ -130,6 +131,7 @@ class MemberGroupBase(EasyVereinBase):
|
|
|
130
131
|
- a: allowed
|
|
131
132
|
- d: forbidden
|
|
132
133
|
"""
|
|
134
|
+
sphere: Sphere | None = None
|
|
133
135
|
|
|
134
136
|
|
|
135
137
|
class MemberGroup(MemberGroupBase, EmptyStringsToNone):
|
|
@@ -153,3 +155,6 @@ class MemberGroupFilter(BaseModel):
|
|
|
153
155
|
kind: str | None = None
|
|
154
156
|
deleted: bool | None = None
|
|
155
157
|
ordering: str | None = None
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
from .billing_account import BillingAccount # noqa: E402
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
All methods related to billing accounts
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from ..core.client import EasyvereinClient
|
|
8
|
+
from ..models.billing_account import (
|
|
9
|
+
BillingAccount,
|
|
10
|
+
BillingAccountCreate,
|
|
11
|
+
BillingAccountFilter,
|
|
12
|
+
BillingAccountUpdate,
|
|
13
|
+
)
|
|
14
|
+
from .mixins.crud import CRUDMixin
|
|
15
|
+
from .mixins.recycle_bin import RecycleBinMixin
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BillingAccountMixin(
|
|
19
|
+
CRUDMixin[BillingAccount, BillingAccountCreate, BillingAccountUpdate, BillingAccountFilter],
|
|
20
|
+
RecycleBinMixin[BillingAccount],
|
|
21
|
+
):
|
|
22
|
+
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.endpoint_name = "billing-account"
|
|
25
|
+
self.return_type = BillingAccount
|
|
26
|
+
self.c = client
|
|
27
|
+
self.logger = logger
|
|
@@ -11,12 +11,13 @@ from ..models.booking import (
|
|
|
11
11
|
BookingFilter,
|
|
12
12
|
BookingUpdate,
|
|
13
13
|
)
|
|
14
|
-
from .mixins.crud import CRUDMixin
|
|
14
|
+
from .mixins.crud import BulkUpdateCreateMixin, CRUDMixin
|
|
15
15
|
from .mixins.recycle_bin import RecycleBinMixin
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class BookingMixin(
|
|
19
19
|
CRUDMixin[Booking, BookingCreate, BookingUpdate, BookingFilter],
|
|
20
|
+
BulkUpdateCreateMixin[Booking, BookingCreate, BookingUpdate],
|
|
20
21
|
RecycleBinMixin[Booking],
|
|
21
22
|
):
|
|
22
23
|
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
|
|
@@ -7,12 +7,13 @@ import logging
|
|
|
7
7
|
from ..core.client import EasyvereinClient
|
|
8
8
|
from ..models import ContactDetails, ContactDetailsCreate, ContactDetailsUpdate
|
|
9
9
|
from ..models.contact_details import ContactDetailsFilter
|
|
10
|
-
from .mixins.crud import CRUDMixin
|
|
10
|
+
from .mixins.crud import BulkUpdateCreateMixin, CRUDMixin
|
|
11
11
|
from .mixins.recycle_bin import RecycleBinMixin
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class ContactDetailsMixin(
|
|
15
15
|
CRUDMixin[ContactDetails, ContactDetailsCreate, ContactDetailsUpdate, ContactDetailsFilter],
|
|
16
|
+
BulkUpdateCreateMixin[ContactDetails, ContactDetailsCreate, ContactDetailsUpdate],
|
|
16
17
|
RecycleBinMixin[ContactDetails],
|
|
17
18
|
):
|
|
18
19
|
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
|
|
@@ -11,6 +11,7 @@ from ..models.custom_field import (
|
|
|
11
11
|
CustomFieldFilter,
|
|
12
12
|
CustomFieldUpdate,
|
|
13
13
|
)
|
|
14
|
+
from .custom_field_select_option import CustomFieldSelectOptionMixin
|
|
14
15
|
from .mixins.crud import CRUDMixin
|
|
15
16
|
from .mixins.recycle_bin import RecycleBinMixin
|
|
16
17
|
|
|
@@ -25,3 +26,6 @@ class CustomFieldMixin(
|
|
|
25
26
|
self.return_type = CustomField
|
|
26
27
|
self.c = client
|
|
27
28
|
self.logger = logger
|
|
29
|
+
|
|
30
|
+
def select_option(self, custom_field_id: int) -> CustomFieldSelectOptionMixin:
|
|
31
|
+
return CustomFieldSelectOptionMixin(self.c, self.logger, custom_field_id)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from ..core.client import EasyvereinClient
|
|
4
|
+
from ..models import (
|
|
5
|
+
CustomField,
|
|
6
|
+
CustomFieldSelectOption,
|
|
7
|
+
CustomFieldSelectOptionCreate,
|
|
8
|
+
CustomFieldSelectOptionFilter,
|
|
9
|
+
CustomFieldSelectOptionUpdate,
|
|
10
|
+
)
|
|
11
|
+
from .mixins.crud import CRUDMixin
|
|
12
|
+
from .mixins.helper import get_id
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CustomFieldSelectOptionMixin(
|
|
16
|
+
CRUDMixin[
|
|
17
|
+
CustomFieldSelectOption,
|
|
18
|
+
CustomFieldSelectOptionCreate,
|
|
19
|
+
CustomFieldSelectOptionUpdate,
|
|
20
|
+
CustomFieldSelectOptionFilter,
|
|
21
|
+
]
|
|
22
|
+
):
|
|
23
|
+
def __init__(self, client: EasyvereinClient, logger: logging.Logger, custom_field: CustomField | int):
|
|
24
|
+
self.return_type = CustomFieldSelectOption
|
|
25
|
+
self.c = client
|
|
26
|
+
self.logger = logger
|
|
27
|
+
self.custom_field_id = get_id(custom_field)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def endpoint_name(self) -> str:
|
|
31
|
+
return f"custom-field/{self.custom_field_id}/select-options"
|
|
@@ -11,12 +11,13 @@ from ..core.client import EasyvereinClient
|
|
|
11
11
|
from ..core.exceptions import EasyvereinAPIException
|
|
12
12
|
from ..models.invoice import Invoice, InvoiceCreate, InvoiceFilter, InvoiceUpdate
|
|
13
13
|
from ..models.invoice_item import InvoiceItemCreate
|
|
14
|
-
from .mixins.crud import CRUDMixin
|
|
14
|
+
from .mixins.crud import BulkUpdateCreateMixin, CRUDMixin
|
|
15
15
|
from .mixins.recycle_bin import RecycleBinMixin
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class InvoiceMixin(
|
|
19
19
|
CRUDMixin[Invoice, InvoiceCreate, InvoiceUpdate, InvoiceFilter],
|
|
20
|
+
BulkUpdateCreateMixin[Invoice, InvoiceCreate, InvoiceUpdate],
|
|
20
21
|
RecycleBinMixin[Invoice],
|
|
21
22
|
):
|
|
22
23
|
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
All methods related to invoices
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from easyverein.modules.member_custom_field import MemberCustomFieldMixin
|
|
8
|
+
from easyverein.modules.member_member_group import MemberMemberGroupMixin
|
|
9
|
+
|
|
10
|
+
from ..core.client import EasyvereinClient
|
|
11
|
+
from ..models import Member, MemberCreate, MemberFilter, MemberSetLsb, MemberUpdate
|
|
12
|
+
from ..models.member import MemberSetDosb
|
|
13
|
+
from .mixins.crud import BulkUpdateCreateMixin, CRUDMixin
|
|
14
|
+
from .mixins.helper import get_id
|
|
15
|
+
from .mixins.recycle_bin import RecycleBinMixin
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MemberMixin(
|
|
19
|
+
CRUDMixin[Member, MemberCreate, MemberUpdate, MemberFilter],
|
|
20
|
+
BulkUpdateCreateMixin[Member, MemberCreate, MemberUpdate],
|
|
21
|
+
RecycleBinMixin[Member],
|
|
22
|
+
):
|
|
23
|
+
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
|
|
24
|
+
self.endpoint_name = "member"
|
|
25
|
+
self.c = client
|
|
26
|
+
self.logger = logger
|
|
27
|
+
self.return_type = Member
|
|
28
|
+
|
|
29
|
+
def custom_field(self, member_id: int) -> MemberCustomFieldMixin:
|
|
30
|
+
return MemberCustomFieldMixin(self.c, self.logger, member_id)
|
|
31
|
+
|
|
32
|
+
def member_group(self, member_id: int) -> MemberMemberGroupMixin:
|
|
33
|
+
return MemberMemberGroupMixin(self.c, self.logger, member_id)
|
|
34
|
+
|
|
35
|
+
def set_lsb(self, target: Member | int, data: MemberSetLsb) -> None:
|
|
36
|
+
obj_id = get_id(target)
|
|
37
|
+
|
|
38
|
+
self.logger.info(f"Setting LSB sports for member {obj_id}")
|
|
39
|
+
|
|
40
|
+
url = self.c.get_url(f"/{self.endpoint_name}/{obj_id}/set-lsb")
|
|
41
|
+
self.c.update(url, data, status_code=204)
|
|
42
|
+
|
|
43
|
+
def set_dosb(self, target: Member | int, data: MemberSetDosb) -> None:
|
|
44
|
+
obj_id = get_id(target)
|
|
45
|
+
|
|
46
|
+
self.logger.info(f"Setting DOSB sports for member {obj_id}")
|
|
47
|
+
|
|
48
|
+
url = self.c.get_url(f"/{self.endpoint_name}/{obj_id}/set-dosb")
|
|
49
|
+
self.c.update(url, data, status_code=204)
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/member_custom_field.py
RENAMED
|
@@ -7,6 +7,7 @@ import logging
|
|
|
7
7
|
from ..core.client import EasyvereinClient
|
|
8
8
|
from ..models import (
|
|
9
9
|
CustomField,
|
|
10
|
+
CustomFieldSelectOption,
|
|
10
11
|
Member,
|
|
11
12
|
MemberCustomField,
|
|
12
13
|
MemberCustomFieldCreate,
|
|
@@ -30,17 +31,41 @@ class MemberCustomFieldMixin(
|
|
|
30
31
|
def endpoint_name(self) -> str:
|
|
31
32
|
return f"member/{self.member_id}/custom-fields"
|
|
32
33
|
|
|
33
|
-
def ensure_set(self, custom_field_id: int, value: str) -> MemberCustomField:
|
|
34
|
+
def ensure_set(self, custom_field_id: int, value: str | list[str]) -> MemberCustomField:
|
|
34
35
|
"""
|
|
35
36
|
Convenience method to set the custom field value on a member, no matter if it was
|
|
36
37
|
set before already (PATCH) or not.
|
|
37
38
|
|
|
39
|
+
For select (s) and multiselect (a) custom fields, you can provide a list of values.
|
|
40
|
+
Instead of using the value, the method will automatically fetch and select the
|
|
41
|
+
options from selectOptions that have been introduced in January 2026.
|
|
42
|
+
|
|
38
43
|
Regarding rate limiting, be aware that this method requires at least two API calls.
|
|
39
44
|
|
|
40
45
|
Args:
|
|
41
46
|
custom_field_id (int): Custom field ID that should be set or changed
|
|
42
|
-
value (str): New value the specified custom field should be set to
|
|
47
|
+
value (str | list[str]): New value the specified custom field should be set to
|
|
43
48
|
"""
|
|
49
|
+
custom_field_query = "{id,settings_type,selectOptions{id,value}}"
|
|
50
|
+
custom_field = self.c.api_instance.custom_field.get_by_id(custom_field_id, query=custom_field_query)
|
|
51
|
+
use_selected_options = custom_field.settings_type in ("s", "a")
|
|
52
|
+
|
|
53
|
+
if use_selected_options:
|
|
54
|
+
values_list = [value] if isinstance(value, str) else list(value)
|
|
55
|
+
value_to_id: dict[str, int] = {}
|
|
56
|
+
for opt in custom_field.selectOptions or []:
|
|
57
|
+
if isinstance(opt, CustomFieldSelectOption) and opt.id is not None and opt.value:
|
|
58
|
+
value_to_id[opt.value] = opt.id
|
|
59
|
+
option_ids = [value_to_id[v] for v in values_list if v in value_to_id]
|
|
60
|
+
missing = [v for v in values_list if v not in value_to_id]
|
|
61
|
+
if missing:
|
|
62
|
+
raise ValueError(f"No select option(s) with value(s) {missing!r} for custom field {custom_field_id}")
|
|
63
|
+
payload_value = None
|
|
64
|
+
payload_selected_options: list[int] | None = option_ids
|
|
65
|
+
else:
|
|
66
|
+
assert isinstance(value, str), "Value must be a string for non-select custom fields"
|
|
67
|
+
payload_value = value
|
|
68
|
+
payload_selected_options = None
|
|
44
69
|
|
|
45
70
|
# Get all custom fields this member has already set, use max limit available
|
|
46
71
|
query = "{id,value,customField{id}}"
|
|
@@ -58,10 +83,12 @@ class MemberCustomFieldMixin(
|
|
|
58
83
|
|
|
59
84
|
if existing_custom_field:
|
|
60
85
|
# It's already there, we need to patch the existing field
|
|
61
|
-
patch = MemberCustomFieldUpdate(value=
|
|
86
|
+
patch = MemberCustomFieldUpdate(value=payload_value, selectedOptions=payload_selected_options)
|
|
62
87
|
assert existing_custom_field.id
|
|
63
88
|
return self.update(existing_custom_field.id, patch)
|
|
64
89
|
else:
|
|
65
90
|
# It's not there, we need to create it
|
|
66
|
-
create = MemberCustomFieldCreate(
|
|
91
|
+
create = MemberCustomFieldCreate(
|
|
92
|
+
customField=custom_field_id, value=payload_value, selectedOptions=payload_selected_options
|
|
93
|
+
)
|
|
67
94
|
return self.create(create)
|
|
@@ -194,3 +194,71 @@ class CRUDMixin(Generic[ModelType, CreateModelType, UpdateModelType, FilterType]
|
|
|
194
194
|
self.logger.info(f"Deleting object of type {self.endpoint_name} with id {obj_id} from wastebasket")
|
|
195
195
|
purge: Callable = getattr(self, "purge")
|
|
196
196
|
purge(obj_id)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class BulkUpdateCreateMixin(Generic[ModelType, CreateModelType, UpdateModelType]):
|
|
200
|
+
"""
|
|
201
|
+
Mixin providing bulk create and update functionality for endpoints that support it.
|
|
202
|
+
Currently only supported for the following endpoints:
|
|
203
|
+
- booking
|
|
204
|
+
- contact-details
|
|
205
|
+
- member
|
|
206
|
+
- invoice
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def bulk_create(self: EVClientProtocol[ModelType], data: list[CreateModelType]) -> list[bool]:
|
|
210
|
+
"""
|
|
211
|
+
Creates multiple objects in a single API request and returns the created objects.
|
|
212
|
+
|
|
213
|
+
**Example**:
|
|
214
|
+
|
|
215
|
+
```py
|
|
216
|
+
from easyverein import EasyvereinAPI
|
|
217
|
+
|
|
218
|
+
ev_client = EasyvereinAPI("your_api_key")
|
|
219
|
+
|
|
220
|
+
contact_details = ev_client.contact_details.bulk_create([
|
|
221
|
+
ContactDetailsCreate(fistName="example1", lastName="Example1", isCompany=False),
|
|
222
|
+
ContactDetailsCreate(fistName="example2", lastName="Example2", isCompany=False),
|
|
223
|
+
])
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
data: List of Pydantic models containing the data for the objects to be created.
|
|
228
|
+
"""
|
|
229
|
+
self.logger.info(f"Creating object of type {self.endpoint_name}")
|
|
230
|
+
|
|
231
|
+
url = self.c.get_url(f"/{self.endpoint_name}/bulk-create")
|
|
232
|
+
response = self.c.bulk_create(url, data)
|
|
233
|
+
return [r["data"]["success"] for r in response.result] # type: ignore
|
|
234
|
+
|
|
235
|
+
def bulk_update(
|
|
236
|
+
self: EVClientProtocol[ModelType], data: list[UpdateModelType], exclude_none: bool = True
|
|
237
|
+
) -> list[bool]:
|
|
238
|
+
"""
|
|
239
|
+
Updates multiple objects in a single API request and returns the updated objects.
|
|
240
|
+
|
|
241
|
+
Note that the update models must include the `id` of the objects to be updated.
|
|
242
|
+
|
|
243
|
+
**Example**:
|
|
244
|
+
|
|
245
|
+
```py
|
|
246
|
+
from easyverein import EasyvereinAPI
|
|
247
|
+
|
|
248
|
+
ev_client = EasyvereinAPI("your_api_key")
|
|
249
|
+
|
|
250
|
+
updated_members = ev_client.member.bulk_update([
|
|
251
|
+
MemberUpdate(id=1, membershipNumber="M1"),
|
|
252
|
+
MemberUpdate(id=2, membershipNumber="M2"),
|
|
253
|
+
])
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
data: List of Pydantic models containing the data to update.
|
|
258
|
+
exclude_none: If True, fields with None values will be excluded from the update.
|
|
259
|
+
"""
|
|
260
|
+
self.logger.info(f"Bulk updating objects of type {self.endpoint_name}")
|
|
261
|
+
|
|
262
|
+
url = self.c.get_url(f"/{self.endpoint_name}/bulk-update")
|
|
263
|
+
response = self.c.bulk_update(url, data, exclude_none=exclude_none)
|
|
264
|
+
return [r["data"]["success"] for r in response.result] # type: ignore
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
[
|
|
1
|
+
[project]
|
|
2
2
|
name = "python-easyverein"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.3.0"
|
|
4
4
|
description = "Python library to interact with the EasyVerein API"
|
|
5
|
-
authors = ["Daniel Herrmann
|
|
5
|
+
authors = [{ name = "Daniel Herrmann", email = "daniel.herrmann1@gmail.com" }]
|
|
6
6
|
readme = "README.md"
|
|
7
|
-
|
|
7
|
+
requires-python = ">=3.11,<4"
|
|
8
|
+
license = { file = "LICENSE" }
|
|
9
|
+
dependencies = ["pydantic>=2,<3", "requests>=2,<3", "email-validator>=2,<3"]
|
|
8
10
|
|
|
9
|
-
[tool.poetry
|
|
10
|
-
|
|
11
|
-
pydantic = "^2"
|
|
12
|
-
requests = "^2"
|
|
13
|
-
email-validator = "^2"
|
|
11
|
+
[tool.poetry]
|
|
12
|
+
packages = [{ include = "easyverein" }]
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
[tool.poetry.group.dev.dependencies]
|
|
17
|
-
sphinx =
|
|
16
|
+
sphinx = [
|
|
17
|
+
{ version = "^9,<9.1", markers = "python_version < '3.12'" },
|
|
18
|
+
{ version = "^9", markers = "python_version >= '3.12'" },
|
|
19
|
+
]
|
|
18
20
|
sphinx-rtd-theme = "^3"
|
|
19
21
|
ruff = "*"
|
|
20
22
|
pytest = "^9"
|
|
21
23
|
pytest-dotenv = "^0"
|
|
22
24
|
mkdocs-material = "^9"
|
|
23
|
-
mkdocstrings = {extras = ["python"], version = "^1"}
|
|
25
|
+
mkdocstrings = { extras = ["python"], version = "^1" }
|
|
24
26
|
pymdown-extensions = "^10.7"
|
|
25
27
|
pre-commit = "^4"
|
|
26
28
|
pytest-cov = "^7"
|
|
@@ -60,7 +62,7 @@ ignore = [
|
|
|
60
62
|
"UP007", # use-pep604-annotation
|
|
61
63
|
"E741", # Ambiguous variable name
|
|
62
64
|
# "UP035", # deprecated-assertion
|
|
63
|
-
"PLE1205"
|
|
65
|
+
"PLE1205", # PLE1205 due to custom logger implementation
|
|
64
66
|
]
|
|
65
67
|
|
|
66
68
|
[tool.ruff.lint.mccabe]
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
All methods related to invoices
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
from easyverein.modules.member_custom_field import MemberCustomFieldMixin
|
|
8
|
-
from easyverein.modules.member_member_group import MemberMemberGroupMixin
|
|
9
|
-
|
|
10
|
-
from ..core.client import EasyvereinClient
|
|
11
|
-
from ..models import Member, MemberCreate, MemberFilter, MemberUpdate
|
|
12
|
-
from .mixins.crud import CRUDMixin
|
|
13
|
-
from .mixins.recycle_bin import RecycleBinMixin
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class MemberMixin(CRUDMixin[Member, MemberCreate, MemberUpdate, MemberFilter], RecycleBinMixin[Member]):
|
|
17
|
-
def __init__(self, client: EasyvereinClient, logger: logging.Logger):
|
|
18
|
-
self.endpoint_name = "member"
|
|
19
|
-
self.c = client
|
|
20
|
-
self.logger = logger
|
|
21
|
-
self.return_type = Member
|
|
22
|
-
|
|
23
|
-
def custom_field(self, member_id: int) -> MemberCustomFieldMixin:
|
|
24
|
-
return MemberCustomFieldMixin(self.c, self.logger, member_id)
|
|
25
|
-
|
|
26
|
-
def member_group(self, member_id: int) -> MemberMemberGroupMixin:
|
|
27
|
-
return MemberMemberGroupMixin(self.c, self.logger, member_id)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/member_member_group.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/mixins/empty_strings_mixin.py
RENAMED
|
File without changes
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/models/mixins/required_attributes.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/member_member_group.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_easyverein-2.1.2 → python_easyverein-2.3.0}/easyverein/modules/mixins/recycle_bin.py
RENAMED
|
File without changes
|