Sales Tax Calculated Before Coupon Application - Question | JoomShaper

Sales Tax Calculated Before Coupon Application

EK

Eddie Kominek

EasyStore 3 months ago

From orders, it appears that you are calculating the sales tax on an order before the application of a coupon. Customers should be charged sales tax based on the subtotal after coupon application. Either there needs to be a setting where you can say whether to charge before or after coupon application, or this needs to be fixed. I believe the problem is in the: administrator\components\com_easystore\src\Traits\Order.php You're pulling 'discount_type' and 'discount_reason' from the coupon table, but you're not applying them to the sales tax calculation.:

    public function updateOrders()
    {
        $response = (object) [
            'status'  => false,
            'message' => '',
        ];

        $canDo          = ContentHelper::getActions('com_easystore');
        $hasPermission  = $canDo->get('core.create');

        if (!$hasPermission) {
            $this->sendResponse(['message' => Text::_("COM_EASYSTORE_PERMISSION_ERROR_MSG")], 403);
        }

        $id          = $this->getInput('id', 0, 'INT');
        $orderStatus = $this->getInput('order_status', 'draft', 'STRING');

        if (empty($id)) {
            $response->status  = false;
            $response->message = Text::_("COM_EASYSTORE_FAILED_TO_UPDATE_ORDER");

            $this->sendResponse($response);
        }

        $model = new OrderModel();

        $response     = new stdClass();
        $response->id = null;

        $orderInformation = new stdClass();

        $orderInformation->id            = $id;
        $orderInformation->order_status  = $orderStatus;
        $orderInformation->creation_date = null;
        $isActivated                     = false;

        if (!$model->checkCreationDateExists($id) && $orderStatus === 'active') {
            $orderInformation->creation_date = Factory::getDate('now');
            $isActivated                     = true;
        }

        $orderInformation->customer_id      = $this->getInput('customer_id', 0, 'INT');
        $orderInformation->customer_note    = $this->getInput('customer_note', '', 'RAW');
        $orderInformation->payment_status   = $this->getInput('payment_status', 'unpaid', 'STRING');
        $orderInformation->fulfilment       = $this->getInput('fulfilment', 'unfulfilled', 'STRING');
        $orderInformation->discount_type    = $this->getInput('discount_type', 'percent', 'STRING');
        $orderInformation->discount_value   = $this->formatToNumeric($this->getInput('discount_value'), 'decimal');
        $orderInformation->discount_reason  = $this->getInput('discount_reason', '', 'STRING');
        $orderInformation->shipping         = $this->getInput('shipping', '', 'STRING');
        $orderInformation->access           = $this->formatToNumeric($this->getInput('access'));
        $orderInformation->ordering         = $this->formatToNumeric($this->getInput('ordering'));
        $orderInformation->shipping_address = null;
        $orderInformation->billing_address  = null;

        if (!empty($orderInformation->customer_id)) {
            $customerInfo                       = EasyStoreHelper::getCustomerById($orderInformation->customer_id);
            $orderInformation->shipping_address = $customerInfo->shipping_address ?? null;
            $orderInformation->billing_address  = $customerInfo->billing_address ?? null;
        }

        $updateStatus = $model->update($orderInformation);

        if ($updateStatus) {
            $orderProducts = $this->getInput('order_products', '', 'ARRAY');
            $orderProducts = array_map(function ($product) {
                return \json_decode($product, true);
            }, $orderProducts);

            $model->storeMultipleOrderedProducts($orderProducts, $id);

            if (!is_null($orderInformation->shipping_address)) {
                $shippingAddress  = json_decode($orderInformation->shipping_address);
                $country          = $shippingAddress->country;
                $state            = $shippingAddress->state;

                $settingsModel   = new SettingsModel();
                $tax             = $settingsModel->getTaxRate($country, $state);

                $taxRate         = !empty($tax) ? $tax->rate : 0;
                $isTaxOnShipping = !empty($tax) ? $tax->applyOnShipping : false;
                $saleTax         = 0.0;
                $subTotal        = 0.0;
                $calculatedPrice = [];
                foreach ($orderProducts as $product) {
                    $calculatedPrice[] = ($product['discounted_price'] > 0 ? $product['discounted_price'] * $product['quantity'] : $product['price'] * $product['quantity']);
                }
                $subTotal = array_sum($calculatedPrice);
                $saleTax  = ($subTotal * $taxRate) / 100;

                $orderInformation->sale_tax = $saleTax;

                $model->update($orderInformation);
            }

            if ($isActivated) {
                $model->addOrderActivity($id, 'order_created');
            }

            $response->status  = true;
            $response->message = Text::_("COM_EASYSTORE_ORDER_UPDATED");
            $response->id      = $id;
        } else {
            $response->status  = false;
            $response->message = Text::_("COM_EASYSTORE_FAILED_TO_UPDATE_ORDER");
        }

        $this->sendResponse($response);
    }
    I've altered mine as follows:
                foreach ($orderProducts as $product) {
                    $calculatedPrice[] = $product['quantity'] * ($product['discounted_price'] > 0 ? $product['discounted_price'] : $product['price']);
                }
                //foreach ($orderProducts as $product) {
                //  $calculatedPrice[] = ($product['discounted_price'] > 0 ? $product['discounted_price'] * $product['quantity'] : $product['price'] * $product['quantity']);
                //}
                $subTotal = array_sum($calculatedPrice);
                if ($orderInformation->discount_value) {
                  switch ($orderInformation->discount_type) {
                    case 'percent':
                      $subTotal = $subTotal * (100 - $orderInformation->discount_value)/100;
                    case 'amount':
                      $subTotal -= (($subTotal > $orderInformation->discount_value) ? $orderInformation->discount_value : $subTotal);
                    default:
                  }
                } 
                $saleTax  = ($subTotal * $taxRate) / 100;

                $orderInformation->sale_tax = $saleTax;

                $model->update($orderInformation);
0
2 Answers
EK
Eddie Kominek
Accepted Answer
3 months ago #147922

Acutally, I fixed it in here: components\com_easystore\src\Model\CartModel.php

        if ($isTaxOnShipping) {
            $totalPriceWithShipping = $cart->sub_total + $cart->shipping_price;
            $cart->taxable_amount   = (($totalPriceWithShipping - $cart->coupon_discount) * $cart->tax_rate) / 100;
        } else {
            $cart->taxable_amount = (($cart->sub_total - $cart->coupon_discount) * $cart->tax_rate) / 100;
        }

I would REALLY put in a toggle for coupons: applied before or after taxes.

It depends on the type of coupon. See below:

The sales price includes the total amount received by the seller for the transaction. If the seller is reimbursed by a third party for a coupon, e.g., a manufacturer’s coupon, the sales tax is due on the sales price before the coupon is applied. Here, the seller would still receive the full sales price for the transaction – partly from the consumer and partly from the third party. If the coupon is a dealer discount coupon, and the seller is not reimbursed by a third party, then the amount the seller actually receives is what is paid by the customer. In this situation, the seller would collect sales tax on the sales price after the coupon is applied.

0
Mehtaz Afsana Borsha
Mehtaz Afsana Borsha
Accepted Answer
Support Agent 3 months ago #147956

Hi

Thanks for contacting us. Glad to know that it is now fixed and thanks for sharing the solution. You can now close this post by accepting the answer.

-Regards.

0