🔍 Code Extractor

function extend_approval_deadline

Maturity: 72

Extends the deadline for an active approval cycle, validating permissions and business rules, then notifying affected approvers.

File:
/tf/active/vicechatdev/CDocs/controllers/approval_controller.py
Lines:
1280 - 1392
Complexity:
complex

Purpose

This function allows authorized users to extend the due date of an approval cycle that is still pending or in progress. It performs comprehensive validation including permission checks, approval status verification, and date validation. After successfully extending the deadline, it logs the event to the audit trail and sends notifications to all active approvers. This is typically used when approval processes need more time due to unforeseen circumstances or when additional review time is required.

Source Code

def extend_approval_deadline(
    user: DocUser,
    approval_uid: str,
    new_due_date: datetime,
    reason: Optional[str] = None
) -> Dict[str, Any]:
    """
    Extend the deadline for a approval cycle.
    
    Args:
        user: User extending the deadline
        approval_uid: UID of approval cycle
        new_due_date: New due date
        reason: Optional reason for extension
        
    Returns:
        Dictionary with extension details
        
    Raises:
        ResourceNotFoundError: If approval cycle not found
        ValidationError: If validation fails
        PermissionError: If user doesn't have permission
        BusinessRuleError: If extension 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 manage approvals without MANAGE_APPROVALS permission")
            raise PermissionError("You do not have permission to manage approvals")
            
        logger.info(f"User {user.uid} has permission to manage approvals {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 is still active
        if approval_cycle.status not in ["PENDING", "IN_PROGRESS"]:
            raise BusinessRuleError(f"Cannot extend deadline for approval with status {approval_cycle.status}")
            
        # Validate new due date
        if new_due_date <= datetime.now():
            raise ValidationError("New due date must be in the future")
            
        if approval_cycle.due_date and new_due_date <= approval_cycle.due_date:
            raise ValidationError("New due date must be later than current due date")
            
        # Check if user has permission
        if not permissions.user_has_permission(user, "MANAGE_APPROVALS"):
            # Check if user is the document owner or approval initiator
            document = ControlledDocument(uid=approval_cycle.document_uid)
            if not (document and document.owner_uid == user.uid) and approval_cycle.initiated_by_uid != user.uid:
                raise PermissionError("User is not authorized to extend this approval deadline")
                
        # Store old due date for audit
        old_due_date = approval_cycle.due_date
        
        # Update approval cycle
        approval_cycle.due_date = new_due_date
        approval_cycle.save()
        
        # Log extension event
        audit_trail.log_approval_event(
            event_type="APPROVE_DEADLINE_EXTENDED",
            user=user,
            approval_uid=approval_uid,
            details={
                "old_due_date": old_due_date.isoformat() if old_due_date else None,
                "new_due_date": new_due_date.isoformat(),
                "reason": reason
            }
        )
        
        # Notify active approvers
        assignments = approval_cycle.get_approver_assignments()
        active_approver_uids = [a.approver_uid for a in assignments if a.status in ["PENDING", "ACTIVE"]]
        
        if active_approver_uids:
            document = ControlledDocument(uid=approval_cycle.document_uid)
            notifications.send_notification(
                notification_type="APPROVE_DEADLINE_EXTENDED",
                users=active_approver_uids,
                resource_uid=approval_cycle.uid,
                resource_type="ApprovalCycle",
                message=f"Approval deadline for {document.doc_number if document else 'document'} has been extended",
                details={
                    "approval_uid": approval_uid,
                    "document_uid": document.uid if document else None,
                    "doc_number": document.doc_number if document else None,
                    "title": document.title if document else None,
                    "new_due_date": new_due_date.isoformat(),
                    "reason": reason
                },
                send_email=True,
                email_template="approval_deadline_extended"
            )
        
        return {
            "success": True,
            "approval_uid": approval_uid,
            "old_due_date": old_due_date,
            "new_due_date": new_due_date,
            "reason": reason,
            "message": "Approval deadline extended successfully"
        }
        
    except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
        # Re-raise known errors
        raise
    except Exception as e:
        logger.error(f"Error extending approval deadline: {e}")
        raise BusinessRuleError(f"Failed to extend approval deadline: {e}")

Parameters

Name Type Default Kind
user DocUser - positional_or_keyword
approval_uid str - positional_or_keyword
new_due_date datetime - positional_or_keyword
reason Optional[str] None positional_or_keyword

Parameter Details

user: DocUser object representing the user attempting to extend the deadline. Must have MANAGE_APPROVALS permission, or be the document owner or approval initiator. Used for permission checks and audit logging.

approval_uid: String containing the unique identifier (UID) of the approval cycle whose deadline needs to be extended. Must correspond to an existing approval cycle in the system.

new_due_date: datetime object representing the new deadline for the approval cycle. Must be in the future and later than the current due date. This will replace the existing due_date on the approval cycle.

reason: Optional string providing justification for the deadline extension. Used for audit trail logging and included in notifications sent to approvers. Can be None if no reason is provided.

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (bool, always True on successful execution), 'approval_uid' (str, the UID of the approval cycle), 'old_due_date' (datetime or None, the previous due date), 'new_due_date' (datetime, the updated due date), 'reason' (str or None, the provided reason), and 'message' (str, success confirmation message).

Dependencies

  • logging
  • uuid
  • os
  • typing
  • datetime
  • traceback
  • CDocs.db
  • CDocs.config.settings
  • CDocs.config.permissions
  • CDocs.models.document
  • CDocs.models.approval
  • CDocs.models.user_extensions
  • CDocs.utils.audit_trail
  • CDocs.utils.notifications
  • CDocs.controllers
  • CDocs.controllers.share_controller
  • CDocs.db.db_operations

Required Imports

from datetime import datetime
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
from CDocs.utils import notifications
from CDocs.controllers import ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError
from CDocs.controllers import log_controller_action
import logging

Usage Example

from datetime import datetime, timedelta
from CDocs.models.user_extensions import DocUser
from your_module import extend_approval_deadline

# Get the user and approval cycle UID
user = DocUser(uid='user123')
approval_uid = 'approval456'

# Set new due date to 7 days from now
new_due_date = datetime.now() + timedelta(days=7)
reason = 'Additional time needed for technical review'

# Extend the deadline
try:
    result = extend_approval_deadline(
        user=user,
        approval_uid=approval_uid,
        new_due_date=new_due_date,
        reason=reason
    )
    print(f"Success: {result['message']}")
    print(f"Old deadline: {result['old_due_date']}")
    print(f"New deadline: {result['new_due_date']}")
except PermissionError as e:
    print(f"Permission denied: {e}")
except ValidationError as e:
    print(f"Validation failed: {e}")
except BusinessRuleError as e:
    print(f"Business rule violation: {e}")

Best Practices

  • Always wrap calls in try-except blocks to handle ResourceNotFoundError, ValidationError, PermissionError, and BusinessRuleError exceptions
  • Ensure the new_due_date is a timezone-aware datetime object if your system uses timezone-aware datetimes
  • Provide a meaningful reason parameter for audit trail purposes and transparency to approvers
  • Verify that the user object is properly authenticated and loaded before calling this function
  • The function performs duplicate permission checks - the first check at the beginning is the primary gate, the second is redundant and will never execute
  • This function is decorated with log_controller_action('extend_approval_deadline') which provides automatic logging
  • The function sends email notifications to all active approvers, so ensure email system is properly configured
  • Only approval cycles with status 'PENDING' or 'IN_PROGRESS' can have their deadlines extended
  • The new due date must be both in the future and later than the current due date
  • Consider the impact on approvers' schedules when extending deadlines significantly

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function extend_approval_deadline_v1 94.4% similar

    Extends the deadline of an active approval cycle, validates permissions, logs the change, and notifies pending approvers of the new due date.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
  • function extend_review_deadline 89.4% similar

    Extends the deadline for a document review cycle, validating permissions and business rules, then notifying active reviewers of the change.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function add_approver_to_active_approval 67.5% similar

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

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function add_approver_to_active_approval_v1 65.0% 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 complete_approval 64.9% 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