Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Commerce 3.x: Purchasing products with stock quantity of 1 causing overpayment and missing items in completed order (after upgrade from v2 > v3) #1431

Closed
isjackwild opened this issue May 3, 2020 · 6 comments
Labels
🧾 orders ℹ️ status: need more info When waiting for user to supply database or more information.

Comments

@isjackwild
Copy link

Description

When purchasing final item of stock, items are included in the payment price, but are not added to the order. This has caused quite a few issues with overpayments and missing order items.

It appears that this happens when hooking into the Payments::EVENT_AFTER_PROCESS_PAYMENT event.

Steps to reproduce

In a custom module I have the following:

Event::on(
            Payments::class,
            Payments::EVENT_AFTER_PROCESS_PAYMENT,
            function(ProcessPaymentEvent $e) {
                    $order = $e->order;
                    $order->setFieldValues([
                        'myCustomProperty' => null, // in my case, this is a custom property we use when booking a delivery slot, but want to remove when the order is complete.
                    ]);
                    Craft::$app->elements->saveElement($order);
                } else {
                }
            }
        );
  1. Include the code snippet in a custom module.
  2. Purchase an item which has a Stock quantity of 1.
  3. The item will be in the cart, and in the order summary, and the total payment amount
  4. After the order is paid, the item with a stock quantity of 1 is not in the completed order. Any other items are remain in the order, and the stock decrements in the back-end properly.

I have modified my code to instead use the Order::EVENT_AFTER_ORDER_PAID, and this works fine, but keen to get to the bottom of why this was happening.

The old code has been working without any issues for a couple of months, and the issue started as soon as we upgraded to Craft Commerce 3.

Additional info

  • Craft version: Craft Pro 3.4.16
  • PHP version: 7.2.24
  • Database driver & version: MySQL 8.0.18
  • Craft Commerce version: 3.1.3
@lukeholder
Copy link
Member

lukeholder commented May 4, 2020

What is the recalculation mode of the order when you save it in your event listener? Commerce 3 introduced a recalculation mode for orders, and no longer just relies on the 'isCompleted' flag to determine if the order should recalculate.

I am guessing it is set to the 'recalculate all' mode? You can check that that with:

$order->getRecalculationMode()

My guess is that since you are saving the order, the order is being recalculated (depending on the recalculation mode set) and the line items that no longer have stock are removed.

You need to put the order into "Recalculate None" Mode before saving the order, this will force the order to not change any line items or values other than your custom field.

$originalRecalculationMode = $order->getRecalculationMode();
$this->setRecalculationMode(\craft\commerce\elements\Order::RECALCULATION_MODE_NONE);

Craft::$app->elements->saveElement($order);

$order->setRecalculationMode($originalRecalculationMode);

Let me know if that makes sense.

@lukeholder lukeholder added ℹ️ status: need more info When waiting for user to supply database or more information. 🧾 orders labels May 4, 2020
@isjackwild
Copy link
Author

isjackwild commented May 4, 2020 via email

@isjackwild
Copy link
Author

isjackwild commented May 4, 2020 via email

@lukeholder
Copy link
Member

Thanks, added it to the docs, let me know if something is unclear: https://docs.craftcms.com/commerce/v3/upgrading.html#order-recalculations

@isjackwild
Copy link
Author

isjackwild commented May 4, 2020 via email

@lukeholder
Copy link
Member

No you only need to save the order once to save your custom field values.

$originalRecalculationMode = $order->getRecalculationMode();
$this->setRecalculationMode(\craft\commerce\elements\Order::RECALCULATION_MODE_NONE);


$order->setFieldValues(['foo' => 'bar']);
Craft::$app->elements->saveElement($order);

$order->setRecalculationMode($originalRecalculationMode);

After setting the order back, you are only doing that for the purpose of putting it back to the original mode in memory, incase other parts of the application expect it to be in a certain mode during that request.

lukeholder added a commit that referenced this issue May 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🧾 orders ℹ️ status: need more info When waiting for user to supply database or more information.
Projects
None yet
Development

No branches or pull requests

2 participants