public function getTaxesAmount($price_before_tax, $price_after_tax = null, $round_precision = 2, $round_mode = null)
 {
     $amounts = array();
     $total_base = 0;
     foreach ($this->getProductTaxes() as $row) {
         if (!array_key_exists($row['id_tax'], $amounts)) {
             $amounts[$row['id_tax']] = array('rate' => $row['rate'], 'base' => 0);
         }
         $amounts[$row['id_tax']]['base'] += $row['total_price_tax_excl'];
         $total_base += $row['total_price_tax_excl'];
     }
     $actual_tax = 0;
     foreach ($amounts as &$data) {
         $data = Tools::ps_round($price_before_tax * ($data['base'] / $total_base) * $data['rate'] / 100, $round_precision, $round_mode);
         $actual_tax += $data;
     }
     unset($data);
     if ($price_after_tax) {
         Tools::spreadAmount($price_after_tax - $price_before_tax - $actual_tax, $round_precision, $amounts, 'id_tax');
     }
     return $amounts;
 }
Exemple #2
0
    /**
     * Returns the wrapping taxes breakdown
     *
     * @return array
     */
    public function getWrappingTaxesBreakdown()
    {
        if ($this->total_wrapping_tax_excl == 0) {
            return array();
        }
        $wrapping_tax_amount = $this->total_wrapping_tax_incl - $this->total_wrapping_tax_excl;
        $wrapping_breakdown = Db::getInstance()->executeS('SELECT t.id_tax, t.rate, oit.amount as total_amount
			FROM `' . _DB_PREFIX_ . 'tax` t
			INNER JOIN `' . _DB_PREFIX_ . 'order_invoice_tax` oit ON oit.id_tax = t.id_tax
			WHERE oit.type = "wrapping" AND oit.id_order_invoice = ' . (int) $this->id);
        $sum_of_split_taxes = 0;
        $sum_of_tax_bases = 0;
        $total_tax_rate = 0;
        foreach ($wrapping_breakdown as &$row) {
            if (Configuration::get('PS_ATCP_SHIPWRAP')) {
                $row['total_tax_excl'] = Tools::ps_round($row['total_amount'] / $row['rate'] * 100, _PS_PRICE_COMPUTE_PRECISION_, $this->getOrder()->round_mode);
                $sum_of_tax_bases += $row['total_tax_excl'];
            } else {
                $row['total_tax_excl'] = $this->total_wrapping_tax_excl;
            }
            $row['total_amount'] = Tools::ps_round($row['total_amount'], _PS_PRICE_COMPUTE_PRECISION_, $this->getOrder()->round_mode);
            $sum_of_split_taxes += $row['total_amount'];
            $total_tax_rate += (double) $row['rate'];
        }
        unset($row);
        $delta_amount = $wrapping_tax_amount - $sum_of_split_taxes;
        if ($delta_amount != 0) {
            Tools::spreadAmount($delta_amount, _PS_PRICE_COMPUTE_PRECISION_, $wrapping_breakdown, 'total_amount');
        }
        $delta_base = $this->total_wrapping_tax_excl - $sum_of_tax_bases;
        if ($delta_base != 0) {
            Tools::spreadAmount($delta_base, _PS_PRICE_COMPUTE_PRECISION_, $wrapping_breakdown, 'total_tax_excl');
        }
        if (!Configuration::get('PS_INVOICE_TAXES_BREAKDOWN') && !Configuration::get('PS_ATCP_SHIPWRAP')) {
            $wrapping_breakdown = array(array('total_tax_excl' => $this->total_wrapping_tax_excl, 'rate' => $total_tax_rate, 'total_amount' => $wrapping_tax_amount));
        }
        return $wrapping_breakdown;
    }
 /**
  * By default this function was made for invoice, to compute tax amounts and balance delta (because of computation made on round values).
  * If you provide $limitToOrderDetails, only these item will be taken into account. This option is usefull for order slip for example,
  * where only sublist of the order is refunded.
  *
  * @param $limitToOrderDetails Optional array of OrderDetails to take into account. False by default to take all OrderDetails from the current Order.
  * @return array A list of tax rows applied to the given OrderDetails (or all OrderDetails linked to the current Order).
  */
 public function getProductTaxesDetails($limitToOrderDetails = false)
 {
     $round_type = $this->round_type;
     if ($round_type == 0) {
         // if this is 0, it means the field did not exist
         // at the time the order was made.
         // Set it to old type, which was closest to line.
         $round_type = Order::ROUND_LINE;
     }
     // compute products discount
     $order_discount_tax_excl = $this->total_discounts_tax_excl;
     $free_shipping_tax = 0;
     $product_specific_discounts = array();
     $expected_total_base = $this->total_products - $this->total_discounts_tax_excl;
     foreach ($this->getCartRules() as $order_cart_rule) {
         if ($order_cart_rule['free_shipping'] && $free_shipping_tax === 0) {
             $free_shipping_tax = $this->total_shipping_tax_incl - $this->total_shipping_tax_excl;
             $order_discount_tax_excl -= $this->total_shipping_tax_excl;
             $expected_total_base += $this->total_shipping_tax_excl;
         }
         $cart_rule = new CartRule($order_cart_rule['id_cart_rule']);
         if ($cart_rule->reduction_product > 0) {
             if (empty($product_specific_discounts[$cart_rule->reduction_product])) {
                 $product_specific_discounts[$cart_rule->reduction_product] = 0;
             }
             $product_specific_discounts[$cart_rule->reduction_product] += $order_cart_rule['value_tax_excl'];
             $order_discount_tax_excl -= $order_cart_rule['value_tax_excl'];
         }
     }
     $products_tax = $this->total_products_wt - $this->total_products;
     $discounts_tax = $this->total_discounts_tax_incl - $this->total_discounts_tax_excl;
     // We add $free_shipping_tax because when there is free shipping, the tax that would
     // be paid if there wasn't is included in $discounts_tax.
     $expected_total_tax = $products_tax - $discounts_tax + $free_shipping_tax;
     $actual_total_tax = 0;
     $actual_total_base = 0;
     $order_detail_tax_rows = array();
     $breakdown = array();
     // Get order_details
     $order_details = $limitToOrderDetails ? $limitToOrderDetails : $this->getOrderDetailList();
     $order_ecotax_tax = 0;
     $tax_rates = array();
     foreach ($order_details as $order_detail) {
         $id_order_detail = $order_detail['id_order_detail'];
         $tax_calculator = OrderDetail::getTaxCalculatorStatic($id_order_detail);
         // TODO: probably need to make an ecotax tax breakdown here instead,
         // but it seems unlikely there will be different tax rates applied to the
         // ecotax in the same order in the real world
         $unit_ecotax_tax = $order_detail['ecotax'] * $order_detail['ecotax_tax_rate'] / 100.0;
         $order_ecotax_tax += $order_detail['product_quantity'] * $unit_ecotax_tax;
         $discount_ratio = 0;
         if ($this->total_products > 0) {
             $discount_ratio = ($order_detail['unit_price_tax_excl'] + $order_detail['ecotax']) / $this->total_products;
         }
         // share of global discount
         $discounted_price_tax_excl = $order_detail['unit_price_tax_excl'] - $discount_ratio * $order_discount_tax_excl;
         // specific discount
         if (!empty($product_specific_discounts[$order_detail['product_id']])) {
             $discounted_price_tax_excl -= $product_specific_discounts[$order_detail['product_id']];
         }
         $quantity = $order_detail['product_quantity'];
         foreach ($tax_calculator->taxes as $tax) {
             $tax_rates[$tax->id] = $tax->rate;
         }
         foreach ($tax_calculator->getTaxesAmount($discounted_price_tax_excl) as $id_tax => $unit_amount) {
             $total_tax_base = 0;
             switch ($round_type) {
                 case Order::ROUND_ITEM:
                     $total_tax_base = $quantity * Tools::ps_round($discounted_price_tax_excl, _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
                     $total_amount = $quantity * Tools::ps_round($unit_amount, _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
                     break;
                 case Order::ROUND_LINE:
                     $total_tax_base = Tools::ps_round($quantity * $discounted_price_tax_excl, _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
                     $total_amount = Tools::ps_round($quantity * $unit_amount, _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
                     break;
                 case Order::ROUND_TOTAL:
                     $total_tax_base = $quantity * $discounted_price_tax_excl;
                     $total_amount = $quantity * $unit_amount;
                     break;
             }
             if (!isset($breakdown[$id_tax])) {
                 $breakdown[$id_tax] = array('tax_base' => 0, 'tax_amount' => 0);
             }
             $breakdown[$id_tax]['tax_base'] += $total_tax_base;
             $breakdown[$id_tax]['tax_amount'] += $total_amount;
             $order_detail_tax_rows[] = array('id_order_detail' => $id_order_detail, 'id_tax' => $id_tax, 'tax_rate' => $tax_rates[$id_tax], 'unit_tax_base' => $discounted_price_tax_excl, 'total_tax_base' => $total_tax_base, 'unit_amount' => $unit_amount, 'total_amount' => $total_amount);
         }
     }
     if (!empty($order_detail_tax_rows)) {
         foreach ($breakdown as $data) {
             $actual_total_tax += Tools::ps_round($data['tax_amount'], _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
             $actual_total_base += Tools::ps_round($data['tax_base'], _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
         }
         $order_ecotax_tax = Tools::ps_round($order_ecotax_tax, _PS_PRICE_COMPUTE_PRECISION_, $this->round_mode);
         $tax_rounding_error = $expected_total_tax - $actual_total_tax - $order_ecotax_tax;
         if ($tax_rounding_error !== 0) {
             Tools::spreadAmount($tax_rounding_error, _PS_PRICE_COMPUTE_PRECISION_, $order_detail_tax_rows, 'total_amount');
         }
         $base_rounding_error = $expected_total_base - $actual_total_base;
         if ($base_rounding_error !== 0) {
             Tools::spreadAmount($base_rounding_error, _PS_PRICE_COMPUTE_PRECISION_, $order_detail_tax_rows, 'total_tax_base');
         }
     }
     return $order_detail_tax_rows;
 }