Skip to content

Instantly share code, notes, and snippets.

@trajche
Created April 22, 2020 23:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trajche/6be4350f472b6fd3eca2053e240c5c17 to your computer and use it in GitHub Desktop.
Save trajche/6be4350f472b6fd3eca2053e240c5c17 to your computer and use it in GitHub Desktop.
Patch for allowing admins to create Dokan coupons.
From 962ea6ad8940dc9339423e1fbc7e01a40db75c99 Mon Sep 17 00:00:00 2001
From: TJ <trajche@kralev.eu>
Date: Thu, 23 Apr 2020 02:52:55 +0300
Subject: [PATCH] applying fix from
https://github.com/weDevsOfficial/dokan/pull/685
---
includes/Admin/Settings.php | 11 ++
includes/Commission.php | 6 +-
includes/Order/Hooks.php | 86 +++++++++++++-
includes/Order/functions.php | 209 ++++++++++++++++++++++++++++++++++-
includes/functions.php | 11 ++
templates/orders/details.php | 9 +-
6 files changed, 324 insertions(+), 8 deletions(-)
diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php
index f98ffa6..7c21424 100644
--- a/includes/Admin/Settings.php
+++ b/includes/Admin/Settings.php
@@ -348,6 +348,17 @@ class Settings {
'options' => array( 'seller' => __( 'Vendor', 'dokan-lite' ), 'admin' => __( 'Admin', 'dokan-lite' ) ),
'default' => 'seller'
),
+ 'admin_coupon_bearer' => array(
+ 'name' => 'admin_coupon_bearer',
+ 'label' => __( 'Admin Coupon', 'dokan-lite' ),
+ 'desc' => __( 'Who will bear the coupon discount created by Admin', 'dokan-lite' ),
+ 'type' => 'select',
+ 'options' => array(
+ 'admin' => __( 'Admin', 'dokan-lite' ),
+ 'vendor' => __( 'Vendor', 'dokan-lite' ),
+ ),
+ 'default' => 'admin'
+ ),
) );
$selling_option_vendor_capability = apply_filters( 'dokan_settings_selling_option_vendor_capability', array(
diff --git a/includes/Commission.php b/includes/Commission.php
index 5ddac9f..5d610f9 100644
--- a/includes/Commission.php
+++ b/includes/Commission.php
@@ -147,7 +147,7 @@ class Commission {
*
* @return float|null on failure
*/
- public function get_earning_by_order( $order, $context = 'seller' ) {
+ public function get_earning_by_order( $order, $context = 'seller', $force = false ) {
if ( ! $order instanceof \WC_Order ) {
$order = wc_get_order( $order );
}
@@ -172,7 +172,7 @@ class Commission {
// Set user passed `order_id` so that we can track if any commission_rate has been saved previously.
// Specially on order table `re-generation`.
$this->set_order_id( $order->get_id() );
- $earning = $this->get_earning_from_order_table( $order->get_id(), $context );
+ $earning = ! $force ? $this->get_earning_from_order_table( $order->get_id(), $context ) : null;
if ( ! is_null( $earning ) ) {
return $earning;
@@ -194,7 +194,7 @@ class Commission {
if ( $refund ) {
$earning += $this->get_earning_by_product( $product_id, $context, $item->get_total() - $refund );
} else {
- $earning += $this->get_earning_by_product( $product_id, $context, $item->get_total() );
+ $earning += $this->get_earning_by_product( $product_id, $context, ! $force ? $item->get_total() : $item->get_subtotal() );
}
}
diff --git a/includes/Order/Hooks.php b/includes/Order/Hooks.php
index 6b3ac39..0789985 100644
--- a/includes/Order/Hooks.php
+++ b/includes/Order/Hooks.php
@@ -32,15 +32,15 @@ class Hooks {
// order table synced for dokan update order meta
add_action( 'dokan_checkout_update_order_meta', 'dokan_sync_insert_order' );
- // prevent non-vendor coupons from being added
- add_filter( 'woocommerce_coupon_is_valid', array( $this, 'ensure_vendor_coupon' ), 10, 2 );
-
if ( is_admin() ) {
add_action( 'woocommerce_process_shop_order_meta', 'dokan_sync_insert_order' );
}
// restore order stock if it's been reduced by twice
add_action( 'woocommerce_reduce_order_stock', array( $this, 'restore_reduced_order_stock' ) );
+
+ // Adjust vendor balance if admin coupon is being applied on an order
+ add_filter( 'dokan_order_net_amount', array( $this, 'maybe_add_admin_discount_to_vendor_balance' ), 10, 2 );
}
/**
@@ -155,6 +155,86 @@ class Hooks {
dokan()->order->maybe_split_orders( $parent_order_id );
}
+ /**
+ * Maybe add admin discount to vendor_balance
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @param float $net_amount
+ * @param WC_Order $sub_order
+ *
+ * @return float
+ */
+ public function maybe_add_admin_discount_to_vendor_balance( $net_amount, $sub_order ) {
+ // If marketplace doesn't bear the discount created by admin, don't need to add the balace to vendor account.
+ if ( 'admin' !== dokan_get_admin_coupon_discount_bearer() ) {
+ return $net_amount;
+ }
+
+ $admin_coupon_data = dokan_get_admin_coupon_data_from_order( $sub_order );
+
+ if ( empty( $admin_coupon_data['discount'] ) ) {
+ return $net_amount;
+ }
+
+ if ( ! dokan_is_suborder( $sub_order ) ) {
+ /**
+ * Admin coupon is fully applied on this main order. So we'll remove the coupon first from this order.
+ *
+ * @since DOKAN_LITE_SINCE
+ */
+ $coupons = $admin_coupon_data['coupons'];
+
+ foreach ( $coupons as $coupon_code ) {
+ $sub_order->remove_coupon( wc_format_coupon_code( $coupon_code ) );
+ }
+ } else {
+ /**
+ * Admin coupon is not fully applied on sub-orders but the discount has been calculated.
+ * So we'll call WC_Order::recalculate_coupons to remove those applied discount from this order.
+ *
+ * We simply can't call WC_Order::remove_coupon here cause doing so will remove the `coupon` from the parent order.
+ * So there will be no way to distinguish `discount amount` for sub-orders.
+ *
+ * @since DOKAN_LITE_SINCE
+ */
+ if ( version_compare( WC()->version, '3.8.0', '>=' ) ) {
+ $sub_order->recalculate_coupons();
+ } else {
+ $rm = new ReflectionMethod( $sub_order, 'recalculate_coupons' );
+ $rm->setAccessible( true );
+ $rm->invoke( $sub_order );
+ }
+ }
+
+ $discount_total = isset( $admin_coupon_data['discount'] ) ? $admin_coupon_data['discount'] : 0;
+ $coupon_code = $sub_order->get_id() . '_coupon';
+
+ /**
+ * We'll apply on-time coupon for this order and delete it as this coupon is no longer needed.
+ *
+ * @since DOKAN_LITE_SINCE
+ */
+ dokan_apply_coupon_on_order_and_delete( $coupon_code, $discount_total, $sub_order );
+
+ /**
+ * If order status is pending, make it processing so that we can show it in the report.
+ *
+ * @since DOKAN_LITE_SINCE
+ */
+ add_filter( 'dokan_get_order_status', function( $status ) {
+ return 'pending' === $status ? 'processing' : $status;
+ } );
+
+ $admin_commission = dokan()->commission->get_earning_by_order( $sub_order, 'admin', true );
+ $order_total = $sub_order->get_total() + $discount_total;
+ $net_amount = $order_total - $admin_commission;
+ $net_amount = $net_amount > 0 ? $net_amount : 0;
+
+ return apply_filters( 'dokan_get_adjusted_net_amount', $net_amount );
+ }
+
+
/**
* Ensure vendor coupon
*
diff --git a/includes/Order/functions.php b/includes/Order/functions.php
index 1114267..5dd3926 100644
--- a/includes/Order/functions.php
+++ b/includes/Order/functions.php
@@ -315,12 +315,13 @@ function dokan_sync_insert_order( $order_id ) {
$order = dokan()->order->get( $order_id );
$seller_id = dokan_get_seller_id_by_order( $order_id );
$order_total = $order->get_total();
- $order_status = dokan_get_prop( $order, 'status' );
$admin_commission = dokan()->commission->get_earning_by_order( $order, 'admin' );
$net_amount = $order_total - $admin_commission;
+ $net_amount = $net_amount > 0 ? $net_amount : 0;
$net_amount = apply_filters( 'dokan_order_net_amount', $net_amount, $order );
$threshold_day = dokan_get_option( 'withdraw_date_limit', 'dokan_withdraw', 0 );
$threshold_day = $threshold_day ? $threshold_day : 0;
+ $order_status = apply_filters( 'dokan_get_order_status', dokan_get_prop( $order, 'status' ), $order );
dokan_delete_sync_duplicate_order( $order_id, $seller_id );
@@ -965,3 +966,209 @@ function dokan_is_order_already_exists( $id ) {
return $order_id ? true : false;
}
+
+/**
+ * Dokan get admin coupon data from an order
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @param int|WC_Order $order
+ *
+ * @return array|null on failure
+ */
+function dokan_get_admin_coupon_data_from_order( $order ) {
+ if ( ! $order instanceof WC_Order ) {
+ $order = wc_get_order( $order );
+ }
+
+ if ( ! $order ) {
+ return;
+ }
+
+ $parent_order = dokan_get_parent_order( $order );
+
+ if ( ! $parent_order ) {
+ $parent_order = $order;
+ }
+
+ $coupon_codes = $parent_order->get_coupon_codes();
+
+ if ( empty( $coupon_codes ) ) {
+ return;
+ }
+
+ $admin_coupons = [];
+
+ foreach ( $coupon_codes as $coupon_code ) {
+ $coupon = new WC_Coupon( $coupon_code );
+
+ if ( ! $coupon instanceof WC_Coupon ) {
+ continue;
+ }
+
+ $author = get_post_field( 'post_author', $coupon->get_id() );
+
+ if ( $author && user_can( $author, 'manage_woocommerce' ) ) {
+ array_push( $admin_coupons, $coupon->get_code() );
+ }
+ }
+
+ $vendor_discount = dokan_get_vendor_coupon_discount_from_order( $order );
+ $admin_discount = $order->get_discount_total() - $vendor_discount;
+
+ return apply_filters( 'dokan_get_admin_coupon_data_from_order', [
+ 'coupons' => $admin_coupons,
+ 'discount' => $admin_discount
+ ] );
+}
+
+/**
+ * Dokan get vendor coupon discount from an order
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @param int|WC_Order $order
+ *
+ * @return float|null on failure
+ */
+function dokan_get_vendor_coupon_discount_from_order( $order ) {
+ if ( ! $order instanceof WC_Order ) {
+ $order = wc_get_order( $order );
+ }
+
+ if ( ! $order ) {
+ return;
+ }
+
+ $coupon_codes = $order->get_coupon_codes();
+
+ if ( empty( $coupon_codes ) ) {
+ return;
+ }
+
+ $discount_total = 0;
+
+ foreach ( $coupon_codes as $coupon_code ) {
+ $coupon = new WC_Coupon( $coupon_code );
+
+ if ( ! $coupon instanceof WC_Coupon ) {
+ continue;
+ }
+
+ $author = get_post_field( 'post_author', $coupon->get_id() );
+
+ if ( $author && ! user_can( $author, 'manage_woocommerce' ) ) {
+ $discount_total += $coupon->get_amount();
+ }
+ }
+
+ return apply_filters( 'dokan_get_vendor_coupon_discount_from_order', $discount_total );
+}
+
+/**
+ * Dokan apply coupon on order and delete
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @param string $coupon_code
+ * @param float $amount
+ * @param int|WC_Order $order
+ *
+ * @return void
+ */
+function dokan_apply_coupon_on_order_and_delete( $coupon_code, $amount = 0, $order ) {
+ $admin = get_user_by( 'email', get_option( 'admin_email' ) );
+ $coupon_id = wp_insert_post( apply_filters( 'dokan_create_tmp_coupon', [
+ 'post_title' => $coupon_code,
+ 'post_type' => 'shop_coupon',
+ 'post_status' => 'publish',
+ 'post_author' => ! empty( $admin->ID ) ? $admin->ID : 1,
+ ] ) );
+
+ if ( ! $coupon_id || is_wp_error( $coupon_id ) ) {
+ return;
+ }
+
+ $meta = apply_filters( 'dokan_create_tmp_coupon_meta', [
+ 'discount_type' => 'fixed_cart',
+ 'coupon_amount' => $amount,
+ 'individual_use' => 'no',
+ 'product_ids' => '',
+ 'exclude_product_ids' => '',
+ 'usage_limit' => '',
+ 'usage_limit_per_user' => '',
+ 'limit_usage_to_x_items' => '',
+ 'expiry_date' => '',
+ 'free_shipping' => 'no',
+ 'exclude_sale_items' => 'no',
+ 'product_categories' => [],
+ 'exclude_product_categories' => [],
+ 'minimum_amount' => '',
+ 'maximum_amount' => '',
+ 'customer_email' => [],
+ 'usage_count' => '0',
+ ] );
+
+ foreach ( $meta as $key => $value ) {
+ update_post_meta( $coupon_id, $key, $value );
+ }
+
+ $coupon = new WC_Coupon( $coupon_code );
+
+ do_action( 'dokan_pre_apply_coupon_on_order_and_delete', $coupon, $order );
+
+ if ( $order->apply_coupon( $coupon ) ) {
+
+ /**
+ * Delete the coupon once it's fully applied, to prevent error such as 'Error during status transition. Invalid coupon'.
+ *
+ * @since DOKAN_LITE_SINCE
+ */
+ add_action( 'shutdown', function() use ( $coupon ) {
+ wp_delete_post( $coupon->get_id(), true );
+ } );
+ }
+}
+
+/**
+ * Check whether the order is suborder or not
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @param int|WC_Order $order
+ *
+ * @return bool
+ */
+function dokan_is_suborder( $order ) {
+ $parent_order = dokan_get_parent_order( $order );
+
+ if ( ! $parent_order ) {
+ return false;
+ }
+
+ return apply_filters( 'dokan_is_suborder', (bool) $parent_order->get_meta( 'has_sub_order' ) );
+}
+
+/**
+ * Dokan get parent order. If no sub orders is found count it as a parent order too.
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @param int|WC_Order $order
+ *
+ * @return WC_order|false on failure
+ */
+function dokan_get_parent_order( $order ) {
+ if ( ! $order instanceof WC_Order ) {
+ $order = wc_get_order( $order );
+ }
+
+ if ( ! $order ) {
+ return false;
+ }
+
+ $parent_order_id = $order->get_parent_id();
+ $parent_order = $parent_order_id ? wc_get_order( $parent_order_id ) : $order;
+
+ return apply_filters( 'dokan_get_parent_order', $parent_order );
+}
diff --git a/includes/functions.php b/includes/functions.php
index a5a4fce..c4705ff 100755
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -3745,6 +3745,17 @@ function dokan_redirect_to_admin_setup_wizard() {
exit;
}
+/**
+ * Dokan get admin coupon discount bearer
+ *
+ * @since DOKAN_LITE_SINCE
+ *
+ * @return string
+ */
+function dokan_get_admin_coupon_discount_bearer() {
+ return apply_filters( 'dokan_get_admin_coupon_discount_bearer', dokan_get_option( 'admin_coupon_bearer', 'dokan_selling', 'admin' ) );
+}
+
/**
* Dokan generate star ratings
*
diff --git a/templates/orders/details.php b/templates/orders/details.php
index 1e0e377..338d750 100755
--- a/templates/orders/details.php
+++ b/templates/orders/details.php
@@ -96,11 +96,18 @@ $hide_customer_info = dokan_get_option( 'hide_customer_info', 'dokan_selling', '
<tr>
<th><?php esc_html_e( 'Coupons', 'dokan-lite' ); ?></th>
<td>
- <ul class="list-inline"><?php
+ <ul class="dokan-coupon-list"><?php
foreach ( $coupons as $item_id => $item ) {
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item['name'] ) );
+ $author = get_post_field( 'post_author', $post_id );
+
+ if ( $author && user_can( $author, 'manage_options' ) ) {
+ echo '<li><a data-html="true" class="tips code" title="' . esc_attr( wc_price( $item['discount_amount'] ) ) . '" href="#"><span>' . esc_html( $item['name'] ). '</span></a> (' . sprintf( __( 'This %s coupon is created by admin', 'dokan' ), wc_price( $item['discount_amount'] ) ) . ')</li>';
+ continue;
+ }
+
$link = dokan_get_coupon_edit_url( $post_id );
echo '<li><a data-html="true" class="tips code" title="' . esc_attr( wc_price( $item['discount_amount'] ) ) . '" href="' . esc_url( $link ) . '"><span>' . esc_html( $item['name'] ). '</span></a></li>';
--
2.20.1 (Apple Git-117)
@trajche
Copy link
Author

trajche commented Sep 27, 2021

We have switched to Product Vendors (WooCommerce) and extended that plugin. Unfortunately I can't help here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment