magento / magento2

Prior to making any Submission(s), you must sign an Adobe Contributor License Agreement, available here at: https://opensource.adobe.com/cla.html. All Submissions you make to Adobe Inc. and its affiliates, assigns and subsidiaries (collectively “Adobe”) are subject to the terms of the Adobe Contributor License Agreement.
http://www.magento.com
Open Software License 3.0
11.47k stars 9.28k forks source link

Missing tax from Grand Total for Invoice created in the admin manually, and order shows an amount due when it should not #38978

Closed xtremevision closed 2 weeks ago

xtremevision commented 1 month ago

Preconditions and environment

screenshot-www libhumanitas ro-2024 07 28-10_13_09 screenshot-www libhumanitas ro-2024 07 28-10_12_42 screenshot-www libhumanitas ro-2024 07 28-10_15_19

Steps to reproduce

  1. Add 2 simple products to the cart
  2. Add 1 virtual product to the cart
  3. Checkout
  4. Pay with Credit Card - in authorize mode only
  5. In admin create two invoices as per above

Expected result

Grand total should match the subtotal, i.e. contain the tax amount.

Actual result

Invoiced amount is incorrect, it is less than the subtotal, i.e. missing the tax amount. Looks like invoiced amount is short by 0.72 lei, i.e. the tax amount that belongs to the second invoice for the virtual product.

Additional information

No response

Release note

No response

Triage and priority

m2-assistant[bot] commented 1 month ago

Hi @xtremevision. Thank you for your report. To speed up processing of this issue, make sure that the issue is reproducible on the vanilla Magento instance following Steps to reproduce. To deploy vanilla Magento instance on our environment, Add a comment to the issue:

xtremevision commented 1 month ago

@magento give me 2.4-develop instance

magento-deployment-service[bot] commented 1 month ago

Hi @xtremevision. Thank you for your request. I'm working on Magento instance for you.

magento-deployment-service[bot] commented 1 month ago

Hi @xtremevision, here is your Magento Instance: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering Admin access: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering/admin_4146 Login: e63059b6 Password: c74abb791cda

xtremevision commented 1 month ago

Hi, what happened to the instance? I managed to set it up, test an order and now it's gone.

xtremevision commented 1 month ago

@magento give me 2.4-develop instance

magento-deployment-service[bot] commented 1 month ago

Hi @xtremevision. Thank you for your request. I'm working on Magento instance for you.

magento-deployment-service[bot] commented 1 month ago

Hi @xtremevision, here is your Magento Instance: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering Admin access: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering/admin_488e Login: 506f4b27 Password: 9756255980e0

xtremevision commented 1 month ago

@magento I am working on this

m2-assistant[bot] commented 1 month ago

Hi @xtremevision! :wave: Thank you for collaboration. Only members of Community Contributors Team are allowed to be assigned to the issue. Please use @magento add to contributors team command to join Contributors team.

m2-assistant[bot] commented 1 month ago

Hi @engcom-Delta. Thank you for working on this issue. In order to make sure that issue has enough information and ready for development, please read and check the following instruction: :point_down:

engcom-Delta commented 1 month ago

@magento give me 2.4-develop instance

magento-deployment-service[bot] commented 1 month ago

Hi @engcom-Delta. Thank you for your request. I'm working on Magento instance for you.

magento-deployment-service[bot] commented 1 month ago

Hi @engcom-Delta, here is your Magento Instance: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering Admin access: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering/admin_6855 Login: 8a993e22 Password: 07c43ef5db5f

engcom-Delta commented 1 month ago

@magento give me 2.4-develop instance

magento-deployment-service[bot] commented 1 month ago

Hi @engcom-Delta. Thank you for your request. I'm working on Magento instance for you.

magento-deployment-service[bot] commented 1 month ago

Hi @engcom-Delta, here is your Magento Instance: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering Admin access: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering/admin_65a3 Login: 4964b17c Password: 2bb478419df2

engcom-Delta commented 1 month ago

@magento give me 2.4-develop instance

magento-deployment-service[bot] commented 1 month ago

Hi @engcom-Delta. Thank you for your request. I'm working on Magento instance for you.

magento-deployment-service[bot] commented 1 month ago

Hi @engcom-Delta, here is your Magento Instance: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering Admin access: https://4e955af351135d451f98c5d8e6695bcf.instances-prod.magento-community.engineering/admin_8bc2 Login: 40819a46 Password: 6aa803eef105

engcom-Delta commented 1 month ago

Hi @xtremevision ,

Thanks for your reporting and collaboration. We have verified the issue in latest 2.4-develop instance and the issue is not reproducible. Kindly refer the screenshots.

Steps to Reproduce -

  1. Add 2 Simple products to the cart.
  2. Add 1 Virtual product to the cart.
  3. Set 5% tax
  4. Navigate to cart and observe the subtotal and tax. image
  5. Place order successfully. image image
  6. Create 2 invoices, 1st for simple products and 2nd for virtual product. Invoice 1st image Invoice 2nd image
  7. Observe the order total calculation should be as expected after invoice creation. image Total Due is 0 as expected after both invoice creation. Please let us know if we missed anything.

Thanks.

xtremevision commented 1 month ago

@engcom-Delta Hi, and thank you for taking the time to work on this. I did the same test and could not reproduce the problem following the above steps. However, in the meantime I've discovered the following:

  1. There is an "Auto Invoice" module in place that automatically creates invoices for virtual products - paid by credit card.
  2. The Virtual product invoice was automatically created by the module - first.
  3. The Simple products invoice was created manually by admin staff - after the virtual products invoice.
  4. The Simple products were also shipped, see screenshot.

Something is going on, somewhere, I just have not found out exactly what and where. Could it be we're not creating the invoice correctly?

xtremevision commented 1 month ago

We use a plugin to intercept the payment method's IPN (credit card) and instead of creating one invoice for the order, we create a partial invoice only for virtual products, letting the admin staff create invoices for simple products.

Is there anything wrong with the invoice creationg code by any chance?

<?php

namespace Xtreme\AutoInvoice\Gateway\Command\Ipn;

use Innobyte\PayU\Gateway\Helper\SubjectReader;
use Innobyte\PayU\Model\Config\Source\PaymentAction;
use Magento\Payment\Gateway\CommandInterface;
use Magento\Payment\Gateway\ConfigInterface;
use Magento\Payment\Gateway\Helper\ContextHelper;
use Magento\Payment\Gateway\Response\HandlerInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
use Magento\Sales\Model\Order\Email\Sender\OrderSender;
use Magento\Sales\Model\Service\InvoiceService;
use Magento\Framework\DB\Transaction;

class CaptureStrategyCommandPlugin extends \Innobyte\PayU\Gateway\Command\Ipn\CaptureStrategyCommand
{
    /**
     * @var SubjectReader
     */
    private $subjectReader;

    /**
     * @var HandlerInterface
     */
    private $handler;

    /**
     * @var \Innobyte\PayU\Gateway\Config\Config
     */
    private $config;

    /**
     * @var OrderRepositoryInterface
     */
    private $orderRepository;

    /**
     * @var OrderSender
     */
    private $orderSender;

    /**
     * @var InvoiceSender
     */
    private $invoiceSender;

    protected $transaction;
    protected $invoiceService;

    /** @var \Magento\Eav\Api\AttributeSetRepositoryInterface $attributeSet **/
    protected $attributeSet;

    /**
     * CaptureStrategyCommand constructor
     *
     * @param SubjectReader $subjectReader
     * @param HandlerInterface $handler
     * @param ConfigInterface $config
     * @param OrderRepositoryInterface $orderRepository
     * @param OrderSender $orderSender
     * @param InvoiceSender $invoiceSender
     */
    public function __construct(
        SubjectReader $subjectReader,
        HandlerInterface $handler,
        ConfigInterface $config,
        OrderRepositoryInterface $orderRepository,
        OrderSender $orderSender,
        InvoiceSender $invoiceSender,
        Transaction $transaction,
        InvoiceService $invoiceService,
        \Magento\Eav\Api\AttributeSetRepositoryInterface $attributeSet
    ) {
        $this->subjectReader = $subjectReader;
        $this->handler = $handler;
        $this->config = $config;
        $this->orderRepository = $orderRepository;
        $this->orderSender = $orderSender;
        $this->invoiceSender = $invoiceSender;
        $this->attributeSet = $attributeSet;
        $this->invoiceService = $invoiceService;
        $this->transaction = $transaction;
        /*parent::__construct(
            $subjectReader,
            $handler,
            $config,
            $orderRepository,
            $orderSender,
            $invoiceSender);*/
    }

    public function execute(array $commandSubject)
    {
        $paymentDO = $this->subjectReader->readPayment($commandSubject);

        /** @var \Magento\Sales\Model\Order\Payment $payment */
        $payment = $paymentDO->getPayment();
        ContextHelper::assertOrderPayment($payment);

        $order = $payment->getOrder();
        $methodInstance = $payment->getMethodInstance();
        $methodInstance->setStore($order->getStoreId());

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $logger = $objectManager->create('\Psr\Log\LoggerInterface');

        $amount = $this->subjectReader->readAmount($commandSubject);
        $response = $this->subjectReader->readResponseObject($commandSubject);

        $this->handler->handle(
            $commandSubject,
            [
                'response' => ['object' => $response]
            ]
        );

        if ($methodInstance->getConfigPaymentAction() == PaymentAction::ACTION_AUTHORIZE_CAPTURE
            || $order->getIsVirtual()) {

            $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() ." is Virtual - capturing");

            $payment->capture();
            $this->orderRepository->save($order);

            $invoice = $payment->getCreatedInvoice();
            $logger->info("Xtreme_CaptureStrategyCommand: order is Virtual - invoice created, id: ", ['invoiceId' => $invoice->getId()]);
            if (!$order->getIsInProcess()
                && $invoice
                && $this->config->canSendInvoiceNotification($order->getStoreId())
            ) {
                $logger->info("Xtreme_CaptureStrategyCommand: order is Virtual - sending invoice email");
                $this->invoiceSender->send($invoice);
            }
        } else {
            $payment->registerAuthorizationNotification($amount);
            $order->setState(\Magento\Sales\Model\Order::STATE_NEW)->setStatus('pending_payu_paid');
            //$history = $order->addStatusHistoryComment('Order is : '.$orderLabel, $order->getStatus());
            //$history->setIsCustomerNotified(true);
            //$history->save(); 
            $this->orderRepository->save($order);

            $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() . " is NOT Virtual - creating partial invoice");

