🔍 Code Extractor

function generate_document_number

Maturity: 62

Generates unique, sequential document numbers for a given document type and department using persistent counters stored in Neo4j database.

File:
/tf/active/vicechatdev/CDocs/settings_prod.py
Lines:
561 - 652
Complexity:
complex

Purpose

This function creates standardized document identifiers by maintaining atomic counters in a Neo4j graph database. It ensures unique document numbering across the system with support for yearly counter resets, custom formatting patterns, and fallback mechanisms. The function is designed for document management systems requiring persistent, collision-free document identification with department and type-based organization.

Source Code

def generate_document_number(doc_type: str, department: str) -> str:
    """
    Generate a new document number based on type and department using persistent
    counters stored in Neo4j.
    
    Args:
        doc_type: Document type code (e.g., 'SOP')
        department: Department code (e.g., 'QA')
        
    Returns:
        Formatted document number string
    """
    from CDocs.db import db_operations as db
    from datetime import datetime
    
    # Get document type info from config if available
    doc_info = DOCUMENT_CONFIG.get(doc_type, {})
    
    # If not found in config, use simple type name
    prefix = doc_info.get('prefix', doc_type)
    
    # Get current year
    current_year = datetime.now().year
    
    try:
        # First, get the CDocs root node UID
        cdocs_result = db.run_query(
            "MATCH (c:CDocs) RETURN c.UID as uid LIMIT 1"
        )
        
        if not cdocs_result or not cdocs_result[0].get('uid'):
            logger.error("Could not find CDocs root node")
            # Fall back to in-memory counter as a last resort
            return _generate_document_number_fallback(doc_type, department)
            
        cdocs_uid = cdocs_result[0]['uid']
        
        # Get or create counter with atomic increment using MERGE
        result = db.run_query(
            """
            MATCH (c:CDocs {UID: $cdocs_uid})
            MERGE (c)-[:HAS_COUNTER]->(counter:DocumentCounter {department: $department, docType: $docType})
            ON CREATE SET 
                counter.UID = randomUUID(),
                counter.value = 1,
                counter.currentYear = $current_year,
                counter.createdDate = datetime()
            ON MATCH SET 
                counter.value = CASE 
                    WHEN $reset_yearly AND counter.currentYear < $current_year 
                    THEN 1
                    ELSE counter.value + 1
                END,
                counter.currentYear = $current_year,
                counter.lastUpdated = datetime()
            RETURN counter.value as value
            """,
            {
                "cdocs_uid": cdocs_uid,
                "department": department, 
                "docType": doc_type, 
                "current_year": current_year,
                "reset_yearly": DOCUMENT_NUMBERING.get('reset_counter_yearly', True)
            }
        )
        
        if not result:
            logger.error("Failed to get/update document counter")
            return _generate_document_number_fallback(doc_type, department)
            
        next_value = result[0]['value']
        
        # Use document type specific numbering format if available, otherwise use default
        numbering_format = doc_info.get('numbering_format', 
                                        DOCUMENT_NUMBERING.get('numbering_format', 
                                                            '{prefix}-{dept}-{year}-{number:04d}'))
        
        # Format document number
        return numbering_format.format(
            prefix=prefix, 
            dept=department,
            year=current_year if DOCUMENT_NUMBERING.get('use_year_in_numbering', False) else "",
            number=next_value
        )
    
    except Exception as e:
        # Log the error
        logger.error(f"Error generating document number: {e}")
        import traceback
        logger.error(traceback.format_exc())
        # Fall back to in-memory counter
        return _generate_document_number_fallback(doc_type, department)

Parameters

Name Type Default Kind
doc_type str - positional_or_keyword
department str - positional_or_keyword

Parameter Details

doc_type: Document type code string (e.g., 'SOP', 'WI', 'FORM') that categorizes the document. This is used to look up configuration settings and determine the document number prefix. Expected to be a short alphanumeric code.

