🔍 Code Extractor

function add_approver_to_active_approval

Maturity: 75

Adds a new approver to an active approval cycle for a controlled document, with optional sequence ordering and custom instructions.

File:
/tf/active/vicechatdev/CDocs/controllers/approval_controller.py
Lines:
1395 - 1529
Complexity:
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

  • logging
  • uuid
  • os
  • typing
  • datetime
  • traceback
  • CDocs.db
  • CDocs.config
  • CDocs.models.document
  • CDocs.models.approval
  • CDocs.models.user_extensions
  • CDocs.utils.audit_trail
  • CDocs.utils.notifications
  • CDocs.controllers
  • CDocs.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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function add_approver_to_active_approval_v1 92.1% similar

    Adds a new approver to an active approval cycle with permission checks, validation, audit logging, and email notifications.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
  • function create_approval_cycle 79.9% similar

    Creates a new approval cycle for a document, assigning approvers with configurable workflow options (sequential/parallel), instructions, and due dates.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
  • function remove_approver_from_active_approval 77.6% similar

    Removes an approver from an active approval cycle with permission checks, validation, and notification handling.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function add_reviewer_to_active_review 77.1% similar

    Adds a reviewer to an active review cycle with optional sequence ordering and instructions, handling permissions, notifications, and audit logging.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function complete_approval 75.1% similar

    Completes an approval cycle by recording a user's approval decision (APPROVED, REJECTED, etc.) and managing the approval workflow, including sequential approver activation and final cycle completion.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
← Back to Browse