purchase-orders-for-woocommerce.php
<?php
/*
Plugin Name: Purchase Orders for WooCommerce
Plugin URI: https://www.stationeryvenue.com
Description: Adds a Purchase Order payment method to WooCommerce with corporate user restrictions.
Author: Abhinav Rohatgi
Version: 1.12.7
Stable tag: 1.12.7
Text Domain: pofwc
Requires at least: 4.8
Requires PHP: 7.4
Requires plugins: woocommerce
WC requires at least: 3.0
WC tested up to: 9.3
License: GNU General Public License v2.0
License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Came directly here? Vamoose. Go on, scram.
}
use Automattic\WooCommerce\Utilities\OrderUtil;
/**
* Declare HPOS compatibility
*
* @since 1.9.0 Added compatibility
*/
add_action( 'before_woocommerce_init', function() {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables',
__FILE__, true );
}
} );
/**
* Loads translation files
*
* @since 1.2.0 Added function
*/
function pofwc_load_textdomain() {
load_plugin_textdomain( 'pofwc', false, basename( dirname( __FILE__ ) ) . '/languages' );
}
add_action( 'plugins_loaded', 'pofwc_load_textdomain' );
/**
* Get list of Indian states and union territories
*
* @since 1.12.0 Added Indian states list
* @return array List of states and union territories
*/
function pofwc_get_indian_states() {
return array(
// States
'andhra-pradesh' => __( 'Andhra Pradesh', 'pofwc' ),
'arunachal-pradesh' => __( 'Arunachal Pradesh', 'pofwc' ),
'assam' => __( 'Assam', 'pofwc' ),
'bihar' => __( 'Bihar', 'pofwc' ),
'chhattisgarh' => __( 'Chhattisgarh', 'pofwc' ),
'goa' => __( 'Goa', 'pofwc' ),
'gujarat' => __( 'Gujarat', 'pofwc' ),
'haryana' => __( 'Haryana', 'pofwc' ),
'himachal-pradesh' => __( 'Himachal Pradesh', 'pofwc' ),
'jharkhand' => __( 'Jharkhand', 'pofwc' ),
'karnataka' => __( 'Karnataka', 'pofwc' ),
'kerala' => __( 'Kerala', 'pofwc' ),
'madhya-pradesh' => __( 'Madhya Pradesh', 'pofwc' ),
'maharashtra' => __( 'Maharashtra', 'pofwc' ),
'manipur' => __( 'Manipur', 'pofwc' ),
'meghalaya' => __( 'Meghalaya', 'pofwc' ),
'mizoram' => __( 'Mizoram', 'pofwc' ),
'nagaland' => __( 'Nagaland', 'pofwc' ),
'odisha' => __( 'Odisha', 'pofwc' ),
'punjab' => __( 'Punjab', 'pofwc' ),
'rajasthan' => __( 'Rajasthan', 'pofwc' ),
'sikkim' => __( 'Sikkim', 'pofwc' ),
'tamil-nadu' => __( 'Tamil Nadu', 'pofwc' ),
'telangana' => __( 'Telangana', 'pofwc' ),
'tripura' => __( 'Tripura', 'pofwc' ),
'uttar-pradesh' => __( 'Uttar Pradesh', 'pofwc' ),
'uttarakhand' => __( 'Uttarakhand', 'pofwc' ),
'west-bengal' => __( 'West Bengal', 'pofwc' ),
// Union Territories
'andaman-nicobar' => __( 'Andaman and Nicobar Islands', 'pofwc' ),
'chandigarh' => __( 'Chandigarh', 'pofwc' ),
'dadra-nagar-haveli-daman-diu' => __( 'Dadra and Nagar Haveli and Daman and Diu', 'pofwc' ),
'delhi' => __( 'Delhi', 'pofwc' ),
'jammu-kashmir' => __( 'Jammu and Kashmir', 'pofwc' ),
'ladakh' => __( 'Ladakh', 'pofwc' ),
'lakshadweep' => __( 'Lakshadweep', 'pofwc' ),
'puducherry' => __( 'Puducherry', 'pofwc' ),
);
}
/**
* Get GST code to state mapping
*
* @since 1.12.5 Added GST code mapping
* @return array GST code to state mapping
*/
function pofwc_get_gst_state_mapping() {
return array(
'01' => 'jammu-kashmir',
'02' => 'himachal-pradesh',
'03' => 'punjab',
'04' => 'chandigarh',
'05' => 'uttarakhand',
'06' => 'haryana',
'07' => 'delhi',
'08' => 'rajasthan',
'09' => 'uttar-pradesh',
'10' => 'bihar',
'11' => 'sikkim',
'12' => 'arunachal-pradesh',
'13' => 'nagaland',
'14' => 'manipur',
'15' => 'mizoram',
'16' => 'tripura',
'17' => 'meghalaya',
'18' => 'assam',
'19' => 'west-bengal',
'20' => 'jharkhand',
'21' => 'odisha',
'22' => 'chhattisgarh',
'23' => 'madhya-pradesh',
'24' => 'gujarat',
'25' => 'dadra-nagar-haveli-daman-diu',
'26' => 'dadra-nagar-haveli-daman-diu',
'27' => 'maharashtra',
'29' => 'karnataka',
'30' => 'goa',
'31' => 'lakshadweep',
'32' => 'kerala',
'33' => 'tamil-nadu',
'34' => 'puducherry',
'35' => 'andaman-nicobar',
'36' => 'telangana',
'37' => 'andhra-pradesh',
'38' => 'ladakh'
);
}
/**
* Create corporate user role
*
* @since 1.12.0 Added corporate user role
*/
function pofwc_create_corporate_user_role() {
add_role(
'corporate_user',
__( 'Corporate User', 'pofwc' ),
array(
'read' => true,
'edit_posts' => false,
'delete_posts' => false,
)
);
}
register_activation_hook( __FILE__, 'pofwc_create_corporate_user_role' );
/**
* Adds option on activation to check if newly activated. If true, runs WooCommerce check after
* register_activation_hook redirection
*
* @since 1.2.0 Added function
*/
function pofwc_activate(){
add_option( 'pofwc_activated', 'pofwc' );
pofwc_create_corporate_user_role();
}
register_activation_hook( __FILE__, 'pofwc_activate' );
/**
* Set default role to corporate_user for new user creation
*
* @since 1.12.5 Added default role setting
*/
add_filter( 'pre_option_default_role', 'pofwc_set_default_role_corporate_user' );
function pofwc_set_default_role_corporate_user( $default_role ) {
// Only change default role on user creation page in admin
if ( is_admin() && isset( $_GET['action'] ) && $_GET['action'] === 'add-new-user' ) {
return 'corporate_user';
}
return $default_role;
}
/**
* Set corporate_user as selected in dropdown
*
* @since 1.12.5 Added JavaScript role selection
*/
add_action( 'admin_footer-user-new.php', 'pofwc_set_corporate_user_selected' );
function pofwc_set_corporate_user_selected() {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Set Corporate User as selected in role dropdown
$('#role').val('corporate_user');
});
</script>
<?php
}
/**
* Ensure corporate_user role exists in dropdown
*
* @since 1.12.5 Added role to editable roles
*/
add_filter( 'editable_roles', 'pofwc_add_corporate_user_to_roles' );
function pofwc_add_corporate_user_to_roles( $roles ) {
// Make sure corporate_user role appears in admin dropdowns
if ( ! isset( $roles['corporate_user'] ) ) {
$roles['corporate_user'] = array(
'name' => 'Corporate User',
'capabilities' => array(
'read' => true,
),
);
}
return $roles;
}
/**
* Add custom fields to user profile for existing users
*
* @since 1.12.0 Added custom user fields
* @since 1.12.5 Added payment gateway settings integration, PAN, email, mobile, auth validity
*/
function pofwc_add_custom_user_fields( $user ) {
pofwc_render_corporate_fields( $user );
}
/**
* Add custom fields after role selection in user creation
*
* @since 1.12.5 Added for proper user creation order
*/
function pofwc_add_custom_user_fields_after_role() {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Move Corporate Information section after Role field
var corporateSection = $('.corporate-info-section');
if (corporateSection.length) {
corporateSection.insertAfter($('tr:has(#role)').closest('tr'));
}
});
</script>
<?php
// Create a user object for new user creation
$user = new stdClass();
$user->ID = 0;
pofwc_render_corporate_fields( $user );
}
/**
* Render corporate fields (shared between edit and new user)
*
* @since 1.12.5 Extracted common rendering logic
*/
function pofwc_render_corporate_fields( $user ) {
$user_roles = isset( $user->roles ) ? $user->roles : array();
?>
<div class="corporate-info-section">
<h3><?php _e( 'Corporate Information', 'pofwc' ); ?></h3>
<table class="form-table">
<tr>
<th><label for="pofwc_company_name"><?php _e( 'Company Name', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_company_name" id="pofwc_company_name"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_company_name', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_gst_number"><?php _e( 'GSTIN', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_gst_number" id="pofwc_gst_number"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_gst_number', true ) ); ?>"
class="regular-text" required maxlength="15" style="text-transform: uppercase;" />
<p class="description"><?php _e( 'Enter 15-digit GSTIN. State and PAN will be auto-filled.', 'pofwc' ); ?></p>
</td>
</tr>
<tr>
<th><label for="pofwc_pan_number"><?php _e( 'PAN Number', 'pofwc' ); ?></label></th>
<td>
<input type="text" name="pofwc_pan_number" id="pofwc_pan_number"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_pan_number', true ) ); ?>"
class="regular-text" readonly style="background-color: #f1f1f1; text-transform: uppercase;" />
<p class="description"><?php _e( 'Auto-extracted from GSTIN.', 'pofwc' ); ?></p>
</td>
</tr>
<tr>
<th><label for="pofwc_billing_address_1"><?php _e( 'Billing Address 1', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_billing_address_1" id="pofwc_billing_address_1"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_billing_address_1', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_billing_address_2"><?php _e( 'Billing Address 2', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_billing_address_2" id="pofwc_billing_address_2"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_billing_address_2', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_landmark"><?php _e( 'Landmark', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_landmark" id="pofwc_landmark"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_landmark', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_pincode"><?php _e( 'Pin Code', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_pincode" id="pofwc_pincode"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_pincode', true ) ); ?>"
class="regular-text" required pattern="[0-9]{6}" />
</td>
</tr>
<tr>
<th><label for="pofwc_city"><?php _e( 'City', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_city" id="pofwc_city"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_city', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_state"><?php _e( 'State', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<?php
$current_state = get_user_meta( $user->ID, 'pofwc_state', true );
$states = pofwc_get_indian_states();
?>
<select name="pofwc_state" id="pofwc_state" class="regular-text" required>
<option value=""><?php _e( 'Select State', 'pofwc' ); ?></option>
<?php foreach( $states as $state_value => $state_name ) : ?>
<option value="<?php echo esc_attr( $state_value ); ?>" <?php selected( $current_state, $state_value ); ?>>
<?php echo esc_html( $state_name ); ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th><label for="pofwc_contact_number"><?php _e( 'Contact Number', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="tel" name="pofwc_contact_number" id="pofwc_contact_number"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_contact_number', true ) ); ?>"
class="regular-text" required pattern="[0-9]{10}" />
</td>
</tr>
<tr>
<th><label for="pofwc_mobile_number"><?php _e( 'Mobile Number', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="tel" name="pofwc_mobile_number" id="pofwc_mobile_number"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_mobile_number', true ) ); ?>"
class="regular-text" pattern="[0-9]{10}" required />
</td>
</tr>
<tr>
<th><label for="pofwc_email_id"><?php _e( 'Email ID', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="email" name="pofwc_email_id" id="pofwc_email_id"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_email_id', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_contact_person"><?php _e( 'Authorized Person', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_contact_person" id="pofwc_contact_person"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_contact_person', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<tr>
<th><label for="pofwc_designation"><?php _e( 'Designation', 'pofwc' ); ?> <span class="required">*</span></label></th>
<td>
<input type="text" name="pofwc_designation" id="pofwc_designation"
value="<?php echo esc_attr( get_user_meta( $user->ID, 'pofwc_designation', true ) ); ?>"
class="regular-text" required />
</td>
</tr>
<?php
$auth_validity_enabled = get_user_meta( $user->ID, 'pofwc_authorization_validity_enabled', true );
$auth_start_date = get_user_meta( $user->ID, 'pofwc_auth_start_date', true );
$auth_end_date = get_user_meta( $user->ID, 'pofwc_auth_end_date', true );
?>
<tr>
<th><label for="pofwc_authorization_validity_enabled"><?php _e( 'Authorization Validity', 'pofwc' ); ?></label></th>
<td>
<input type="checkbox" name="pofwc_authorization_validity_enabled" id="pofwc_authorization_validity_enabled"
value="1" <?php checked( $auth_validity_enabled, '1' ); ?> />
<label for="pofwc_authorization_validity_enabled"><?php _e( 'Enable authorization validity dates', 'pofwc' ); ?></label>
</td>
</tr>
<tr id="auth_dates_row" style="<?php echo $auth_validity_enabled ? '' : 'display:none;'; ?>">
<th><label><?php _e( 'Validity Period', 'pofwc' ); ?></label></th>
<td>
<label for="pofwc_auth_start_date"><?php _e( 'Start Date:', 'pofwc' ); ?></label>
<input type="date" name="pofwc_auth_start_date" id="pofwc_auth_start_date"
value="<?php echo esc_attr( $auth_start_date ); ?>" />
<br><br>
<label for="pofwc_auth_end_date"><?php _e( 'End Date:', 'pofwc' ); ?></label>
<input type="date" name="pofwc_auth_end_date" id="pofwc_auth_end_date"
value="<?php echo esc_attr( $auth_end_date ); ?>" />
</td>
</tr>
</table>
</div>
<style>
.required { color: red; }
.form-table th { width: 200px; }
</style>
<script type="text/javascript">
jQuery(document).ready(function($) {
// GST state mapping
var gstStateMapping = <?php echo json_encode( pofwc_get_gst_state_mapping() ); ?>;
// GST number processing
$('#pofwc_gst_number').on('input', function() {
var gstNumber = $(this).val().toUpperCase();
$(this).val(gstNumber);
if (gstNumber.length >= 2) {
var stateCode = gstNumber.substring(0, 2);
// Don't auto-select for codes 97 and 99
if (stateCode !== '97' && stateCode !== '99' && gstStateMapping[stateCode]) {
$('#pofwc_state').val(gstStateMapping[stateCode]);
}
}
if (gstNumber.length >= 12) {
// Extract PAN (characters 3-12, making it 10 characters)
var panNumber = gstNumber.substring(2, 12);
$('#pofwc_pan_number').val(panNumber);
}
});
// Authorization validity checkbox toggle
$('#pofwc_authorization_validity_enabled').change(function() {
if ($(this).is(':checked')) {
$('#auth_dates_row').show();
} else {
$('#auth_dates_row').hide();
$('#pofwc_auth_start_date, #pofwc_auth_end_date').val('');
}
});
});
</script>
<?php
// Payment gateway settings section (existing code)
if ( current_user_can( 'manage_options' ) ) {
$enabled_gateways = get_user_meta( $user->ID, 'enabled_payment_gateways', true );
if ( ! is_array( $enabled_gateways ) ) {
$enabled_gateways = array();
}
// Get all payment gateways (not just available ones for admin settings)
if ( class_exists( 'WC_Payment_Gateways' ) ) {
$payment_gateways = WC_Payment_Gateways::instance();
$all_gateways = $payment_gateways->payment_gateways();
$available_gateways = array();
// Filter to only enabled gateways
foreach ( $all_gateways as $gateway_id => $gateway ) {
if ( $gateway->enabled === 'yes' ) {
$available_gateways[$gateway_id] = $gateway;
}
}
} else {
$available_gateways = array();
}
?>
<h3><?php _e( 'Payment Gateway Settings', 'pofwc' ); ?></h3>
<p class="description"><?php _e( 'Select which payment gateways this corporate user can access. Payment gateways will only be shown if corporate information is provided above.', 'pofwc' ); ?></p>
<table class="form-table">
<tr id="payment_gateways_row">
<th><label><?php _e( 'Available Payment Gateways', 'pofwc' ); ?></label></th>
<td>
<fieldset>
<?php if ( ! empty( $available_gateways ) ) : ?>
<?php foreach ( $available_gateways as $gateway_id => $gateway ) : ?>
<label for="gateway_<?php echo esc_attr( $gateway_id ); ?>">
<input type="checkbox"
name="enabled_payment_gateways[]"
id="gateway_<?php echo esc_attr( $gateway_id ); ?>"
value="<?php echo esc_attr( $gateway_id ); ?>"
<?php checked( in_array( $gateway_id, $enabled_gateways ) ); ?> />
<?php echo esc_html( $gateway->get_method_title() ); ?>
</label><br>
<?php endforeach; ?>
<?php else : ?>
<p><?php _e( 'No payment gateways available.', 'pofwc' ); ?></p>
<?php endif; ?>
<span class="description"><?php _e( 'Corporate users will only see the selected payment gateways during checkout.', 'pofwc' ); ?></span>
</fieldset>
</td>
</tr>
</table>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Function to check if user has corporate information
function checkCorporateInfo() {
var hasCompanyName = $('#pofwc_company_name').val() && $('#pofwc_company_name').val().trim() !== '';
var hasGstNumber = $('#pofwc_gst_number').val() && $('#pofwc_gst_number').val().trim() !== '';
if (hasCompanyName || hasGstNumber) {
$('#payment_gateways_row').show();
} else {
$('#payment_gateways_row').hide();
// Uncheck all payment gateways if no corporate info
$('input[name="enabled_payment_gateways[]"]').prop('checked', false);
}
}
// Check on page load
checkCorporateInfo();
// Monitor corporate information fields
$('#pofwc_company_name, #pofwc_gst_number').on('input', function() {
checkCorporateInfo();
});
});
</script>
<?php
}
}
add_action( 'show_user_profile', 'pofwc_add_custom_user_fields' );
add_action( 'edit_user_profile', 'pofwc_add_custom_user_fields' );
// Use different approach for user creation - insert after role field
add_action( 'user_new_form', 'pofwc_add_custom_user_fields_after_role' );
/**
* Save custom user fields
*
* @since 1.12.0 Added save function for custom fields
* @since 1.12.5 Added payment gateway settings, corporate user auto-detection, and billing sync
*/
function pofwc_save_custom_user_fields( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return false;
}
$fields = array(
'pofwc_company_name',
'pofwc_gst_number',
'pofwc_pan_number',
'pofwc_billing_address_1',
'pofwc_billing_address_2',
'pofwc_landmark',
'pofwc_pincode',
'pofwc_city',
'pofwc_state',
'pofwc_contact_number',
'pofwc_mobile_number',
'pofwc_email_id',
'pofwc_contact_person',
'pofwc_designation'
);
// Validate required fields
$required_fields = array(
'pofwc_company_name' => __( 'Company Name', 'pofwc' ),
'pofwc_gst_number' => __( 'GSTIN', 'pofwc' ),
'pofwc_billing_address_1' => __( 'Billing Address 1', 'pofwc' ),
'pofwc_billing_address_2' => __( 'Billing Address 2', 'pofwc' ),
'pofwc_landmark' => __( 'Landmark', 'pofwc' ),
'pofwc_pincode' => __( 'Pin Code', 'pofwc' ),
'pofwc_city' => __( 'City', 'pofwc' ),
'pofwc_state' => __( 'State', 'pofwc' ),
'pofwc_contact_number' => __( 'Contact Number', 'pofwc' ),
'pofwc_mobile_number' => __( 'Mobile Number', 'pofwc' ),
'pofwc_email_id' => __( 'Email ID', 'pofwc' ),
'pofwc_contact_person' => __( 'Authorized Person', 'pofwc' ),
'pofwc_designation' => __( 'Designation', 'pofwc' )
);
$errors = array();
foreach ( $required_fields as $field => $label ) {
if ( empty( $_POST[ $field ] ) ) {
$errors[] = $label;
}
}
if ( ! empty( $errors ) ) {
wp_die( sprintf( __( 'Required fields missing: %s', 'pofwc' ), implode( ', ', $errors ) ) );
}
// Save all fields
foreach ( $fields as $field ) {
if ( isset( $_POST[ $field ] ) ) {
update_user_meta( $user_id, $field, sanitize_text_field( $_POST[ $field ] ) );
}
}
// Save authorization validity fields
$auth_validity_enabled = isset( $_POST['pofwc_authorization_validity_enabled'] ) ? '1' : '';
update_user_meta( $user_id, 'pofwc_authorization_validity_enabled', $auth_validity_enabled );
if ( $auth_validity_enabled ) {
$auth_start_date = isset( $_POST['pofwc_auth_start_date'] ) ? sanitize_text_field( $_POST['pofwc_auth_start_date'] ) : '';
$auth_end_date = isset( $_POST['pofwc_auth_end_date'] ) ? sanitize_text_field( $_POST['pofwc_auth_end_date'] ) : '';
update_user_meta( $user_id, 'pofwc_auth_start_date', $auth_start_date );
update_user_meta( $user_id, 'pofwc_auth_end_date', $auth_end_date );
} else {
delete_user_meta( $user_id, 'pofwc_auth_start_date' );
delete_user_meta( $user_id, 'pofwc_auth_end_date' );
}
// Auto-populate WooCommerce billing fields
$company_name = sanitize_text_field( $_POST['pofwc_company_name'] );
$address_1 = sanitize_text_field( $_POST['pofwc_billing_address_1'] );
$address_2 = sanitize_text_field( $_POST['pofwc_billing_address_2'] );
$city = sanitize_text_field( $_POST['pofwc_city'] );
$state = sanitize_text_field( $_POST['pofwc_state'] );
$postcode = sanitize_text_field( $_POST['pofwc_pincode'] );
$phone = sanitize_text_field( $_POST['pofwc_contact_number'] );
$contact_person = sanitize_text_field( $_POST['pofwc_contact_person'] );
// Map corporate fields to WooCommerce billing fields
update_user_meta( $user_id, 'billing_company', $company_name );
update_user_meta( $user_id, 'billing_address_1', $address_1 );
update_user_meta( $user_id, 'billing_address_2', $address_2 );
update_user_meta( $user_id, 'billing_city', $city );
update_user_meta( $user_id, 'billing_state', $state );
update_user_meta( $user_id, 'billing_postcode', $postcode );
update_user_meta( $user_id, 'billing_country', 'IN' );
update_user_meta( $user_id, 'billing_phone', $phone );
// Split contact person name for first/last name
$name_parts = explode( ' ', $contact_person, 2 );
update_user_meta( $user_id, 'billing_first_name', $name_parts[0] );
update_user_meta( $user_id, 'billing_last_name', isset( $name_parts[1] ) ? $name_parts[1] : '' );
// Also update shipping fields to match billing
update_user_meta( $user_id, 'shipping_company', $company_name );
update_user_meta( $user_id, 'shipping_address_1', $address_1 );
update_user_meta( $user_id, 'shipping_address_2', $address_2 );
update_user_meta( $user_id, 'shipping_city', $city );
update_user_meta( $user_id, 'shipping_state', $state );
update_user_meta( $user_id, 'shipping_postcode', $postcode );
update_user_meta( $user_id, 'shipping_country', 'IN' );
update_user_meta( $user_id, 'shipping_first_name', $name_parts[0] );
update_user_meta( $user_id, 'shipping_last_name', isset( $name_parts[1] ) ? $name_parts[1] : '' );
// Payment gateway settings and corporate user auto-detection (existing code)
if ( current_user_can( 'manage_options' ) ) {
// Save enabled payment gateways
$enabled_gateways = isset( $_POST['enabled_payment_gateways'] ) ? $_POST['enabled_payment_gateways'] : array();
update_user_meta( $user_id, 'enabled_payment_gateways', $enabled_gateways );
// Auto-detect if user is corporate based on corporate information
$gst_number = isset( $_POST['pofwc_gst_number'] ) ? sanitize_text_field( $_POST['pofwc_gst_number'] ) : '';
// Also check existing meta values if not in POST
if ( empty( $company_name ) ) {
$company_name = get_user_meta( $user_id, 'pofwc_company_name', true );
}
if ( empty( $gst_number ) ) {
$gst_number = get_user_meta( $user_id, 'pofwc_gst_number', true );
}
// Mark as corporate user if they have company information
$is_corporate = ( ! empty( $company_name ) || ! empty( $gst_number ) ) ? '1' : '';
update_user_meta( $user_id, 'is_corporate_user', $is_corporate );
}
}
add_action( 'personal_options_update', 'pofwc_save_custom_user_fields' );
add_action( 'edit_user_profile_update', 'pofwc_save_custom_user_fields' );
add_action( 'user_register', 'pofwc_save_custom_user_fields' );
/**
* Display corporate company information at checkout
*
* @since 1.12.2 Added company info display
* @since 1.12.5 Added PAN, mobile, email, authorized person with enhanced design
* @since 1.12.6 Updated alignment and styling
*/
function pofwc_display_corporate_info_at_checkout() {
if ( ! is_user_logged_in() ) {
return;
}
$current_user = wp_get_current_user();
if ( ! in_array( 'corporate_user', $current_user->roles ) ) {
return;
}
$company_name = get_user_meta( $current_user->ID, 'pofwc_company_name', true );
$gst_number = get_user_meta( $current_user->ID, 'pofwc_gst_number', true );
$pan_number = get_user_meta( $current_user->ID, 'pofwc_pan_number', true );
$contact_person = get_user_meta( $current_user->ID, 'pofwc_contact_person', true );
$mobile_number = get_user_meta( $current_user->ID, 'pofwc_mobile_number', true );
$email_id = get_user_meta( $current_user->ID, 'pofwc_email_id', true );
if ( empty( $company_name ) && empty( $gst_number ) ) {
return; // Don't show if no company info
}
// Extract PAN from GSTIN if PAN is empty but GSTIN exists
if ( empty( $pan_number ) && ! empty( $gst_number ) && strlen( $gst_number ) >= 12 ) {
$pan_number = substr( $gst_number, 2, 10 );
}
?>
<div class="pofwc-company-info-checkout">
<div class="pofwc-company-header">
<h3><?php _e( 'CORPORATE INFORMATION', 'pofwc' ); ?></h3>
</div>
<div class="pofwc-company-details-checkout">
<div class="pofwc-info-left">
<?php if ( $company_name ) : ?>
<div class="pofwc-info-row">
<span class="pofwc-label">COMPANY NAME :</span>
<span class="pofwc-value"><?php echo esc_html( strtoupper( $company_name ) ); ?></span>
</div>
<?php endif; ?>
<?php if ( $gst_number ) : ?>
<div class="pofwc-info-row">
<span class="pofwc-label">GSTIN :</span>
<span class="pofwc-value"><?php echo esc_html( strtoupper( $gst_number ) ); ?></span>
</div>
<?php endif; ?>
<?php if ( $pan_number ) : ?>
<div class="pofwc-info-row">
<span class="pofwc-label">PAN :</span>
<span class="pofwc-value"><?php echo esc_html( strtoupper( $pan_number ) ); ?></span>
</div>
<?php endif; ?>
</div>
<div class="pofwc-info-right">
<?php if ( $contact_person ) : ?>
<div class="pofwc-info-row">
<span class="pofwc-label">AUTHORIZED PERSON :</span>
<span class="pofwc-value"><?php echo esc_html( strtoupper( $contact_person ) ); ?></span>
</div>
<?php endif; ?>
<?php if ( $email_id ) : ?>
<div class="pofwc-info-row">
<span class="pofwc-label">EMAIL ID :</span>
<span class="pofwc-value"><?php echo esc_html( strtoupper( $email_id ) ); ?></span>
</div>
<?php endif; ?>
<?php if ( $mobile_number ) : ?>
<div class="pofwc-info-row">
<span class="pofwc-label">MOBILE NO. :</span>
<span class="pofwc-value"><?php echo esc_html( $mobile_number ); ?></span>
</div>
<?php endif; ?>
</div>
</div>
</div>
<style>
.pofwc-company-info-checkout {
border: 2px solid #e74c3c;
border-radius: 8px;
margin: 20px 0;
background: white;
overflow: hidden;
}
.pofwc-company-header {
background: #e74c3c;
padding: 12px 20px;
text-align: center;
}
.pofwc-company-header h3 {
margin: 0 !important;
font-size: 16px !important;
font-weight: 700 !important;
color: white !important;
letter-spacing: 1px;
}
.pofwc-company-details-checkout {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 20px;
gap: 30px;
background: white;
}
.pofwc-info-left {
display: flex;
flex-direction: column;
gap: 8px;
}
.pofwc-info-right {
display: flex;
flex-direction: column;
gap: 8px;
}
.pofwc-info-row {
display: flex;
align-items: center;
line-height: 1.4;
}
.pofwc-label {
font-weight: 700;
color: #333;
font-size: 14px;
min-width: 150px;
display: inline-block;
}
.pofwc-value {
font-weight: 400;
color: #333;
font-size: 14px;
margin-left: 5px;
}
@media (max-width: 768px) {
.pofwc-company-details-checkout {
grid-template-columns: 1fr;
gap: 20px;
}
}
</style>
<?php
}
add_action( 'woocommerce_checkout_before_customer_details', 'pofwc_display_corporate_info_at_checkout' );
/**
* Modify checkout fields for corporate users - Pre-filled fields only
*
* @since 1.12.0 Added checkout field modification
* @since 1.12.3 Simplified to pre-filled fields only
*/
function pofwc_modify_checkout_fields_for_corporate_users( $fields ) {
if ( ! is_user_logged_in() ) {
return $fields;
}
$current_user = wp_get_current_user();
if ( ! in_array( 'corporate_user', $current_user->roles ) ) {
return $fields;
}
// Get user meta data
$company_name = get_user_meta( $current_user->ID, 'pofwc_company_name', true );
$address_1 = get_user_meta( $current_user->ID, 'pofwc_billing_address_1', true );
$address_2 = get_user_meta( $current_user->ID, 'pofwc_billing_address_2', true );
$city = get_user_meta( $current_user->ID, 'pofwc_city', true );
$state = get_user_meta( $current_user->ID, 'pofwc_state', true );
$postcode = get_user_meta( $current_user->ID, 'pofwc_pincode', true );
$contact_person = get_user_meta( $current_user->ID, 'pofwc_contact_person', true );
if ( isset( $fields['shipping'] ) ) {
// Pre-fill shipping fields
$fields['shipping']['shipping_company']['default'] = $company_name;
$fields['shipping']['shipping_address_1']['default'] = $address_1;
$fields['shipping']['shipping_address_2']['default'] = $address_2;
$fields['shipping']['shipping_city']['default'] = $city;
$fields['shipping']['shipping_postcode']['default'] = $postcode;
// Add first name and last name if contact person is available
if ( $contact_person ) {
$name_parts = explode(' ', $contact_person, 2);
$fields['shipping']['shipping_first_name']['default'] = isset($name_parts[0]) ? $name_parts[0] : '';
$fields['shipping']['shipping_last_name']['default'] = isset($name_parts[1]) ? $name_parts[1] : '';
}
// Handle state field with dropdown options
$fields['shipping']['shipping_state']['type'] = 'select';
$fields['shipping']['shipping_state']['options'] = array( '' => __( 'Select State', 'pofwc' ) ) + pofwc_get_indian_states();
$fields['shipping']['shipping_state']['default'] = $state;
// Set country to India by default for corporate users
$fields['shipping']['shipping_country']['default'] = 'IN';
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'pofwc_modify_checkout_fields_for_corporate_users' );
/**
* Add "Add New Address" functionality for corporate users
*
* @since 1.12.0 Added new address functionality
* @since 1.12.3 Simplified version
*/
function pofwc_add_new_address_button() {
if ( ! is_user_logged_in() ) {
return;
}
$current_user = wp_get_current_user();
if ( ! in_array( 'corporate_user', $current_user->roles ) ) {
return;
}
?>
<div class="pofwc-address-controls-section">
<div class="pofwc-address-status">
<h4><?php _e( 'Shipping Address', 'pofwc' ); ?></h4>
<p><span id="pofwc-current-status"><?php _e( 'Using company address', 'pofwc' ); ?></span></p>
</div>
<div class="pofwc-toggle-buttons">
<button type="button" id="pofwc-enable-custom" class="button button-primary">
<?php _e( 'Add New Address', 'pofwc' ); ?>
</button>
</div>
</div>
<style>
.pofwc-address-controls-section {
background: #f8f9fa;
border: 1px solid #ddd;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
}
.pofwc-address-status h4 {
margin: 0 0 5px 0;
font-size: 16px;
color: #333;
}
.pofwc-address-status p {
margin: 0 0 15px 0;
}
.pofwc-toggle-buttons {
display: flex;
gap: 10px;
}
.pofwc-toggle-buttons .button {
padding: 10px 20px;
font-size: 14px;
border-radius: 4px;
transition: all 0.3s ease;
}
#pofwc-current-status {
font-weight: 600;
color: #28a745;
}
</style>
<?php
}
add_action( 'woocommerce_after_checkout_shipping_form', 'pofwc_add_new_address_button' );
/**
* CORRECTED: Register custom order status - Pending PO Verification
* Fixed typo and simplified to prevent issues
*
* @since 1.12.4 Added custom order status
* @since 1.12.7 Fixed typo and simplified
*/
function pofwc_register_pending_po_verification_order_status() {
register_post_status( 'wc-pending-po-verification', array(
'label' => 'Pending PO Verification',
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Pending PO Verification (%s)', 'Pending PO Verification (%s)' )
));
}
add_action( 'init', 'pofwc_register_pending_po_verification_order_status' );
/**
* CORRECTED: Add custom status to WooCommerce order status list
* Simplified version to prevent conflicts
*
* @since 1.12.4 Added to order statuses
* @since 1.12.7 Simplified to prevent issues
*/
function pofwc_add_pending_po_verification_to_order_statuses( $order_statuses ) {
$new_order_statuses = array();
foreach ( $order_statuses as $key => $status ) {
$new_order_statuses[ $key ] = $status;
if ( 'wc-pending' === $key ) {
$new_order_statuses['wc-pending-po-verification'] = 'Pending PO Verification';
}
}
return $new_order_statuses;
}
add_filter( 'wc_order_statuses', 'pofwc_add_pending_po_verification_to_order_statuses' );
/**
* CORRECTED: Anti-cancellation protection (simplified, no infinite loops)
* This prevents WooCommerce from cancelling PO orders automatically
*
* @since 1.12.7 Simplified anti-cancellation system
*/
function pofwc_exclude_po_orders_from_cancellation( $order_ids ) {
if ( empty( $order_ids ) || ! is_array( $order_ids ) ) {
return $order_ids;
}
foreach( $order_ids as $key => $order_id ) {
$order = wc_get_order( $order_id );
if( $order && $order->get_payment_method() === 'purchase_order_gateway' ) {
unset( $order_ids[$key] );
}
}
return $order_ids;
}
add_filter( 'woocommerce_cancel_unpaid_orders', 'pofwc_exclude_po_orders_from_cancellation' );
/**
* CORRECTED: Extend stock hold time for PO orders
* Prevents stock-based cancellation without causing issues
*
* @since 1.12.7 Simplified stock hold extension
*/
function pofwc_extend_po_order_stock_hold( $hold_stock_minutes, $order = null ) {
// Check if we have an order and it's a PO order
if ( $order && is_object( $order ) && method_exists( $order, 'get_payment_method' ) ) {
if ( $order->get_payment_method() === 'purchase_order_gateway' ) {
return 60 * 24 * 90; // 90 days for PO orders
}
}
// Fallback: check current order if in checkout context
if ( ! $order && isset( WC()->session ) ) {
$order_id = WC()->session->get( 'order_awaiting_payment' );
if ( $order_id ) {
$current_order = wc_get_order( $order_id );
if ( $current_order && $current_order->get_payment_method() === 'purchase_order_gateway' ) {
return 60 * 24 * 90;
}
}
}
return $hold_stock_minutes;
}
add_filter( 'woocommerce_hold_stock_minutes', 'pofwc_extend_po_order_stock_hold', 10, 2 );
/**
* CORRECTED: Fix existing orders function (prevents HTTP 500)
* Enhanced error handling and batch processing
*
* @since 1.12.7 Enhanced with better error handling
*/
function pofwc_fix_existing_po_orders() {
// Security check
if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( __( 'You do not have sufficient permissions to access this page.', 'pofwc' ) );
}
// Add time limit and memory limit to prevent timeouts
set_time_limit( 300 ); // 5 minutes
ini_set( 'memory_limit', '512M' );
try {
// Get orders in smaller batches to prevent memory issues
$fixed_count = 0;
$offset = 0;
$batch_size = 50;
do {
$orders = wc_get_orders( array(
'limit' => $batch_size,
'offset' => $offset,
'status' => array( 'pending', 'on-hold' ),
'payment_method' => 'purchase_order_gateway',
'return' => 'objects'
) );
foreach( $orders as $order ) {
$current_status = $order->get_status();
if( in_array( $current_status, array( 'pending', 'on-hold' ) ) ) {
$order->update_status(
'pending-po-verification',
sprintf( __( 'Status corrected from %s to pending-po-verification by admin tool.', 'pofwc' ), $current_status )
);
$fixed_count++;
}
}
$offset += $batch_size;
} while ( count( $orders ) === $batch_size );
if( $fixed_count > 0 ) {
echo '<div class="notice notice-success"><p>' .
sprintf( __( 'Successfully fixed %d purchase order statuses.', 'pofwc' ), $fixed_count ) .
'</p></div>';
} else {
echo '<div class="notice notice-info"><p>' .
__( 'No purchase orders found that need status correction.', 'pofwc' ) .
'</p></div>';
}
} catch ( Exception $e ) {
echo '<div class="notice notice-error"><p>' .
sprintf( __( 'Error occurred: %s', 'pofwc' ), esc_html( $e->getMessage() ) ) .
'</p></div>';
}
}
/**
* CORRECTED: Restore cancelled orders function (prevents HTTP 500)
* Enhanced error handling and extended search period
*
* @since 1.12.7 Enhanced with better error handling
*/
function pofwc_restore_cancelled_po_orders() {
// Security check
if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) {
wp_die( __( 'You do not have sufficient permissions to access this page.', 'pofwc' ) );
}
// Add time limit and memory limit
set_time_limit( 300 );
ini_set( 'memory_limit', '512M' );
try {
// Look for cancelled PO orders from the last 7 days (extended period)
$orders = wc_get_orders( array(
'limit' => 100, // Limit to prevent memory issues
'status' => 'cancelled',
'payment_method' => 'purchase_order_gateway',
'date_created' => '>' . ( time() - ( 7 * DAY_IN_SECONDS ) ), // Last 7 days
'return' => 'objects'
) );
$restored_count = 0;
foreach( $orders as $order ) {
// Always restore cancelled PO orders (they shouldn't be cancelled automatically)
$order->update_status(
'pending-po-verification',
__( 'Order restored from cancelled status - Purchase Orders should not be auto-cancelled.', 'pofwc' )
);
$restored_count++;
}
if( $restored_count > 0 ) {
echo '<div class="notice notice-success"><p>' .
sprintf( __( 'Successfully restored %d purchase orders from cancelled status.', 'pofwc' ), $restored_count ) .
'</p></div>';
} else {
echo '<div class="notice notice-info"><p>' .
__( 'No cancelled purchase orders found to restore from the last 7 days.', 'pofwc' ) .
'</p></div>';
}
} catch ( Exception $e ) {
echo '<div class="notice notice-error"><p>' .
sprintf( __( 'Error occurred: %s', 'pofwc' ), esc_html( $e->getMessage() ) ) .
'</p></div>';
}
}
/**
* CORRECTED: Admin tools registration
* Simplified and safer tool registration
*
* @since 1.12.7 Simplified admin tools
*/
function pofwc_add_status_fix_tools( $tools ) {
$tools['fix_po_order_status'] = array(
'name' => __( 'Fix Purchase Order Statuses', 'pofwc' ),
'button' => __( 'Fix PO Statuses', 'pofwc' ),
'desc' => __( 'Updates purchase orders from "pending" to "pending-po-verification" status.', 'pofwc' ),
'callback' => 'pofwc_fix_existing_po_orders'
);
$tools['restore_cancelled_po_orders'] = array(
'name' => __( 'Restore Cancelled Purchase Orders', 'pofwc' ),
'button' => __( 'Restore PO Orders', 'pofwc' ),
'desc' => __( 'Restores cancelled purchase orders from the last 7 days.', 'pofwc' ),
'callback' => 'pofwc_restore_cancelled_po_orders'
);
return $tools;
}
add_filter( 'woocommerce_debug_tools', 'pofwc_add_status_fix_tools' );
/**
* Register the custom email class with WooCommerce
*
* @since 1.12.4 Added email system
*/
function pofwc_add_po_verification_email_class( $email_classes ) {
require_once dirname( __FILE__ ) . '/class-po-verification-email.php';
$email_classes['WC_PO_Verification_Email'] = new WC_PO_Verification_Email();
return $email_classes;
}
add_filter( 'woocommerce_email_classes', 'pofwc_add_po_verification_email_class' );
/**
* Trigger email when order status changes to pending-po-verification
*
* @since 1.12.4 Added email trigger
*/
function pofwc_trigger_po_verification_email( $order_id, $order = false ) {
if ( ! $order ) {
$order = wc_get_order( $order_id );
}
// Only send if payment method is purchase order
if ( $order && $order->get_payment_method() === 'purchase_order_gateway' ) {
$emails = WC()->mailer()->get_emails();
if ( isset( $emails['WC_PO_Verification_Email'] ) ) {
$emails['WC_PO_Verification_Email']->trigger( $order_id, $order );
}
}
}
add_action( 'woocommerce_order_status_pending-po-verification', 'pofwc_trigger_po_verification_email', 10, 2 );
/**
* CORRECTED: Simplified status display CSS
* Clean styling without conflicts
*
* @since 1.12.7 Simplified styling
*/
function pofwc_add_status_styles() {
echo '<style>
.order-status.status-pending-po-verification,
mark.order-status.status-pending-po-verification {
background: #ffba00 !important;
color: #fff !important;
border-radius: 3px;
padding: 2px 8px;
font-weight: bold;
font-size: 11px;
}
.widefat .column-order_status mark.order-status.status-pending-po-verification {
background: #ffba00 !important;
color: #fff !important;
}
.woocommerce-orders-table .order-status.status-pending-po-verification {
background: #ffba00 !important;
color: #fff !important;
padding: 3px 8px;
border-radius: 3px;
font-weight: bold;
font-size: 12px;
}
</style>';
}
add_action( 'admin_head', 'pofwc_add_status_styles' );
add_action( 'wp_head', 'pofwc_add_status_styles' );
/**
* CORRECTED: Simplified cancelled orders notice (no performance issues)
* Only shows when there are actually cancelled orders
*
* @since 1.12.7 Simplified notice system
*/
function pofwc_add_cancelled_po_orders_notice() {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$screen = get_current_screen();
if ( $screen->id !== 'dashboard' ) {
return;
}
// Only check once per hour to avoid performance issues
$last_check = get_transient( 'pofwc_cancelled_orders_check' );
if ( $last_check ) {
return;
}
// Set transient for 1 hour
set_transient( 'pofwc_cancelled_orders_check', time(), HOUR_IN_SECONDS );
// Quick check for cancelled PO orders
$cancelled_count = wc_get_orders( array(
'limit' => 1,
'status' => 'cancelled',
'payment_method' => 'purchase_order_gateway',
'date_created' => '>' . ( time() - ( 7 * DAY_IN_SECONDS ) ),
'return' => 'ids',
'count' => true
) );
if ( $cancelled_count > 0 ) {
?>
<div class="notice notice-error">
<p>
<strong><?php _e( 'Purchase Orders Alert:', 'pofwc' ); ?></strong>
<?php _e( 'Cancelled purchase orders detected.', 'pofwc' ); ?>
<a href="<?php echo admin_url( 'admin.php?page=wc-status&tab=tools' ); ?>" class="button button-primary" style="margin-left: 10px;">
<?php _e( 'Check Tools', 'pofwc' ); ?>
</a>
</p>
</div>
<?php
}
}
add_action( 'admin_notices', 'pofwc_add_cancelled_po_orders_notice' );
/**
* Add validation to ensure corporate users have required address data
*
* @since 1.12.1 Added validation for corporate user address
*/
function pofwc_validate_corporate_user_address() {
if ( ! is_user_logged_in() ) {
return;
}
$current_user = wp_get_current_user();
if ( ! in_array( 'corporate_user', $current_user->roles ) ) {
return;
}
// Check if essential address fields are missing
$required_fields = array(
'pofwc_company_name' => __( 'Company Name', 'pofwc' ),
'pofwc_billing_address_1' => __( 'Address', 'pofwc' ),
'pofwc_city' => __( 'City', 'pofwc' ),
'pofwc_state' => __( 'State', 'pofwc' ),
'pofwc_pincode' => __( 'Pin Code', 'pofwc' )
);
$missing_fields = array();
foreach ( $required_fields as $field => $label ) {
if ( empty( get_user_meta( $current_user->ID, $field, true ) ) ) {
$missing_fields[] = $label;
}
}
if ( ! empty( $missing_fields ) ) {
wc_add_notice(
sprintf(
__( 'Your corporate profile is incomplete. Please contact administrator to update: %s', 'pofwc' ),
implode( ', ', $missing_fields )
),
'error'
);
}
}
add_action( 'woocommerce_checkout_process', 'pofwc_validate_corporate_user_address' );
/**
* Add admin notice for incomplete corporate user profiles
*
* @since 1.12.1 Added admin notifications
*/
function pofwc_admin_notice_incomplete_profiles() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// Get corporate users with incomplete profiles
$corporate_users = get_users( array( 'role' => 'corporate_user' ) );
$incomplete_users = array();
foreach ( $corporate_users as $user ) {
$required_fields = array( 'pofwc_company_name', 'pofwc_billing_address_1', 'pofwc_city', 'pofwc_state', 'pofwc_pincode' );
foreach ( $required_fields as $field ) {
if ( empty( get_user_meta( $user->ID, $field, true ) ) ) {
$incomplete_users[] = $user;
break;
}
}
}
if ( ! empty( $incomplete_users ) && get_current_screen()->id === 'dashboard' ) {
?>
<div class="notice notice-warning">
<p>
<strong><?php _e( 'Purchase Orders Plugin:', 'pofwc' ); ?></strong>
<?php
printf(
_n(
'There is %d corporate user with incomplete address information.',
'There are %d corporate users with incomplete address information.',
count( $incomplete_users ),
'pofwc'
),
count( $incomplete_users )
);
?>
<a href="<?php echo admin_url( 'users.php?role=corporate_user' ); ?>"><?php _e( 'Review users', 'pofwc' ); ?></a>
</p>
</div>
<?php
}
}
add_action( 'admin_notices', 'pofwc_admin_notice_incomplete_profiles' );
/**
* Adds the gateway to WC Available Gateways
*
* @param array $gateways all available WC gateways
* @return array $gateways all WC gateways + offline gateway
*
* @since 1.0.0
*/
function pofwc_add_to_gateways( $gateways ) {
$gateways[] = 'WC_Gateway_Purchase_Order';
return $gateways;
}
add_filter( 'woocommerce_payment_gateways', 'pofwc_add_to_gateways' );
/**
* Adds plugin page links
*
* @param array $links all plugin links
* @return array $links all plugin links + our custom links
*
* @since 1.0.0
*/
function pofwc_purchase_order_gateway_plugin_links( $links ) {
$plugin_links = array(
'<a href="' . admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=purchase_order_gateway' ) . '">' .
__( 'Settings', 'pofwc' ) . '</a>'
);
return array_merge( $plugin_links, $links );
}
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ),
'pofwc_purchase_order_gateway_plugin_links' );
/**
* Displays the purchase order information
*
* @param int $order_id The order ID
* @return string The formatted html
*
* @since 1.10.0
* @since 1.11.0 Added action hooks
* @since 1.12.0 Simplified to show only PO number and date
*/
function pofwc_show_invoice_address( $order_id ){
$order = wc_get_order( $order_id );
echo '<h2>' . __( 'Purchase order information', 'pofwc' ) . '</h2>';
echo '<p>';
echo '<strong>' . __( 'Purchase order number', 'pofwc' ) . ':</strong> ' .
$order->get_meta( '_purchase_order_number', true ) . '<br>';
$po_date = $order->get_meta( '_purchase_order_date', true );
if ( $po_date ) {
echo '<strong>' . __( 'Purchase order date', 'pofwc' ) . ':</strong> ' .
esc_html( $po_date ) . '<br>';
}
do_action( 'pofwc_account_display_after_po_form', $order );
echo '</p>';
}
add_action( 'woocommerce_view_order', 'pofwc_show_invoice_address', 20, 1 );
/**
* CRITICAL: Fix for PO Data Not Saving
* This hook ensures PO data is saved properly during checkout
*
* @since 1.12.7 Added to ensure PO data saving
*/
function pofwc_save_po_data_on_checkout( $order_id ) {
// Only process if this is a purchase order payment
$order = wc_get_order( $order_id );
if ( ! $order || $order->get_payment_method() !== 'purchase_order_gateway' ) {
return;
}
// Check if PO data is already saved (to avoid duplicating)
$existing_po_number = $order->get_meta( '_purchase_order_number', true );
if ( ! empty( $existing_po_number ) ) {
return; // Data already saved
}
// Save PO data from POST if it exists
if ( isset( $_POST['purchase-order-number'] ) && ! empty( trim( $_POST['purchase-order-number'] ) ) ) {
$order->update_meta_data( '_purchase_order_number', sanitize_text_field( $_POST['purchase-order-number'] ) );
}
if ( isset( $_POST['purchase-order-date'] ) && ! empty( trim( $_POST['purchase-order-date'] ) ) ) {
$order->update_meta_data( '_purchase_order_date', sanitize_text_field( $_POST['purchase-order-date'] ) );
}
// Save the order to ensure meta data is persisted
$order->save();
}
add_action( 'woocommerce_checkout_order_processed', 'pofwc_save_po_data_on_checkout', 10, 1 );
/**
* Debug function for troubleshooting (only in debug mode)
* Enable this if you need to troubleshoot issues
*
* @since 1.12.7 Enhanced debugging
*/
function pofwc_debug_order_processing( $order_id ) {
if ( ! WP_DEBUG ) {
return; // Only run in debug mode
}
$order = wc_get_order( $order_id );
if ( $order ) {
error_log( sprintf(
'PO Debug - Order ID: %d, Payment Method: %s, Status: %s, PO Number: %s, PO Date: %s, Time: %s',
$order_id,
$order->get_payment_method(),
$order->get_status(),
$order->get_meta( '_purchase_order_number', true ),
$order->get_meta( '_purchase_order_date', true ),
current_time( 'Y-m-d H:i:s' )
) );
}
}
// Uncomment the lines below if you need debugging
// add_action( 'woocommerce_checkout_order_processed', 'pofwc_debug_order_processing', 30, 1 );
// add_action( 'woocommerce_order_status_changed', 'pofwc_debug_order_processing', 30, 1 );
// Load corporate user management functions
require_once dirname( __FILE__ ) . '/includes/corporate-user-functions.php';
// Require files
require dirname( __FILE__ ) . '/class-purchase-order-gateway.php';
class-purchase-order-gateway.php
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Came directly here? Vamoose.
}
use Automattic\WooCommerce\Utilities\OrderUtil;
add_action( 'plugins_loaded', 'pofwc_purchase_order_gateway_init', 11 );
function pofwc_purchase_order_gateway_init() {
if ( class_exists( 'WC_Payment_Gateway' ) ) {
class WC_Gateway_Purchase_Order extends WC_Payment_Gateway {
/**
* @since 1.10.1 Declared properties
* @since 1.12.0 Simplified properties
* @since 1.12.5 Added corporate user restriction
* @since 1.12.7 Enhanced for better PO data handling
*/
public $title;
public $status;
public $description;
public $instructions;
public $po_number_label;
public $po_number_required;
public $po_date_label;
public $po_date_required;
public $po_show_in_email;
/**
* Constructor for the gateway
*
* @access public
*
* @since 1.0.0
* @since 1.4.0 Added field settings
* @since 1.12.0 Simplified for PO number and date only
* @since 1.12.5 Added corporate user support
* @since 1.12.7 Enhanced for better reliability
*/
public function __construct() {
$this->id = 'purchase_order_gateway';
$this->icon = apply_filters( 'woocommerce_offline_icon', '' );
$this->has_fields = true;
$this->method_title = __( 'Purchase Order', 'pofwc' );
$this->method_description = __( 'Allows purchase order payments for corporate users only.', 'pofwc' );
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables
$this->title = $this->get_option( 'title' );
$this->status = $this->get_option( 'status' );
$this->description = $this->get_option( 'description' );
$this->instructions = $this->get_option( 'instructions', $this->description );
$this->po_number_label = $this->get_option( 'po_number_label' );
$this->po_number_required = $this->get_option( 'po_number_required' );
$this->po_date_label = $this->get_option( 'po_date_label' );
$this->po_date_required = $this->get_option( 'po_date_required' );
$this->po_show_in_email = $this->get_option( 'po_show_in_email' );
// Actions
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id,
array( $this, 'process_admin_options' ) );
add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'pofwc_thankyou' ) );
// Emails
add_action( 'woocommerce_email_after_order_table',
array( $this, 'pofwc_email_instructions' ), 10, 3 );
add_filter( 'woocommerce_email_order_meta_fields',
array( $this, 'pofwc_email_order_meta_fields' ), 10, 3 );
// Display meta data
add_action( 'woocommerce_admin_order_data_after_billing_address',
array( $this, 'pofwc_display_purchase_order_meta' ), 10, 1 );
add_action( 'woocommerce_thankyou',
array( $this, 'pofwc_add_po_number_to_order_received_page' ), 1 );
// Other
add_filter( 'wc_stripe_validate_checkout_required_fields',
array( $this, 'pofwc_stripe_validate_checkout_unset_gateways_required_fields' ) );
}
/**
* Check if the gateway is available for use
* Corporate user restriction added
*
* @return bool
* @since 1.12.5 Added corporate user restriction
*/
public function is_available() {
$available = parent::is_available();
// Check if user is logged in
if ( ! is_user_logged_in() ) {
return false;
}
$user_id = get_current_user_id();
// Check if user is corporate user (auto-detected based on corporate info)
$is_corporate = get_user_meta( $user_id, 'is_corporate_user', true );
if ( ! $is_corporate ) {
return false;
}
// Check if purchase order gateway is enabled for this corporate user
$enabled_gateways = get_user_meta( $user_id, 'enabled_payment_gateways', true );
if ( ! is_array( $enabled_gateways ) || ! in_array( $this->id, $enabled_gateways ) ) {
return false;
}
return $available;
}
/**
* Gets WooCommerce order statuses
*
* @since 1.8.5 Added ability to select from all order statuses, including custom statuses
*/
private function get_order_statuses(){
$statuses = wc_get_order_statuses();
return $statuses;
}
/**
* Initialises Gateway Settings Form Fields
*
* @access public
*
* @return string The formatted HTML
*
* @since 1.0.0
* @since 1.12.0 Simplified form fields to PO number and date only
* @since 1.12.5 Updated description for corporate user restriction
* @since 1.12.7 Enhanced default status setting
*/
public function init_form_fields() {
$statuses = $this->get_order_statuses();
$this->form_fields = apply_filters( 'wc_offline_form_fields', array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'pofwc' ),
'type' => 'checkbox',
'label' => __( 'Enable purchase order payment', 'pofwc' ),
'default' => 'no',
'description' => __( 'This payment method will only be available to corporate users who have been granted access by administrators.', 'pofwc' ),
'desc_tip' => true,
),
'status' => array(
'title' => __( 'Checkout order status', 'pofwc' ),
'type' => 'select',
'options' => $statuses,
'description' => __( 'This controls the order status after checkout.', 'pofwc' ),
'desc_tip' => true,
'default' => 'wc-pending-po-verification',
),
'title' => array(
'title' => __( 'Title', 'pofwc' ),
'type' => 'text',
'description' => __( 'This controls the title for the payment method the customer sees during checkout.', 'pofwc' ),
'default' => __( 'Purchase order', 'pofwc' ),
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', 'pofwc' ),
'type' => 'textarea',
'description' => __( 'Payment method description that the customer will see on your checkout.', 'pofwc' ),
'default' => __( 'Please provide your purchase order details.', 'pofwc' ),
'desc_tip' => true,
),
'instructions' => array(
'title' => __( 'Instructions', 'pofwc' ),
'type' => 'textarea',
'description' => __( 'Instructions that will be added to the thank you page and emails.', 'pofwc' ),
'default' => 'Thank you for your purchase order. We will process your order shortly.',
'desc_tip' => true,
),
'po_number_label' => array(
'title' => __( 'Purchase order number label', 'pofwc' ),
'type' => 'text',
'description' => __( 'This controls the label of the purchase order number field the customer sees during checkout.', 'pofwc' ),
'default' => __( 'Purchase order number', 'pofwc' ),
'desc_tip' => true,
),
'po_number_required' => array(
'title' => __( 'Purchase order number', 'pofwc' ),
'type' => 'checkbox',
'label' => __( 'Is purchase order number a required field?', 'pofwc' ),
'default' => 'yes'
),
'po_date_label' => array(
'title' => __( 'Purchase order date label', 'pofwc' ),
'type' => 'text',
'description' => __( 'This controls the label of the purchase order date field the customer sees during checkout.', 'pofwc' ),
'default' => __( 'Purchase order date', 'pofwc' ),
'desc_tip' => true,
),
'po_date_required' => array(
'title' => __( 'Purchase order date', 'pofwc' ),
'type' => 'checkbox',
'label' => __( 'Is purchase order date a required field?', 'pofwc' ),
'default' => 'yes'
),
'po_show_in_email' => array(
'title' => __( 'Show purchase order details in order emails', 'pofwc' ),
'type' => 'checkbox',
'label' => __( 'Show details', 'pofwc' ),
'default' => 'yes',
'description' => __( 'Choose to display the purchase order details in order emails. Default is checked.', 'pofwc' ),
'desc_tip' => true,
),
) );
}
/**
* Defines output for the order received page
*
* @access public
*
* @return string The formatted HTML
*
* @since 1.0.0
*/
public function pofwc_thankyou() {
if ( $this->instructions ) {
echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) );
}
}
/**
* Adds content to the WC emails
*
* @access public
*
* @param WC_Order $order
* @param bool $sent_to_admin
* @param bool $plain_text
*
* @since 1.0.0
* @since 1.8.1 Added wp_kses_post() to output
*/
public function pofwc_email_instructions( $order, $sent_to_admin, $plain_text = false ) {
if ( $this->instructions && ! $sent_to_admin && $this->id === $order->get_payment_method() && $order->has_status( $this->status ) ) {
echo wp_kses_post( wpautop( wptexturize( $this->instructions ) ) . PHP_EOL );
}
}
/**
* CRITICAL FIX: Processes the payment and returns the result
* Enhanced to ensure PO data is properly saved and status is correctly set
*
* @access public
*
* @param int $order_id The order ID
* @return array Redirects to thankyou page with relevant data
*
* @since 1.0.0
* @since 1.12.0 Simplified to handle only PO number and date, removed zero-total auto-complete
* @since 1.12.4 Updated to use pending-po-verification status for all PO orders
* @since 1.12.7 CRITICAL FIX - Added proper order saving to ensure PO data persists
*/
public function process_payment( $order_id ) {
$order = wc_get_order( $order_id );
// CRITICAL: Add order meta data FIRST and validate
$po_number = '';
$po_date = '';
if( isset( $_POST['purchase-order-number'] ) && trim( $_POST['purchase-order-number'] ) != '' ){
$po_number = sanitize_text_field( $_POST['purchase-order-number'] );
$order->update_meta_data( '_purchase_order_number', $po_number );
}
if( isset( $_POST['purchase-order-date'] ) && trim( $_POST['purchase-order-date'] ) != '' ){
$po_date = sanitize_text_field( $_POST['purchase-order-date'] );
$order->update_meta_data( '_purchase_order_date', $po_date );
}
// CRITICAL FIX: Save the order IMMEDIATELY after updating meta data
// This ensures the PO data is persisted before any status changes
$order->save();
// Verify the data was saved (for debugging purposes)
if ( WP_DEBUG ) {
error_log( sprintf(
'PO Gateway Debug - Order ID: %d, PO Number: %s, PO Date: %s',
$order_id,
$order->get_meta( '_purchase_order_number', true ),
$order->get_meta( '_purchase_order_date', true )
) );
}
// Set status to pending PO verification for ALL purchase order payments
$order->update_status( 'pending-po-verification', 'Purchase order received - awaiting verification.' );
// Reduce stock levels.
wc_reduce_stock_levels( $order_id );
// Remove cart.
WC()->cart->empty_cart();
// Return thankyou redirect.
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
}
/**
* Adds form fields to checkout gateway
*
* @access public
*
* @return string The formatted HTML
*
* @since 1.0.0
* @since 1.12.0 Simplified to show only PO number and date fields
* @since 1.12.7 Enhanced field validation
*/
public function payment_fields(){
?>
<p class="form-row form-row-wide">
<?php echo $this->description; ?>
</p>
<?php
// Purchase Order Number Field
$po_number_label = ( $this->po_number_label != '' ) ? $this->po_number_label : __( 'Purchase order number', 'pofwc' );
$po_number_required_text = ( $this->po_number_required == 'yes' ) ? '<span class="required">*</span>' : '';
$po_number_required_class = ( $this->po_number_required == 'yes' ) ? 'validate-required' : '';
?>
<p class="form-row form-row-wide <?php echo $po_number_required_class; ?>">
<label for="purchase-order-number"><?php echo esc_html( $po_number_label ); ?><?php echo $po_number_required_text; ?></label>
<input type="text" id="purchase-order-number" name="purchase-order-number" class="input-text" placeholder="<?php echo esc_attr( $po_number_label ); ?>">
</p>
<?php
// Purchase Order Date Field
$po_date_label = ( $this->po_date_label != '' ) ? $this->po_date_label : __( 'Purchase order date', 'pofwc' );
$po_date_required_text = ( $this->po_date_required == 'yes' ) ? '<span class="required">*</span>' : '';
$po_date_required_class = ( $this->po_date_required == 'yes' ) ? 'validate-required' : '';
?>
<p class="form-row form-row-wide <?php echo $po_date_required_class; ?>">
<label for="purchase-order-date"><?php echo esc_html( $po_date_label ); ?><?php echo $po_date_required_text; ?></label>
<input type="date" id="purchase-order-date" name="purchase-order-date" class="input-text" placeholder="<?php echo esc_attr( $po_date_label ); ?>">
</p>
<?php do_action( 'pofwc_form_after_po_form' ); ?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Enhanced validation for PO fields
$('body').on('checkout_error', function() {
// Highlight any empty required PO fields
if ($('#purchase-order-number').hasClass('validate-required') && !$('#purchase-order-number').val()) {
$('#purchase-order-number').addClass('woocommerce-invalid');
}
if ($('#purchase-order-date').hasClass('validate-required') && !$('#purchase-order-date').val()) {
$('#purchase-order-date').addClass('woocommerce-invalid');
}
});
// Clear validation styling on input
$('#purchase-order-number, #purchase-order-date').on('input change', function() {
$(this).removeClass('woocommerce-invalid');
});
});
</script>
<?php
}
/**
* ENHANCED: Validates gateway checkout form fields
* Better validation with specific error messages
*
* @access public
*
* @return mixed Boolean or formatted HTML string
*
* @since 1.0.0
* @since 1.12.0 Simplified validation for PO number and date only
* @since 1.12.7 Enhanced validation with better error messages
*/
public function validate_fields() {
$valid = true;
// Check if required fields are set, if not add an error
if ( $this->po_number_required == 'yes' && ( ! isset( $_POST['purchase-order-number'] ) || empty( trim( $_POST['purchase-order-number'] ) ) ) ){
wc_add_notice( __( 'Purchase Order Number is required. Please enter a valid purchase order number.', 'pofwc' ), 'error' );
$valid = false;
}
if ( $this->po_date_required == 'yes' && ( ! isset( $_POST['purchase-order-date'] ) || empty( trim( $_POST['purchase-order-date'] ) ) ) ){
wc_add_notice( __( 'Purchase Order Date is required. Please select a valid date.', 'pofwc' ), 'error' );
$valid = false;
}
// Additional validation for PO date format if provided
if ( isset( $_POST['purchase-order-date'] ) && ! empty( trim( $_POST['purchase-order-date'] ) ) ) {
$date = trim( $_POST['purchase-order-date'] );
if ( ! $this->validate_date_format( $date ) ) {
wc_add_notice( __( 'Purchase Order Date format is invalid. Please use a valid date format.', 'pofwc' ), 'error' );
$valid = false;
}
}
return $valid;
}
/**
* NEW: Validate date format
*
* @since 1.12.7 Added date format validation
*/
private function validate_date_format( $date ) {
$d = DateTime::createFromFormat( 'Y-m-d', $date );
return $d && $d->format( 'Y-m-d' ) === $date;
}
/**
* Bypasses the Stripe plugin's validation of fields
*
* @access public
*
* @param array $required_fields The required fields
*
* @since 1.2.0
* @since 1.12.0 Simplified for PO number and date only
*/
public function pofwc_stripe_validate_checkout_unset_gateways_required_fields( $required_fields ){
if( isset( $required_fields['purchase-order-number'] ) ){
unset( $required_fields['purchase-order-number'] );
}
if( isset( $required_fields['purchase-order-date'] ) ){
unset( $required_fields['purchase-order-date'] );
}
return $required_fields;
}
/**
* ENHANCED: Displays meta data in order screen
* Better formatting and error handling
*
* @access public
*
* @return string The formatted HTML
*
* @since 1.0.0
* @since 1.12.0 Simplified to show only PO number and date
* @since 1.12.7 Enhanced with better formatting
*/
public function pofwc_display_purchase_order_meta(){
$order = wc_get_order( get_the_ID() );
// Ensure we have a valid order and it's a PO order
if ( ! $order || $order->get_payment_method() !== 'purchase_order_gateway' ) {
return;
}
$po_number = $order->get_meta('_purchase_order_number', true );
$po_date = $order->get_meta('_purchase_order_date', true );
// Only display if we have PO data
if ( $po_number || $po_date ) {
echo '<div class="pofwc-admin-order-meta">';
echo '<h3>' . __( 'Purchase Order Information', 'pofwc' ) . '</h3>';
echo '<div class="pofwc-po-details">';
if ( $po_number ) {
echo '<p><strong>' . __( 'Purchase Order Number:', 'pofwc' ) . '</strong> ' . esc_html( $po_number ) . '</p>';
}
if ( $po_date ) {
// Format the date nicely
$formatted_date = date_i18n( get_option( 'date_format' ), strtotime( $po_date ) );
echo '<p><strong>' . __( 'Purchase Order Date:', 'pofwc' ) . '</strong> ' . esc_html( $formatted_date ) . '</p>';
}
do_action( 'pofwc_admin_display_after_po_form', $order );
echo '</div>';
echo '</div>';
// Add some styling
echo '<style>
.pofwc-admin-order-meta {
background: #f9f9f9;
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
margin-top: 20px;
}
.pofwc-admin-order-meta h3 {
margin-top: 0;
color: #0073aa;
}
.pofwc-po-details p {
margin: 5px 0;
}
</style>';
}
}
/**
* ENHANCED: Displays the purchase order number on the order-received page
* Better formatting and data handling
*
* @access public
*
* @return string The formatted HTML
*
* @since 1.7.4
* @since 1.12.0 Simplified to show only PO number and date
* @since 1.12.7 Enhanced formatting
*/
public function pofwc_add_po_number_to_order_received_page() {
global $wp;
$order_id = absint( $wp->query_vars['order-received'] );
$order = wc_get_order( $order_id );
// Ensure we have a valid order and it's a PO order
if ( ! $order || $order->get_payment_method() !== 'purchase_order_gateway' ) {
return;
}
$purchase_order_number = $order->get_meta('_purchase_order_number', true );
$po_date = $order->get_meta('_purchase_order_date', true );
if ( $purchase_order_number || $po_date ) {
echo '<div class="pofwc-thankyou-po-info">';
if ( $purchase_order_number ) {
echo '<p><strong>' . __( 'Purchase Order Number', 'pofwc' ) . ':</strong> ' . esc_html( $purchase_order_number ) . '</p>';
}
if ( $po_date ) {
$formatted_date = date_i18n( get_option( 'date_format' ), strtotime( $po_date ) );
echo '<p><strong>' . __( 'Purchase Order Date', 'pofwc' ) . ':</strong> ' . esc_html( $formatted_date ) . '</p>';
}
do_action( 'pofwc_thankyou_display_after_po_form', $order );
echo '</div>';
// Add styling
echo '<style>
.pofwc-thankyou-po-info {
background: #e8f4f8;
border: 1px solid #0073aa;
border-radius: 4px;
padding: 15px;
margin: 20px 0;
}
.pofwc-thankyou-po-info p {
margin: 5px 0;
}
</style>';
}
}
/**
* ENHANCED: Adds the purchase order number to the order emails
* Better data handling and formatting
*
* @access public
*
* @param array $fields The order meta fields
* @param bool $sent_to_admin Send email to admin as well as customer?
* @param object $order The order object
*
* @return array $fields The updated order meta fields
*
* @since 1.7.4
* @since 1.12.0 Simplified to show only PO number and date
* @since 1.12.7 Enhanced with better data handling
*/
public function pofwc_email_order_meta_fields( $fields, $sent_to_admin, $order ) {
// Only show if admin setting is enabled and it's a PO order
if( $this->po_show_in_email == 'yes' && $order->get_payment_method() === 'purchase_order_gateway' ){
$purchase_order_number = $order->get_meta( '_purchase_order_number', true );
$purchase_order_date = $order->get_meta( '_purchase_order_date', true );
if ( $purchase_order_number ) {
$fields['purchase_order_number'] = array(
'label' => __( 'Purchase Order Number', 'pofwc' ),
'value' => esc_html( $purchase_order_number )
);
}
if ( $purchase_order_date ) {
$formatted_date = date_i18n( get_option( 'date_format' ), strtotime( $purchase_order_date ) );
$fields['purchase_order_date'] = array(
'label' => __( 'Purchase Order Date', 'pofwc' ),
'value' => esc_html( $formatted_date )
);
}
}
return $fields;
}
}
}
}
class-po-verification-email.php
<?php
/**
* Purchase Order Verification Email Class
*
* This file should be placed in your plugin folder alongside purchase-orders-for-woocommerce.php
*
* @package PurchaseOrdersForWooCommerce
* @version 1.12.4
* @since 1.12.4
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( ! class_exists( 'WC_PO_Verification_Email' ) ) :
/**
* Purchase Order Verification Email
*
* Sends email to customer when order status changes to "pending-po-verification"
*
* @class WC_PO_Verification_Email
* @extends WC_Email
*/
class WC_PO_Verification_Email extends WC_Email {
/**
* Constructor.
*/
public function __construct() {
$this->id = 'po_verification';
$this->title = __( 'Purchase Order Verification', 'pofwc' );
$this->description = __( 'Purchase order verification emails are sent to customers when their order is received and awaiting PO verification.', 'pofwc' );
$this->template_html = 'emails/po-verification.php';
$this->template_plain = 'emails/plain/po-verification.php';
// Set template base to look in theme first
$this->template_base = trailingslashit( get_template_directory() ) . 'woocommerce/';
// Set placeholders
$this->placeholders = array(
'{order_date}' => '',
'{order_number}' => '',
'{site_title}' => $this->get_blogname(),
);
// Call parent constructor.
parent::__construct();
// This is a customer email
$this->customer_email = true;
}
/**
* Get email subject.
*
* @return string
*/
public function get_default_subject() {
return __( 'Your order #{order_number} is awaiting purchase order verification', 'pofwc' );
}
/**
* Get email heading.
*
* @return string
*/
public function get_default_heading() {
return __( 'Purchase Order Received - Verification Required', 'pofwc' );
}
/**
* Trigger the sending of this email.
*
* @param int $order_id The order ID.
* @param WC_Order|false $order Order object.
*/
public function trigger( $order_id, $order = false ) {
$this->setup_locale();
if ( $order_id && ! is_a( $order, 'WC_Order' ) ) {
$order = wc_get_order( $order_id );
}
if ( is_a( $order, 'WC_Order' ) ) {
$this->object = $order;
$this->recipient = $this->object->get_billing_email();
$this->placeholders['{order_date}'] = wc_format_datetime( $this->object->get_date_created() );
$this->placeholders['{order_number}'] = $this->object->get_order_number();
}
if ( $this->is_enabled() && $this->get_recipient() ) {
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
$this->restore_locale();
}
/**
* Get content html.
*
* @return string
*/
public function get_content_html() {
return wc_get_template_html(
$this->template_html,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => false,
'plain_text' => false,
'email' => $this,
),
'woocommerce/',
$this->template_base
);
}
/**
* Get content plain.
*
* @return string
*/
public function get_content_plain() {
return wc_get_template_html(
$this->template_plain,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => false,
'plain_text' => true,
'email' => $this,
),
'woocommerce/',
$this->template_base
);
}
/**
* Initialize settings form fields.
*/
public function init_form_fields() {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable this email notification', 'woocommerce' ),
'default' => 'yes',
),
'subject' => array(
'title' => __( 'Subject', 'woocommerce' ),
'type' => 'text',
'desc_tip' => true,
'description' => sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>{site_title}, {order_date}, {order_number}</code>' ),
'placeholder' => $this->get_default_subject(),
'default' => '',
),
'heading' => array(
'title' => __( 'Email heading', 'woocommerce' ),
'type' => 'text',
'desc_tip' => true,
'description' => sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>{site_title}, {order_date}, {order_number}</code>' ),
'placeholder' => $this->get_default_heading(),
'default' => '',
),
'additional_content' => array(
'title' => __( 'Additional content', 'woocommerce' ),
'description' => __( 'Text to appear below the main email content.', 'woocommerce' ) . ' ' . sprintf( __( 'Available placeholders: %s', 'woocommerce' ), '<code>{site_title}, {order_date}, {order_number}</code>' ),
'css' => 'width:400px; height: 75px;',
'placeholder' => __( 'N/A', 'woocommerce' ),
'type' => 'textarea',
'default' => $this->get_default_additional_content(),
'desc_tip' => true,
),
'email_type' => array(
'title' => __( 'Email type', 'woocommerce' ),
'type' => 'select',
'description' => __( 'Choose which format of email to send.', 'woocommerce' ),
'default' => 'html',
'class' => 'email_type wc-enhanced-select',
'options' => $this->get_email_type_options(),
'desc_tip' => true,
),
);
}
/**
* Get default additional content.
*
* @return string
*/
public function get_default_additional_content() {
return __( 'We have received your purchase order and it is currently being verified. You will receive another email once your order is confirmed.', 'pofwc' );
}
}
endif;
includes/corporate-user-functions.php
<?php
/**
* Corporate User Functions
*
* @package Purchase_Orders_WooCommerce
* @since 1.12.5
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Add PO information after order number in My Account Orders
* NEW: Shows PO details in customer order history
*/
add_action( 'woocommerce_my_account_my_orders_column_order-number', 'pofwc_add_po_info_to_my_orders', 20 );
function pofwc_add_po_info_to_my_orders( $order ) {
// Check if this order used purchase order gateway
if ( $order->get_payment_method() === 'purchase_order_gateway' ) {
$po_number = $order->get_meta( '_purchase_order_number', true );
$po_date = $order->get_meta( '_purchase_order_date', true );
if ( $po_number ) {
echo '<br><small style="color: #666;"><strong>' . __( 'Purchase Order:', 'pofwc' ) . '</strong></small>';
echo '<br><small style="color: #666;">' . __( 'Number:', 'pofwc' ) . ' ' . esc_html( $po_number ) . '</small>';
if ( $po_date ) {
echo '<br><small style="color: #666;">' . __( 'Date:', 'pofwc' ) . ' ' . esc_html( $po_date ) . '</small>';
}
}
}
}
/**
* Restrict payment gateways for corporate users
* NEW: Filters available payment methods based on user permissions
*/
add_filter( 'woocommerce_available_payment_gateways', 'pofwc_restrict_payment_gateways_for_corporate_users' );
function pofwc_restrict_payment_gateways_for_corporate_users( $available_gateways ) {
// Only on frontend checkout/cart and for logged in users
// Don't filter in admin or when getting all gateways for settings
if ( is_admin() || ! is_user_logged_in() || ! ( is_checkout() || is_cart() || wp_doing_ajax() ) ) {
return $available_gateways;
}
$user_id = get_current_user_id();
$is_corporate = get_user_meta( $user_id, 'is_corporate_user', true );
if ( $is_corporate ) {
$enabled_gateways = get_user_meta( $user_id, 'enabled_payment_gateways', true );
if ( ! is_array( $enabled_gateways ) ) {
$enabled_gateways = array();
}
// Filter available gateways to only show enabled ones for this corporate user
foreach ( $available_gateways as $gateway_id => $gateway ) {
if ( ! in_array( $gateway_id, $enabled_gateways ) ) {
unset( $available_gateways[$gateway_id] );
}
}
}
return $available_gateways;
}
/**
* Auto-detect corporate users based on checkout data
* NEW: Automatically mark users as corporate during checkout
*/
add_action( 'woocommerce_checkout_update_user_meta', 'pofwc_auto_detect_corporate_user' );
function pofwc_auto_detect_corporate_user( $user_id ) {
// Check if user has corporate information in checkout
$company = WC()->checkout->get_value( 'billing_company' );
if ( ! empty( $company ) ) {
// Auto-mark as corporate user if they provided company information
update_user_meta( $user_id, 'is_corporate_user', '1' );
}
}
/**
* Add admin notice for corporate users without payment gateway access
* NEW: Warning for admins about corporate users without gateways
*/
add_action( 'admin_notices', 'pofwc_corporate_user_gateway_notice' );
function pofwc_corporate_user_gateway_notice() {
$screen = get_current_screen();
if ( $screen->id !== 'user-edit' && $screen->id !== 'profile' ) {
return;
}
// Check if we're editing a user
$user_id = isset( $_GET['user_id'] ) ? intval( $_GET['user_id'] ) : get_current_user_id();
$is_corporate = get_user_meta( $user_id, 'is_corporate_user', true );
$enabled_gateways = get_user_meta( $user_id, 'enabled_payment_gateways', true );
if ( $is_corporate && ( empty( $enabled_gateways ) || ! is_array( $enabled_gateways ) ) ) {
echo '<div class="notice notice-warning"><p><strong>' . __( 'Corporate User Notice:', 'pofwc' ) . '</strong> ' . __( 'This corporate user has no payment gateways enabled. Please select payment gateways in the Payment Gateway Settings section below.', 'pofwc' ) . '</p></div>';
}
}
/**
* Add corporate status indicator to users list
* NEW: Shows corporate status in admin users list
*/
add_filter( 'manage_users_columns', 'pofwc_add_corporate_column' );
function pofwc_add_corporate_column( $columns ) {
$columns['corporate_user'] = __( 'Corporate User', 'pofwc' );
return $columns;
}
/**
* Display corporate status in users list
*/
add_action( 'manage_users_custom_column', 'pofwc_show_corporate_column_content', 10, 3 );
function pofwc_show_corporate_column_content( $value, $column_name, $user_id ) {
if ( $column_name == 'corporate_user' ) {
$is_corporate = get_user_meta( $user_id, 'is_corporate_user', true );
if ( $is_corporate ) {
return '<span style="color: green;">✓ ' . __( 'Yes', 'pofwc' ) . '</span>';
} else {
return '<span style="color: #ccc;">– ' . __( 'No', 'pofwc' ) . '</span>';
}
}
return $value;
}
/**
* Add bulk action to mark users as corporate
* NEW: Bulk operations for corporate users
*/
add_filter( 'bulk_actions-users', 'pofwc_add_corporate_bulk_actions' );
function pofwc_add_corporate_bulk_actions( $bulk_actions ) {
$bulk_actions['mark_corporate'] = __( 'Mark as Corporate User', 'pofwc' );
$bulk_actions['unmark_corporate'] = __( 'Remove Corporate Status', 'pofwc' );
return $bulk_actions;
}
/**
* Handle bulk actions for corporate users
*/
add_filter( 'handle_bulk_actions-users', 'pofwc_handle_corporate_bulk_actions', 10, 3 );
function pofwc_handle_corporate_bulk_actions( $redirect_to, $doaction, $user_ids ) {
if ( $doaction !== 'mark_corporate' && $doaction !== 'unmark_corporate' ) {
return $redirect_to;
}
foreach ( $user_ids as $user_id ) {
if ( $doaction == 'mark_corporate' ) {
update_user_meta( $user_id, 'is_corporate_user', '1' );
} else {
delete_user_meta( $user_id, 'is_corporate_user' );
delete_user_meta( $user_id, 'enabled_payment_gateways' );
}
}
$redirect_to = add_query_arg( 'bulk_' . $doaction, count( $user_ids ), $redirect_to );
return $redirect_to;
}
/**
* Display bulk action notices
*/
add_action( 'admin_notices', 'pofwc_corporate_bulk_action_notices' );
function pofwc_corporate_bulk_action_notices() {
if ( ! empty( $_REQUEST['bulk_mark_corporate'] ) ) {
$count = intval( $_REQUEST['bulk_mark_corporate'] );
printf( '<div class="notice notice-success is-dismissible"><p>' .
_n( 'Marked %s user as corporate user.',
'Marked %s users as corporate users.',
$count, 'pofwc' ) . '</p></div>', $count );
}
if ( ! empty( $_REQUEST['bulk_unmark_corporate'] ) ) {
$count = intval( $_REQUEST['bulk_unmark_corporate'] );
printf( '<div class="notice notice-success is-dismissible"><p>' .
_n( 'Removed corporate status from %s user.',
'Removed corporate status from %s users.',
$count, 'pofwc' ) . '</p></div>', $count );
}
}
/**
* Add shortcode to display corporate user info
* NEW: Shortcode for displaying corporate information
*/
add_shortcode( 'corporate_user_info', 'pofwc_corporate_user_info_shortcode' );
function pofwc_corporate_user_info_shortcode( $atts ) {
if ( ! is_user_logged_in() ) {
return __( 'Please log in to view corporate information.', 'pofwc' );
}
$user_id = get_current_user_id();
$is_corporate = get_user_meta( $user_id, 'is_corporate_user', true );
if ( ! $is_corporate ) {
return __( 'You are not registered as a corporate user.', 'pofwc' );
}
$enabled_gateways = get_user_meta( $user_id, 'enabled_payment_gateways', true );
if ( ! is_array( $enabled_gateways ) ) {
$enabled_gateways = array();
}
$output = '<div class="corporate-user-info">';
$output .= '<h4>' . __( 'Corporate User Information', 'pofwc' ) . '</h4>';
$output .= '<p>' . __( 'Status: Corporate User', 'pofwc' ) . '</p>';
if ( ! empty( $enabled_gateways ) ) {
$output .= '<p>' . __( 'Available Payment Methods:', 'pofwc' ) . '</p>';
$output .= '<ul>';
if ( class_exists( 'WC_Payment_Gateways' ) ) {
$payment_gateways = WC_Payment_Gateways::instance();
$available_gateways = $payment_gateways->get_available_payment_gateways();
foreach ( $enabled_gateways as $gateway_id ) {
if ( isset( $available_gateways[$gateway_id] ) ) {
$output .= '<li>' . esc_html( $available_gateways[$gateway_id]->get_method_title() ) . '</li>';
}
}
}
$output .= '</ul>';
} else {
$output .= '<p>' . __( 'No payment methods currently available. Please contact administrator.', 'pofwc' ) . '</p>';
}
$output .= '</div>';
return $output;
}
/**
* Add CSS for corporate user elements
*/
add_action( 'wp_head', 'pofwc_corporate_user_styles' );
function pofwc_corporate_user_styles() {
echo '<style>
.corporate-user-info {
background: #f9f9f9;
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
margin: 10px 0;
}
.corporate-user-info h4 {
margin-top: 0;
color: #0073aa;
}
.corporate-user-info ul {
margin: 10px 0;
padding-left: 20px;
}
</style>';
}
/**
* Enhanced corporate user detection based on existing fields
* NEW: Better integration with existing corporate fields
*/
add_action( 'profile_update', 'pofwc_enhanced_corporate_detection', 10, 1 );
function pofwc_enhanced_corporate_detection( $user_id ) {
// Check existing corporate fields
$company_name = get_user_meta( $user_id, 'pofwc_company_name', true );
$gst_number = get_user_meta( $user_id, 'pofwc_gst_number', true );
// Auto-detect corporate status based on existing fields
if ( ! empty( $company_name ) || ! empty( $gst_number ) ) {
update_user_meta( $user_id, 'is_corporate_user', '1' );
} else {
// Remove corporate status if no corporate info
delete_user_meta( $user_id, 'is_corporate_user' );
delete_user_meta( $user_id, 'enabled_payment_gateways' );
}
}