Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- 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