• norbre

    (@norbre)


    Hi,

    I have a custom plugin (developed by help of AI) to add custom packaging cost (unfortunatelly I’m not that good at programming in this level).

    The currency switcher plugin works well with default Subtotal and Shipping costs, but doesn’t switch my custom fee. Can you help me what is missing from my code to cooperate with the currency switcher?

    Here is my plugin code below.
    It calculates the required containers based on the shipping class and shipping class capacity set on the plugin’s settings page. The Shipping price also multiplied by the number of containers.

    <?php
    /*
    * Plugin Name: WooCommerce Dynamic Packing Charge
    * Description: Extra packaging fee for every X products, shipping fee multiplied by the number of containers. Separate step setting for each shipping class, plus option for mixing.
    * Version: 1.7.1
    * Requires at least: 5.2
    * Requires PHP: 7.2
    * Author: Gal
    * Text Domain: packing-charge
    * Requires Plugins: woocommerce
    */

    if ( ! defined( 'ABSPATH' ) ) exit;

    /* Settings link */
    add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), function( $links ) {
    $settings_url = add_query_arg( array( 'page' => 'wc-settings', 'tab' => 'packing_charges' ), admin_url('admin.php') );
    $settings_link = '<a href="' . esc_url( $settings_url ) . '">' . __('Settings', 'packing-charge') . '</a>';
    array_unshift($links, $settings_link);
    return $links;
    });

    /* Settings tab */
    add_filter( 'woocommerce_settings_tabs_array', function( $tabs ) {
    $tabs['packing_charges'] = __( 'Packing Charges', 'packing-charge' );
    return $tabs;
    }, 999 );

    add_action( 'woocommerce_settings_tabs_packing_charges', function() {
    woocommerce_admin_fields( pacs_get_settings() );
    });

    add_action( 'woocommerce_update_options_packing_charges', function() {
    woocommerce_update_options( pacs_get_settings() );
    });

    /* Settings fields */
    function pacs_get_settings() {
    $settings = array(
    array(
    'name' => __( 'Packing Charge Settings', 'packing-charge' ),
    'type' => 'title',
    'id' => 'pacs_section_title'
    ),
    array(
    'name' => __( 'Enable Packing Charge', 'packing-charge' ),
    'type' => 'checkbox',
    'id' => 'pacs_enable',
    'default' => 'no'
    ),
    array(
    'name' => __( 'Calculation Type', 'packing-charge' ),
    'type' => 'select',
    'id' => 'pacs_calculation_type',
    'options' => array(
    'fixed' => __( 'Fixed (Flat Fee)', 'packing-charge' ),
    'percentage' => __( 'Percentage', 'packing-charge' ),
    'per_step' => __( 'Per step (every X items)', 'packing-charge' )
    ),
    'default' => 'fixed'
    ),
    array(
    'name' => __( 'Packing Charge Amount', 'packing-charge' ),
    'type' => 'number',
    'id' => 'pacs_amount',
    'default' => '0',
    'custom_attributes' => array( 'min' => '0' )
    ),
    array(
    'name' => __( 'Step (products per fee)', 'packing-charge' ),
    'type' => 'number',
    'id' => 'pacs_step',
    'default' => '1',
    'custom_attributes' => array( 'min' => '1' )
    ),
    array(
    'name' => __( 'Packing Charge Label', 'packing-charge' ),
    'type' => 'text',
    'id' => 'pacs_label',
    'default' => 'Packaging fee'
    ),
    array(
    'name' => __( 'Packing Container Label', 'packing-charge' ),
    'type' => 'text',
    'id' => 'pacs_container_label',
    'default' => 'Container'
    ),
    array(
    'name' => __( 'Packing Unit Label', 'packing-charge' ),
    'type' => 'text',
    'id' => 'pacs_unit_label',
    'default' => 'db'
    ),
    array(
    'name' => __( 'Packing Counts Per Shipping Class', 'packing-charge' ),
    'type' => 'checkbox',
    'id' => 'pacs_counts_per_shipping_class',
    'default' => 'no'
    ),
    array(
    'name' => __( 'Step for products without shipping class', 'packing-charge' ),
    'type' => 'number',
    'id' => 'pacs_step_none',
    'default' => '1',
    'custom_attributes' => array( 'min' => '1' )
    ),
    array(
    'name' => __( 'Allow mixing shipping classes into one container', 'packing-charge' ),
    'type' => 'checkbox',
    'id' => 'pacs_allow_mix',
    'default' => 'yes'
    ),
    );

    $shipping_classes = WC()->shipping->get_shipping_classes();
    foreach ( $shipping_classes as $class ) {
    $settings[] = array(
    'name' => sprintf( __( 'Step for %s', 'packing-charge' ), $class->name ),
    'type' => 'number',
    'id' => 'pacs_step_' . $class->slug,
    'default' => '1',
    'custom_attributes' => array( 'min' => '1' )
    );
    }

    $settings[] = array(
    'type' => 'sectionend',
    'id' => 'pacs_section_end'
    );

    return $settings;
    }

    /* Container calculation */
    function pacs_calculate_groups() {
    $allow_mix = get_option( 'pacs_allow_mix', 'yes' ) === 'yes';
    $per_class = get_option( 'pacs_counts_per_shipping_class' ) === 'yes';

    $global_step = max( 1, intval( get_option( 'pacs_step', 1 ) ) );
    $none_step = max( 1, intval( get_option( 'pacs_step_none', 1 ) ) );

    if ( ! $per_class ) {
    $qty = WC()->cart->get_cart_contents_count();
    return max( 1, ceil( $qty / $global_step ) );
    }

    if ( ! $allow_mix ) {
    $groups = 0;
    foreach ( WC()->cart->get_cart() as $item ) {
    $qty = intval( $item['quantity'] );
    $class_id = $item['data']->get_shipping_class_id();
    if ( $class_id ) {
    $class = get_term( $class_id, 'product_shipping_class' );
    $step = max( 1, intval( get_option( 'pacs_step_' . $class->slug, 1 ) ) );
    } else {
    $step = $none_step;
    }
    $groups += ceil( $qty / $step );
    }
    return max( 1, $groups );
    }

    // Greedy: fill smallest container first
    $classes = [];
    foreach ( WC()->cart->get_cart() as $item ) {
    $qty = intval( $item['quantity'] );
    $class_id = $item['data']->get_shipping_class_id();
    if ( $class_id ) {
    $class = get_term( $class_id, 'product_shipping_class' );
    $cap = max( 1, intval( get_option( 'pacs_step_' . $class->slug, 1 ) ) );
    } else {
    $cap = $none_step;
    }
    $classes[] = ['qty' => $qty, 'cap' => $cap];
    }

    usort($classes, function($a, $b) {
    return $a['cap'] <=> $b['cap'];
    });

    $containers = 0;
    while (true) {
    $remaining = 0;
    foreach ($classes as $c) { $remaining += $c['qty']; }
    if ($remaining <= 0) break;

    // actual container capacity: smallest available class
    $cap = 0;
    foreach ($classes as $c) {
    if ($c['qty'] > 0) { $cap = $c['cap']; break; }
    }
    if ($cap == 0) break;

    $containers++;
    $space_left = $cap;

    // first smallest class
    for ($i = 0; $i < count($classes); $i++) {
    if ($space_left <= 0) break;
    if ($classes[$i]['qty'] <= 0) continue;

    $put = min($classes[$i]['qty'], $space_left);
    $classes[$i]['qty'] -= $put;
    $space_left -= $put;
    }
    }

    return max(1, $containers);
    }

    /* Add packing fee as real WooCommerce fee to calculate total */
    add_action( 'woocommerce_cart_calculate_fees', function( $cart ) {
    if ( get_option( 'pacs_enable' ) !== 'yes' ) return;

    $amount = (float) get_option( 'pacs_amount', 0 );
    $type = get_option( 'pacs_calculation_type', 'fixed' );
    $groups = pacs_calculate_groups();
    $subtotal = (float) $cart->get_subtotal();

    // Calculating fee
    $fee = 0.0;
    if ( $type === 'fixed' ) {
    $fee = $groups * $amount;
    } elseif ( $type === 'percentage' ) {

    $fee = $subtotal * ( $amount / 100.0 );
    } elseif ( $type === 'per_step' ) {
    $fee = $groups * $amount;
    }

    if ( $fee <= 0 ) return;

    $charge_label = get_option( 'pacs_label', 'Packaging fee' );
    $container_label = get_option( 'pacs_container_label', 'Container' );
    $unit_label = get_option( 'pacs_unit_label', 'db' );
    $label = $charge_label . ' (' . $groups . ' ' . $unit_label . ' ' . $container_label . ')';


    $cart->add_fee( $label, $fee, true );
    });

    /* Shupping costs multiply by containers */
    add_filter( 'woocommerce_package_rates', function( $rates, $package ) {
    if ( get_option( 'pacs_enable' ) !== 'yes' ) return $rates;

    $groups = pacs_calculate_groups();
    foreach ( $rates as $rate_id => $rate ) {
    $rates[ $rate_id ]->cost = $rates[ $rate_id ]->cost * $groups;


    if ( ! empty( $rates[ $rate_id ]->taxes ) && is_array( $rates[ $rate_id ]->taxes ) ) {
    $taxes = $rates[ $rate_id ]->taxes;
    foreach ( $taxes as $i => $tax_amount ) {
    $taxes[$i] = $tax_amount * $groups;
    }
    $rates[ $rate_id ]->taxes = $taxes;
    }
    }
    return $rates;
    }, 10, 2 );


    /* Defaults on activation */
    register_activation_hook( __FILE__, function() {
    $defaults = array(
    'pacs_enable' => 'no',
    'pacs_label' => 'Packing fee',
    'pacs_amount' => '0',
    'pacs_calculation_type' => 'fixed',
    'pacs_step' => '1',
    'pacs_container_label' => 'Packing container',
    'pacs_unit_label' => 'db',
    'pacs_counts_per_shipping_class' => 'no',
    'pacs_step_none' => '1',
    'pacs_allow_mix' => 'yes'
    );

    foreach ( $defaults as $key => $value ) {
    if ( get_option( $key ) === false ) {
    update_option( $key, $value );
    }
    }

    if ( function_exists( 'WC' ) && WC()->shipping ) {
    $shipping_classes = WC()->shipping->get_shipping_classes();
    if ( is_array( $shipping_classes ) ) {
    foreach ( $shipping_classes as $class ) {
    if ( isset( $class->slug ) ) {
    $opt_key = 'pacs_step_' . $class->slug;
    if ( get_option( $opt_key ) === false ) {
    update_option( $opt_key, '1' );
    }
    }
    }
    }
    }
    });
Viewing 3 replies - 1 through 3 (of 3 total)
  • Plugin Author RazyRx

    (@razyrx)

    Hello,

    It is depend on option “Visual only” in plugin settings.
    If it is enabled then price on page must be displayed with correct structure same as other price.
    If this option disabled then plugin use specific hook to replace it.

    Regards,
    Oleg

    Thread Starter norbre

    (@norbre)

    Hi,

    I’ve tried the Visual only option and works well, but does not effect on the outgoing emails and the content of the admin orders menu, so I would like to use the plugin in not visual mode.

    If I am right, my plugin adds the new fee to the cart . This fee is became visible in the emails and in the admin orders menu.

    $cart->add_fee( $label, $fee, true );

    Does your hook goes through every fees or just specific ones?
    Can I tag or mark the extra fees for your plugin’s hook to calculate with?


    Thank you

    Plugin Author RazyRx

    (@razyrx)

    Hello,

    Visual only will be applied only on site frontend.

    In most cases plugin modify price before set it to cart because it can be used in different places.

    Maybe it is required to apply similar hook for thise custom fee before add it to cart.

    Regards,
    Oleg

Viewing 3 replies - 1 through 3 (of 3 total)

You must be logged in to reply to this topic.