• 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 custom subscription logs ledgers using mpdf engine

Implementing automated compliance reporting for custom custom subscription logs ledgers using mpdf engine

// includes/class-csr-reporting.php

namespace Antigravity\CSR;

use Mpdf\Mpdf;
use WP_Query;

class CSR_Reporting {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Reporting();
        }
        return self::$instance;
    }

    private function __construct() {
        // Hook into WordPress actions/filters if needed
        // For example, to trigger report generation via an AJAX call or a cron job
    }

    /**
     * Fetches subscription ledger data for a given date range.
     *
     * @param string $start_date Start date in YYYY-MM-DD format.
     * @param string $end_date End date in YYYY-MM-DD format.
     * @return array Array of ledger entries.
     */
    public function get_ledger_data( $start_date, $end_date ) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'subscription_ledger';

        $sql = $wpdb->prepare(
            "SELECT * FROM {$table_name}
             WHERE DATE(timestamp) BETWEEN %s AND %s
             ORDER BY timestamp ASC",
            $start_date,
            $end_date
        );

        $results = $wpdb->get_results( $sql, ARRAY_A );
        return $results;
    }

    /**
     * Generates a PDF report using mPDF.
     *
     * @param array $data Ledger data to include in the report.
     * @param string $report_title Title for the report.
     * @return string Path to the generated PDF file.
     */
    public function generate_pdf_report( $data, $report_title = 'Subscription Ledger Report' ) {
        try {
            $mpdf = new Mpdf( [
                'mode' => 'utf-8',
                'format' => 'A4',
                'margin_left' => 15,
                'margin_right' => 15,
                'margin_top' => 16,
                'margin_bottom' => 16,
                'margin_header' => 9,
                'margin_footer' => 9,
                'default_font_size' => 10,
                'default_font' => 'dejavusans', // Ensure this font is available or install it
            ] );

            // Set document metadata
            $mpdf->SetTitle( $report_title );
            $mpdf->SetAuthor( 'Your Company Name' );
            $mpdf->SetCreator( 'Custom Subscription Reporter Plugin' );

            // Add header and footer
            $mpdf->SetHTMLHeader( '<div style="text-align: right; font-weight: bold;">' . $report_title . '</div>' );
            $mpdf->SetHTMLFooter( '<div style="font-size: 9pt; text-align: center;">Page {PAGENO} of {nbpg} | Generated on ' . date( 'Y-m-d H:i:s' ) . '</div>' );

            // Build HTML content
            $html = '<h1 style="text-align: center;">' . esc_html( $report_title ) . '</h1>';
            $html .= '<p>Report Period: ' . date( 'Y-m-d', strtotime( $_POST['start_date'] ) ) . ' to ' . date( 'Y-m-d', strtotime( $_POST['end_date'] ) ) . '</p>'; // Assuming dates are passed via POST

            if ( ! empty( $data ) ) {
                $html .= '<table border="1" cellpadding="5" cellspacing="0" style="width: 100%; border-collapse: collapse;">';
                $html .= '<thead><tr>';
                // Dynamically generate headers from the first row's keys
                $headers = array_keys( $data[0] );
                foreach ( $headers as $header ) {
                    $html .= '<th>' . esc_html( ucwords( str_replace( '_', ' ', $header ) ) ) . '</th>';
                }
                $html .= '</tr></thead><tbody>';

                foreach ( $data as $row ) {
                    $html .= '<tr>';
                    foreach ( $row as $key => $value ) {
                        // Format specific fields for better readability
                        if ( $key === 'timestamp' ) {
                            $value = date( 'Y-m-d H:i:s', strtotime( $value ) );
                        } elseif ( $key === 'amount' ) {
                            $value = number_format( (float) $value, 2 );
                        }
                        $html .= '<td>' . esc_html( $value ) . '</td>';
                    }
                    $html .= '</tr>';
                }
                $html .= '</tbody></table>';
            } else {
                $html .= '<p>No data found for the specified period.</p>';
            }

            $mpdf->WriteHTML( $html );

            // Output the PDF
            // You can choose to output directly to browser, save to a file, or return as string
            $upload_dir = wp_upload_dir();
            $report_filename = sanitize_title( $report_title ) . '_' . date( 'YmdHis' ) . '.pdf';
            $file_path = $upload_dir['basedir'] . '/subscription-reports/' . $report_filename;

            // Ensure the directory exists
            if ( ! file_exists( dirname( $file_path ) ) ) {
                wp_mkdir_p( dirname( $file_path ) );
            }

            $mpdf->Output( $file_path, \Mpdf\Output\Destination::FILE );

            return $file_path;

        } catch ( \Mpdf\MpdfException $e ) {
            // Log the error or handle it appropriately
            error_log( 'mPDF Error: ' . $e->getMessage() );
            return false;
        }
    }
}

