לקוח שלי פנה אליי השבוע בבקשה קצת מוזרה, הוא מוכר מגוון של מוצרים בחנות, והוא רצה שעבור קטגורייה מסויימת של מוצרים, ייגבה סכום כסף נוסף עבור כל מוצר שנרכש, מעין דמי שימוש או רישיון שימוש.
בהתחלה חשבתי שהוספת מיסים בווקומרס יתן פה מענה, אך לצערי המיסים חלו על כל המוצרים ולא על מוצרים ספציפיים.
לבסוף החלטתי להוסיף את המיסים הללו בצורה דינאמית ע״י ״הזרקה״ של המס לעגלה כאשר מתווסף מוצר מאותה קטגוריה לעגלה.

במאמר זה אסביר כיצד ביצעתי את בקשת הלקוח ואסביר את הקוד מאחורי הפיתרון. הקוד שיוצג במאמר הוא לא מלא זאת בגלל שעטפתי את הקוד כתוסף ועל מנת לאפשר לו שליטה בסיסית במנגנון הוספתי לו פונקציות נוספות ועמוד הגדרות תחת WooCommerce.

שלב ראשון – יצירת שלד תוסף

בשלב הראשון יצרתי שלד של התוסף אשר יכיל את המגנון ויהיה תלוי בווקומורס, ככה שאם ינסו להתקין את התוסף וווקומרס לא יהיה מופעל או מותקן, התוסף שלי לא ירוץ ויגרום לשגיאות. יצרתי קובץ בשם wc-dynamic-taxes.php והכנסתי אליו את הקוד הבא:

<?php
/**
 * WooCommerce plugin to add dynamic taxes.
 *
 * Plugin Name: WooCommerce Dynamic Taxes
 * Plugin URI: https://www.dorzki.io/adding-taxes-dynamically-in-woocommerce/
 * Description: WooCommerce plugin to add dynamic taxes.
 * Version: 1.0.0
 * Author: dorzki
 * Author URI: https://www.dorzki.io
 * License: GPL-2.0+
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 * Text Domain: dorzki-wc-dynamic-taxes
 *
 * @package    WordPress
 * @subpackage Plugins
 * @author     Dor Zuberi <webmaster@dorzki.co.il>
 * @link       https://www.dorzki.io
 * @version    1.0.0
 */

// Block if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}


/**
 * Checks if WooCommerce is installed and activated.
 *
 * @return bool
 */
function dorzki_check_required_plugins() {

	if ( ! in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ), true ) ) {

		add_action( 'admin_notices', 'dorzki_display_woocommerce_notice' );

		return false;

	}

	include_once 'class-plugin.php';

}

add_action( 'plugins_loaded', 'dorzki_check_required_plugins' );


/**
 * Displays an admin notice for WooCommerce.
 */
function dorzki_display_woocommerce_notice() {

	$notice = sprintf(
	/* translators: 1: WooCommerce 2: Plugin Name */
		esc_html__( '"%1$s" is required to be installed and activated in order to use "%2$s".', 'dorzki-wc-dynamic-taxes' ),
		'<strong> ' . esc_html__( 'WooCommerce', 'dorzki-wc-dynamic-taxes' ) . '</strong>',
		'<strong>' . esc_html__( 'WooCommerce Dynamic Taxes', 'dorzki-wc-dynamic-taxes' ) . '</strong>'
	);

	echo "<div class='notice notice-error'><p>{$notice}</p></div>";

}

בפונקציה dorzki_check_required_plugins, אנחנו בודקים האם התוסף ווקומרס עובד ומופעל, במידה ולא, אנחנו מציגים התראה מסוג שגיאה לבעל האתר בפאנל הניהול אשר מבקש ממנו להתקין ולהפעיל את ווקומרס. במידה והתוסף מותקן ומופעל, הקוד טוען את קובץ התוסף הראשי class-plugin.php.

שלב שני – יצירת עמוד הגדרות

לאחר מכן בקובץ class-plugin.php יצרתי מחלקה חדשה עם השם Plugin ובתוכנה ישנן מספר פונקציות. הפונקציה הראשונה שנתייחס אליה היא הפונקציה אשר יוצרת לנו עמוד תחת התפריט של ווקומרס.

רישום עמוד הגדרות

/**
 * Register plugin settings page under woocommerce.
 */
