From eea15efd7b52b855b34d7efe540f894feb919c30 Mon Sep 17 00:00:00 2001 From: Caleb Burks <19caleb95@gmail.com> Date: Wed, 20 Dec 2017 17:16:33 -0600 Subject: [PATCH 1/2] Refactor classes for 1.3 --- .../class-wcpf-admin-global-settings.php | 6 +- .../class-wcpf-admin-product-settings.php | 6 +- classes/class-woocommerce-product-fees.php | 231 +++++++++++++----- classes/fees/class-wcpf-fee.php | 46 ---- classes/fees/class-wcpf-product-fee.php | 128 ---------- classes/fees/class-wcpf-variation-fee.php | 68 ------ readme.txt | 8 +- woocommerce-product-fees.php | 10 +- 8 files changed, 190 insertions(+), 313 deletions(-) delete mode 100644 classes/fees/class-wcpf-fee.php delete mode 100644 classes/fees/class-wcpf-product-fee.php delete mode 100644 classes/fees/class-wcpf-variation-fee.php diff --git a/classes/admin/class-wcpf-admin-global-settings.php b/classes/admin/class-wcpf-admin-global-settings.php index 84cdb34..726056a 100644 --- a/classes/admin/class-wcpf-admin-global-settings.php +++ b/classes/admin/class-wcpf-admin-global-settings.php @@ -2,7 +2,7 @@ /** * WooCommerce Product Fees * - * Creates and saves the global settings. + * Creates global product settings, coupon options, and adds csv import support. * * @class WCPF_Admin_Global_Settings * @author Caleb Burks @@ -202,4 +202,6 @@ public function add_export_data_fee_multiplier( $value, $product ) { return $product->get_meta( 'product-fee-multiplier', true, 'edit' ); } -} // End Class +} + +return new WCPF_Admin_Global_Settings(); diff --git a/classes/admin/class-wcpf-admin-product-settings.php b/classes/admin/class-wcpf-admin-product-settings.php index c048a20..2577a0f 100644 --- a/classes/admin/class-wcpf-admin-product-settings.php +++ b/classes/admin/class-wcpf-admin-product-settings.php @@ -2,7 +2,7 @@ /** * WooCommerce Product Fees * - * Creates and saves the product and variation settings. + * Create the product and variation settings. * * @class WCPF_Admin_Product_Settings * @author Caleb Burks @@ -148,4 +148,6 @@ public function admin_css() { "; } -} // End Class +} + +return new WCPF_Admin_Product_Settings(); diff --git a/classes/class-woocommerce-product-fees.php b/classes/class-woocommerce-product-fees.php index fb3edb3..532cd3c 100644 --- a/classes/class-woocommerce-product-fees.php +++ b/classes/class-woocommerce-product-fees.php @@ -17,28 +17,18 @@ class WooCommerce_Product_Fees { /** * Constructor for the main product fees class. - * - * @access public */ public function __construct() { if ( is_admin() ) { - // Product Settings + // Product & global settings require_once 'admin/class-wcpf-admin-product-settings.php'; - new WCPF_Admin_Product_Settings(); - // Global Settings require_once 'admin/class-wcpf-admin-global-settings.php'; - new WCPF_Admin_Global_Settings(); } - // Fee Classes - require_once( 'fees/class-wcpf-fee.php' ); - require_once( 'fees/class-wcpf-product-fee.php' ); - require_once( 'fees/class-wcpf-variation-fee.php' ); - - // Text Domain + // Text domain add_action( 'plugins_loaded', array( $this, 'text_domain' ) ); - // Hook in for fees to be added + // Hook-in for fees to be added add_action( 'woocommerce_cart_calculate_fees', array( $this, 'add_fees' ), 15 ); } @@ -50,77 +40,200 @@ public function text_domain() { } /** - * Add all fees at checkout. + * Check if a product contains fee data. * - * @access public + * @param int $id Product ID. + * @return bool True or false based on existance of custom meta. */ - public function add_fees( $cart ) { + public function product_contains_fee_data( $id ) { + $fee_name = get_post_meta( $id, 'product-fee-name', true ); + $fee_amount = get_post_meta( $id, 'product-fee-amount', true ); + + if ( '' !== $fee_name && '' !== $fee_amount && $fee_amount > 0 ) { + return true; + } + + return false; + } + + /** + * Convert a fee amount from percentage to the actual cost. + * + * @param string $fee_amount Fee amount. + * @param int $item_price Item price. + * @return int $fee_amount The actual cost of the fee. + */ + public function make_percentage_adjustments( $fee_amount, $item_price ) { + // Replace with a standard decimal separator for calculations. + $fee_amount = str_replace( wc_get_price_decimal_separator(), '.', $fee_amount ); + + if ( strpos( $fee_amount, '%' ) ) { + // Convert to decimal, then multiply by the cart item's price. + $fee_amount = ( str_replace( '%', '', $fee_amount ) / 100 ) * $item_price; + } + + return $fee_amount; + } + + /** + * Multiply the fee by the cart item quantity if needed. + * + * @param int $amount Fee amount. + * @param string $multiplier Whether the item should be multiplied by qty or not. + * @param int $qty Cart item quantity. + * @return int $amount The actual cost of the fee. + */ + public function maybe_multiply_by_quantity( $amount, $multiplier, $qty ) { + // Multiply the fee by the quantity if needed. + if ( 'yes' === $multiplier ) { + $amount = $qty * $amount; + } + + return $amount; + } + + /** + * Get the fee data from a product. + * + * @param array $item Cart item data. + * @return array $fee_data Fee data. + */ + public function get_fee_data( $item ) { + $fee_data = false; + + // Assign the variation's parent ID if no fee at the variation level. + if ( 0 !== $item['variation_id'] ) { + if ( 0 !== $item['variation_id'] && ! $this->product_contains_fee_data( $item['id'] ) ) { + $item['id'] = $item['parent_id']; + } + } + + if ( $this->product_contains_fee_data( $item['id'] ) ) { + $fee_data = array( + 'name' => get_post_meta( $item['id'], 'product-fee-name', true ), + 'amount' => get_post_meta( $item['id'], 'product-fee-amount', true ), + 'multiplier' => get_post_meta( $item['id'], 'product-fee-multiplier', true ) + ); + + $fee_data['amount'] = $this->make_percentage_adjustments( $fee_data['amount'], $item['price'] ); + $fee_data['amount'] = $this->maybe_multiply_by_quantity( $fee_data['amount'], $fee_data['multiplier'], $item['qty'] ); + } + + return $fee_data; + } + + /** + * Check if fees should be removed due to a coupon. + * + * @param object $cart WC Cart object. + * @return bool True or false based on existance of coupon meta. + */ + public function maybe_remove_fees_for_coupon( $cart ) { + + if ( version_compare( WC_VERSION, '3.0', '<' ) ) { + return false; + } // Look for a fee-removing coupon. $cart_coupons = $cart->get_coupons(); - if ( ! empty( $cart_coupons ) && version_compare( WC_VERSION, '3.0', '>=' ) ) { + if ( ! empty( $cart_coupons ) ) { foreach ( $cart_coupons as $coupon ) { if ( 'yes' === $coupon->get_meta( 'wcpf_coupon_remove_fees' ) ) { - // Exit now. No need to look for fees. - return; + return true; } } } - foreach( $cart->get_cart() as $cart_item => $values ) { - $_product = $values['data']; - $fee = false; + return false; + } - // Data we need from each product in the cart. - $product_data = array( - 'id' => $values['product_id'], - 'qty' => $values['quantity'], - 'price' => $_product->get_price() - ); + /** + * Get the fee's tax class. + * + * @param object $product WC Cart item object. + * @return string $fee_tax_class Which tax class to use for the fee. + */ + public function get_fee_tax_class( $product ) { + $fee_tax_class = get_option( 'wcpf_fee_tax_class', '_no_tax' ); - // Check first for a variation specific fee, and use that if it exists. - if ( 0 !== $values['variation_id'] ) { - $product_data['variation_id'] = $values['variation_id']; + if ( ! wc_tax_enabled() ) { + return '_no_tax'; + } - // Get variation fee. - $fee = new WCPF_Variation_Fee( $product_data, $cart ); + // Change fee tax settings to the product's tax settings. + if ( 'inherit_product_tax' === $fee_tax_class ) { + if ( 'taxable' === $product->get_tax_status() ) { + $fee_tax_class = $product->get_tax_class(); + } else { + $fee_tax_class = '_no_tax'; } + } - if ( ! $fee ) { - // Get product fee. - $fee = new WCPF_Product_Fee( $product_data, $cart ); - } + return $fee_tax_class; + } - if ( $fee && $fee->return_fee() ) { - $fee_data = $fee->return_fee(); - $fee_tax_class = get_option( 'wcpf_fee_tax_class', '_no_tax' ); + /** + * Get all the fees. + * + * @param object $cart WC Cart object. + * @return array $fees An array of fees to be added. + */ + public function get_fees( $cart ) { + $fees = array(); - // Change fee tax settings to the product's tax settings. - if ( 'inherit_product_tax' === $fee_tax_class ) { - $product_tax_status = $_product->get_tax_status(); - $product_tax_class = $_product->get_tax_class(); + if ( $this->maybe_remove_fees_for_coupon( $cart ) ) { + return $fees; + } - if ( 'taxable' === $product_tax_status ) { - $fee_tax_class = $product_tax_class; - } else { - $fee_tax_class = '_no_tax'; - } - } + foreach( $cart->get_cart() as $cart_item => $item ) { + + // Get the data we need from each product in the cart. + $item_data = array( + 'id' => $item['data']->get_id(), + 'variation_id' => $item['variation_id'], + 'parent_id' => $item['data']->get_parent_id(), + 'qty' => $item['quantity'], + 'price' => $item['data']->get_price() + ); + + $fee = $this->get_fee_data( $item_data ); - do_action( 'wcpf_before_fee_is_added', $fee_data, $_product ); + if ( $fee ) { + $fee_id = strtolower( $fee['name'] ); + $fee_tax_class = $this->get_fee_tax_class( $item['data'] ); - // Check if taxes need to be added. - if ( wc_tax_enabled() && '_no_tax' !== $fee_tax_class ) { - // Add fee with taxes. - $cart->add_fee( $fee_data['name'], $fee_data['amount'], true, $fee_tax_class ); + if ( array_key_exists( $fee_id, $fees ) && 'combine' === get_option( 'wcpf_name_conflicts', 'combine' ) ) { + $fees[$fee_id]['amount'] += $fee['amount']; } else { - // Add fee without taxes. - $cart->add_fee( $fee_data['name'], $fee_data['amount'] ); + $fees[$fee_id] = apply_filters( 'wcpf_filter_fee_data', array( + 'name' => $fee['name'], + 'amount' => $fee['amount'], + 'taxable' => ( '_no_tax' === $fee_tax_class ) ? false : true, + 'tax_class' => $fee_tax_class + ), $item_data ); } - - do_action( 'wcpf_after_fee_is_added', $fee_data, $_product ); } } + + return $fees; + } + + /** + * Add the fees to the cart. + * + * @param object $cart WC Cart object. + * @return null + */ + public function add_fees( $cart ) { + $fees = $this->get_fees( $cart ); + + if ( empty( $fees ) ) { + return; + } + + foreach ( $fees as $fee ) { + $cart->add_fee( $fee['name'], $fee['amount'], $fee['taxable'], $fee['tax_class'] ); + } } } diff --git a/classes/fees/class-wcpf-fee.php b/classes/fees/class-wcpf-fee.php deleted file mode 100644 index 4cdf9eb..0000000 --- a/classes/fees/class-wcpf-fee.php +++ /dev/null @@ -1,46 +0,0 @@ -get_fees(); - - foreach ( $already_applied_fees as $applied_fee ) { - // If the fee has the same name as a fee already in the cart, - // then add the fee amounts together and present as a single fee. - if ( strtolower( $applied_fee->name ) === strtolower( $fee_data['name'] ) && apply_filters( 'wcpf_add_same_name_fees', true ) ) { - if ( get_option( 'wcpf_name_conflicts', 'combine' ) === 'combine' ) { - $applied_fee->amount += $fee_data['amount']; - do_action( 'wcpf_fee_names_combined', $applied_fee, $fee_data ); - } - } - } - - return $fee_data; - } - -} diff --git a/classes/fees/class-wcpf-product-fee.php b/classes/fees/class-wcpf-product-fee.php deleted file mode 100644 index 0343353..0000000 --- a/classes/fees/class-wcpf-product-fee.php +++ /dev/null @@ -1,128 +0,0 @@ -id = $product_data['id']; - $this->qty = $product_data['qty']; - $this->price = $product_data['price']; - $this->cart = $cart; - } - - /** - * Get fee data from the product settings. - * - * @access public - */ - public function get_product_fee_data() { - // Product ID - $id = $this->id; - - // Check for a fee name and fee amount in the product settings - if ( get_post_meta( $id, 'product-fee-name', true ) != '' && get_post_meta( $id, 'product-fee-amount', true ) != '' ) { - $fee = array( - 'name' => get_post_meta( $id, 'product-fee-name', true ), - 'amount' => get_post_meta( $id, 'product-fee-amount', true ), - 'multiplier' => get_post_meta( $id, 'product-fee-multiplier', true ), - 'product_id' => $id - ); - - return apply_filters( 'wcpf_indivual_product_fee_data', $fee ); - } else { - // Return false if the product has no fee. - return false; - } - } - - /** - * Checks if the fee has a percentage in it, and then convert the fee from a percentage to a decimal. - * - * @access public - */ - public function percentage_conversion( $fee_amount ) { - if ( strpos( $fee_amount, '%' ) ) { - // Convert to Decimal - $decimal = str_replace( '%', '', $fee_amount ) / 100; - - // Multiply by Product Price - $fee_amount = $this->price * $decimal; - } - - return $fee_amount; - } - - /** - * Check if the fee should be multiplied by the quantity of the product in the cart. - * - * @access public - */ - public function quantity_multiply( $fee_data = '' ) { - // Allow child classes to use this with their own fee data. - if ( $fee_data == '' ) { - $fee_data = $this->get_product_fee_data(); - } - - // Return if the product has no fee data. - if ( ! $fee_data ) { - return false; - } - - // Replace with a standard decimal place for calculations. - $decimal_separator = wc_get_price_decimal_separator(); - $fee_amount = str_replace( $decimal_separator, '.', $fee_data['amount'] ); - - // Run the percentage check, and convert to a standard decimal place. - $converted_amount = $this->percentage_conversion( $fee_amount ); - - // Multiply the fee by the quantity if necessary. - if ( $fee_data['multiplier'] == 'yes' ) { - $fee_data['amount'] = $this->qty * $converted_amount; - } else { - $fee_data['amount'] = $converted_amount; - } - - return $fee_data; - } - - /** - * Return final fee data - * - * @access public - */ - public function return_fee() { - $fee_data = $this->quantity_multiply(); - - return parent::get_fee( $fee_data, $this->cart ); - } - -} diff --git a/classes/fees/class-wcpf-variation-fee.php b/classes/fees/class-wcpf-variation-fee.php deleted file mode 100644 index f8abd0e..0000000 --- a/classes/fees/class-wcpf-variation-fee.php +++ /dev/null @@ -1,68 +0,0 @@ -variation_id = $product_data['variation_id']; - } - - /** - * Get fee data from product variation settings. - * - * @access public - */ - public function get_variation_fee_data() { - // Variation ID - $variation_id = $this->variation_id; - - // Check if the variation has a fee - if ( get_post_meta( $variation_id, 'product-fee-name', true ) != '' && get_post_meta( $variation_id, 'product-fee-amount', true ) != '' ) { - $fee = array( - 'name' => get_post_meta( $variation_id, 'product-fee-name', true ), - 'amount' => get_post_meta( $variation_id, 'product-fee-amount', true ), - 'multiplier' => get_post_meta( $variation_id, 'product-fee-multiplier', true ), - 'product_id' => $variation_id - ); - - return apply_filters( 'wcpf_individual_variation_fee_data', $fee ); - } else { - // No variation fee data found. - return false; - } - } - - /** - * Return final fee data - * - * @access public - */ - public function return_fee() { - $variation_fee_data = $this->get_variation_fee_data(); - $fee_data = parent::quantity_multiply( $variation_fee_data ); - - return WCPF_Fee::get_fee( $fee_data, $this->cart ); - } - -} diff --git a/readme.txt b/readme.txt index 78a4f63..38a3151 100755 --- a/readme.txt +++ b/readme.txt @@ -88,15 +88,17 @@ Bugs can be reported either in the support forum or preferably on the [WooCommer == Changelog == -= 1.3.0 - xx/xx/xxxx = += 1.3.0 - 12/20/2017 = * Feature - Coupons have an option to remove fees in WC 3.0+ * Feature - Support for CSV Import/Export in WC 3.1+ -* Feature - Fees have an option inherit product tax settings. +* Feature - Fees have an option to inherit product tax settings. * Patch - WooCommerce 3.0 Compatibility. * Fix - The standard tax rate wasn't working/available. * Fix - Added support for localized decimal point separators. * Fix - Added WC Subscriptions compatibility. -* Fix - Fees with the same letters but different capitalizations will now match. +* Fix - Fees with the same letters but different capitalizations will now match and can be merged. +* Fix - Don't add $0 fees for every product. +* Refactor - Sorry, had to do it again :). Reduced code and prepared for adding automated testing. = 1.2.0 - 11/29/2015 = * Feature - Ability to assign a tax class to be used on fees. diff --git a/woocommerce-product-fees.php b/woocommerce-product-fees.php index 838c2aa..d99e076 100644 --- a/woocommerce-product-fees.php +++ b/woocommerce-product-fees.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce Product Fees * Plugin URI: http://calebburks.com/woocommerce-product-fees * Description: Add additional fees at checkout based on products that are in the cart. - * Version: 1.3.0-beta + * Version: 1.3.0 * Author: Caleb Burks * Author URI: http://calebburks.com * @@ -11,9 +11,11 @@ * Domain Path: /languages/ * * Requires at least: 4.5 - * Tested up to: 4.8 + * Tested up to: 4.9 + * WC tested up to: 3.3 + * WC requires at least: 3.0 * - * Copyright: (c) 2015 Caleb Burks + * Copyright: (c) 2018 Caleb Burks * License: GPL v3 or later * License URI: http://www.gnu.org/licenses/old-licenses/gpl-3.0.html */ @@ -30,5 +32,3 @@ function wc_product_fees_load_after_plugins_loaded() { new WooCommerce_Product_Fees; } } - -/* Silence is Golden */ From 7375b3883a8c61481ed3a6af331bcf6ee44051d5 Mon Sep 17 00:00:00 2001 From: Caleb Burks <19caleb95@gmail.com> Date: Wed, 20 Dec 2017 17:17:35 -0600 Subject: [PATCH 2/2] Add more .gitignore entries --- .gitignore | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.gitignore b/.gitignore index e43b0f9..71f1485 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,28 @@ +# Editors +project.xml +project.properties +/nbproject/private/ +.buildpath +.project +.settings* +.idea +.vscode +*.sublime-project +*.sublime-workspace +.sublimelinterrc + +# OS X metadata .DS_Store + +# Windows junk +Thumbs.db + +# Unit tests +/tmp +/tests/bin/tmp + +# Logs +/logs + +# Composer +/vendor/ \ No newline at end of file