Admin Interface for Report Generation

To make this user-friendly, we’ll add an admin page where users can select a date range and trigger the report generation. This will be handled by the CSR_Admin class.

// includes/class-csr-admin.php

namespace Antigravity\CSR;

class CSR_Admin {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Admin();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'admin_init', array( $this, 'handle_report_generation' ) );
    }

    /**
     * Adds the admin menu page.
     */
    public function add_admin_menu() {
        add_menu_page(
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            'manage_options', // Capability required to access
            'csr-reports',
            array( $this, 'render_report_page' ),
            'dashicons-chart-bar', // Icon
            80 // Position
        );
    }

    /**
     * Renders the HTML for the admin report page.
     */
    public function render_report_page() {
        ?>
        

Advanced Considerations and Enhancements

This basic implementation can be extended significantly:

  • Scheduled Reporting: Integrate with WordPress Cron (WP-Cron) to automatically generate and email reports on a daily, weekly, or monthly basis. This would involve creating a WP-Cron event and a callback function that triggers the report generation and email sending process.
  • Customizable Templates: Instead of hardcoding HTML, use a templating engine or WordPress's built-in template hierarchy to allow for more flexible report designs. mPDF supports loading HTML from files.
  • Error Handling and Logging: Implement more sophisticated error logging using WordPress's built-in logging functions or a dedicated logging library.
  • User Roles and Permissions: Refine access control beyond 'manage_options' to allow specific user roles to generate or view reports.
  • Data Filtering and Aggregation: Add options for filtering data by subscription type, user status, or transaction type. Implement summary statistics (e.g., total revenue, number of new subscriptions) within the report.
  • Internationalization (i18n): Ensure all user-facing strings are translatable using WordPress's internationalization functions (e.g., __(), _e()).
  • Font Management: Ensure mPDF has access to necessary fonts for different languages. You might need to explicitly configure font paths or embed fonts.
  • Security: Always sanitize user inputs and use nonces for form submissions to prevent CSRF attacks. Ensure file permissions are set correctly for the upload directory.

By combining the power of mPDF with a well-structured WordPress plugin, you can create a highly effective and automated system for generating compliance-ready subscription ledger reports, saving significant time and reducing the risk of manual errors.

// custom-subscription-reporter.php

/*
Plugin Name: Custom Subscription Reporter
Description: Automates compliance reporting for custom subscription ledgers.
Version: 1.0
Author: Antigravity
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

// Include Composer autoloader
require_once __DIR__ . '/vendor/autoload.php';

// Define constants for clarity
define( 'CSR_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'CSR_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

// Include core files
require_once CSR_PLUGIN_PATH . 'includes/class-csr-reporting.php';
require_once CSR_PLUGIN_PATH . 'includes/class-csr-admin.php';

// Initialize classes
CSR_Reporting::init();
CSR_Admin::init();

// Activation hook (optional, for initial setup if needed)
register_activation_hook( __FILE__, 'csr_activate_plugin' );
function csr_activate_plugin() {
    // Example: Create custom table if it doesn't exist
    global $wpdb;
    $table_name = $wpdb->prefix . 'subscription_ledger';
    $charset_collate = $wpdb->get_charset_collate();

    if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
        $sql = "CREATE TABLE $table_name (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            transaction_id varchar(255) NOT NULL UNIQUE,
            user_id bigint(20) unsigned NOT NULL,
            subscription_id bigint(20) unsigned NOT NULL,
            transaction_type varchar(50) NOT NULL,
            amount decimal(10,2) NOT NULL,
            timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            status varchar(50) NOT NULL DEFAULT 'completed',
            PRIMARY KEY  (id),
            KEY user_id (user_id),
            KEY subscription_id (subscription_id)
        ) $charset_collate;";
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
    }
}

Generating PDF Reports with mPDF

The core logic for report generation will reside in a class, say CSR_Reporting. This class will fetch data from our custom ledger table and use mPDF to render it into a PDF document.

// includes/class-csr-reporting.php

namespace Antigravity\CSR;

use Mpdf\Mpdf;
use WP_Query;

class CSR_Reporting {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Reporting();
        }
        return self::$instance;
    }

    private function __construct() {
        // Hook into WordPress actions/filters if needed
        // For example, to trigger report generation via an AJAX call or a cron job
    }

    /**
     * Fetches subscription ledger data for a given date range.
     *
     * @param string $start_date Start date in YYYY-MM-DD format.
     * @param string $end_date End date in YYYY-MM-DD format.
     * @return array Array of ledger entries.
     */
    public function get_ledger_data( $start_date, $end_date ) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'subscription_ledger';

        $sql = $wpdb->prepare(
            "SELECT * FROM {$table_name}
             WHERE DATE(timestamp) BETWEEN %s AND %s
             ORDER BY timestamp ASC",
            $start_date,
            $end_date
        );

        $results = $wpdb->get_results( $sql, ARRAY_A );
        return $results;
    }

    /**
     * Generates a PDF report using mPDF.
     *
     * @param array $data Ledger data to include in the report.
     * @param string $report_title Title for the report.
     * @return string Path to the generated PDF file.
     */
    public function generate_pdf_report( $data, $report_title = 'Subscription Ledger Report' ) {
        try {
            $mpdf = new Mpdf( [
                'mode' => 'utf-8',
                'format' => 'A4',
                'margin_left' => 15,
                'margin_right' => 15,
                'margin_top' => 16,
                'margin_bottom' => 16,
                'margin_header' => 9,
                'margin_footer' => 9,
                'default_font_size' => 10,
                'default_font' => 'dejavusans', // Ensure this font is available or install it
            ] );

            // Set document metadata
            $mpdf->SetTitle( $report_title );
            $mpdf->SetAuthor( 'Your Company Name' );
            $mpdf->SetCreator( 'Custom Subscription Reporter Plugin' );

            // Add header and footer
            $mpdf->SetHTMLHeader( '<div style="text-align: right; font-weight: bold;">' . $report_title . '</div>' );
            $mpdf->SetHTMLFooter( '<div style="font-size: 9pt; text-align: center;">Page {PAGENO} of {nbpg} | Generated on ' . date( 'Y-m-d H:i:s' ) . '</div>' );

            // Build HTML content
            $html = '<h1 style="text-align: center;">' . esc_html( $report_title ) . '</h1>';
            $html .= '<p>Report Period: ' . date( 'Y-m-d', strtotime( $_POST['start_date'] ) ) . ' to ' . date( 'Y-m-d', strtotime( $_POST['end_date'] ) ) . '</p>'; // Assuming dates are passed via POST

            if ( ! empty( $data ) ) {
                $html .= '<table border="1" cellpadding="5" cellspacing="0" style="width: 100%; border-collapse: collapse;">';
                $html .= '<thead><tr>';
                // Dynamically generate headers from the first row's keys
                $headers = array_keys( $data[0] );
                foreach ( $headers as $header ) {
                    $html .= '<th>' . esc_html( ucwords( str_replace( '_', ' ', $header ) ) ) . '</th>';
                }
                $html .= '</tr></thead><tbody>';

                foreach ( $data as $row ) {
                    $html .= '<tr>';
                    foreach ( $row as $key => $value ) {
                        // Format specific fields for better readability
                        if ( $key === 'timestamp' ) {
                            $value = date( 'Y-m-d H:i:s', strtotime( $value ) );
                        } elseif ( $key === 'amount' ) {
                            $value = number_format( (float) $value, 2 );
                        }
                        $html .= '<td>' . esc_html( $value ) . '</td>';
                    }
                    $html .= '</tr>';
                }
                $html .= '</tbody></table>';
            } else {
                $html .= '<p>No data found for the specified period.</p>';
            }

            $mpdf->WriteHTML( $html );

            // Output the PDF
            // You can choose to output directly to browser, save to a file, or return as string
            $upload_dir = wp_upload_dir();
            $report_filename = sanitize_title( $report_title ) . '_' . date( 'YmdHis' ) . '.pdf';
            $file_path = $upload_dir['basedir'] . '/subscription-reports/' . $report_filename;

            // Ensure the directory exists
            if ( ! file_exists( dirname( $file_path ) ) ) {
                wp_mkdir_p( dirname( $file_path ) );
            }

            $mpdf->Output( $file_path, \Mpdf\Output\Destination::FILE );

            return $file_path;

        } catch ( \Mpdf\MpdfException $e ) {
            // Log the error or handle it appropriately
            error_log( 'mPDF Error: ' . $e->getMessage() );
            return false;
        }
    }
}

Admin Interface for Report Generation

To make this user-friendly, we'll add an admin page where users can select a date range and trigger the report generation. This will be handled by the CSR_Admin class.

// includes/class-csr-admin.php

namespace Antigravity\CSR;

class CSR_Admin {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Admin();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'admin_init', array( $this, 'handle_report_generation' ) );
    }

    /**
     * Adds the admin menu page.
     */
    public function add_admin_menu() {
        add_menu_page(
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            'manage_options', // Capability required to access
            'csr-reports',
            array( $this, 'render_report_page' ),
            'dashicons-chart-bar', // Icon
            80 // Position
        );
    }

    /**
     * Renders the HTML for the admin report page.
     */
    public function render_report_page() {
        ?>
        

Advanced Considerations and Enhancements

This basic implementation can be extended significantly:

  • Scheduled Reporting: Integrate with WordPress Cron (WP-Cron) to automatically generate and email reports on a daily, weekly, or monthly basis. This would involve creating a WP-Cron event and a callback function that triggers the report generation and email sending process.
  • Customizable Templates: Instead of hardcoding HTML, use a templating engine or WordPress's built-in template hierarchy to allow for more flexible report designs. mPDF supports loading HTML from files.
  • Error Handling and Logging: Implement more sophisticated error logging using WordPress's built-in logging functions or a dedicated logging library.
  • User Roles and Permissions: Refine access control beyond 'manage_options' to allow specific user roles to generate or view reports.
  • Data Filtering and Aggregation: Add options for filtering data by subscription type, user status, or transaction type. Implement summary statistics (e.g., total revenue, number of new subscriptions) within the report.
  • Internationalization (i18n): Ensure all user-facing strings are translatable using WordPress's internationalization functions (e.g., __(), _e()).
  • Font Management: Ensure mPDF has access to necessary fonts for different languages. You might need to explicitly configure font paths or embed fonts.
  • Security: Always sanitize user inputs and use nonces for form submissions to prevent CSRF attacks. Ensure file permissions are set correctly for the upload directory.

By combining the power of mPDF with a well-structured WordPress plugin, you can create a highly effective and automated system for generating compliance-ready subscription ledger reports, saving significant time and reducing the risk of manual errors.

composer require mpdf/mpdf

This will create a vendor directory and download mPDF and its dependencies. You'll need to include the Composer autoloader in your plugin's main file.

Plugin Structure and Core Components

We'll create a simple WordPress plugin. Let's assume the main plugin file is custom-subscription-reporter.php.

// custom-subscription-reporter.php

/*
Plugin Name: Custom Subscription Reporter
Description: Automates compliance reporting for custom subscription ledgers.
Version: 1.0
Author: Antigravity
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

// Include Composer autoloader
require_once __DIR__ . '/vendor/autoload.php';

// Define constants for clarity
define( 'CSR_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'CSR_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

// Include core files
require_once CSR_PLUGIN_PATH . 'includes/class-csr-reporting.php';
require_once CSR_PLUGIN_PATH . 'includes/class-csr-admin.php';

// Initialize classes
CSR_Reporting::init();
CSR_Admin::init();

// Activation hook (optional, for initial setup if needed)
register_activation_hook( __FILE__, 'csr_activate_plugin' );
function csr_activate_plugin() {
    // Example: Create custom table if it doesn't exist
    global $wpdb;
    $table_name = $wpdb->prefix . 'subscription_ledger';
    $charset_collate = $wpdb->get_charset_collate();

    if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
        $sql = "CREATE TABLE $table_name (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            transaction_id varchar(255) NOT NULL UNIQUE,
            user_id bigint(20) unsigned NOT NULL,
            subscription_id bigint(20) unsigned NOT NULL,
            transaction_type varchar(50) NOT NULL,
            amount decimal(10,2) NOT NULL,
            timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            status varchar(50) NOT NULL DEFAULT 'completed',
            PRIMARY KEY  (id),
            KEY user_id (user_id),
            KEY subscription_id (subscription_id)
        ) $charset_collate;";
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
    }
}

Generating PDF Reports with mPDF

The core logic for report generation will reside in a class, say CSR_Reporting. This class will fetch data from our custom ledger table and use mPDF to render it into a PDF document.

// includes/class-csr-reporting.php

namespace Antigravity\CSR;

use Mpdf\Mpdf;
use WP_Query;

class CSR_Reporting {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Reporting();
        }
        return self::$instance;
    }

    private function __construct() {
        // Hook into WordPress actions/filters if needed
        // For example, to trigger report generation via an AJAX call or a cron job
    }

    /**
     * Fetches subscription ledger data for a given date range.
     *
     * @param string $start_date Start date in YYYY-MM-DD format.
     * @param string $end_date End date in YYYY-MM-DD format.
     * @return array Array of ledger entries.
     */
    public function get_ledger_data( $start_date, $end_date ) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'subscription_ledger';

        $sql = $wpdb->prepare(
            "SELECT * FROM {$table_name}
             WHERE DATE(timestamp) BETWEEN %s AND %s
             ORDER BY timestamp ASC",
            $start_date,
            $end_date
        );

        $results = $wpdb->get_results( $sql, ARRAY_A );
        return $results;
    }

    /**
     * Generates a PDF report using mPDF.
     *
     * @param array $data Ledger data to include in the report.
     * @param string $report_title Title for the report.
     * @return string Path to the generated PDF file.
     */
    public function generate_pdf_report( $data, $report_title = 'Subscription Ledger Report' ) {
        try {
            $mpdf = new Mpdf( [
                'mode' => 'utf-8',
                'format' => 'A4',
                'margin_left' => 15,
                'margin_right' => 15,
                'margin_top' => 16,
                'margin_bottom' => 16,
                'margin_header' => 9,
                'margin_footer' => 9,
                'default_font_size' => 10,
                'default_font' => 'dejavusans', // Ensure this font is available or install it
            ] );

            // Set document metadata
            $mpdf->SetTitle( $report_title );
            $mpdf->SetAuthor( 'Your Company Name' );
            $mpdf->SetCreator( 'Custom Subscription Reporter Plugin' );

            // Add header and footer
            $mpdf->SetHTMLHeader( '<div style="text-align: right; font-weight: bold;">' . $report_title . '</div>' );
            $mpdf->SetHTMLFooter( '<div style="font-size: 9pt; text-align: center;">Page {PAGENO} of {nbpg} | Generated on ' . date( 'Y-m-d H:i:s' ) . '</div>' );

            // Build HTML content
            $html = '<h1 style="text-align: center;">' . esc_html( $report_title ) . '</h1>';
            $html .= '<p>Report Period: ' . date( 'Y-m-d', strtotime( $_POST['start_date'] ) ) . ' to ' . date( 'Y-m-d', strtotime( $_POST['end_date'] ) ) . '</p>'; // Assuming dates are passed via POST

            if ( ! empty( $data ) ) {
                $html .= '<table border="1" cellpadding="5" cellspacing="0" style="width: 100%; border-collapse: collapse;">';
                $html .= '<thead><tr>';
                // Dynamically generate headers from the first row's keys
                $headers = array_keys( $data[0] );
                foreach ( $headers as $header ) {
                    $html .= '<th>' . esc_html( ucwords( str_replace( '_', ' ', $header ) ) ) . '</th>';
                }
                $html .= '</tr></thead><tbody>';

                foreach ( $data as $row ) {
                    $html .= '<tr>';
                    foreach ( $row as $key => $value ) {
                        // Format specific fields for better readability
                        if ( $key === 'timestamp' ) {
                            $value = date( 'Y-m-d H:i:s', strtotime( $value ) );
                        } elseif ( $key === 'amount' ) {
                            $value = number_format( (float) $value, 2 );
                        }
                        $html .= '<td>' . esc_html( $value ) . '</td>';
                    }
                    $html .= '</tr>';
                }
                $html .= '</tbody></table>';
            } else {
                $html .= '<p>No data found for the specified period.</p>';
            }

            $mpdf->WriteHTML( $html );

            // Output the PDF
            // You can choose to output directly to browser, save to a file, or return as string
            $upload_dir = wp_upload_dir();
            $report_filename = sanitize_title( $report_title ) . '_' . date( 'YmdHis' ) . '.pdf';
            $file_path = $upload_dir['basedir'] . '/subscription-reports/' . $report_filename;

            // Ensure the directory exists
            if ( ! file_exists( dirname( $file_path ) ) ) {
                wp_mkdir_p( dirname( $file_path ) );
            }

            $mpdf->Output( $file_path, \Mpdf\Output\Destination::FILE );

            return $file_path;

        } catch ( \Mpdf\MpdfException $e ) {
            // Log the error or handle it appropriately
            error_log( 'mPDF Error: ' . $e->getMessage() );
            return false;
        }
    }
}

