# easy-invoice/2.4.0/includes/Controllers/StatementController.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.4.0. 227 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.4.0/code/includes/Controllers/StatementController.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.4.0/raw/includes/Controllers/StatementController.php
- Modified: 2026-09-15T12:31:20+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.4.0/code/includes/Controllers/StatementController.php#L10-L20`.

```php
<?php
/**
 * Statement of account for a client.
 *
 * @package Easy_Invoice
 * @subpackage Controllers
 */

namespace EasyInvoice\Controllers;

use EasyInvoice\Services\ClientLedger;
use EasyInvoice\Services\PdfRenderer;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Shows and prints what a client owes across every document.
 *
 * Why this is not just an invoice list
 * ------------------------------------
 * "What do I owe you?" is the most common question a client asks, and an
 * invoice list cannot answer it: the answer depends on invoices, payments and
 * credit notes together, in date order, with a running balance. Every
 * accounting package has this, and it is the thing a bookkeeper reconciles
 * against at the end of a quarter.
 *
 * It is also the cheapest way to get paid. Sending a client one page that shows
 * three unpaid invoices is more effective than sending three reminders.
 */
class StatementController {

    /** Admin page slug. */
    const PAGE_SLUG = 'easy-invoice-statement';

    /** admin-post action that serves the PDF. */
    const ACTION_PDF = 'easy_invoice_statement_pdf';

    /**
     * Wire it up.
     *
     * @return void
     */
    public static function init(): void {
        add_action( 'admin_menu', [ __CLASS__, 'registerPage' ], 99 );
        add_action( 'easy_invoice_admin_main_content', [ __CLASS__, 'maybeRenderPage' ], 11 );
        add_action( 'admin_post_' . self::ACTION_PDF, [ __CLASS__, 'handlePdf' ] );

        add_filter( 'easy_invoice_self_rendering_pages', [ __CLASS__, 'claimPage' ] );
    }

    /**
     * Tell the dispatcher this page draws itself.
     *
     * @param array $slugs Slugs that render themselves.
     * @return array
     */
    public static function claimPage( $slugs ): array {
        $slugs   = is_array( $slugs ) ? $slugs : [];
        $slugs[] = self::PAGE_SLUG;

        return $slugs;
    }

    /**
     * Register the hidden page.
     *
     * @return void
     */
    public static function registerPage(): void {
        add_submenu_page(
            'easy-invoice-hidden',
            __( 'Statement of Account', 'easy-invoice' ),
            __( 'Statement', 'easy-invoice' ),
            (string) apply_filters( 'easy_invoice_menu_capability', 'manage_options', self::PAGE_SLUG ),
            self::PAGE_SLUG,
            [ __CLASS__, 'renderShell' ]
        );
    }

    /**
     * Render the admin chrome.
     *
     * @return void
     */
    public static function renderShell(): void {
        include EASY_INVOICE_PLUGIN_DIR . 'templates/main-template.php';
    }

    /**
     * Render the statement when the shell asks for it.
     *
     * @param string $page Page slug.
     * @return void
     */
    public static function maybeRenderPage( $page ): void {
        // A statement is a read-only view of documents the reader may already
        // see. Gating it on client *management* locked out the Accountant role,
        // which is exactly who reconciles against one.
        if ( self::PAGE_SLUG !== $page || ! easy_invoice_user_can( 'ei_view_invoices' ) ) {
            return;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only view.
        $client_id = isset( $_GET['client_id'] ) ? absint( $_GET['client_id'] ) : 0;
        $from      = isset( $_GET['from'] ) ? sanitize_text_field( wp_unslash( $_GET['from'] ) ) : '';
        $to        = isset( $_GET['to'] ) ? sanitize_text_field( wp_unslash( $_GET['to'] ) ) : '';
        // phpcs:enable

        $client = $client_id > 0 ? get_userdata( $client_id ) : false;

        if ( ! $client ) {
            echo '<div class="p-8"><p>' . esc_html__( 'That client does not exist.', 'easy-invoice' ) . '</p></div>';
            return;
        }

        $statement = ClientLedger::statement( $client_id, $from, $to );

        include EASY_INVOICE_PLUGIN_DIR . 'templates/admin/statement-page.php';
    }

    /**
     * Serve the statement as a PDF.
     *
     * @return void
     */
    public static function handlePdf(): void {
        $client_id = isset( $_GET['client_id'] ) ? absint( $_GET['client_id'] ) : 0;
        $nonce     = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';

        if ( ! wp_verify_nonce( $nonce, self::ACTION_PDF . '_' . $client_id ) ) {
            wp_die( esc_html__( 'Security check failed.', 'easy-invoice' ), '', [ 'response' => 403 ] );
        }

        if ( ! easy_invoice_user_can( 'ei_view_invoices' ) ) {
            wp_die( esc_html__( 'You do not have permission to do that.', 'easy-invoice' ), '', [ 'response' => 403 ] );
        }

        $client = $client_id > 0 ? get_userdata( $client_id ) : false;
        if ( ! $client ) {
            wp_die( esc_html__( 'That client does not exist.', 'easy-invoice' ), '', [ 'response' => 404 ] );
        }

        if ( ! PdfRenderer::isAvailable() ) {
            wp_die(
                esc_html__( 'Server-side PDF rendering is unavailable because the PDF library is missing.', 'easy-invoice' ),
                '',
                [ 'response' => 501, 'back_link' => true ]
            );
        }

        // phpcs:disable WordPress.Security.NonceVerification.Recommended -- covered by the nonce above.
        $from = isset( $_GET['from'] ) ? sanitize_text_field( wp_unslash( $_GET['from'] ) ) : '';
        $to   = isset( $_GET['to'] ) ? sanitize_text_field( wp_unslash( $_GET['to'] ) ) : '';
        // phpcs:enable

        $statement = ClientLedger::statement( $client_id, $from, $to );

        ob_start();
        include EASY_INVOICE_PLUGIN_DIR . 'templates/pdf/statement.php';
        $html = (string) ob_get_clean();

        $pdf = PdfRenderer::fromHtml( $html );

        if ( is_wp_error( $pdf ) ) {
            wp_die( esc_html( $pdf->get_error_message() ), '', [ 'response' => 500, 'back_link' => true ] );
        }

        $name = sanitize_file_name( 'statement-' . ( $client->display_name ?: $client->user_login ) . '.pdf' );

        if ( ob_get_length() ) {
            @ob_end_clean(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
        }

        nocache_headers();
        header( 'Content-Type: application/pdf' );
        header( 'Content-Disposition: attachment; filename="' . $name . '"' );
        header( 'Content-Length: ' . strlen( $pdf ) );
        header( 'X-Content-Type-Options: nosniff' );

        echo $pdf; // phpcs:ignore WordPress.Security.EscapeOutput -- binary document.
        exit;
    }

    /**
     * URL of the statement screen for a client.
     *
     * @param int $client_id Client ID.
     * @return string
     */
    public static function pageUrl( int $client_id ): string {
        return add_query_arg(
            [
                'page'      => self::PAGE_SLUG,
                'client_id' => $client_id,
            ],
            admin_url( 'admin.php' )
        );
    }

    /**
     * URL that downloads the statement PDF.
     *
     * @param int    $client_id Client ID.
     * @param string $from      Start date.
     * @param string $to        End date.
     * @return string
     */
    public static function pdfUrl( int $client_id, string $from = '', string $to = '' ): string {
        return wp_nonce_url(
            add_query_arg(
                array_filter(
                    [
                        'action'    => self::ACTION_PDF,
                        'client_id' => $client_id,
                        'from'      => $from,
                        'to'        => $to,
                    ]
                ),
                admin_url( 'admin-post.php' )
            ),
            self::ACTION_PDF . '_' . $client_id
        );
    }
}

```
