function fn_calculate_cart_content(&$cart, $auth, $calculate_shipping = 'A', $calculate_taxes = true, $options_style = 'F', $apply_cart_promotions = true) { $shipping_rates = array(); $product_groups = array(); $cart_products = array(); $cart['subtotal'] = $cart['display_subtotal'] = $cart['original_subtotal'] = $cart['amount'] = $cart['total'] = $cart['discount'] = $cart['tax_subtotal'] = 0; $cart['use_discount'] = false; $cart['shipping_required'] = false; $cart['shipping_failed'] = $cart['company_shipping_failed'] = false; $cart['stored_taxes'] = empty($cart['stored_taxes']) ? 'N' : $cart['stored_taxes']; $cart['display_shipping_cost'] = $cart['shipping_cost'] = 0; $cart['coupons'] = empty($cart['coupons']) ? array() : $cart['coupons']; $cart['recalculate'] = isset($cart['recalculate']) ? $cart['recalculate'] : false; $cart['free_shipping'] = array(); $cart['options_style'] = $options_style; $cart['products'] = !empty($cart['products']) ? $cart['products'] : array(); fn_add_exclude_products($cart, $auth); if (isset($cart['products']) && is_array($cart['products'])) { $amount_totals = array(); if (Registry::get('settings.General.disregard_options_for_discounts') == 'Y') { foreach ($cart['products'] as $k => $v) { if (!empty($amount_totals[$v['product_id']])) { $amount_totals[$v['product_id']] += $v['amount']; } else { $amount_totals[$v['product_id']] = $v['amount']; } } } // Collect product data foreach ($cart['products'] as $k => $v) { $cart['products'][$k]['amount_total'] = isset($amount_totals[$v['product_id']]) ? $amount_totals[$v['product_id']] : $v['amount']; $_cproduct = fn_get_cart_product_data($k, $cart['products'][$k], false, $cart, $auth); if (empty($_cproduct)) { // FIXME - for deleted products for OM fn_delete_cart_product($cart, $k); continue; } $cart_products[$k] = $_cproduct; } fn_set_hook('calculate_cart_items', $cart, $cart_products, $auth); // Apply cart promotions if ($apply_cart_promotions == true && $cart['subtotal'] >= 0) { if (!empty($cart['stored_subtotal_discount'])) { $prev_discount = $cart['subtotal_discount']; } $cart['applied_promotions'] = fn_promotion_apply('cart', $cart, $auth, $cart_products); if (!empty($cart['stored_subtotal_discount'])) { $cart['subtotal_discount'] = $prev_discount; } } fn_check_promotion_notices(); if (Registry::get('settings.General.disable_shipping') == 'Y') { $cart['shipping_required'] = false; } if (!empty($cart['change_cart_products'])) { $location = fn_get_customer_location($auth, $cart); $product_groups = Shippings::groupProductsList($cart_products, $location); if (!empty($cart['product_groups']) && count($product_groups) == count($cart['product_groups'])) { foreach ($product_groups as $key_group => $group) { $cart['product_groups'][$key_group]['products'] = $group['products']; } } else { if (!empty($cart['chosen_shipping']) && count($cart['chosen_shipping']) == count($product_groups)) { $cart['calculate_shipping'] = true; } if (!empty($cart['product_groups']) && count($cart['product_groups']) !== count($product_groups)) { unset($cart['product_groups']); } } unset($cart['change_cart_products']); $cart['stored_taxes'] = 'N'; } if (!empty($cart['calculate_shipping']) || empty($cart['product_groups'])) { $location = fn_get_customer_location($auth, $cart); $product_groups = Shippings::groupProductsList($cart_products, $location); $shippings = array(); if ($cart['shipping_required'] !== false) { $cart['shipping_required'] = false; foreach ($product_groups as $key_group => $group) { if ($group['shipping_no_required'] === false) { $cart['shipping_required'] = true; break; } } } foreach ($product_groups as $key_group => $group) { if ($cart['shipping_required'] === false) { $product_groups[$key_group]['free_shipping'] = true; $product_groups[$key_group]['shipping_no_required'] = true; } $product_groups[$key_group]['shippings'] = array(); $shippings_group = Shippings::getShippingsList($group); // Adding a shipping method from the created order, if the shipping is not yet in the list. if (!empty($cart['chosen_shipping']) && !empty($cart['shipping']) && !empty($cart['order_id'])) { foreach ($cart['shipping'] as $shipping) { if (!isset($shippings_group[$shipping['shipping_id']])) { $shippings_group[$shipping['shipping_id']] = $shipping; } } } foreach ($shippings_group as $shipping_id => $shipping) { if (!empty($shipping['service_params']['max_weight_of_box'])) { $_group = Shippings::repackProductsByWeight($group, $shipping['service_params']['max_weight_of_box']); } else { $_group = $group; } $_shipping = $shipping; $_shipping['package_info'] = $_group['package_info']; $_shipping['keys'] = array('group_key' => $key_group, 'shipping_id' => $shipping_id); $shippings[] = $_shipping; $shipping['group_key'] = $key_group; $shipping['rate'] = 0; if (in_array($shipping_id, $cart['free_shipping']) || $group['free_shipping']) { $shipping['free_shipping'] = true; } $product_groups[$key_group]['shippings'][$shipping_id] = $shipping; } } if (!empty($cart['calculate_shipping'])) { $rates = Shippings::calculateRates($shippings); foreach ($rates as $rate) { $g_key = $rate['keys']['group_key']; $sh_id = $rate['keys']['shipping_id']; if ($rate['price'] !== false) { $rate['price'] += !empty($product_groups[$g_key]['package_info']['shipping_freight']) ? $product_groups[$g_key]['package_info']['shipping_freight'] : 0; $product_groups[$g_key]['shippings'][$sh_id]['rate'] = empty($product_groups[$g_key]['shippings'][$sh_id]['free_shipping']) ? $rate['price'] : 0; } else { unset($product_groups[$g_key]['shippings'][$sh_id]); } } } $cart['product_groups'] = $product_groups; } $product_groups =& $cart['product_groups']; // FIXME $cart['shipping_cost'] = 0; $cart['shipping'] = array(); if (empty($cart['chosen_shipping'])) { $cart['chosen_shipping'] = array(); if (fn_allowed_for('ULTIMATE') && Registry::get('settings.Checkout.display_shipping_step') != 'Y' && !empty($cart['calculate_shipping'])) { foreach ($product_groups as $key_group => $group) { if (!empty($group['shippings'])) { $first_shipping = reset($group['shippings']); $cart['chosen_shipping'][$key_group] = $first_shipping['shipping_id']; } } } } $count_shipping_failed = 0; foreach ($product_groups as $key_group => $group) { if (!empty($cart['calculate_shipping']) && (!isset($cart['chosen_shipping'][$key_group]) || empty($group['shippings'][$cart['chosen_shipping'][$key_group]])) && !$group['free_shipping']) { $cart['chosen_shipping'][$key_group] = key($group['shippings']); } if ($group['shipping_no_required']) { unset($cart['chosen_shipping'][$key_group]); } if (!isset($cart['chosen_shipping'][$key_group]) && !$group['free_shipping'] && !$group['shipping_no_required']) { $count_shipping_failed++; $cart['company_shipping_failed'] = true; } foreach ($group['shippings'] as $shipping_id => $shipping) { if (isset($cart['chosen_shipping'][$key_group]) && $cart['chosen_shipping'][$key_group] == $shipping_id) { $cart['shipping_cost'] += $shipping['rate']; } } if (!empty($group['shippings']) && isset($cart['chosen_shipping'][$key_group])) { $shipping = $group['shippings'][$cart['chosen_shipping'][$key_group]]; $shipping_id = $shipping['shipping_id']; if (empty($cart['shipping'][$shipping_id])) { $cart['shipping'][$shipping_id] = $shipping; $cart['shipping'][$shipping_id]['rates'] = array(); } $cart['shipping'][$shipping_id]['rates'][$key_group] = $shipping['rate']; } } $cart['display_shipping_cost'] = $cart['shipping_cost']; if (!empty($product_groups) && count($product_groups) == $count_shipping_failed) { $cart['shipping_failed'] = true; } foreach ($cart['chosen_shipping'] as $key_group => $shipping_id) { if (!empty($product_groups[$key_group]) && !empty($product_groups[$key_group]['shippings'][$shipping_id])) { $shipping = $product_groups[$key_group]['shippings'][$shipping_id]; $shipping['group_name'] = $product_groups[$key_group]['name']; $product_groups[$key_group]['chosen_shippings'] = array($shipping); } else { unset($cart['chosen_shipping'][$key_group]); } } fn_apply_stored_shipping_rates($cart); fn_set_hook('calculate_cart_taxes_pre', $cart, $cart_products, $product_groups, $calculate_taxes, $auth); $calculated_taxes_summary = array(); foreach ($product_groups as $key_group => &$group) { foreach ($group['products'] as $cart_id => $product) { if (!empty($cart_products[$cart_id])) { $group['products'][$cart_id] = $cart_products[$cart_id]; } } // Calculate taxes if ($calculate_taxes == true && $auth['tax_exempt'] != 'Y') { $calculated_taxes = fn_calculate_taxes($cart, $key_group, $group['products'], $group['shippings'], $auth); if (empty($calculated_taxes_summary)) { $calculated_taxes_summary = array(); } foreach ($calculated_taxes as $tax_id => $tax) { if (empty($calculated_taxes_summary[$tax_id])) { $calculated_taxes_summary[$tax_id] = $calculated_taxes[$tax_id]; } else { $calculated_taxes_summary[$tax_id]['tax_subtotal'] += $calculated_taxes[$tax_id]['applies']['S']; $calculated_taxes_summary[$tax_id]['applies']['S'] += $calculated_taxes[$tax_id]['applies']['S']; $calculated_taxes_summary[$tax_id]['tax_subtotal'] += $calculated_taxes[$tax_id]['applies']['P']; $calculated_taxes_summary[$tax_id]['applies']['P'] += $calculated_taxes[$tax_id]['applies']['P']; } } } elseif ($cart['stored_taxes'] != 'Y') { $cart['taxes'] = $cart['tax_summary'] = array(); } unset($group); } fn_apply_calculated_taxes($calculated_taxes_summary, $cart); // /FIXME fn_set_hook('calculate_cart_taxes_post', $cart, $cart_products, $shipping_rates, $calculate_taxes, $auth); $cart['subtotal'] = $cart['display_subtotal'] = 0; fn_update_cart_data($cart, $cart_products); foreach ($cart['products'] as $product_code => $product) { foreach ($product_groups as $key_group => $group) { if (in_array($product_code, array_keys($group['products']))) { $product_groups[$key_group]['products'][$product_code] = $product; } } } // Calculate totals foreach ($product_groups as $key_group => $group) { foreach ($group['products'] as $product_code => $product) { $_tax = !empty($product['tax_summary']) ? $product['tax_summary']['added'] / $product['amount'] : 0; $cart_products[$product_code]['display_price'] = $cart_products[$product_code]['price'] + (Registry::get('settings.Appearance.cart_prices_w_taxes') == 'Y' ? $_tax : 0); $cart_products[$product_code]['subtotal'] = $cart_products[$product_code]['price'] * $product['amount']; $cart_products[$product_code]['display_subtotal'] = $cart_products[$product_code]['display_price'] * $product['amount']; if (!empty($product['tax_summary'])) { $cart_products[$product_code]['tax_summary'] = $product['tax_summary']; } $cart['subtotal'] += $cart_products[$product_code]['subtotal']; $cart['display_subtotal'] += $cart_products[$product_code]['display_subtotal']; $cart['products'][$product_code]['display_price'] = $cart_products[$product_code]['display_price']; $product_groups[$key_group]['products'][$product_code]['display_price'] = $cart_products[$product_code]['display_price']; $cart['tax_subtotal'] += !empty($product['tax_summary']) ? $product['tax_summary']['added'] : 0; $cart['total'] += ($cart_products[$product_code]['price'] - 0) * $product['amount']; if (!empty($product['discount'])) { $cart['discount'] += $product['discount'] * $product['amount']; } } } if (Registry::get('settings.General.tax_calculation') == 'subtotal') { $cart['tax_subtotal'] += !empty($cart['tax_summary']['added']) ? $cart['tax_summary']['added'] : 0; } $cart['subtotal'] = fn_format_price($cart['subtotal']); $cart['display_subtotal'] = fn_format_price($cart['display_subtotal']); $cart['total'] += $cart['tax_subtotal']; $cart['total'] = fn_format_price($cart['total'] + $cart['shipping_cost']); if (!empty($cart['subtotal_discount'])) { $cart['total'] -= $cart['subtotal_discount'] < $cart['total'] ? $cart['subtotal_discount'] : $cart['total']; } } /** * Processes cart data after calculating all prices and other data (taxes, shippings etc) * * @param array $cart Cart data * @param array $cart_products Cart products * @param array $auth Auth data * @param string $calculate_shipping // 1-letter flag * A - calculate all available methods * E - calculate selected methods only (from cart[shipping]) * S - skip calculation * @param bool $calculate_taxes Flag determines if taxes should be calculated * @param bool $apply_cart_promotions Flag determines if promotions should be applied to the cart */ fn_set_hook('calculate_cart', $cart, $cart_products, $auth, $calculate_shipping, $calculate_taxes, $apply_cart_promotions); if (!empty($cart['calculate_shipping']) || empty($cart['product_groups'])) { $cart['product_groups'] = $product_groups; } $cart['recalculate'] = false; $cart['calculate_shipping'] = false; /** * Processes cart data after calculating all prices and other data (taxes, shippings etc) including products group * * @param array $cart Cart data * @param array $auth Auth data * @param string $calculate_shipping // 1-letter flag * A - calculate all available methods * E - calculate selected methods only (from cart[shipping]) * S - skip calculation * @param bool $calculate_taxes Flag determines if taxes should be calculated * @param string $options_style 1-letter flag * "F" - Full option information (with exceptions) * "I" - Short info * "" - "Source" info. Only ids array (option_id => variant_id) * @param bool $apply_cart_promotions Flag determines if promotions should be applied to the cart * @param array $cart_products Cart products * @param array $product_groups Products grouped by packages, suppliers, vendors */ fn_set_hook('calculate_cart_post', $cart, $auth, $calculate_shipping, $calculate_taxes, $options_style, $apply_cart_promotions, $cart_products, $product_groups); return array($cart_products, $product_groups); }
function fn_calculate_taxes(&$cart, &$cart_products, &$shipping_rates, $auth) { $calculated_data = array(); if (Registry::get('settings.General.tax_calculation') == 'unit_price') { // Tax calculation method based on UNIT PRICE // Calculate product taxes foreach ($cart_products as $k => $product) { $taxes = fn_get_product_taxes($k, $cart, $cart_products); if (empty($taxes)) { continue; } if (isset($product['subtotal'])) { $price = fn_format_price($product['subtotal'] / $product['amount']); $calculated_data['P_' . $k] = fn_calculate_tax_rates($taxes, $price, $product['amount'], $auth, $cart); $cart_products[$k]['tax_summary'] = array('included' => 0, 'added' => 0, 'total' => 0); // tax summary for 1 unit of product // Apply taxes to product subtotal if (!empty($calculated_data['P_' . $k])) { foreach ($calculated_data['P_' . $k] as $_k => $v) { $cart_products[$k]['taxes'][$_k] = $v; if ($taxes[$_k]['price_includes_tax'] != 'Y') { $cart_products[$k]['tax_summary']['added'] += $v['tax_subtotal']; } else { $cart_products[$k]['tax_summary']['included'] += $v['tax_subtotal']; } } $cart_products[$k]['tax_summary']['total'] = $cart_products[$k]['tax_summary']['added'] + $cart_products[$k]['tax_summary']['included']; } } } // Calculate shipping taxes if (!empty($shipping_rates)) { foreach ($shipping_rates as $shipping_id => $shipping) { $taxes = fn_get_shipping_taxes($shipping_id, $shipping_rates, $cart); if (!empty($taxes)) { $shipping_rates[$shipping_id]['taxes'] = array(); $calculate_rate = true; if (!empty($cart['shipping'][$shipping_id])) { foreach ($cart['shipping'][$shipping_id]['rates'] as $k => $v) { $calculated_data['S_' . $shipping_id . '_' . $k] = fn_calculate_tax_rates($taxes, $v, 1, $auth, $cart); if (!empty($calculated_data['S_' . $shipping_id . '_' . $k])) { foreach ($calculated_data['S_' . $shipping_id . '_' . $k] as $__k => $__v) { if ($taxes[$__k]['price_includes_tax'] != 'Y') { $cart['display_shipping_cost'] += Registry::get('settings.Appearance.cart_prices_w_taxes') == 'Y' ? $__v['tax_subtotal'] : 0; $cart['tax_subtotal'] += $__v['tax_subtotal']; } if ($cart['stored_taxes'] == 'Y') { $cart['taxes'][$__k]['applies']['S_' . $shipping_id . '_' . $k] = $__v['tax_subtotal']; } } $shipping_rates[$shipping_id]['taxes']['S_' . $shipping_id . '_' . $k] = $calculated_data['S_' . $shipping_id . '_' . $k]; $calculate_rate = false; } } } if ($calculate_rate) { foreach ($shipping['rates'] as $k => $v) { if (isset($shipping['rates'][$k])) { $cur_shipping_rates = fn_calculate_tax_rates($taxes, $v, 1, $auth, $cart); if (!empty($cur_shipping_rates)) { $shipping_rates[$shipping_id]['taxes']['S_' . $shipping_id . '_' . $k] = $cur_shipping_rates; } } } } } } } } else { // Tax calculation method based on SUBTOTAL // Calculate discounted subtotal if (!isset($cart['subtotal_discount'])) { $cart['subtotal_discount'] = 0; } $discounted_subtotal = $cart['original_subtotal'] - $cart['subtotal_discount']; // Get discount distribution coefficient (DDC) between taxes $ddc = $discounted_subtotal / $cart['original_subtotal']; // // Group subtotal by taxes // $subtotal = array(); // Get products taxes foreach ($cart_products as $k => $product) { $taxes = fn_get_product_taxes($k, $cart, $cart_products); if (!empty($taxes)) { foreach ($taxes as $tax_id => $tax) { if (empty($subtotal[$tax_id])) { $subtotal[$tax_id] = $tax; $subtotal[$tax_id]['subtotal'] = $subtotal[$tax_id]['applies']['P'] = $subtotal[$tax_id]['applies']['S'] = 0; $subtotal[$tax_id]['applies']['items']['P'] = $subtotal[$tax_id]['applies']['items']['S'] = array(); } $subtotal[$tax_id]['subtotal'] += $product['subtotal']; $subtotal[$tax_id]['applies']['P'] += $product['subtotal']; $subtotal[$tax_id]['applies']['items']['P'][$k] = true; } } } // Get shipping taxes if (!empty($shipping_rates)) { foreach ($shipping_rates as $shipping_id => $shipping) { // Calculate taxes for each shipping rate $taxes = fn_get_shipping_taxes($shipping_id, $shipping_rates, $cart); if (!empty($taxes)) { $shipping_rates[$shipping_id]['taxes'] = array(); foreach ($shipping['rates'] as $k => $v) { $tax = fn_calculate_tax_rates($taxes, fn_format_price($v), 1, $auth, $cart); $shipping_rates[$shipping_id]['taxes']['S_' . $shipping_id . '_' . $k] = $tax; } } if (!isset($cart['shipping'][$shipping_id])) { continue; } if (!empty($taxes)) { foreach ($taxes as $tax_id => $tax) { if (empty($subtotal[$tax_id])) { $subtotal[$tax_id] = $tax; $subtotal[$tax_id]['subtotal'] = $subtotal[$tax_id]['applies']['P'] = $subtotal[$tax_id]['applies']['S'] = 0; $subtotal[$tax_id]['applies']['items']['P'] = $subtotal[$tax_id]['applies']['items']['S'] = array(); } $subtotal[$tax_id]['subtotal'] += array_sum($cart['shipping'][$shipping_id]['rates']); $subtotal[$tax_id]['applies']['S'] += array_sum($cart['shipping'][$shipping_id]['rates']); $subtotal[$tax_id]['applies']['items']['S'][$shipping_id] = true; } } } } // Apply DDC and calculate tax rates $calculated_taxes = array(); foreach ($subtotal as $tax_id => $_st) { if (empty($_st['tax_id'])) { $_st['tax_id'] = $tax_id; } $tax = fn_calculate_tax_rates(array($_st), fn_format_price($_st['applies']['P'] * $ddc + $_st['applies']['S']), 1, $auth, $cart); if (empty($tax)) { continue; } $calculated_data[$tax_id] = reset($tax); $products_tax = fn_calculate_tax_rates(array($_st), fn_format_price($_st['applies']['P'] * $ddc), 1, $auth, $cart); $calculated_data[$tax_id]['applies']['P'] = $products_tax[$tax_id]['tax_subtotal']; $shipping_tax = fn_calculate_tax_rates(array($_st), fn_format_price($_st['applies']['S']), 1, $auth, $cart); $calculated_data[$tax_id]['applies']['S'] = $shipping_tax[$tax_id]['tax_subtotal']; $calculated_data[$tax_id]['tax_subtotal'] = $products_tax[$tax_id]['tax_subtotal'] + $shipping_tax[$tax_id]['tax_subtotal']; $calculated_data[$tax_id]['applies']['items'] = $_st['applies']['items']; } } fn_apply_calculated_taxes($calculated_data, $cart); return false; }