fix crosstable trustee

This commit is contained in:
ValueOn AG 2026-01-26 23:48:19 +01:00
parent 7ca957f664
commit ee15fd64b0
2 changed files with 40 additions and 8 deletions

View file

@ -1095,7 +1095,8 @@ class TrusteeObjects:
def deleteDocument(self, documentId: str) -> bool: def deleteDocument(self, documentId: str) -> bool:
"""Delete a document. """Delete a document.
Note: organisationId and contractId removed - feature instance IS the organisation. All position-document cross-table entries (TrusteePositionDocument) referencing
this document are deleted first, then the document.
""" """
# Get existing document to check creator # Get existing document to check creator
existingRecords = self.db.getRecordset(TrusteeDocument, recordFilter={"id": documentId}) existingRecords = self.db.getRecordset(TrusteeDocument, recordFilter={"id": documentId})
@ -1112,6 +1113,7 @@ class TrusteeObjects:
logger.warning(f"User {self.userId} lacks permission to delete document") logger.warning(f"User {self.userId} lacks permission to delete document")
return False return False
self._deletePositionDocumentLinksForDocument(documentId)
return self.db.recordDelete(TrusteeDocument, documentId) return self.db.recordDelete(TrusteeDocument, documentId)
# ===== Position CRUD ===== # ===== Position CRUD =====
@ -1259,7 +1261,8 @@ class TrusteeObjects:
def deletePosition(self, positionId: str) -> bool: def deletePosition(self, positionId: str) -> bool:
"""Delete a position. """Delete a position.
Note: organisationId and contractId removed - feature instance IS the organisation. All position-document cross-table entries (TrusteePositionDocument) referencing
this position are deleted first, then the position.
""" """
# Get existing position to check creator # Get existing position to check creator
existingRecords = self.db.getRecordset(TrusteePosition, recordFilter={"id": positionId}) existingRecords = self.db.getRecordset(TrusteePosition, recordFilter={"id": positionId})
@ -1276,6 +1279,7 @@ class TrusteeObjects:
logger.warning(f"User {self.userId} lacks permission to delete position") logger.warning(f"User {self.userId} lacks permission to delete position")
return False return False
self._deletePositionDocumentLinksForPosition(positionId)
return self.db.recordDelete(TrusteePosition, positionId) return self.db.recordDelete(TrusteePosition, positionId)
# ===== Position-Document Link CRUD ===== # ===== Position-Document Link CRUD =====
@ -1423,6 +1427,22 @@ class TrusteeObjects:
return self.db.recordDelete(TrusteePositionDocument, linkId) return self.db.recordDelete(TrusteePositionDocument, linkId)
def _deletePositionDocumentLinksForDocument(self, documentId: str) -> None:
"""Delete all position-document cross-table entries referencing this document."""
links = self.db.getRecordset(TrusteePositionDocument, recordFilter={"documentId": documentId})
for link in links:
linkId = link.get("id")
if linkId:
self.db.recordDelete(TrusteePositionDocument, linkId)
def _deletePositionDocumentLinksForPosition(self, positionId: str) -> None:
"""Delete all position-document cross-table entries referencing this position."""
links = self.db.getRecordset(TrusteePositionDocument, recordFilter={"positionId": positionId})
for link in links:
linkId = link.get("id")
if linkId:
self.db.recordDelete(TrusteePositionDocument, linkId)
# ===== Trustee-specific Access Check ===== # ===== Trustee-specific Access Check =====
def getUserAccessForOrganisation(self, userId: str, organisationId: str) -> List[Dict[str, Any]]: def getUserAccessForOrganisation(self, userId: str, organisationId: str) -> List[Dict[str, Any]]:

View file

@ -878,6 +878,12 @@ class FeatureInstanceUserResponse(BaseModel):
enabled: bool enabled: bool
class FeatureInstanceUserUpdate(BaseModel):
"""Request model for updating a feature instance user (roles and active flag)"""
roleIds: List[str] = Field(..., description="Role IDs to assign")
enabled: Optional[bool] = Field(None, description="Whether this user's access is active (omit to leave unchanged)")
@router.get("/instances/{instanceId}/users", response_model=List[FeatureInstanceUserResponse]) @router.get("/instances/{instanceId}/users", response_model=List[FeatureInstanceUserResponse])
@limiter.limit("60/minute") @limiter.limit("60/minute")
async def list_feature_instance_users( async def list_feature_instance_users(
@ -1161,18 +1167,19 @@ async def update_feature_instance_user_roles(
request: Request, request: Request,
instanceId: str, instanceId: str,
userId: str, userId: str,
roleIds: List[str], data: FeatureInstanceUserUpdate,
context: RequestContext = Depends(getRequestContext) context: RequestContext = Depends(getRequestContext)
) -> Dict[str, Any]: ) -> Dict[str, Any]:
""" """
Update a user's roles in a feature instance. Update a user's roles and active flag in a feature instance.
Replaces all existing FeatureAccessRole records with new ones. Replaces all existing FeatureAccessRole records with new ones.
If enabled is provided, updates the FeatureAccess.enabled flag.
Args: Args:
instanceId: FeatureInstance ID instanceId: FeatureInstance ID
userId: User ID to update userId: User ID to update
roleIds: New list of role IDs data: roleIds and optional enabled
""" """
try: try:
rootInterface = getRootInterface() rootInterface = getRootInterface()
@ -1215,6 +1222,10 @@ async def update_feature_instance_user_roles(
featureAccessId = existingAccess[0].get("id") featureAccessId = existingAccess[0].get("id")
# Update enabled flag if provided
if data.enabled is not None:
rootInterface.db.recordModify(FeatureAccess, featureAccessId, {"enabled": data.enabled})
# Delete existing FeatureAccessRole records # Delete existing FeatureAccessRole records
existingRoles = rootInterface.db.getRecordset( existingRoles = rootInterface.db.getRecordset(
FeatureAccessRole, FeatureAccessRole,
@ -1224,7 +1235,7 @@ async def update_feature_instance_user_roles(
rootInterface.db.recordDelete(FeatureAccessRole, role.get("id")) rootInterface.db.recordDelete(FeatureAccessRole, role.get("id"))
# Create new FeatureAccessRole records # Create new FeatureAccessRole records
for roleId in roleIds: for roleId in data.roleIds:
featureAccessRole = FeatureAccessRole( featureAccessRole = FeatureAccessRole(
featureAccessId=featureAccessId, featureAccessId=featureAccessId,
roleId=roleId roleId=roleId
@ -1232,14 +1243,15 @@ async def update_feature_instance_user_roles(
rootInterface.db.recordCreate(FeatureAccessRole, featureAccessRole.model_dump()) rootInterface.db.recordCreate(FeatureAccessRole, featureAccessRole.model_dump())
logger.info( logger.info(
f"User {context.user.id} updated roles for user {userId} in feature instance {instanceId}: {roleIds}" f"User {context.user.id} updated roles for user {userId} in feature instance {instanceId}: {data.roleIds}"
) )
return { return {
"featureAccessId": featureAccessId, "featureAccessId": featureAccessId,
"userId": userId, "userId": userId,
"featureInstanceId": instanceId, "featureInstanceId": instanceId,
"roleIds": roleIds "roleIds": data.roleIds,
"enabled": data.enabled if data.enabled is not None else existingAccess[0].get("enabled", True)
} }
except HTTPException: except HTTPException: