/home/nbcgowuy/kdosheleads.com/wp-content/plugins/dokan-lite/includes/ReverseWithdrawal/Helper.php
<?php
namespace WeDevs\Dokan\ReverseWithdrawal;
use WP_Error;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Helper class for reverse withdrawal
*
* @since 3.5.1
*
* @package WeDevs\Dokan\ReverseWithdrawal
*/
class Helper {
/**
* This method will return option key for reverse withdrawal base product
*
* @since 3.5.1
*
* @return string
*/
public static function get_base_product_option_key() {
return 'dokan_reverse_withdrawal_product_id';
}
/**
* This method will return balance_threshold_exceed_date_key
*
* @since 3.5.1
*
* @return string
*/
public static function balance_threshold_exceed_date_key() {
return '_dokan_reverse_withdrawal_threshold_exceeded_date';
}
/**
* This method will return failed actions key
*
* @return string
*/
public static function failed_actions_key() {
return '_dokan_reverse_withdrawal_failed_actions';
}
/**
* Get reverse withdrawal failed payment actions
*
* @since 3.5.1
*
* @return array|string return associated array of transaction types if no argument is provided. If $transaction_type is provided and if data exists then return the label otherwise return empty string
*/
public static function get_transaction_types( $transaction_type = null ) {
/**
* ! do not change the keys, it will break the query
* ! also do not use any filter here, if new transaction type is needed add it to the below array
*/
$transaction_types = [
'opening_balance' => esc_html__( 'Opening Balance', 'dokan-lite' ),
// admin will get payment (debit)
'order_commission' => esc_html__( 'Commission', 'dokan-lite' ),
'failed_transfer_reversal' => esc_html__( 'Failed Transfer Reversal', 'dokan-lite' ),
'product_advertisement' => esc_html__( 'Product Advertisement', 'dokan-lite' ),
'manual_order_commission' => esc_html__( 'Manual Order Commission', 'dokan-lite' ),
// vendor paid to admin (credit)
'vendor_payment' => esc_html__( 'Payment', 'dokan-lite' ),
'order_refund' => esc_html__( 'Refund', 'dokan-lite' ),
'manual_product' => esc_html__( 'Manual', 'dokan-lite' ),
'manual_order' => esc_html__( 'Manual', 'dokan-lite' ),
'other' => esc_html__( 'Other', 'dokan-lite' ),
];
if ( $transaction_type ) {
return isset( $transaction_types[ $transaction_type ] ) ? $transaction_types[ $transaction_type ] : '';
}
return $transaction_types;
}
/**
* Get reverse withdrawal failed payment actions
*
* @since 3.5.1
*
* @param $vendor_id
*
* @return array
*/
public static function get_failed_actions_by_vendor( $vendor_id ) {
$failed_actions = get_user_meta( $vendor_id, self::failed_actions_key(), true );
return is_array( $failed_actions ) ? $failed_actions : [];
}
/**
* Set reverse withdrawal failed payment actions
*
* @since 3.5.1
*
* @param int $vendor_id
* @param array $failed_actions
*
* @return void
*/
public static function set_failed_actions_by_vendor( $vendor_id, $failed_actions ) {
update_user_meta( $vendor_id, self::failed_actions_key(), $failed_actions );
}
/**
* This method will return the balance threshold exceeded date
*
* @since 3.5.1
*
* @param $vendor_id
*
* @return string
*/
public static function get_balance_threshold_exceed_date( $vendor_id ) {
return get_user_meta( $vendor_id, self::balance_threshold_exceed_date_key(), true );
}
/**
* This method will update the balance threshold exceeded date
*
* @since DOKA_SINCE
*
* @param int $vendor_id
* @param string $date
*
* @return void
*/
public static function set_balance_threshold_exceed_date( $vendor_id, $date = '' ) {
update_user_meta( $vendor_id, self::balance_threshold_exceed_date_key(), $date );
}
/**
* This method will check if cart contain reverse withdrawal product
*
* @since 3.5.1
*
* @return bool
*/
public static function has_reverse_withdrawal_payment_in_order( $order ) {
// check if we get order object or order id
if ( ! $order instanceof \WC_Abstract_Order && is_numeric( $order ) ) {
// get order object from order_id
$order = wc_get_order( $order );
}
if ( ! $order instanceof \WC_Abstract_Order ) {
return false;
}
foreach ( $order->get_items() as $item ) {
if ( $item->get_meta( '_dokan_reverse_withdrawal_balance' ) ) {
return true;
}
}
return false;
}
/**
* This method will return reverse withdrawal payment amount
*
* @since 3.5.1
*
* @param \WC_Abstract_Order $order
*
* @return float|bool false if meta key not found
*/
public static function get_balance_from_order( \WC_Abstract_Order $order ) {
$balance = false;
foreach ( $order->get_items() as $item ) {
if ( $item->get_meta( '_dokan_reverse_withdrawal_balance' ) ) {
$balance = floatval( wc_format_decimal( $item->get_meta( '_dokan_reverse_withdrawal_balance' ) ) );
break;
}
}
return $balance;
}
/**
* Get reverse withdrawal base product id
*
* @since 3.5.1
*
* @return int
*/
public static function get_reverse_withdrawal_base_product() {
// get product id from option table
$base_product_id = (int) get_option( static::get_base_product_option_key(), 0 );
$product = wc_get_product( $base_product_id );
if ( $product ) {
// temporary adding this code to set sold individually to true, will remove this after some time
if ( ! $product->is_sold_individually() ) {
$product->set_sold_individually( true );
$product->save();
}
return $base_product_id;
}
return 0;
}
/**
* This method will check if a product is reverse withdrawal product
*
* @since 3.8.1
*
* @param int $product_id
*
* @return bool
*/
public static function is_reverse_withdrawal_product( $product_id ) {
$base_product_id = static::get_reverse_withdrawal_base_product();
return $base_product_id === $product_id;
}
/**
* This method will check if cart contain reverse withdrawal payment product
*
* @since 3.5.1
*
* @return bool
*/
public static function has_reverse_withdrawal_payment_in_cart() {
if ( ! WC()->cart ) {
return false;
}
foreach ( WC()->cart->get_cart() as $item ) {
if ( isset( $item['dokan_reverse_withdrawal_balance'] ) ) {
return true;
}
}
return false;
}
/**
* This method will return formatted transaction id
*
* @since 3.5.1
*
* @param int $transaction_id
* @param string $transaction_type
* @param string $contex admin or seller
*
* @return string
*/
public static function get_transaction_url_from_transaction_id( $transaction_id, $transaction_type, $contex = 'admin' ) {
switch ( $transaction_type ) {
case 'product_advertisement':
// get product edit link
$url = $contex === 'admin'
? get_admin_url( null, 'post.php?post=' . $transaction_id . '&action=edit' )
: dokan_edit_product_url( $transaction_id );
break;
case 'vendor_payment':
$order = wc_get_order( $transaction_id );
// get vendor order view url
$order_view_url = '#';
if ( $order ) {
$order_view_url = $order->get_view_order_url();
}
$url = $contex === 'admin'
? get_admin_url( null, 'post.php?post=' . $transaction_id . '&action=edit' )
: $order_view_url;
break;
default:
// get order edit link
$url = $contex === 'admin'
? get_admin_url( null, 'post.php?post=' . $transaction_id . '&action=edit' )
: wp_nonce_url( add_query_arg( [ 'order_id' => $transaction_id ], dokan_get_navigation_url( 'orders' ) ), 'dokan_view_order' );
}
return esc_url_raw( $url );
}
/**
* This method will return formatted transaction data
*
* @since 3.5.1
*
* @param array $item
* @param float $current_balance
* @param string $context
*
* @return array
*/
public static function get_formated_transaction_data( $item, &$current_balance, $context = 'admin' ) {
$id = ! empty( $item['id'] ) && is_numeric( $item['id'] ) ? absint( $item['id'] ) : '--';
$trn_id = ! empty( $item['trn_id'] ) && is_numeric( $item['trn_id'] ) ? absint( $item['trn_id'] ) : '--';
$trn_url = is_numeric( $trn_id ) ? static::get_transaction_url_from_transaction_id( $trn_id, sanitize_text_field( $item['trn_type'] ), $context ) : '#';
$trn_date = ! empty( $item['trn_date'] ) ? dokan_format_date( $item['trn_date'] ) : '--';
$trn_type = ! empty( $item['trn_type'] ) ? static::get_transaction_types( sanitize_text_field( $item['trn_type'] ) ) : '--';
$vendor_id = ! empty( $item['vendor_id'] ) && is_numeric( $item['vendor_id'] ) ? absint( $item['vendor_id'] ) : '--';
$note = ! empty( $item['note'] ) ? esc_html( $item['note'] ) : '--';
$debit = isset( $item['debit'] ) && is_numeric( $item['debit'] ) ? floatval( $item['debit'] ) : 0;
$credit = isset( $item['credit'] ) && is_numeric( $item['credit'] ) ? floatval( $item['credit'] ) : 0;
// get current balance
$current_balance = (float) wc_format_decimal( ( $current_balance + $debit ) - $credit );
// fix some fields for opening_balance transaction type
if ( 'opening_balance' === $item['trn_type'] ) {
$debit = '--';
$credit = '--';
}
return [
'id' => $id,
'trn_id' => $trn_id,
'trn_url' => $trn_url,
'trn_date' => $trn_date,
'trn_type' => $trn_type,
'trn_type_raw' => $item['trn_type'],
'vendor_id' => $vendor_id,
'note' => $note,
'debit' => $debit,
'credit' => $credit,
'balance' => $current_balance,
];
}
/**
* This method will return default transaction data for vendor reverse withdrawal balance
*
* @since 3.5.1
*
* @return array
*/
public static function get_default_transaction_date() {
return [
'from' => dokan_current_datetime()->modify( '-1 month' )->format( 'Y-m-d H:i:s' ),
'to' => dokan_current_datetime()->format( 'Y-m-d H:i:s' ),
];
}
/**
* This method will return payable amount of a vendor for a month
*
* @param int $vendor_id
* @param int|string $current_date
*
* @return float|WP_Error
*/
public static function get_vendor_payable_amount_by_month( $vendor_id, $current_date ) {
// check for valid vendor id
if ( empty( $vendor_id ) || ! is_numeric( $vendor_id ) ) {
return new WP_Error( 'invalid_vendor_id', esc_html__( 'No vendor id provided', 'dokan-lite' ) );
}
// get current_date
$current_date = is_numeric( $current_date ) ? dokan_current_datetime()->setTimestamp( $current_date ) : dokan_current_datetime()->modify( $current_date );
// validate current date
if ( ! $current_date ) {
return new WP_Error( 'invalid_date', esc_html__( 'Invalid date provided', 'dokan-lite' ) );
}
$manager = new Manager();
// get previous month balance
$previous_month_balance = $manager->get_store_balance(
[
'vendor_id' => $vendor_id,
'trn_date' => [
// we need remaining balance till previous month
'to' => $current_date->modify( 'last day of previous month' )->format( 'Y-m-d' ),
],
]
);
if ( is_wp_error( $previous_month_balance ) ) {
return $previous_month_balance;
}
if ( $previous_month_balance <= 0 ) {
return 0;
}
// is user paid for previous month
$paid_balance = $manager->get_payments_by_vendor(
[
'vendor_id' => $vendor_id,
'trn_date' => [
'from' => $current_date->modify( 'first day of this month' )->format( 'Y-m-d' ),
'to' => $current_date->modify( 'last day of this month' )->format( 'Y-m-d' ),
],
]
);
if ( is_wp_error( $paid_balance ) ) {
return $paid_balance;
}
return (float) wc_format_decimal( $previous_month_balance - $paid_balance );
}
/**
* This method will return payable balance of a vendor
*
* @since 3.5.1
*
* @param int|null $vendor_id
* @param int|string|null $current_date
*
* @return array|WP_Error
*/
public static function get_vendor_balance( $vendor_id = null, $current_date = null ) {
// check for valid vendor id
if ( ! is_numeric( $vendor_id ) ) {
$vendor_id = dokan_get_current_user_id();
}
// validate current_date
if ( null === $current_date ) {
$current_date = dokan_current_datetime();
} else {
$current_date = is_numeric( $current_date ) ? dokan_current_datetime()->setTimestamp( $current_date ) : dokan_current_datetime()->modify( $current_date );
}
if ( ! $current_date ) {
return new WP_Error( 'invalid_date', esc_html__( 'Invalid date provided', 'dokan-lite' ) );
}
$manager = new Manager();
// get balance of the vendor till now
$balance = $manager->get_store_balance(
[
'vendor_id' => $vendor_id,
]
);
if ( is_wp_error( $balance ) ) {
return $balance;
}
// get required settings
$data = [
'balance' => $balance,
'billing_type' => SettingsHelper::get_billing_type(),
'billing_day' => SettingsHelper::get_billing_day(),
'due_period' => SettingsHelper::get_due_period(),
'threshold' => SettingsHelper::get_reverse_balance_threshold(),
'payable_amount' => 0,
];
// check settings for billing type
switch ( $data['billing_type'] ) {
case 'by_month':
// subtract paid balance from previous month balance
$data['payable_amount'] = static::get_vendor_payable_amount_by_month( $vendor_id, $current_date->getTimestamp() );
break;
case 'by_amount':
$data['payable_amount'] = $balance;
break;
}
return $data;
}
/**
* This method will check if vendor needs to pay balance along with details data
*
* @since 3.5.1
*
* @param int|null $vendor_id
*
* @return array|WP_Error
*/
public static function get_vendor_due_status( $vendor_id = null, $current_date = null ) {
// check for valid vendor id
if ( ! is_numeric( $vendor_id ) ) {
$vendor_id = dokan_get_current_user_id();
}
if ( ! $vendor_id ) {
return new WP_Error( 'invalid_vendor_id', esc_html__( 'Invalid vendor provided.', 'dokan-lite' ) );
}
// validate current_date
if ( null === $current_date ) {
$current_date = dokan_current_datetime();
} else {
$current_date = is_numeric( $current_date ) ? dokan_current_datetime()->setTimestamp( $current_date ) : dokan_current_datetime()->modify( $current_date );
}
if ( ! $current_date ) {
return new WP_Error( 'invalid_date', esc_html__( 'Invalid date provided', 'dokan-lite' ) );
}
// get balance
$balance = static::get_vendor_balance( $vendor_id, $current_date->getTimestamp() );
// check for error
if ( is_wp_error( $balance ) ) {
return $balance;
}
$ret = [
'status' => false,
'due_date' => '',
'balance' => $balance,
];
// check which billing type setting is enabled
switch ( SettingsHelper::get_billing_type() ) {
case 'by_month':
// check if we need to display payment notice
if ( $balance['payable_amount'] <= 0 ) {
// there is no balance to be paid
break;
}
// vendor needs to pay due amount
$ret['status'] = true;
// check if user crossed the due period
$due_date = $current_date->modify( 'first day of this month' )->setTime( 23, 59, 59 );
if ( SettingsHelper::get_billing_day() ) {
$billing_day = SettingsHelper::get_billing_day() - 1;
$due_date = $billing_day > 0 ? $due_date->modify( "+ {$billing_day} days" ) : $due_date;
}
if ( SettingsHelper::get_due_period() ) {
$due_period = SettingsHelper::get_due_period() - 1;
$due_date = $due_period > 0 ? $due_date->modify( "+ {$due_period} days" ) : $due_date;
}
if ( $current_date > $due_date ) {
// vendor needs to pay due balance immediately
$ret['due_date'] = 'immediate';
} else {
// vendor needs to pay due balance on due date
$ret['due_date'] = $due_date->format( 'Y-m-d' );
}
break;
case 'by_amount':
$threshold = SettingsHelper::get_reverse_balance_threshold();
if ( $balance['payable_amount'] < $threshold ) {
// balance amount is less than threshold
$ret['status'] = false;
break;
}
$ret['status'] = true;
// check when user crossed the threshold limit
$last_threshold_limit_exceed_date = static::get_balance_threshold_exceed_date( $vendor_id );
$threshold_exceeded = false;
if ( empty( $last_threshold_limit_exceed_date ) ) {
$last_threshold_limit_exceed_date = dokan_current_datetime()->format( 'Y-m-d' );
// we will only update this value if current date is today, because this function can be used to get older month's data
if ( $last_threshold_limit_exceed_date === $current_date->format( 'Y-m-d' ) ) {
$threshold_exceeded = true;
static::set_balance_threshold_exceed_date( $vendor_id, $last_threshold_limit_exceed_date );
}
}
//get due date
$due_date = $current_date->modify( $last_threshold_limit_exceed_date )->setTime( 23, 59, 59 );
if ( SettingsHelper::get_due_period() ) {
$due_period = SettingsHelper::get_due_period() - 1;
$due_date = $due_period > 0 ? $due_date->modify( "+ {$due_period} days" ) : $due_date;
}
if ( $current_date > $due_date ) {
// vendor needs to pay due balance immediately
$ret['due_date'] = 'immediate';
} else {
// vendor needs to pay due balance on due date
$ret['due_date'] = $due_date->format( 'Y-m-d' );
}
if ( $threshold_exceeded ) { // this will make sure that below hook will not be called twice
do_action( 'dokan_reverse_withdrawal_balance_threshold_exceed', $vendor_id, $ret, $last_threshold_limit_exceed_date );
}
break;
}
return $ret;
}
/**
* This method will check if a vendors need to pay their unpaid balance
*
* @since 3.5.1
*
* @param int|null $vendor_id
*
* @return bool|WP_Error
*/
public static function is_balance_due( $vendor_id = null ) {
$due_status = static::get_vendor_due_status( $vendor_id );
if ( is_wp_error( $due_status ) ) {
return $due_status;
}
return $due_status['status'];
}
/**
* This method will return formatted failed action messages
*
* @since 3.5.1
*
* @return string
*/
public static function get_formatted_failed_actions() {
$failed_actions = SettingsHelper::get_failed_actions();
$messages = [];
foreach ( $failed_actions as $failed_action ) {
switch ( $failed_action ) {
case 'enable_catalog_mode':
$messages[] = esc_html__( 'Your products add to cart will be hidden. Hence users will not be able to purchase any of your products.', 'dokan-lite' );
break;
case 'hide_withdraw_menu':
$messages[] = esc_html__( 'Withdraw menu will be hidden. Hence you will not be able to make any withdraw request from your account.', 'dokan-lite' );
break;
case 'status_inactive':
$messages[] = esc_html__( 'Your account will be disabled for selling. Hence you will no longer be able to sell any products.', 'dokan-lite' );
break;
}
}
$ret = '';
if ( empty( $messages ) ) {
return $ret;
}
$ret = '<ol>';
foreach ( $messages as $message ) {
$ret .= '<li>' . $message . '</li>';
}
$ret .= '</ol>';
return $ret;
}
/**
* This method will return formatted failed action messages
*
* @since 3.5.1
*
* @param int $vendor_id
*
* @return string
*/
public static function get_formatted_failed_actions_by_vendor( $vendor_id ) {
$failed_actions = static::get_failed_actions_by_vendor( $vendor_id );
$messages = [];
foreach ( $failed_actions as $failed_action ) {
switch ( $failed_action ) {
case 'enable_catalog_mode':
$messages[] = esc_html__( 'Your products add to cart button has been temporarily hidden. Hence users are not able to purchase any of your products currently.', 'dokan-lite' );
break;
case 'hide_withdraw_menu':
$messages[] = esc_html__( 'Withdraw menu has been temporarily hidden. Hence you are not able to make any withdrawal requests from your account.', 'dokan-lite' );
break;
case 'status_inactive':
$messages[] = esc_html__( 'Your account has been temporarily disabled for selling. Hence you are no longer able to sell any products.', 'dokan-lite' );
break;
}
}
$ret = '';
if ( empty( $messages ) ) {
return $ret;
}
$ret = '<ol>';
foreach ( $messages as $message ) {
$ret .= '<li>' . $message . '</li>';
}
$ret .= '</ol>';
return $ret;
}
/**
* Get formatted billing type
*
* @since 3.7.16
*
* @param string $billing_type
*
* @return array|string
*/
public static function get_formatted_billing_type( $billing_type = '' ) {
$available_billing_type = SettingsHelper::get_billing_type_options();
return ! empty( $billing_type ) && array_key_exists( $billing_type, $available_billing_type )
? $available_billing_type[ $billing_type ] : $available_billing_type;
}
/**
* This method will add reverse payment amount to cart
*
* @since 3.7.16
*
* @param string $amount
*
* @return WP_Error|bool true if product is added to cart, WP_Error otherwise
*/
public static function add_payment_to_cart( $amount ) {
$amount = ! empty( $amount ) ? (float) wc_format_decimal( $amount, wc_get_price_decimals() ) : 0;
// now check for data validation
if ( $amount <= 0 ) {
return new WP_Error( 'invalid-amount', __( 'Payment can not be less than or equal to zero.', 'dokan-lite' ) );
}
// get reverse withdrawal product id
$reverse_pay_product_id = self::get_reverse_withdrawal_base_product();
if ( ! is_numeric( $reverse_pay_product_id ) ) {
return new WP_Error( 'invalid-reverse-withdrawal-product', __( 'Invalid base payment product id. Please contact with site admin.', 'dokan-lite' ) );
}
$product = wc_get_product( $reverse_pay_product_id );
if ( ! $product ) {
return new WP_Error( 'invalid-reverse-withdrawal-product', __( 'Invalid base payment product id. Please contact with site admin.', 'dokan-lite' ) );
}
if ( 'publish' !== $product->get_status() ) {
return new WP_Error( 'invalid-reverse-withdrawal-product', __( 'Base payment product status is not published. Please contact with site admin.', 'dokan-lite' ) );
}
/*
* It is possible for the cart to be unavailable in some cases,
* specially while requesting API from third party agent like PostMan.
* To avoid any inconsistency, we need to load the cart manually if not exists.
*/
if ( ! WC()->cart ) {
wc_load_cart();
}
// add product to cart
WC()->cart->empty_cart();
$cart_item_data = [
'dokan_reverse_withdrawal_balance' => $amount,
];
// try catch block used just to get rid of phpcs error
try {
$added = WC()->cart->add_to_cart( $product->get_id(), 1, '', '', $cart_item_data ); // phpcs:ignore
} catch ( \Exception $exception ) {
return new WP_Error( 'invalid-reverse-withdrawal-product', __( 'Something went wrong while adding product to cart. Please contact site admin.', 'dokan-lite' ) );
}
if ( $added ) {
return true;
}
return new WP_Error( 'invalid-reverse-withdrawal-product', __( 'Something went wrong while adding product to cart. Please contact site admin.', 'dokan-lite' ) );
}
}