From 6ffef4bb0986ce4696a62b9ae5731a6e3b1a6036 Mon Sep 17 00:00:00 2001 From: Daniel Herrmann Date: Mon, 19 Aug 2024 21:28:36 +0200 Subject: [PATCH] feat: add support for managing member groups (/member-group endpoint) --- easyverein/api.py | 2 + easyverein/models/__init__.py | 2 + easyverein/models/member_group.py | 155 +++++++++++++++++++++++ easyverein/modules/member_group.py | 22 ++++ easyverein/modules/mixins/recycle_bin.py | 12 -- tests/test_member_group.py | 42 ++++++ 6 files changed, 223 insertions(+), 12 deletions(-) create mode 100644 easyverein/models/member_group.py create mode 100644 easyverein/modules/member_group.py create mode 100644 tests/test_member_group.py diff --git a/easyverein/api.py b/easyverein/api.py index 1b205dc..133fd77 100644 --- a/easyverein/api.py +++ b/easyverein/api.py @@ -10,6 +10,7 @@ from .modules.invoice import InvoiceMixin from .modules.invoice_item import InvoiceItemMixin from .modules.member import MemberMixin +from .modules.member_group import MemberGroupMixin class EasyvereinAPI: @@ -40,3 +41,4 @@ def __init__( self.invoice = InvoiceMixin(self.c, self.logger) self.invoice_item = InvoiceItemMixin(self.c, self.logger) self.member = MemberMixin(self.c, self.logger) + self.member_group = MemberGroupMixin(self.c, self.logger) diff --git a/easyverein/models/__init__.py b/easyverein/models/__init__.py index af29989..b8a9595 100644 --- a/easyverein/models/__init__.py +++ b/easyverein/models/__init__.py @@ -25,10 +25,12 @@ MemberCustomFieldFilter, MemberCustomFieldUpdate, ) +from .member_group import MemberGroup, MemberGroupCreate, MemberGroupFilter, MemberGroupUpdate ContactDetails.model_rebuild() CustomField.model_rebuild() Invoice.model_rebuild() InvoiceItem.model_rebuild() Member.model_rebuild() +MemberGroup.model_rebuild() MemberCustomField.model_rebuild() diff --git a/easyverein/models/member_group.py b/easyverein/models/member_group.py new file mode 100644 index 0000000..460ce87 --- /dev/null +++ b/easyverein/models/member_group.py @@ -0,0 +1,155 @@ +""" +Member related models +""" + +from __future__ import annotations + +from typing import Literal + +from pydantic import BaseModel, Field, PositiveInt + +from ..core.types import ( + EasyVereinReference, + FilterIntList, + HexColor, + PositiveIntWithZero, +) +from .base import EasyVereinBase +from .mixins.empty_strings_mixin import EmptyStringsToNone +from .mixins.required_attributes import required_mixin + + +class MemberGroupBase(EasyVereinBase): + """ + | Representative Model Class | Update Model Class | Create Model Class | + | --- | --- | --- | + | `MemberGroup` | `MemberGroupUpdate` | `MemberGroupCreate` | + + Member groups are used to categorize members into different groups. + They can then be used to manage the members permissions, their membersip fee + or to send out messages to specific groups of members. + + !!! info "Member Groups vs associations" + This endpoint is used to manage the member groups themselves, not the assignment of members to groups. + """ + + name: str | None = Field(default=None, max_length=200) + color: HexColor = None + short: str | None = Field(default=None, max_length=4) + userGroupAccount: EasyVereinReference | None = None + paymentAmount: float | None = None + assignmentDeleteAfterBooking: bool | None = None + usePaymentFormula: bool | None = None + paymentFormula: str | None = Field(default=None, max_length=512) + paymentInterval: PositiveInt | None = None + nameOnInvoice: str | None = Field(default=None, max_length=256) + descriptionOnInvoice: str | None = None + showInApplicationform: bool | None = None + agePermission: PositiveIntWithZero | None = None + nextGroup: EasyVereinReference | None = None + taxRate: float | None = None + billingAccount: EasyVereinReference | None = None + costCentre: str | None = Field(default=None, max_length=8) + isOnlyVisibleToAdmins: bool | None = None + user_shares: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_bookings: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_protocols: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_members: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_members_groupaccess: Literal["n", "a", "d", "x"] | None = None + """ + - n: standard + - a: limited + - d: unlimited + - x: ignore group + """ + user_membershipCte: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_edit: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_forum: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_board: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_boardLinks: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_importcalendar: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_invoiceRequest: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + user_inventory: Literal["n", "a", "d"] | None = None + """ + - n: standard + - a: allowed + - d: forbidden + """ + + +class MemberGroup(MemberGroupBase, EmptyStringsToNone): + pass + + +class MemberGroupCreate(MemberGroupBase, required_mixin(["name", "color", "short"])): # type: ignore + pass + + +class MemberGroupUpdate(MemberGroupBase): + pass + + +class MemberGroupFilter(BaseModel): + id__in: FilterIntList | None = None + name: str | None = None + paymentAmount: float | None = None + paymentAmount__gt: float | None = None + paymentAmount__lt: float | None = None + kind: str | None = None + deleted: bool | None = None + ordering: str | None = None diff --git a/easyverein/modules/member_group.py b/easyverein/modules/member_group.py new file mode 100644 index 0000000..0544184 --- /dev/null +++ b/easyverein/modules/member_group.py @@ -0,0 +1,22 @@ +""" +All methods related to custom fields +""" + +import logging + +from ..core.client import EasyvereinClient +from ..models import MemberGroup, MemberGroupCreate, MemberGroupFilter, MemberGroupUpdate +from .mixins.crud import CRUDMixin +from .mixins.recycle_bin import RecycleBinMixin + + +class MemberGroupMixin( + CRUDMixin[MemberGroup, MemberGroupCreate, MemberGroupUpdate, MemberGroupFilter], + RecycleBinMixin[MemberGroup], +): + def __init__(self, client: EasyvereinClient, logger: logging.Logger): + super().__init__() + self.endpoint_name = "member-group" + self.return_type = MemberGroup + self.c = client + self.logger = logger diff --git a/easyverein/modules/mixins/recycle_bin.py b/easyverein/modules/mixins/recycle_bin.py index afa1f9b..967ff7d 100644 --- a/easyverein/modules/mixins/recycle_bin.py +++ b/easyverein/modules/mixins/recycle_bin.py @@ -21,18 +21,6 @@ def get_deleted(self: EVClientProtocol[ModelType]) -> tuple[list[ModelType], int assert isinstance(parsed_objects, list) return parsed_objects, response.count or 0 - # def restore(self: EVClientProtocol, item_id: int): - # """ - # Restores a given item from the recycle bin - # """ - # self.logger.info("Restoring item from recycle bin") - # - # url = self.c.get_url(f"/wastebasket/{self.endpoint_name}/{item_id}/") - # - # return self.c.handle_response( - # self.c.do_request("patch", url), expected_status_code=200 - # ) - def purge(self: EVClientProtocol[ModelType], item: ModelType | int): """ Finally deletes a given item from the recycle bin. This is irreversible and cannot be undone. diff --git a/tests/test_member_group.py b/tests/test_member_group.py new file mode 100644 index 0000000..c43cead --- /dev/null +++ b/tests/test_member_group.py @@ -0,0 +1,42 @@ +from easyverein import EasyvereinAPI +from easyverein.models.member_group import MemberGroup, MemberGroupCreate, MemberGroupUpdate + + +class TestMembeGropr: + def test_create_member_group_minimal(self, ev_connection: EasyvereinAPI): + member_group = ev_connection.member_group.create( + MemberGroupCreate(name="Test-Group", color="#FF0000", short="TG") + ) + assert isinstance(member_group, MemberGroup) + assert member_group.name == "Test-Group" + assert member_group.color == "#FF0000" + assert member_group.short == "TG" + + # Check some defaults + assert member_group.taxRate is None + assert member_group.usePaymentFormula is False + assert member_group.user_members_groupaccess == "n" + assert member_group.id + + # Change the color + mg = ev_connection.member_group.update(member_group.id, MemberGroupUpdate(color="#00FF00")) + assert isinstance(mg, MemberGroup) + + # Delete member group again + ev_connection.member_group.delete(member_group) + + # Check waste basket + member_groups, c = ev_connection.member_group.get_deleted() + assert isinstance(member_groups, list) + assert len(member_groups) == 1 + assert c == 1 + assert member_groups[0].id == member_group.id + + # Purge member group + ev_connection.member_group.purge(member_group) + + # Check waste basket + member_groups, c = ev_connection.member_group.get_deleted() + assert isinstance(member_groups, list) + assert len(member_groups) == 0 + assert c == 0