You cannot able to move order_items
through layout file. Since order_tab_info
block not renders it's child blocks automatically. So even though moving the blocks inside the order_tab_info
block from the layout results in the same structure.
To do so, override /vendor/magento/module-sales/view/adminhtml/templates/order/view/tab/info.phtml
and move the items ordered block before Line no:19 <?php echo $block->getChildHtml('order_info') ?>
<section class="admin__page-section">
<div class="admin__page-section-title">
<span class="title"><?php /* @escapeNotVerified */ echo __('Items Ordered') ?></span>
</div>
<?php echo $block->getItemsHtml() ?>
</section>
Now the items ordered block will be loaded before Order information.
After days I came up with a solution, but it's worth noting that this solution won't work if, for whatever reason, you change the price of the custom option and resend the new order email;
this is because the I am generating the product model through the productFactory that is NOT the same model as the one in:
Magento\Sales\Model\Order\Item
I am pretty sure there is a better way to do it , but at the moment I am not able to find it, so this is what I came up with:
In
/app/code/vendor/Email/Block/Order/Email/Items/Order/DefaultOrder.php
Injected PriceCurrencyInterface
to format the price once we retrieve it:
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
ProductFactory $productFactory,
Magento\Framework\Pricing\PriceCurrencyInterface $amount,
array $data = []
)
and then added this function:
function getCustomOptionPrice($_product, $option_id, $option_value)
{
$options = $_product->getData('options');
$price = '';
foreach ($options as $option)
{
if ($option->getValues() !== null)
{
$values = $option->getValues();
foreach ($values as $key => $value)
{
if ($key === intval($option_value))
{
$optionValues = $values[$option_value]->getData();
if ($optionValues['price'] !== null && $optionValues['price'] != "0.0000")
{
$price = ' (+ ' . $this->amount->convertAndFormat(floatval($optionValues['price']), false) . ')';
}
}
}
}
elseif ($option->getValues() === null)
{
$optionData = $option->getData();
if ($optionData['option_id'] == $option_id && ($optionData['price'] != "0.0000" && $optionData['price'] !== null))
{
$price = ' (+ ' . $this->amount->convertAndFormat(floatval($optionData['price']), false) . ')';
}
}
}
return $price;
}
This function checks if the price is either not null
or 0.0000
and, in this case, we decide not to display it; if either those options are false then the price is returned and is wrapped in a convertAndFormat
function which will return a nicely formatted price with your currency of choice.
In the template is then passed in this way:
File: /app/code/vendor/Email/view/frontend/templates/email/items/order/default.phtml
<?php foreach ($block->getItemOptions() as $option): ?>
...
<?php if (isset($option['option_value']) && (isset($option['option_id'])) ): ?><span><?php echo $block->getCustomOptionPrice($_product, $option['option_id'], $option['option_value'] ) ?></span>
<?php endif; ?>
...
<?php endforeach; ?>
There are lots of ifs and checking because sometimes the option value it's not a number or it doesn't exists at all.
The product object model it's quite complex and deep as structure and that required lots of iteration and getters.
There are hundreds of way to do this better so if you feel like to improve this answer please do.
Best Answer
After a few hours debug i found that the price in email order is rendered in