public function register_settings_page() {

	add_submenu_page(
		'woocommerce',
		__( 'Dynamic Taxes', 'dorzki-wc-dynamic-taxes' ),
		__( 'Dynamic Taxes', 'dorzki-wc-dynamic-taxes' ),
		'manage_options',
		'dorzki-wc-dynamic-taxes',
		[ $this, 'settings_page_output' ]
	);

}

הפונקציה הנ״ל קוראת לפונקציית add_submenu_page של וורדפרס ומעבירה לה מספר פרטמרים, כאשר הראשון מתייחס לכך שהעמוד צריך להיות מתחת לתפריט של ווקומרס בפאנל הניהול. בנוסף פונקציה זו קוראת לפונקצייה settings_page_output שכתבנו אשר אחראית להדפיס את מסך ההגדרות.

בדיקת הרשאות גישה

/**
 * Display settings page output if the user have the right permissions.
 */
public function settings_page_output() {

	// check user capabilities.
	if ( ! current_user_can( 'manage_options' ) ) {
		return;
	}

	include_once plugin_dir_path( __FILE__ ) . 'templates/settings-page.php';

}

פונקציה זו בודקת האם המשתמש הנוכחי יכול לערוך הגדרות אתר, אנחנו משתמשים בבדיקה זו כדי למנוע מצב שמישהו שיש לו את הקישור לעמוד יוכל לבצע שם שינויים כאשר אין לו הרשאה לעשות זאת. במידה והמשתמש מאושר מבחינת הרשאות, התוסף יטען את הקובץ אשר יציג את מסך ההגדרות.

הדפסת עמוד הגדרות

<div class="wrap">
	<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
	<form action="options.php" method="post">

		<?php settings_fields( 'wc_dynamic_taxes' ); ?>

		<?php do_settings_sections( 'dorzki-wc-dynamic-taxes' ); ?>

		<?php submit_button( __( 'Save Settings', 'dorzki-wc-dynamic-taxes' ) ); ?>

	</form>
</div>

הקוד הנ״ל משתמש שתי פונקציות מובנות של וורדפרס אשר בהמשך יהיו אחריות על הדפסת השדות השונים של עמוד ההגדרות של התוסף.

שלב שלישי – רישום שדות מסך הגדרות

בשלב הבא אנחנו צריכים לרשום את השדות אשר יאפשרו שליטה על חלקים מסויימים של המנגנון שלנו על מנת לאפשר גמישות קלה בתוסף.

/**
 * Register plugin settings fields.
 */
public function register_settings() {

	// Register new settings for plugin page.
	register_setting( 'wc_dynamic_taxes', 'wc_dynamic_taxes' );

	// Register a new section.
	add_settings_section(
		'wc_dynamic_taxes_general',
		__( 'General', 'dorzki-wc-dynamic-taxes' ),
		'__return_null',
		'dorzki-wc-dynamic-taxes'
	);

	// Register plugin settings fields.
	add_settings_field(
		'wc_dynamic_taxes_name',
		__( 'Tax Name', 'dorzki-wc-dynamic-taxes' ),
		[ $this, 'settings_page_field_output' ],
		'dorzki-wc-dynamic-taxes',
		'wc_dynamic_taxes_general',
		[
			'type'      => 'text',
			'label_for' => 'wc_dynamic_taxes_name',
		]
	);

	add_settings_field(
		'wc_dynamic_taxes_amount',
		__( 'Tax Value', 'dorzki-wc-dynamic-taxes' ),
		[ $this, 'settings_page_field_output' ],
		'dorzki-wc-dynamic-taxes',
		'wc_dynamic_taxes_general',
		[
			'type'      => 'text',
			'label_for' => 'wc_dynamic_taxes_amount',
		]
	);

	$cats = get_terms( [
		'taxonomy'   => 'product_cat',
		'hide_empty' => false,
		'fields'     => 'id=>name',
	] );

	add_settings_field(
		'wc_dynamic_taxes_category',
		__( 'Apply to Category', 'dorzki-wc-dynamic-taxes' ),
		[ $this, 'settings_page_field_output' ],
		'dorzki-wc-dynamic-taxes',
		'wc_dynamic_taxes_general',
		[
			'type'      => 'select',
			'label_for' => 'wc_dynamic_taxes_category',
			'options'   => $cats,
		]
	);

}

