🔍 Code Extractor

function get_next_document_number

Maturity: 62

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

File:
/tf/active/vicechatdev/CDocs/db/db_operations.py
Lines:
190 - 256
Complexity:
moderate

Purpose

This function manages document numbering sequences in a Neo4j database by maintaining counters per document type and department. It ensures atomic increments to prevent duplicate document numbers in concurrent environments. The function supports yearly counter resets and creates counter nodes on-demand if they don't exist. It's designed for document management systems that require unique, sequential identifiers for documents organized by type and department.

Source Code

def get_next_document_number(doc_type: str, department: str) -> Optional[int]:
    """
    Get the next document number for a specific document type and department.
    This increments the counter atomically in the database.
    
    Args:
        doc_type: Document type code
        department: Department code
        
    Returns:
        Next document number as integer or None if failed
    """
    from datetime import datetime
    
    try:
        # Get CDocs root node UID
        cdocs_result = run_query(
            "MATCH (c:CDocs) RETURN c.UID as uid LIMIT 1"
        )
        
        if not cdocs_result:
            logger.error("Could not find CDocs root node")
            return None
            
        cdocs_uid = cdocs_result[0]['uid']
        current_year = datetime.now().year
        
        # Get counter with atomic increment
        result = 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": True  # Get this from settings if needed
            }
        )
        
        if result and 'value' in result[0]:
            return result[0]['value']
            
        return None
        
    except Exception as e:
        logger.error(f"Error getting next document number: {e}")
        return None

Parameters

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

Parameter Details

doc_type: A string code representing the document type (e.g., 'INV' for invoice, 'PO' for purchase order). This is used to maintain separate numbering sequences for different document categories.

department: A string code identifying the department (e.g., 'FIN' for finance, 'HR' for human resources). This allows each department to maintain independent document numbering sequences.

Return Value

Type: Optional[int]

Returns an Optional[int] - either an integer representing the next sequential document number for the specified document type and department combination, or None if the operation fails (e.g., database connection issues, missing CDocs root node, or query execution errors). The returned number is guaranteed to be unique and sequential within the scope of the document type and department.

Dependencies

  • neo4j
  • logging
  • uuid
  • typing
  • datetime
  • traceback
  • CDocs.db
  • CDocs.db.schema_manager

Required Imports

from typing import Optional
import logging
from CDocs.db import get_driver
from CDocs.db.schema_manager import NodeLabels
from CDocs.db.schema_manager import RelTypes

Conditional/Optional Imports

These imports are only needed under specific conditions:

from datetime import datetime

Condition: imported inside the function to get current year for counter management

Required (conditional)

Usage Example

# Assuming CDocs database is configured and run_query/logger are available

# Get next invoice number for finance department
next_number = get_next_document_number('INV', 'FIN')
if next_number:
    print(f'Next invoice number: {next_number}')
    # Use the number to create a document ID like 'INV-FIN-2024-00042'
    doc_id = f'INV-FIN-{datetime.now().year}-{next_number:05d}'
else:
    print('Failed to get next document number')

# Get next purchase order number for procurement
po_number = get_next_document_number('PO', 'PROC')
if po_number:
    print(f'Next PO number: {po_number}')

Best Practices

  • This function requires a CDocs root node to exist in the database before use - ensure database initialization is complete
  • The function uses atomic MERGE operations to prevent race conditions in concurrent environments
  • Counter values reset yearly by default (reset_yearly=True) - modify this parameter if different behavior is needed
  • Always check for None return value before using the result, as database operations can fail
  • The function depends on module-level run_query() and logger objects being properly configured
  • Document type and department codes should follow a consistent naming convention across your application
  • Consider implementing retry logic at the caller level for transient database failures
  • The counter creates a unique UID using randomUUID() for each DocumentCounter node
  • Timestamps (createdDate, lastUpdated) are automatically maintained for audit purposes

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function generate_document_number 85.2% similar

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

    From: /tf/active/vicechatdev/CDocs/settings_prod.py
  • function generate_document_number_v1 69.6% 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 _generate_document_number_fallback 62.8% 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 initialize_document_counters 61.3% 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
  • function validate_document_number 57.3% 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
← Back to Browse