department: Department code string (e.g., 'QA', 'RD', 'MFG') identifying the organizational unit. Used in the document number format and as part of the counter key to maintain separate sequences per department.

Return Value

Type: str

Returns a formatted document number string following the pattern defined in configuration (default: '{prefix}-{dept}-{year}-{number:04d}'). Example outputs: 'SOP-QA-2024-0001', 'WI-MFG-0042'. If database operations fail, returns a fallback-generated number from an in-memory counter.

Dependencies

  • CDocs.db.db_operations
  • datetime
  • logging
  • traceback

Required Imports

from CDocs.db import db_operations as db
from datetime import datetime
import logging
import traceback

Conditional/Optional Imports

These imports are only needed under specific conditions:

from datetime import datetime

Condition: Always needed - imported inside function for getting current year

Required (conditional)
import traceback

Condition: Only used in exception handling for detailed error logging

Optional

Usage Example

# Assuming proper configuration and database setup
from CDocs.config import DOCUMENT_CONFIG, DOCUMENT_NUMBERING
import logging

# Configure logger
logger = logging.getLogger(__name__)

# Define configuration
DOCUMENT_CONFIG = {
    'SOP': {
        'prefix': 'SOP',
        'numbering_format': '{prefix}-{dept}-{year}-{number:04d}'
    }
}

DOCUMENT_NUMBERING = {
    'reset_counter_yearly': True,
    'use_year_in_numbering': True,
    'numbering_format': '{prefix}-{dept}-{year}-{number:04d}'
}

# Generate document number
doc_number = generate_document_number('SOP', 'QA')
print(doc_number)  # Output: 'SOP-QA-2024-0001'

# Generate another for same type/dept
doc_number2 = generate_document_number('SOP', 'QA')
print(doc_number2)  # Output: 'SOP-QA-2024-0002'

# Different department gets separate counter
doc_number3 = generate_document_number('SOP', 'RD')
print(doc_number3)  # Output: 'SOP-RD-2024-0001'

Best Practices

  • Ensure Neo4j database is properly initialized with a CDocs root node before calling this function
  • Configure DOCUMENT_CONFIG and DOCUMENT_NUMBERING dictionaries before use to control numbering behavior
  • Implement the _generate_document_number_fallback function as a safety mechanism for database failures
  • Use consistent department and document type codes across your application to maintain organized counters
  • The function uses MERGE with atomic operations to prevent race conditions in concurrent environments
  • Monitor logger output for errors as the function falls back silently to in-memory counters on database failures
  • Consider the yearly reset behavior when planning document archival and retrieval strategies
  • Test the fallback mechanism to ensure continuity of operations during database outages
  • The function creates DocumentCounter nodes with relationships to the CDocs root node - ensure proper database cleanup policies

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function generate_document_number_v1 85.4% similar

    Generates a unique document number by combining document type code, department code, and using a settings-based generation function, with UUID fallback on error.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function get_next_document_number 85.2% similar

    Atomically retrieves and increments the next sequential document number for a specific document type and department combination from a Neo4j graph database.

    From: /tf/active/vicechatdev/CDocs/db/db_operations.py
  • function _generate_document_number_fallback 73.9% similar

    Generates a fallback document number using in-memory counters when database access fails, creating unique identifiers based on document type, department, and year.

    From: /tf/active/vicechatdev/CDocs/settings_prod.py
  • function validate_document_number 65.5% similar

    Validates a custom document number by checking its format, length constraints, and uniqueness in the database, returning a dictionary with validation results.

    From: /tf/active/vicechatdev/CDocs/controllers/document_controller.py
  • function initialize_document_counters 63.8% similar

    Initializes document counters in Neo4j by analyzing existing ControlledDocument nodes and creating DocumentCounter nodes with values higher than the maximum existing document numbers for each department/type combination.

    From: /tf/active/vicechatdev/CDocs/db/schema_manager.py
← Back to Browse