• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Implementing automated compliance reporting for custom hospital clinic appointments ledgers using native TCP printing streams

Implementing automated compliance reporting for custom hospital clinic appointments ledgers using native TCP printing streams

Architectural Overview: Bridging Clinic Data to Legacy Printers

This document details the implementation of an automated compliance reporting system for custom hospital clinic appointment ledgers. The core challenge lies in interfacing modern web-based data (managed via a WordPress plugin) with a ubiquitous, yet often overlooked, output mechanism: native TCP printing streams. This approach bypasses the need for complex printer drivers or intermediate spooling services, directly pushing formatted data to network-attached receipt printers, a common requirement in many clinical environments for immediate record-keeping and patient-facing summaries.

The system comprises three primary components:

  • WordPress Plugin Backend: A custom PHP plugin within WordPress to manage appointment data, generate reports, and initiate the printing process.
  • Data Formatting Layer: Logic within the plugin to transform structured appointment data into a printer-interpretable format, often a subset of ESC/POS commands or plain text with specific line breaks and character encoding.
  • TCP Print Client: A PHP script or function that establishes a TCP socket connection to the printer’s IP address and port, sending the formatted data stream.

WordPress Plugin Development: Core Data Structures and Hooks

We’ll leverage WordPress’s robust plugin API. For this example, assume a custom post type `clinic_appointment` exists, with relevant meta fields for patient name, appointment time, doctor, and notes. The plugin will register a new admin page for report generation and a shortcode for on-demand printing from the front end.

The primary plugin file (e.g., `clinic-compliance-reporter.php`) will include:

Plugin Activation and Deactivation Hooks

Essential for setting up custom database tables (if needed, though we’ll primarily use post meta here) and flushing rewrite rules. For simplicity, we’ll rely on WordPress’s built-in post meta management.

<?php
/*
Plugin Name: Clinic Compliance Reporter
Description: Generates compliance reports and prints them to network printers.
Version: 1.0
Author: Antigravity
*/

// Ensure this file is called within the WordPress environment.
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

// Activation hook (optional for this example, but good practice)
register_activation_hook( __FILE__, 'ccr_activate' );
function ccr_activate() {
    // Add any initial setup here, e.g., default options.
    // For this example, we assume 'clinic_appointment' CPT is already registered.
}

// Deactivation hook (optional)
register_deactivation_hook( __FILE__, 'ccr_deactivate' );
function ccr_deactivate() {
    // Clean up any transient options or custom tables if created.
}
?>

Admin Menu Registration

This creates a dedicated section in the WordPress admin dashboard for report generation.

add_action( 'admin_menu', 'ccr_admin_menu' );
function ccr_admin_menu() {
    add_menu_page(
        'Clinic Reports',          // Page title
        'Clinic Reports',          // Menu title
        'manage_options',          // Capability required
        'clinic-reports',          // Menu slug
        'ccr_render_report_page',  // Callback function to display the page
        'dashicons-chart-bar',     // Icon URL or Dashicon class
        80                         // Position in the menu
    );
}

function ccr_render_report_page() {
    // Check user capabilities
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    ?>
    <div class="wrap">
        <h1>Generate Clinic Compliance Report</h1>
        <form method="post" action="">
            <!-- Nonce for security -->
            <?php wp_nonce_field( 'ccr_generate_report_action', 'ccr_generate_report_nonce' ); ?>

            <label for="report_date_start">Start Date:</label>
            <input type="date" id="report_date_start" name="report_date_start" required><br><br>

            <label for="report_date_end">End Date:</label>
            <input type="date" id="report_date_end" name="report_date_end" required><br><br>

            <label for="printer_ip">Printer IP Address:</label>
            <input type="text" id="printer_ip" name="printer_ip" placeholder="e.g., 192.168.1.100" required><br><br>

            <label for="printer_port">Printer Port:</label>
            <input type="number" id="printer_port" name="printer_port" value="9100" required><br><br>

            <input type="submit" name="ccr_generate_report" class="button button-primary" value="Generate and Print Report">
        </form>
    </div>
    <?php
}