            if(!$order->getIsVirtual())
            {
                $createinvoice = $order->canInvoice();
                if ($createinvoice)
                {
                    $itemsArray  = array();
                    $subTotal = 0;
                    foreach($order->getAllItems() as $orderitem)
                    {
                        $product = $orderitem->getProduct();
                        $attributeSetName = $this->getAttributeSetName($product);
                        if( in_array($attributeSetName, ['eBook', 'Audiobook MP3', 'eBook Free']) )
                        {
                            if($orderitem->getQtyToInvoice() > 0)
                            {
                                $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() . " is NOT Virtual - adding item to invoiceArray, item id: " . $orderitem->getId());

                                $id = $orderitem->getId();
                                $itemsArray[$id] = $orderitem->getQtyToInvoice();
                                $subTotal += $orderitem->getRowTotalInclTax();
                            }
                        }
                        else
                        {
                            $logger->info("Xtreme_CaptureStrategyCommand: order " . $orderitem->getId() . ", " . $orderitem->getSku() . " attributeSet: " . $attributeSetName . " - not added to invoice");
                        }
                    }
                    if(count($itemsArray)>0)
                    {
                        $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() . " is NOT Virtual - creating invoice");

                        $shippingAmount = '0.00';
                        $subTotal = $subTotal;
                        $baseSubtotal = $subTotal;
                        $grandTotal = $subTotal;
                        $baseGrandTotal = $subTotal;
                        $invoice = $this->invoiceService->prepareInvoice($order, $itemsArray);
                        $invoice->setShippingAmount($shippingAmount);
                        $invoice->setSubtotal($subTotal);
                        $invoice->setBaseSubtotal($baseSubtotal);
                        $invoice->setGrandTotal($grandTotal);
                        $invoice->setBaseGrandTotal($baseGrandTotal);
                        $invoice->register();
                        $invoice->save();

                        $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() . " is NOT Virtual - saving transaction");

                        $transactionSave = $this->transaction->addObject(
                            $invoice
                        )->addObject(
                            $invoice->getOrder()
                        );
                        $transactionSave->save();

                        $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() . " is NOT Virtual - sending email to customer");

                        $this->invoiceSender->send($invoice);
                        $order->setTotalPaid($grandTotal);
                        $order->setBaseTotalPaid($grandTotal);
                        $this->orderRepository->save($order);
                        $logger->info("Xtreme_CaptureStrategyCommand: order " . $order->getId() . " totalPaid: " . $grandTotal . ", subTotal: " . $subTotal);

                        //Send Invoice mail to customer
                        $order->addStatusHistoryComment(
                            __('Notified customer about invoice creation #%1.', $invoice->getId())
                        )
                            ->setIsCustomerNotified(true)
                            ->save();

                        $invoice->setState(2)->save();
                    }
                    else
                    {
                        $logger->info("Xtreme_CaptureStrategyCommand: order is NOT Virtual - no items in invoice, invoice not created " . $order->getId());
                    }

                }else{
                    return $this;
                }

            }
        }

        if (!$order->getEmailSent()) {
            $this->orderSender->send($order);
        }
    }

    //Build method to get attribute set
    public function getAttributeSetName($product) {

        $attributeSetRepository = $this->attributeSet->get($product->getAttributeSetId());
        return $attributeSetRepository->getAttributeSetName();
    }
}
xtremevision commented 1 month ago

Ok, I think I found the bug/fix.

use

$order->getAllVisibleItems()

instead

and uncomment these lines:

$order->setTotalPaid($grandTotal);
$order->setBaseTotalPaid($grandTotal);
$this->orderRepository->save($order);

Thank you.

engcom-Delta commented 1 month ago

Hi @xtremevision ,

As we are not able to replicate in default 2.4-develop instance, Can you please provide detail steps to reproduce the issue.

Thanks.

engcom-Delta commented 2 weeks ago

Hi @xtremevision ,

We have noticed that this issue has not been updated since long time. Hence we assume that this issue is fixed now, so we are closing it. Please feel to raise a fresh ticket or reopen this ticket if you need more assistance on this.

Thanks.