diff --git a/classes/pdf/HTMLTemplateDeliverySlip.php b/classes/pdf/HTMLTemplateDeliverySlip.php
index f1bc28f562676..b45775110b751 100644
--- a/classes/pdf/HTMLTemplateDeliverySlip.php
+++ b/classes/pdf/HTMLTemplateDeliverySlip.php
@@ -24,6 +24,8 @@
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
+use PrestaShop\PrestaShop\Core\Util\Sorter;
+
/**
* @since 1.5
*/
@@ -126,6 +128,10 @@ public function getContent()
}
}
+ // Sort products by Reference ID (and if equals (like combination) by Supplier Reference)
+ $sorter = new Sorter();
+ $order_details = $sorter->natural($order_details, Sorter::ORDER_DESC, 'product_reference', 'product_supplier_reference');
+
$this->smarty->assign([
'order' => $this->order,
'order_details' => $order_details,
diff --git a/classes/pdf/HTMLTemplateInvoice.php b/classes/pdf/HTMLTemplateInvoice.php
index 331468b8da606..c491e85185848 100644
--- a/classes/pdf/HTMLTemplateInvoice.php
+++ b/classes/pdf/HTMLTemplateInvoice.php
@@ -24,6 +24,8 @@
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
+use PrestaShop\PrestaShop\Core\Util\Sorter;
+
/**
* @since 1.5
*/
@@ -238,6 +240,10 @@ public function getContent()
unset($order_detail); // don't overwrite the last order_detail later
}
+ // Sort products by Reference ID (and if equals (like combination) by Supplier Reference)
+ $sorter = new Sorter();
+ $order_details = $sorter->natural($order_details, Sorter::ORDER_DESC, 'product_reference', 'product_supplier_reference');
+
$cart_rules = $this->order->getCartRules();
$free_shipping = false;
foreach ($cart_rules as $key => $cart_rule) {
@@ -307,10 +313,9 @@ public function getContent()
$footer[$key] = Tools::ps_round($value, Context::getContext()->getComputingPrecision(), $this->order->round_mode);
}
- /**
+ /*
* Need the $round_mode for the tests.
*/
- $round_type = null;
switch ($this->order->round_type) {
case Order::ROUND_TOTAL:
$round_type = 'total';
diff --git a/classes/pdf/HTMLTemplateOrderSlip.php b/classes/pdf/HTMLTemplateOrderSlip.php
index c0705131779f9..2b02879f1e8cf 100644
--- a/classes/pdf/HTMLTemplateOrderSlip.php
+++ b/classes/pdf/HTMLTemplateOrderSlip.php
@@ -24,6 +24,8 @@
* @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
*/
+use PrestaShop\PrestaShop\Core\Util\Sorter;
+
/**
* @since 1.5
*/
@@ -159,10 +161,15 @@ public function getContent()
}
}
+ $order_details = $this->order->products;
+ // Sort products by Reference ID (and if equals (like combination) by Supplier Reference)
+ $sorter = new Sorter();
+ $order_details = $sorter->natural($order_details, Sorter::ORDER_DESC, 'product_reference', 'product_supplier_reference');
+
$this->smarty->assign([
'order' => $this->order,
'order_slip' => $this->order_slip,
- 'order_details' => $this->order->products,
+ 'order_details' => $order_details,
'cart_rules' => $this->order_slip->order_slip_type == 1 ? $this->order->getCartRules() : false,
'amount_choosen' => $this->order_slip->order_slip_type == 2 ? true : false,
'delivery_address' => $formatted_delivery_address,
diff --git a/src/Adapter/Cart/QueryHandler/GetCartForViewingHandler.php b/src/Adapter/Cart/QueryHandler/GetCartForViewingHandler.php
index 6f32fcbf5fb7e..8086475dbec99 100644
--- a/src/Adapter/Cart/QueryHandler/GetCartForViewingHandler.php
+++ b/src/Adapter/Cart/QueryHandler/GetCartForViewingHandler.php
@@ -40,6 +40,7 @@
use PrestaShop\PrestaShop\Core\Domain\Cart\QueryHandler\GetCartForViewingHandlerInterface;
use PrestaShop\PrestaShop\Core\Domain\Cart\QueryResult\CartView;
use PrestaShop\PrestaShop\Core\Localization\Locale;
+use PrestaShop\PrestaShop\Core\Util\Sorter;
use Product;
use StockAvailable;
use Validate;
@@ -117,6 +118,10 @@ public function handle(GetCartForViewing $query)
$total_shipping = $summary['total_shipping'];
}
+ // Sort products by Reference ID (and if equals (like combination) by Supplier Reference)
+ $sorter = new Sorter();
+ $products = $sorter->natural($products, Sorter::ORDER_DESC, 'reference', 'supplier_reference');
+
foreach ($products as &$product) {
if ($tax_calculation_method == PS_TAX_EXC) {
$product['product_price'] = $product['price'];
diff --git a/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php b/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php
index 0a15d0f0bcb69..9d600c74f14dc 100644
--- a/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php
+++ b/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php
@@ -47,6 +47,7 @@
use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPreviewShippingDetails;
use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId;
use PrestaShop\PrestaShop\Core\Localization\Locale\Repository as LocaleRepository;
+use PrestaShop\PrestaShop\Core\Util\Sorter;
use State;
use StockAvailable;
use Validate;
@@ -208,7 +209,12 @@ private function getProductDetails(Order $order): array
$taxCalculationMethod = $this->getOrderTaxCalculationMethod($order);
- foreach ($order->getProductsDetail() as $detail) {
+ $orderDetails = $order->getProductsDetail();
+ // Sort products by Reference ID (and if equals (like combination) by Supplier Reference)
+ $sorter = new Sorter();
+ $orderDetails = $sorter->natural($orderDetails, Sorter::ORDER_DESC, 'product_reference', 'product_supplier_reference');
+
+ foreach ($orderDetails as $detail) {
$unitPrice = $detail['unit_price_tax_excl'];
$totalPrice = $detail['total_price_tax_excl'];
diff --git a/src/Adapter/Order/QueryHandler/GetOrderProductsForViewingHandler.php b/src/Adapter/Order/QueryHandler/GetOrderProductsForViewingHandler.php
index 06472b97774cf..c247ce930cb1a 100644
--- a/src/Adapter/Order/QueryHandler/GetOrderProductsForViewingHandler.php
+++ b/src/Adapter/Order/QueryHandler/GetOrderProductsForViewingHandler.php
@@ -43,10 +43,10 @@
use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderProductCustomizationsForViewing;
use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderProductForViewing;
use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderProductsForViewing;
-use PrestaShop\PrestaShop\Core\Domain\ValueObject\QuerySorting;
use PrestaShop\PrestaShop\Core\Image\Parser\ImageTagSourceParserInterface;
use PrestaShop\PrestaShop\Core\Localization\CLDR\ComputingPrecision;
use PrestaShop\PrestaShop\Core\Localization\Locale;
+use PrestaShop\PrestaShop\Core\Util\Sorter;
use Product;
use Shop;
use StockAvailable;
@@ -175,13 +175,14 @@ public function handle(GetOrderProductsForViewing $query): OrderProductsForViewi
unset($product);
- if (QuerySorting::DESC === $query->getProductsSorting()->getValue()) {
- // reorder products by order_detail_id DESC
- krsort($products);
- } else {
- // reorder products by order_detail_id ASC
- ksort($products);
- }
+ // Sort products by Reference ID (and if equals (like combination) by Supplier Reference)
+ $sorter = new Sorter();
+ $products = $sorter->natural(
+ $products,
+ $query->getProductsSorting()->getValue(),
+ 'product_reference',
+ 'product_supplier_reference'
+ );
$productsForViewing = [];
diff --git a/src/Core/Util/Sorter.php b/src/Core/Util/Sorter.php
new file mode 100644
index 0000000000000..6d8574ab95d27
--- /dev/null
+++ b/src/Core/Util/Sorter.php
@@ -0,0 +1,62 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+
+declare(strict_types=1);
+
+namespace PrestaShop\PrestaShop\Core\Util;
+
+class Sorter
+{
+ public const ORDER_ASC = 'ASC';
+ public const ORDER_DESC = 'DESC';
+
+ /**
+ * @param array> $array
+ * @param string $order
+ * @param string ...$criterias
+ *
+ * @return array
+ */
+ public function natural(array $array, string $order, string ...$criterias): array
+ {
+ usort($array, function ($a, $b) use ($order, $criterias) {
+ $cmp = 0;
+ foreach ($criterias as $criteria) {
+ if (!isset($a[$criteria]) || !isset($b[$criteria])) {
+ return 0;
+ }
+ $cmp = strnatcmp($a[$criteria], $b[$criteria]);
+ if ($cmp !== 0) {
+ break;
+ }
+ }
+
+ return static::ORDER_DESC === $order ? $cmp : $cmp * -1;
+ });
+
+ return $array;
+ }
+}
diff --git a/tests/Unit/Core/Util/SorterTest.php b/tests/Unit/Core/Util/SorterTest.php
new file mode 100644
index 0000000000000..eba9e54ab9f1e
--- /dev/null
+++ b/tests/Unit/Core/Util/SorterTest.php
@@ -0,0 +1,185 @@
+
+ * @copyright Since 2007 PrestaShop SA and Contributors
+ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
+ */
+
+declare(strict_types=1);
+
+namespace Tests\Unit\Core\Util;
+
+use PHPUnit\Framework\TestCase;
+use PrestaShop\PrestaShop\Core\Util\Sorter;
+
+class SorterTest extends TestCase
+{
+ /**
+ * @dataProvider dataProviderNatural
+ *
+ * @param array $expected
+ * @param array $array
+ * @param string $order
+ * @param string $criteria1
+ * @param string $criteria2
+ *
+ * @return void
+ */
+ public function testNatural(
+ array $expected,
+ array $array,
+ string $order,
+ string $criteria1 = '',
+ string $criteria2 = ''
+ ): void {
+ $sorter = new Sorter();
+ $this->assertEquals(
+ $expected,
+ $sorter->natural($array, $order, $criteria1, $criteria2)
+ );
+ }
+
+ public function dataProviderNatural(): iterable
+ {
+ yield [
+ [],
+ [],
+ Sorter::ORDER_ASC,
+ ];
+
+ // Array with one criteria
+ yield [
+ [
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ [
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ],
+ Sorter::ORDER_DESC,
+ 'keyA',
+ ];
+
+ yield [
+ [
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ],
+ [
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ Sorter::ORDER_ASC,
+ 'keyA',
+ ];
+
+ // Array with one criteria which doesn't exist
+ yield [
+ [
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ],
+ [
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ],
+ Sorter::ORDER_DESC,
+ 'keyC',
+ ];
+
+ // Array with two criterias
+ yield [
+ [
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ [
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ Sorter::ORDER_DESC,
+ 'keyA',
+ 'keyB',
+ ];
+
+ yield [
+ [
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ],
+ [
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ Sorter::ORDER_ASC,
+ 'keyA',
+ 'keyB',
+ ];
+
+ // Array with two criterias which the first doesn't exist
+ yield [
+ [
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ [
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ Sorter::ORDER_DESC,
+ 'keyC',
+ 'keyB',
+ ];
+
+ // Array with two criterias which the second doesn't exist
+ yield [
+ [
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ [
+ ['keyA' => 'a', 'keyB' => 'b'],
+ ['keyA' => 'b', 'keyB' => 'a'],
+ ['keyA' => 'a', 'keyB' => 'a'],
+ ['keyA' => 'b', 'keyB' => 'b'],
+ ],
+ Sorter::ORDER_DESC,
+ 'keyA',
+ 'keyC',
+ ];
+ }
+}