Report Generation and Printing Logic

This section handles the form submission, data retrieval, formatting, and the actual TCP printing. It’s crucial to validate all inputs and sanitize data before processing.

add_action( 'admin_init', 'ccr_handle_report_generation' );
function ccr_handle_report_generation() {
    // Check if the form was submitted and nonce is valid
    if ( isset( $_POST['ccr_generate_report'] ) && check_admin_referer( 'ccr_generate_report_action', 'ccr_generate_report_nonce' ) ) {

        // Sanitize and validate inputs
        $report_date_start = sanitize_text_field( $_POST['report_date_start'] );
        $report_date_end   = sanitize_text_field( $_POST['report_date_end'] );
        $printer_ip        = sanitize_text_field( $_POST['printer_ip'] );
        $printer_port      = intval( $_POST['printer_port'] );

        // Basic IP validation (can be more robust)
        if ( ! filter_var( $printer_ip, FILTER_VALIDATE_IP ) ) {
            wp_die( 'Invalid Printer IP Address.' );
        }
        if ( $printer_port < 1 || $printer_port > 65535 ) {
            wp_die( 'Invalid Printer Port Number.' );
        }

        // Fetch appointments within the date range
        $appointments = ccr_get_appointments_for_report( $report_date_start, $report_date_end );

        if ( empty( $appointments ) ) {
            wp_die( 'No appointments found for the specified date range.' );
        }

        // Format the report
        $report_content = ccr_format_report_for_printer( $appointments );

        // Send to printer
        if ( ! ccr_send_to_printer( $printer_ip, $printer_port, $report_content ) ) {
            wp_die( 'Failed to send report to printer. Check IP, port, and printer status.' );
        }

        // Success message
        add_action( 'admin_notices', function() {
            echo '<div class="notice notice-success is-dismissible"><p>Report generated and sent to printer successfully!</p></div>';
        });
    }
}

function ccr_get_appointments_for_report( $start_date, $end_date ) {
    $args = array(
        'post_type'      => 'clinic_appointment', // Your custom post type
        'posts_per_page' => -1, // Get all posts
        'meta_query'     => array(
            array(
                'key'     => 'appointment_datetime', // Assuming a datetime meta field
                'value'   => array( $start_date . ' 00:00:00', $end_date . ' 23:59:59' ),
                'type'    => 'DATETIME',
                'compare' => 'BETWEEN',
            ),
        ),
        'orderby'        => 'meta_value',
        'order'          => 'ASC',
        'meta_key'       => 'appointment_datetime',
    );

    $query = new WP_Query( $args );
    $appointments = array();

    if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();
            $post_id = get_the_ID();
            $appointments[] = array(
                'id'         => $post_id,
                'patient'    => get_post_meta( $post_id, 'patient_name', true ),
                'datetime'   => get_post_meta( $post_id, 'appointment_datetime', true ),
                'doctor'     => get_post_meta( $post_id, 'doctor_name', true ),
                'notes'      => get_post_meta( $post_id, 'appointment_notes', true ),
            );
        }
        wp_reset_postdata();
    }
    return $appointments;
}

Data Formatting for Native TCP Printing

Printers that accept native TCP streams often use command sets like ESC/POS (Epson Standard Code for Printers of POS systems). While full ESC/POS implementation is complex, many basic receipt printers can handle plain text with specific control characters for line breaks, bolding, and character encoding. For compliance reports, a clear, structured text output is usually sufficient.

