Skip to content

Instantly share code, notes, and snippets.

@trajche
Created April 22, 2020 23:58
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
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 May 26, 2020

How to use: git am allow-admin-coupons-fix.patch

@drivefar
Copy link

Hi
The problem I’m having is that in Dokan the Earning calculation that is made in the Vendor dashboard incorporates the discount that has been applied, which means the coupon that is applied affects the vendor’s earnings.
Is this a way I can change this so that the earning is calculated on the order without the coupon applied, so that we “the Admin” have to incur the discount charges?

@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