Factella. * * Lets a shop manager pick a date range and see, for that period: the number and * amount of B2B invoices, B2C sales grouped by VAT rate (base + tax), and refunds. * The table can be downloaded as CSV. Free feature — no lock, no upsell. * * @package EFactureFR */ defined( 'ABSPATH' ) || exit; /** * Period report. */ class EFacture_FR_Report { /** * Hook in. */ public static function init() { add_action( 'admin_menu', array( __CLASS__, 'menu' ) ); add_action( 'admin_post_factella_report_csv', array( __CLASS__, 'export_csv' ) ); } /** * Add the submenu under WooCommerce. */ public static function menu() { add_submenu_page( 'woocommerce', __( 'Factella – Report', 'factella' ), 'Factella', 'manage_woocommerce', 'factella-report', array( __CLASS__, 'render_page' ) ); } /** * Resolve the requested date range (defaults to the current month). * * @return array{from:string,to:string} */ protected static function range() { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only report filter. $from = isset( $_GET['from'] ) ? sanitize_text_field( wp_unslash( $_GET['from'] ) ) : gmdate( 'Y-m-01' ); $to = isset( $_GET['to'] ) ? sanitize_text_field( wp_unslash( $_GET['to'] ) ) : gmdate( 'Y-m-d' ); // phpcs:enable WordPress.Security.NonceVerification.Recommended if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $from ) ) { $from = gmdate( 'Y-m-01' ); } if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $to ) ) { $to = gmdate( 'Y-m-d' ); } return array( 'from' => $from, 'to' => $to, ); } /** * Aggregate the invoices/credit notes in the range. * * @param string $from Y-m-d. * @param string $to Y-m-d. * @return array */ public static function compute( $from, $to ) { $ids = wc_get_orders( array( 'limit' => -1, 'type' => 'shop_order', 'date_created' => $from . '...' . $to, 'return' => 'ids', ) ); $b2b = array( 'count' => 0, 'total' => 0.0, ); $b2c_rates = array(); $refunds = array( 'count' => 0, 'total' => 0.0, ); foreach ( $ids as $id ) { $order = wc_get_order( $id ); if ( ! $order || '' === (string) $order->get_meta( EFacture_FR_Invoice_Manager::META_NUMBER ) ) { continue; } $vat = EFacture_FR_Facturx::vat_breakdown( $order ); if ( 'B2B' === (string) $order->get_meta( EFacture_FR_Invoice_Manager::META_TYPE ) ) { ++$b2b['count']; $b2b['total'] += (float) $vat['grand']; } else { foreach ( $vat['groups'] as $group ) { $rate = number_format( (float) $group['rate'], 1, '.', '' ); if ( ! isset( $b2c_rates[ $rate ] ) ) { $b2c_rates[ $rate ] = array( 'base' => 0.0, 'tax' => 0.0, ); } $b2c_rates[ $rate ]['base'] += (float) $group['basis']; $b2c_rates[ $rate ]['tax'] += (float) $group['tax']; } } foreach ( $order->get_refunds() as $refund ) { if ( '' !== (string) $refund->get_meta( EFacture_FR_Invoice_Manager::META_CREDIT_NUMBER ) ) { ++$refunds['count']; $refunds['total'] += abs( (float) $refund->get_total() ); } } } ksort( $b2c_rates, SORT_NUMERIC ); return array( 'b2b' => $b2b, 'b2c_rates' => $b2c_rates, 'refunds' => $refunds, ); } /** * Render the report page. */ public static function render_page() { if ( ! current_user_can( 'manage_woocommerce' ) ) { return; } $range = self::range(); $data = self::compute( $range['from'], $range['to'] ); $symbol = get_woocommerce_currency_symbol(); $money = static function ( $n ) use ( $symbol ) { return number_format( (float) $n, 2, ',', ' ' ) . ' ' . $symbol; }; $csv_url = wp_nonce_url( admin_url( 'admin-post.php?action=factella_report_csv&from=' . rawurlencode( $range['from'] ) . '&to=' . rawurlencode( $range['to'] ) ), 'factella_report_csv' ); ?>