function ccr_format_report_for_printer( $appointments ) {
    // Use UTF-8 for broad compatibility, but ensure printer supports it or use a fallback.
    // For basic printers, ISO-8859-1 might be safer if UTF-8 fails.
    $encoding = 'UTF-8';
    $report = "";

    // Add report header with ESC/POS commands for centering and bolding if supported.
    // Example: ESC ( 0x1B 0x28 0x00 ) - Select parameter for Function-A
    // Example: ESC ! ( 0x1B 0x21 ) - Select character style
    // For simplicity, we'll use basic text and line breaks.

    $report .= "========================================\n";
    $report .= "      Clinic Compliance Report\n";
    $report .= "========================================\n\n";

    // Add date range
    $report .= "Date Range: " . $_POST['report_date_start'] . " to " . $_POST['report_date_end'] . "\n\n";

    // Iterate through appointments
    foreach ( $appointments as $appt ) {
        $report .= "----------------------------------------\n";
        $report .= "Patient: " . mb_substr( $appt['patient'], 0, 30, $encoding ) . "\n"; // Truncate for width
        $report .= "Time:    " . date( 'H:i', strtotime( $appt['datetime'] ) ) . "\n";
        $report .= "Doctor:  " . mb_substr( $appt['doctor'], 0, 30, $encoding ) . "\n";
        if ( ! empty( $appt['notes'] ) ) {
            $report .= "Notes:   " . mb_substr( $appt['notes'], 0, 40, $encoding ) . "\n"; // Truncate notes
        }
        $report .= "----------------------------------------\n\n";
    }

    // Add report footer
    $report .= "Generated on: " . date( 'Y-m-d H:i:s' ) . "\n";
    $report .= "========================================\n";

    // Add a form feed character to eject the page/cut if supported
    // ESC FF (0x1B 0x0C) - Form Feed
    // $report .= "\x1B\x0C";

    // Convert to the target encoding if necessary.
    // For basic printers, direct UTF-8 might work, or convert to ISO-8859-1.
    // $report = mb_convert_encoding($report, 'ISO-8859-1', $encoding);

    return $report;
}

TCP Print Client Implementation

This PHP function establishes a raw TCP socket connection to the printer and sends the formatted data. Error handling is critical here, as network issues or printer unavailability are common.

function ccr_send_to_printer( $ip_address, $port, $data ) {
    $socket = null;
    $timeout = 10; // Seconds to wait for connection and data transfer

    // Attempt to open a socket connection
    $socket = @fsockopen( $ip_address, $port, $errno, $errstr, $timeout );

    if ( ! $socket ) {
        error_log( "CCRSocketError: Could not connect to printer at {$ip_address}:{$port}. Error {$errno}: {$errstr}" );
        return false;
    }

    // Set socket to non-blocking for potential timeouts during write
    stream_set_blocking( $socket, true );
    stream_set_timeout( $socket, $timeout );

    // Send the data
    $bytes_written = fwrite( $socket, $data );

    if ( $bytes_written === false || $bytes_written < strlen( $data ) ) {
        error_log( "CCRSocketError: Failed to write all data to printer at {$ip_address}:{$port}. Bytes written: {$bytes_written}" );
        fclose( $socket );
        return false;
    }

    // Optional: Read any response from the printer (e.g., status codes)
    // This is highly printer-specific and often not implemented for basic receipt printers.
    // $response = fread($socket, 1024);
    // if ($response === false) {
    //     error_log("CCRSocketError: Error reading response from printer.");
    // }

    // Close the connection
    fclose( $socket );

    return true;
}

Shortcode for On-Demand Printing

Allowing users to print individual appointment details or summaries directly from the front end. This requires careful security considerations, especially if printing sensitive patient data.

