function add_approver_to_active_approval
Adds a new approver to an active approval cycle for a controlled document, with optional sequence ordering and custom instructions.
/tf/active/vicechatdev/CDocs/controllers/approval_controller.py
1395 - 1529
complex
Purpose
This function manages the addition of approvers to ongoing document approval workflows. It validates permissions, checks approval cycle status, creates approver assignments, sends notifications, and updates document sharing permissions. It supports both parallel and sequential approval workflows through optional sequence ordering.
Source Code
def add_approver_to_active_approval(
user: DocUser,
approval_uid: str,
approver_uid: str,
sequence_order: Optional[int] = None,
instructions: Optional[str] = None # New parameter
) -> Dict[str, Any]:
"""
Add a approver to an active approval cycle.
Args:
user: User adding the approver
approval_uid: UID of approval cycle
approver_uid: UID of user to add as approver
sequence_order: Optional sequence order for sequential approvals
Returns:
Dictionary with addition details
Raises:
ResourceNotFoundError: If approval cycle not found
ValidationError: If validation fails
PermissionError: If user doesn't have permission
BusinessRuleError: If addition is not allowed
"""
try:
# Direct permission check at the beginning
logger.info(f"Checking if user {user.uid} has MANAGE_APPROVALS permission")
if not permissions.user_has_permission(user, "MANAGE_APPROVALS"):
logger.warning(f"Permission denied: User {user.uid} attempted to add approver without MANAGE_APPROVALS permission")
raise PermissionError("You do not have permission to add approvers")
logger.info(f"User {user.uid} has permission to add approver to approval cycle {approval_uid}")
# Get approval cycle instance
approval_cycle = ApprovalCycle(uid=approval_uid)
if not approval_cycle:
raise ResourceNotFoundError(f"Approval cycle not found: {approval_uid}")
# Check if approval status allows adding approvers
if approval_cycle.status not in ["PENDING", "IN_PROGRESS"]:
raise BusinessRuleError(f"Cannot add approvers to approval with status {approval_cycle.status}")
# Validate approver UID
approver = DocUser(uid=approver_uid)
if not approver or not approver.uid:
raise ValidationError(f"Invalid approver: {approver_uid}")
# Check if already a approver
if approval_cycle.is_approver(approver_uid):
raise BusinessRuleError(f"User {approver_uid} is already a approver for this approval cycle")
# Add approver using the model method - this creates the relationship and assignment
success = approval_cycle.add_approver(approver)
if not success:
raise BusinessRuleError(f"Failed to add approver {approver_uid}")
# If sequence order provided (for sequential approvals), update it
assignment = approval_cycle.get_approver_assignment(approver_uid)
if assignment:
if sequence_order is not None:
assignment.sequence_order = sequence_order
# Set approver-specific instructions if provided
if instructions is not None:
assignment.instructions = instructions
assignment.save()
# Log the approver addition
audit_trail.log_approval_event(
event_type="APPROVE_UPDATED",
user=user,
approval_uid=approval_uid,
details={
"action": "approver_added",
"approver_uid": approver_uid,
"approver_name": approver.name,
"sequence_order": sequence_order
}
)
# Send notification to new approver
try:
# Get document info for the notification
document = None
document_version = approval_cycle.document_version
if document_version:
document = document_version.document
notifications.send_notification(
notification_type="APPROVE_ASSIGNED",
users=[approver_uid],
resource_uid=approval_uid,
resource_type="ApprovalCycle",
message=f"You have been added as a approver",
details={
"approval_uid": approval_uid,
"document_uid": document.uid if document else None,
"doc_number": document.doc_number if document else "Unknown",
"title": document.title if document else "Unknown Document",
"due_date": approval_cycle.due_date.isoformat() if approval_cycle.due_date else None
},
send_email=True,
email_template="approval_assigned"
)
except Exception as notif_err:
logger.error(f"Error sending approver notification: {notif_err}")
# Continue even if notification fails
# Update sharing permissions for document based on new approval cycle
from CDocs.controllers.share_controller import manage_document_permissions
document = ControlledDocument(uid=document.uid)
permission_result = manage_document_permissions(document)
return {
"success": True,
"approval_uid": approval_uid,
"approver": {
"uid": approver.uid,
"name": approver.name,
"email": approver.email
},
"assignment": assignment.to_dict() if assignment else None,
"message": f"Approver {approver.name} added successfully"
}
except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
# Re-raise known errors
raise
except Exception as e:
logger.error(f"Error adding approver to approval cycle: {e}")
logger.error(traceback.format_exc())
raise BusinessRuleError(f"Failed to add approver: {e}")
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
approval_uid |
str | - | positional_or_keyword |
approver_uid |
str | - | positional_or_keyword |
sequence_order |
Optional[int] | None | positional_or_keyword |
instructions |
Optional[str] | None | positional_or_keyword |
Parameter Details
user: DocUser object representing the user performing the action. Must have MANAGE_APPROVALS permission to execute this function.
approval_uid: String UID (unique identifier) of the approval cycle to which the approver will be added. Must reference an existing approval cycle in PENDING or IN_PROGRESS status.
approver_uid: String UID of the user to be added as an approver. Must reference a valid DocUser who is not already an approver on this cycle.
sequence_order: Optional integer specifying the order in which this approver should review in sequential approval workflows. If None, approver can review in parallel with others. Used to control approval workflow sequencing.
instructions: Optional string containing specific instructions for this approver. Allows customization of approval requirements or guidance on a per-approver basis.
Return Value
Type: Dict[str, Any]
Returns a dictionary with keys: 'success' (boolean indicating operation success), 'approval_uid' (string UID of the approval cycle), 'approver' (dict with 'uid', 'name', 'email' of added approver), 'assignment' (dict representation of the ApproverAssignment object or None), and 'message' (string confirmation message). On failure, raises one of the documented exceptions instead of returning.
Dependencies
logginguuidostypingdatetimetracebackCDocs.dbCDocs.configCDocs.models.documentCDocs.models.approvalCDocs.models.user_extensionsCDocs.utils.audit_trailCDocs.utils.notificationsCDocs.controllersCDocs.controllers.share_controller
Required Imports
from typing import Dict, Any, Optional
from CDocs.models.user_extensions import DocUser
from CDocs.models.approval import ApprovalCycle
from CDocs.models.document import ControlledDocument
from CDocs.config import permissions
from CDocs.utils import audit_trail, notifications
from CDocs.controllers import PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError
from CDocs.controllers import log_controller_action
from CDocs.controllers.share_controller import manage_document_permissions
import logging
import traceback
Usage Example
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.approval_controller import add_approver_to_active_approval
# Get the user performing the action (must have MANAGE_APPROVALS permission)
manager = DocUser(uid='user_123')
# Add an approver to an approval cycle
try:
result = add_approver_to_active_approval(
user=manager,
approval_uid='approval_456',
approver_uid='approver_789',
sequence_order=2, # Second in sequence for sequential approval
instructions='Please review sections 3-5 carefully'
)
print(f"Success: {result['success']}")
print(f"Approver added: {result['approver']['name']}")
print(f"Assignment details: {result['assignment']}")
except PermissionError as e:
print(f"Permission denied: {e}")
except ResourceNotFoundError as e:
print(f"Resource not found: {e}")
except BusinessRuleError as e:
print(f"Business rule violation: {e}")
Best Practices
- Always ensure the calling user has MANAGE_APPROVALS permission before invoking this function
- Handle all four exception types (PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError) when calling this function
- The function only works on approval cycles with status PENDING or IN_PROGRESS - check status before calling
- Sequence order should be used consistently across all approvers in a sequential approval workflow
- The function automatically sends email notifications to the new approver - ensure email system is configured
- Document sharing permissions are automatically updated after adding an approver
- The function logs audit trail events - ensure audit logging is properly configured
- If notification sending fails, the function continues successfully - check logs for notification errors
- Use the instructions parameter to provide approver-specific guidance when approval requirements vary by approver
- The function is decorated with @log_controller_action - controller action logging must be configured
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function add_approver_to_active_approval_v1 92.1% similar
-
function create_approval_cycle 79.9% similar
-
function remove_approver_from_active_approval 77.6% similar
-
function add_reviewer_to_active_review 77.1% similar
-
function complete_approval 75.1% similar