Admin Interface for Report Generation

To make this user-friendly, we'll add an admin page where users can select a date range and trigger the report generation. This will be handled by the CSR_Admin class.

// includes/class-csr-admin.php

namespace Antigravity\CSR;

class CSR_Admin {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Admin();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'admin_init', array( $this, 'handle_report_generation' ) );
    }

    /**
     * Adds the admin menu page.
     */
    public function add_admin_menu() {
        add_menu_page(
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            'manage_options', // Capability required to access
            'csr-reports',
            array( $this, 'render_report_page' ),
            'dashicons-chart-bar', // Icon
            80 // Position
        );
    }

    /**
     * Renders the HTML for the admin report page.
     */
    public function render_report_page() {
        ?>
        

Advanced Considerations and Enhancements

This basic implementation can be extended significantly:

  • Scheduled Reporting: Integrate with WordPress Cron (WP-Cron) to automatically generate and email reports on a daily, weekly, or monthly basis. This would involve creating a WP-Cron event and a callback function that triggers the report generation and email sending process.
  • Customizable Templates: Instead of hardcoding HTML, use a templating engine or WordPress's built-in template hierarchy to allow for more flexible report designs. mPDF supports loading HTML from files.
  • Error Handling and Logging: Implement more sophisticated error logging using WordPress's built-in logging functions or a dedicated logging library.
  • User Roles and Permissions: Refine access control beyond 'manage_options' to allow specific user roles to generate or view reports.
  • Data Filtering and Aggregation: Add options for filtering data by subscription type, user status, or transaction type. Implement summary statistics (e.g., total revenue, number of new subscriptions) within the report.
  • Internationalization (i18n): Ensure all user-facing strings are translatable using WordPress's internationalization functions (e.g., __(), _e()).
  • Font Management: Ensure mPDF has access to necessary fonts for different languages. You might need to explicitly configure font paths or embed fonts.
  • Security: Always sanitize user inputs and use nonces for form submissions to prevent CSRF attacks. Ensure file permissions are set correctly for the upload directory.

By combining the power of mPDF with a well-structured WordPress plugin, you can create a highly effective and automated system for generating compliance-ready subscription ledger reports, saving significant time and reducing the risk of manual errors.

Leveraging mPDF for Automated Subscription Ledger Compliance Reporting

For WordPress sites managing custom subscription models, maintaining auditable and compliant ledgers is paramount. This often involves generating periodic reports that detail subscription status, payments, and user activity. While manual report generation is feasible for small operations, scaling requires automation. This post details a robust approach to automating compliance reporting using the mPDF library, integrated within a custom WordPress plugin.

Prerequisites and Setup

Before diving into the implementation, ensure you have the following:

  • A WordPress installation with administrative access.
  • PHP 7.4+ with Composer installed.
  • Basic understanding of WordPress plugin development and custom database tables.
  • A custom database table (e.g., wp_subscription_ledger) to store subscription transaction data. This table should include fields like transaction_id, user_id, subscription_id, transaction_type (e.g., 'subscribe', 'renew', 'cancel', 'payment'), amount, timestamp, and status.

We'll use Composer to manage the mPDF dependency. Navigate to your plugin's root directory in your terminal and run:

composer require mpdf/mpdf

This will create a vendor directory and download mPDF and its dependencies. You'll need to include the Composer autoloader in your plugin's main file.

Plugin Structure and Core Components

We'll create a simple WordPress plugin. Let's assume the main plugin file is custom-subscription-reporter.php.

// custom-subscription-reporter.php

/*
Plugin Name: Custom Subscription Reporter
Description: Automates compliance reporting for custom subscription ledgers.
Version: 1.0
Author: Antigravity
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

// Include Composer autoloader
require_once __DIR__ . '/vendor/autoload.php';

// Define constants for clarity
define( 'CSR_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
define( 'CSR_PLUGIN_URL', plugin_dir_url( __FILE__ ) );

// Include core files
require_once CSR_PLUGIN_PATH . 'includes/class-csr-reporting.php';
require_once CSR_PLUGIN_PATH . 'includes/class-csr-admin.php';

// Initialize classes
CSR_Reporting::init();
CSR_Admin::init();

// Activation hook (optional, for initial setup if needed)
register_activation_hook( __FILE__, 'csr_activate_plugin' );
function csr_activate_plugin() {
    // Example: Create custom table if it doesn't exist
    global $wpdb;
    $table_name = $wpdb->prefix . 'subscription_ledger';
    $charset_collate = $wpdb->get_charset_collate();

    if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_name'" ) != $table_name ) {
        $sql = "CREATE TABLE $table_name (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            transaction_id varchar(255) NOT NULL UNIQUE,
            user_id bigint(20) unsigned NOT NULL,
            subscription_id bigint(20) unsigned NOT NULL,
            transaction_type varchar(50) NOT NULL,
            amount decimal(10,2) NOT NULL,
            timestamp datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
            status varchar(50) NOT NULL DEFAULT 'completed',
            PRIMARY KEY  (id),
            KEY user_id (user_id),
            KEY subscription_id (subscription_id)
        ) $charset_collate;";
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
        dbDelta( $sql );
    }
}

Generating PDF Reports with mPDF

The core logic for report generation will reside in a class, say CSR_Reporting. This class will fetch data from our custom ledger table and use mPDF to render it into a PDF document.

// includes/class-csr-reporting.php

namespace Antigravity\CSR;

use Mpdf\Mpdf;
use WP_Query;

class CSR_Reporting {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Reporting();
        }
        return self::$instance;
    }

    private function __construct() {
        // Hook into WordPress actions/filters if needed
        // For example, to trigger report generation via an AJAX call or a cron job
    }

    /**
     * Fetches subscription ledger data for a given date range.
     *
     * @param string $start_date Start date in YYYY-MM-DD format.
     * @param string $end_date End date in YYYY-MM-DD format.
     * @return array Array of ledger entries.
     */
    public function get_ledger_data( $start_date, $end_date ) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'subscription_ledger';

        $sql = $wpdb->prepare(
            "SELECT * FROM {$table_name}
             WHERE DATE(timestamp) BETWEEN %s AND %s
             ORDER BY timestamp ASC",
            $start_date,
            $end_date
        );

        $results = $wpdb->get_results( $sql, ARRAY_A );
        return $results;
    }

    /**
     * Generates a PDF report using mPDF.
     *
     * @param array $data Ledger data to include in the report.
     * @param string $report_title Title for the report.
     * @return string Path to the generated PDF file.
     */
    public function generate_pdf_report( $data, $report_title = 'Subscription Ledger Report' ) {
        try {
            $mpdf = new Mpdf( [
                'mode' => 'utf-8',
                'format' => 'A4',
                'margin_left' => 15,
                'margin_right' => 15,
                'margin_top' => 16,
                'margin_bottom' => 16,
                'margin_header' => 9,
                'margin_footer' => 9,
                'default_font_size' => 10,
                'default_font' => 'dejavusans', // Ensure this font is available or install it
            ] );

            // Set document metadata
            $mpdf->SetTitle( $report_title );
            $mpdf->SetAuthor( 'Your Company Name' );
            $mpdf->SetCreator( 'Custom Subscription Reporter Plugin' );

            // Add header and footer
            $mpdf->SetHTMLHeader( '<div style="text-align: right; font-weight: bold;">' . $report_title . '</div>' );
            $mpdf->SetHTMLFooter( '<div style="font-size: 9pt; text-align: center;">Page {PAGENO} of {nbpg} | Generated on ' . date( 'Y-m-d H:i:s' ) . '</div>' );

            // Build HTML content
            $html = '<h1 style="text-align: center;">' . esc_html( $report_title ) . '</h1>';
            $html .= '<p>Report Period: ' . date( 'Y-m-d', strtotime( $_POST['start_date'] ) ) . ' to ' . date( 'Y-m-d', strtotime( $_POST['end_date'] ) ) . '</p>'; // Assuming dates are passed via POST

            if ( ! empty( $data ) ) {
                $html .= '<table border="1" cellpadding="5" cellspacing="0" style="width: 100%; border-collapse: collapse;">';
                $html .= '<thead><tr>';
                // Dynamically generate headers from the first row's keys
                $headers = array_keys( $data[0] );
                foreach ( $headers as $header ) {
                    $html .= '<th>' . esc_html( ucwords( str_replace( '_', ' ', $header ) ) ) . '</th>';
                }
                $html .= '</tr></thead><tbody>';

                foreach ( $data as $row ) {
                    $html .= '<tr>';
                    foreach ( $row as $key => $value ) {
                        // Format specific fields for better readability
                        if ( $key === 'timestamp' ) {
                            $value = date( 'Y-m-d H:i:s', strtotime( $value ) );
                        } elseif ( $key === 'amount' ) {
                            $value = number_format( (float) $value, 2 );
                        }
                        $html .= '<td>' . esc_html( $value ) . '</td>';
                    }
                    $html .= '</tr>';
                }
                $html .= '</tbody></table>';
            } else {
                $html .= '<p>No data found for the specified period.</p>';
            }

            $mpdf->WriteHTML( $html );

            // Output the PDF
            // You can choose to output directly to browser, save to a file, or return as string
            $upload_dir = wp_upload_dir();
            $report_filename = sanitize_title( $report_title ) . '_' . date( 'YmdHis' ) . '.pdf';
            $file_path = $upload_dir['basedir'] . '/subscription-reports/' . $report_filename;

            // Ensure the directory exists
            if ( ! file_exists( dirname( $file_path ) ) ) {
                wp_mkdir_p( dirname( $file_path ) );
            }

            $mpdf->Output( $file_path, \Mpdf\Output\Destination::FILE );

            return $file_path;

        } catch ( \Mpdf\MpdfException $e ) {
            // Log the error or handle it appropriately
            error_log( 'mPDF Error: ' . $e->getMessage() );
            return false;
        }
    }
}