add_shortcode( 'print_appointment_receipt', 'ccr_shortcode_print_receipt' );
function ccr_shortcode_print_receipt( $atts ) {
    // Default attributes
    $a = shortcode_atts( array(
        'appointment_id' => 0,
        'printer_ip'     => get_option( 'ccr_default_printer_ip', '' ), // Store default in WP options
        'printer_port'   => get_option( 'ccr_default_printer_port', 9100 ),
    ), $atts, 'print_appointment_receipt' );

    $appointment_id = intval( $a['appointment_id'] );
    $printer_ip     = sanitize_text_field( $a['printer_ip'] );
    $printer_port   = intval( $a['printer_port'] );

    // Basic validation
    if ( $appointment_id <= 0 || empty( $printer_ip ) || $printer_port < 1 || $printer_port > 65535 ) {
        return '<p style="color: red;">Error: Invalid parameters for printing.</p>';
    }

    // Security: Ensure the current user has permission to view/print this appointment.
    // This is crucial for HIPAA compliance. You might need to check post ownership,
    // user roles, or specific meta data indicating permission.
    // For this example, we'll assume a logged-in user with sufficient role can trigger it.
    if ( ! is_user_logged_in() || ! current_user_can( 'read_private_posts' ) ) { // Adjust capability as needed
        return '<p style="color: red;">You do not have permission to print this receipt.</p>';
    }

    // Fetch the appointment data
    $post = get_post( $appointment_id );
    if ( ! $post || $post->post_type !== 'clinic_appointment' ) {
        return '<p style="color: red;">Error: Appointment not found.</p>';
    }

    // Format the receipt for a single appointment
    $appointment_data = array(
        'patient'    => get_post_meta( $appointment_id, 'patient_name', true ),
        'datetime'   => get_post_meta( $appointment_id, 'appointment_datetime', true ),
        'doctor'     => get_post_meta( $appointment_id, 'doctor_name', true ),
        'notes'      => get_post_meta( $appointment_id, 'appointment_notes', true ),
    );

    // Use a simplified formatting function for a single receipt
    $receipt_content = ccr_format_single_receipt( $appointment_data );

    // Add a button to trigger the print action via AJAX or a form submission
    // For simplicity, we'll use a form that submits to a handler.
    // A more advanced solution would use AJAX.

    $output = '<form method="post" action="">';
    $output .= wp_nonce_field( 'ccr_print_single_action_' . $appointment_id, 'ccr_print_single_nonce', true, false );
    $output .= '<input type="hidden" name="appointment_id_to_print" value="' . $appointment_id . '">';
    $output .= '<input type="hidden" name="printer_ip_single" value="' . esc_attr( $printer_ip ) . '">';
    $output .= '<input type="hidden" name="printer_port_single" value="' . esc_attr( $printer_port ) . '">';
    $output .= '<input type="hidden" name="ccr_print_single_receipt" value="1">';
    $output .= '<button type="submit" class="button button-secondary">Print Receipt</button>';
    $output .= '</form>';

    // Add a hidden div to hold the formatted content for potential AJAX printing
    // $output .= '<div id="receipt-content-' . $appointment_id . '" style="display:none;">' . esc_html( $receipt_content ) . '</div>';

    return $output;
}

// Handler for the shortcode form submission
add_action( 'template_redirect', 'ccr_handle_shortcode_print_submission' );
function ccr_handle_shortcode_print_submission() {
    if ( isset( $_POST['ccr_print_single_receipt'] ) && isset( $_POST['appointment_id_to_print'] ) ) {
        $appointment_id = intval( $_POST['appointment_id_to_print'] );
        $printer_ip     = sanitize_text_field( $_POST['printer_ip_single'] );
        $printer_port   = intval( $_POST['printer_port_single'] );

        // Verify nonce
        if ( ! isset( $_POST['ccr_print_single_nonce'] ) || ! wp_verify_nonce( $_POST['ccr_print_single_nonce'], 'ccr_print_single_action_' . $appointment_id ) ) {
            wp_die( 'Security check failed.' );
        }

        // Re-validate permissions (important if user session changed)
        if ( ! is_user_logged_in() || ! current_user_can( 'read_private_posts' ) ) {
            wp_die( 'Permission denied.' );
        }

        // Fetch appointment data again
        $post = get_post( $appointment_id );
        if ( ! $post || $post->post_type !== 'clinic_appointment' ) {
            wp_die( 'Appointment not found.' );
        }

        $appointment_data = array(
            'patient'    => get_post_meta( $appointment_id, 'patient_name', true ),
            'datetime'   => get_post_meta( $appointment_id, 'appointment_datetime', true ),
            'doctor'     => get_post_meta( $appointment_id, 'doctor_name', true ),
            'notes'      => get_post_meta( $appointment_id, 'appointment_notes', true ),
        );

        $receipt_content = ccr_format_single_receipt( $appointment_data );

        if ( ! ccr_send_to_printer( $printer_ip, $printer_port, $receipt_content ) ) {
            // Redirect back with an error message, or display inline.
            // For simplicity, we'll use wp_die. In a real app, use redirects with query args.
            wp_die( 'Failed to send receipt to printer. Check connection details and printer status.' );
        }

        // Redirect back to the page after successful printing
        wp_redirect( remove_query_arg( 'print_error', $_SERVER['REQUEST_URI'] ) );
        exit;
    }
}