בפונקציה זו אנו מגדירים לוורדפרס שבעמוד ההגדרות שבנינו צריך להציג 3 שדות ע״י שימוש בפונקציית add_settings_field ושיוכן לעמוד הגדרות שלנו.

שלב רביעי – הגדרת מנגנון המס הדינאמי

השלב האחרון הוא הליבה של התוסף והוא בעצם זה שאחראי להוסיף את המס בצורה דינאמית.

/**
 * Determine if to add taxes and how much.
 *
 * @param \WC_Cart $cart current user cart.
 */
public function apply_dynamic_taxes( $cart ) {

	$tax_options = get_option( 'wc_dynamic_taxes' );

	if ( empty( $tax_options ) || empty( $tax_options['wc_dynamic_taxes_category'] ) ) {
		return;
	}

	$total_taxes = 0;

	foreach ( $cart->get_cart_contents() as $item ) {

		$cats = wp_get_post_terms( $item['data']->get_ID(), 'product_cat', [ 'fields' => 'ids' ] );

		$total_taxes += ( in_array( (int) $tax_options['wc_dynamic_taxes_category'], $cats, true ) );

	}

	if ( $total_taxes ) {

		$cart->add_fee( sprintf( '%d× %s', $total_taxes, $tax_options['wc_dynamic_taxes_name'] ), $total_taxes * $tax_options['wc_dynamic_taxes_amount'], false );

	}

}

בתור התחלה אנחנו בודקים האם התוסף הוגדר, אנחנו עושים זאת ע״י בדיקה האם הגדרות התוסף קיימות במסד נתונים והאם הם לא ריקות.
לאחר מכן אנחנו מבקשים לקבל את כל המוצרים בעגלה ואנחנו עוברים על כל אחד מהם ומבקשים את הקטגוריות אליו משוייך המוצר. השלב הבא הוא בדיקה האם הקטגוריה שהגדרנו בתוסף קיימת ברשימת הקטגוריות אליו משוייך המוצר, במידה וכן, אנחנו מעלים את המונה שלנו באחד.

לבסוף אנחנו מוסיפים מס חדש עם השם שהגדרנו ומגדירים אותו ככמות המוצרים בעגלה שהמשוייכים לקטגוריה הספציפית כפול המחיר שהגדרנו, כך שלבסוף יוצג לנו מסך על כל המוצרים בעגלה.

צילום מסך אשר מציג את המס הדינאמי שנוסף

סיכום להורדת הקוד המלא

הוספת מס בצורה דינאמית היא יכולת נחמדה שאומנם השימוש בה הוא לא נפוץ, אבל הוא בהחלט שימושי בחלק מהממקרים בו נרצה לחייב מס או כל סכום נוסף על מוצרים ספציפיים אשר מחייבים בתשלום נוסף.

    כתיבת תגובה

    1. ניב

      ווואי כל הכבוד!!!
      הייתי בטוח שזה בנוי בילט אין בווקומרס.

      האם אתה לוקח את אחוזי המע״מ והמס קניה דרך ווקומרס או מכניס ידנית?

      הגב
      1. דור צוברי

        היי ניב,
        לא מדובר פה על מיסים אשר אמורים להיות לכל המוצרים, מדובר פה על הפעלת מיסים על מוצרים ספציפיים.

    2. אורי

      לא היית יכול להשתמש פשוט product add-ons
      ולהוסיף גלובלית לכל מוצר שצריך את זה ?

      הגב
      1. דור צוברי

        היי אורי,
        אני פחות מכיר את התוסף הזה, מה גם שזה תוסף בתשלום אשר כולל בוודאות הרבה קוד שיהיה מיותר בשביל הלקוח שלי, מרבית הקוד שכתבתי כאן הוא קוד אשר פשוט מאפשר לו שליטה בסיסית במנגנון, בתכלס זה לא משהו שהוא הולך לשנות ולכן הקוד שכתבתי לו של המנגנון היה יכול להיות הרבהי ותר קצר ויעיל.

    אפשר להציע לך עוגיות? יש גם קפה! השימוש בקוקיז עוזר לשפר את הביקור שלך באתר. המשך גלישה אומר שהסכמת למדיניות הפרטיות שלי, וגם לקפה.

    שתפו