Admin Interface for Report Generation

To make this user-friendly, we'll add an admin page where users can select a date range and trigger the report generation. This will be handled by the CSR_Admin class.

// includes/class-csr-admin.php

namespace Antigravity\CSR;

class CSR_Admin {

    private static $instance;

    public static function init() {
        if ( self::$instance == null ) {
            self::$instance = new CSR_Admin();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
        add_action( 'admin_init', array( $this, 'handle_report_generation' ) );
    }

    /**
     * Adds the admin menu page.
     */
    public function add_admin_menu() {
        add_menu_page(
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            __( 'Subscription Reports', 'custom-subscription-reporter' ),
            'manage_options', // Capability required to access
            'csr-reports',
            array( $this, 'render_report_page' ),
            'dashicons-chart-bar', // Icon
            80 // Position
        );
    }

    /**
     * Renders the HTML for the admin report page.
     */
    public function render_report_page() {
        ?>
        

Advanced Considerations and Enhancements

This basic implementation can be extended significantly:

  • Scheduled Reporting: Integrate with WordPress Cron (WP-Cron) to automatically generate and email reports on a daily, weekly, or monthly basis. This would involve creating a WP-Cron event and a callback function that triggers the report generation and email sending process.
  • Customizable Templates: Instead of hardcoding HTML, use a templating engine or WordPress's built-in template hierarchy to allow for more flexible report designs. mPDF supports loading HTML from files.
  • Error Handling and Logging: Implement more sophisticated error logging using WordPress's built-in logging functions or a dedicated logging library.
  • User Roles and Permissions: Refine access control beyond 'manage_options' to allow specific user roles to generate or view reports.
  • Data Filtering and Aggregation: Add options for filtering data by subscription type, user status, or transaction type. Implement summary statistics (e.g., total revenue, number of new subscriptions) within the report.
  • Internationalization (i18n): Ensure all user-facing strings are translatable using WordPress's internationalization functions (e.g., __(), _e()).
  • Font Management: Ensure mPDF has access to necessary fonts for different languages. You might need to explicitly configure font paths or embed fonts.
  • Security: Always sanitize user inputs and use nonces for form submissions to prevent CSRF attacks. Ensure file permissions are set correctly for the upload directory.

By combining the power of mPDF with a well-structured WordPress plugin, you can create a highly effective and automated system for generating compliance-ready subscription ledger reports, saving significant time and reducing the risk of manual errors.

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

  • Debugging and Resolving deep-seated hook priority conflicts in third-party Zapier dynamic webhooks connectors
  • Step-by-Step Guide: Offloading high-frequency shipping tracking histories metadata writes to a Redis KV store
  • How to implement custom REST API Controllers endpoints with token authentication in Gutenberg blocks
  • Step-by-Step Guide to building a custom real-time activity logs block for Gutenberg using PHP block-render callbacks
  • How to implement custom WordPress Database Class ($wpdb) endpoints with token authentication in Gutenberg blocks

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 (41)
  • 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 (43)
  • WordPress Plugin Development (43)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Debugging and Resolving deep-seated hook priority conflicts in third-party Zapier dynamic webhooks connectors
  • Step-by-Step Guide: Offloading high-frequency shipping tracking histories metadata writes to a Redis KV store
  • How to implement custom REST API Controllers endpoints with token authentication in Gutenberg blocks

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