function ccr_format_single_receipt( $appt_data ) {
    $encoding = 'UTF-8';
    $receipt = "";

    $receipt .= "========================================\n";
    $receipt .= "        Appointment Receipt\n";
    $receipt .= "========================================\n\n";

    $receipt .= "Patient: " . mb_substr( $appt_data['patient'], 0, 35, $encoding ) . "\n";
    $receipt .= "Date:    " . date( 'Y-m-d', strtotime( $appt_data['datetime'] ) ) . "\n";
    $receipt .= "Time:    " . date( 'H:i', strtotime( $appt_data['datetime'] ) ) . "\n";
    $receipt .= "Doctor:  " . mb_substr( $appt_data['doctor'], 0, 35, $encoding ) . "\n";
    if ( ! empty( $appt_data['notes'] ) ) {
        $receipt .= "Notes:   " . mb_substr( $appt_data['notes'], 0, 40, $encoding ) . "\n";
    }
    $receipt .= "\n";
    $receipt .= "----------------------------------------\n";
    $receipt .= "Thank you!\n";
    $receipt .= "========================================\n";

    // $receipt .= "\x1B\x0C"; // Form Feed

    return $receipt;
}

Configuration and Deployment Considerations

Printer Network Configuration: Ensure printers are accessible on the network via their IP addresses and that the specified port (commonly 9100 for raw TCP printing) is open in any firewalls. Static IP addresses for printers are highly recommended.

Character Encoding: The most common pitfall is character encoding. If the printer does not support UTF-8, you may need to convert the output string to a compatible encoding like ISO-8859-1 using mb_convert_encoding(). Test thoroughly with your specific printer model.

Security and HIPAA: Printing patient-specific appointment details requires strict adherence to HIPAA. Ensure that:

  • Access to the reporting page and shortcodes is restricted to authorized personnel via WordPress roles and capabilities.
  • Nonces are used for all form submissions to prevent CSRF attacks.
  • The system logs printing attempts and any errors.
  • Consider whether printing sensitive notes is appropriate or if they should be omitted from the printed report.
  • Physical printer security: Ensure printers are located in secure areas to prevent unauthorized access to printed records.

Error Handling and Logging: Robust error logging (using error_log() or a more sophisticated logging framework) is essential for diagnosing connection issues, data formatting problems, or permission errors. WordPress’s debug log is invaluable here.

Scalability: For high volumes of reports, consider offloading the printing process to a background job queue (e.g., using WP-Cron with a robust queueing plugin or an external service) to prevent long-running requests that could time out the user’s browser or the web server.

Printer Command Sets: While this example uses basic text, advanced features like barcode printing, specific font styles, or image logos would require implementing the printer’s specific command set (e.g., ESC/POS commands). This involves sending byte sequences that control printer functions.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins
  • How to implement native Redis caching layers for high-volume custom taxonomy queries in Carbon Fields custom wrappers
  • Building secure B2B pricing grids with custom REST API Controllers endpoints and role overrides

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (48)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (182)
  • WordPress Plugin Development (197)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Reducing database query bloat in Sage Roots modern environments layouts using custom lazy loaders
  • Performance Optimization: Tuning PHP-FPM and opcache pools for high-concurrency Firebase Realtime DB handlers
  • Reducing Largest Contentful Paint (LCP) by optimizing custom script enqueuing structures in legacy plugins

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala