fix: mandate subscription provisioning, capacity errors, invitations API
Made-with: Cursor
This commit is contained in:
parent
ecbdd1ea74
commit
50bf59879f
4 changed files with 39 additions and 7 deletions
|
|
@ -1990,8 +1990,10 @@ class AppObjects:
|
|||
cleanedRecord = dict(createdRecord)
|
||||
return UserMandate(**cleanedRecord)
|
||||
except Exception as e:
|
||||
if e.__class__.__name__ == "SubscriptionCapacityException":
|
||||
raise
|
||||
logger.error(f"Error creating UserMandate: {e}")
|
||||
raise ValueError(f"Failed to create UserMandate: {e}")
|
||||
raise ValueError(f"Failed to create UserMandate: {e}") from e
|
||||
|
||||
def _ensureUserBillingAccount(self, userId: str, mandateId: str) -> None:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ from modules.datamodels.datamodelMembership import UserMandate, UserMandateRole
|
|||
from modules.datamodels.datamodelRbac import Role
|
||||
from modules.datamodels.datamodelPagination import PaginationParams, PaginatedResponse, PaginationMetadata, normalize_pagination_dict
|
||||
from modules.routes.routeNotifications import create_access_change_notification
|
||||
from modules.serviceCenter.services.serviceSubscription.mainServiceSubscription import SubscriptionCapacityException
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -795,6 +796,11 @@ def add_user_to_mandate(
|
|||
|
||||
except HTTPException:
|
||||
raise
|
||||
except SubscriptionCapacityException as cap:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=cap.message,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding user to mandate: {e}")
|
||||
raise HTTPException(
|
||||
|
|
|
|||
|
|
@ -41,11 +41,10 @@ class InvitationCreate(BaseModel):
|
|||
- Mandate-level: featureInstanceId omitted, roleIds are mandate-level roles (user, viewer, admin)
|
||||
- Feature-instance-level: featureInstanceId required, roleIds are instance-level roles
|
||||
|
||||
Email is required for new users; targetUsername is optional.
|
||||
At least one of email or targetUsername must be provided.
|
||||
"""
|
||||
targetUsername: Optional[str] = Field(None, description="Username of the user to invite (must match on acceptance)")
|
||||
email: Optional[str] = Field(None, description="Email address to send invitation link (required for new users)")
|
||||
email: Optional[str] = Field(None, description="Email address to send invitation link (optional if targetUsername is set)")
|
||||
featureInstanceId: Optional[str] = Field(None, description="Feature instance to grant access to (optional for mandate-level invitations)")
|
||||
roleIds: List[str] = Field(..., description="Role IDs: mandate-level (user, viewer, admin) or instance-level")
|
||||
frontendUrl: str = Field(..., description="Frontend URL for building the invite link (provided by frontend)")
|
||||
|
|
|
|||
|
|
@ -786,15 +786,40 @@ class SubscriptionInactiveException(Exception):
|
|||
return out
|
||||
|
||||
|
||||
_SUBSCRIPTION_LIMITS_UI_HINT_DE = (
|
||||
" Details zu Ihrem Abonnement, den enthaltenen Limits und Upgrade-Optionen: "
|
||||
"Menü «Administration» → «Billing» → Registerkarte «Abonnement»."
|
||||
)
|
||||
|
||||
|
||||
class SubscriptionCapacityException(Exception):
|
||||
def __init__(self, resourceType: str, currentCount: int, maxAllowed: int, message: Optional[str] = None):
|
||||
self.resourceType = resourceType
|
||||
self.currentCount = currentCount
|
||||
self.maxAllowed = maxAllowed
|
||||
self.message = message or (
|
||||
f"Ihr Plan erlaubt maximal {maxAllowed} {'Benutzer' if resourceType == 'users' else 'Feature-Instanzen'} "
|
||||
f"(aktuell {currentCount}). Bitte wechseln Sie zu einem grösseren Plan."
|
||||
)
|
||||
if message is not None:
|
||||
self.message = message
|
||||
elif resourceType == "users":
|
||||
self.message = (
|
||||
f"Mit dem aktuellen Abonnement sind für diesen Mandanten höchstens {maxAllowed} "
|
||||
f"Benutzer zulässig (derzeit {currentCount}). "
|
||||
f"Ohne Planwechsel können keine weiteren Benutzer hinzugefügt werden."
|
||||
) + _SUBSCRIPTION_LIMITS_UI_HINT_DE
|
||||
elif resourceType == "featureInstances":
|
||||
self.message = (
|
||||
f"Es sind höchstens {maxAllowed} aktive Feature-Instanzen erlaubt (derzeit {currentCount}). "
|
||||
f"Bitte Abonnement erweitern oder eine Instanz entfernen."
|
||||
) + _SUBSCRIPTION_LIMITS_UI_HINT_DE
|
||||
elif resourceType == "dataVolumeMB":
|
||||
self.message = (
|
||||
f"Das im Abonnement enthaltene Datenvolumen ({maxAllowed} MB) reicht nicht "
|
||||
f"(aktuell ca. {currentCount} MB). Bitte Speicher-Limit oder Plan anpassen."
|
||||
) + _SUBSCRIPTION_LIMITS_UI_HINT_DE
|
||||
else:
|
||||
self.message = (
|
||||
f"Abonnement-Limit überschritten (Ressource «{resourceType}»: "
|
||||
f"aktuell {currentCount}, erlaubt {maxAllowed})."
|
||||
) + _SUBSCRIPTION_LIMITS_UI_HINT_DE
|
||||
super().__init__(self.message)
|
||||
|
||||
def toClientDict(self) -> Dict[str, Any]:
|
||||
|
|
|
|||
Loading…
Reference in a new issue