[5.x]: Order in CP: I'm able to mark cart as completed even though there is no transaction defined
What happened?
Description
A client of ours often creates manual orders in the CP. They often complete an order (convert from cart to order) without defining a transaction. When an order is completed an order confirmation email is automatically sent to the client with of course the missing payment information.
Should a transaction not be mandatory before being able to complete an order? Is there a way to prevent our client from completing orders without defining a transaction?
An even bigger issue is that when no transaction is defined and I use 'order.gateway.handle' in the order confirmation email template it will just render out the first possible gateway which is not even selected. So it outputs wrong information.
Steps to reproduce
- Create order and complete an order in the CP without defining a transaction
Expected behavior
The transaction should be mandatory.
Actual behavior
I can complete an order without transaction.
Craft CMS version
5.8.17
Craft Commerce version
5.4.6
PHP version
8.2
Operating system and version
No response
Database type and version
No response
Image driver and version
No response
Installed plugins and versions
Hi @MatthiasBrodelet
Thank you for your message.
Hopefully I can answer some of the questions you have.
It is by design in the system that you are able to mark an order as completed without having to have a transaction. There are a number of use cases for this across many different business types.
If your project has specific business requirements for needing to have a transaction that is something that could be implemented via custom validation.
Again, by design, a default gateway is set on an order if one hasn't been defined. This has been in place to help prevent customers hitting dead ends through checkout systems. Commerce allows for multiple gateways to be setup in a project, but we tend to see normally only 1 or 2 defined in projects. So this behaviour of the Commerce system has seemed to track for developers.
In terms of the order.gateway.handle in your order confirmation email, if you continue to allow orders to be marked as completed without transactions you could always wrap that output in a conditional that checks order.transactions|length.
Hope this information helps, please let us know if you have any further questions.
Thanks!
@nfourtythree Adding custom validation would be a nice solution that fixes both my problems!
I'm guessing I can just add a validation rule using the craft\commerce\elements\Order::EVENT_BEFORE_VALIDATE event?
Hi @MatthiasBrodelet
I was hoping this custom validation would be a little more succinct, but having tested it out the following was the most logical code I can advise. This is due to the nature of how the order edit screen functions in the control panel.
Event::on(Order::class, Order::EVENT_DEFINE_RULES, function(DefineRulesEvent $event) {
if (!Craft::$app->getRequest()->getIsCpRequest() || Craft::$app->getRequest()->getActionSegments() != ['commerce', 'orders', 'save']) {
return;
}
$orderData = Json::decodeIfJson(Craft::$app->getRequest()->getBodyParam('orderData'));
if (!$orderData['order']['isCompleted']) {
return;
}
$event->rules[] = [['transactions'], 'required', 'message' => 'At least one transaction is required for completing orders.'];
});
This does gives us some food for thought on these types of scenario on our end, but hopefully this is at least useful for your project. I would recommend testing this thoroughly through all your various business cases to check that it fully works for you.
Let me know if you have any further questions, thanks!
@nfourtythree That works!
It's just a little confusing that the error message is only shown on the first tab while we have 8 tabs in our order view.
I can already predict some people will have no clue why they could not complete the order because the error is shown on another tab than the current active one.
The session flash is also there but it only says Couldn’t save order..
It would be nice if this could be customised.
But this already is a huge improvement over store employees creating orders without a transaction so I see it as a win.