From 166bfbbb9fbe3ef26c4f502790420df1b5fe7d2d Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Wed, 30 Jan 2019 16:51:57 -0600 Subject: [PATCH 001/195] Fix of payment restriction by carrier Fix the critical error that prevents payment methods don't exist when a particular carrier is chosen. --- .../Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php index 6f5b49a7a9b8c..eee5809210990 100644 --- a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php +++ b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php @@ -72,7 +72,7 @@ public function getChoices() ); foreach ($carriers as $carrier) { - $choices[$carrier['name']] = $carrier['id_reference']; + $choices[$carrier['name'] . ' (' . $carrier['delay'] . ')'] = $carrier['id_reference']; } return $choices; From d8d7fb0d5466eebed04955028abec85cd5dd0e27 Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Tue, 5 Feb 2019 11:03:33 -0600 Subject: [PATCH 002/195] Include the "id_carrier" in the carriers list --- .../Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php index eee5809210990..cf123a0adef2d 100644 --- a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php +++ b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php @@ -72,7 +72,7 @@ public function getChoices() ); foreach ($carriers as $carrier) { - $choices[$carrier['name'] . ' (' . $carrier['delay'] . ')'] = $carrier['id_reference']; + $choices[$carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'] = $carrier['id_reference']; } return $choices; From e22cfc0d93ae810d0c2dbc0ada07a434d3a5521e Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Tue, 5 Feb 2019 12:01:35 -0600 Subject: [PATCH 003/195] Fix carrier choice in the product editor --- src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php index 7d4a1d9eae227..12a6a07a39aa9 100644 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php +++ b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php @@ -68,7 +68,7 @@ public function __construct($translator, $legacyContext, $warehouseDataProvider, ); $this->carriersChoices = []; foreach ($carriers as $carrier) { - $this->carriersChoices[$carrier['name'] . ' (' . $carrier['delay'] . ')'] = $carrier['id_reference']; + $this->carriersChoices[$carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'] = $carrier['id_reference']; } } From 1e761ebaff2950b93aabd2ff0822ceb05ddcab83 Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Tue, 5 Feb 2019 12:04:43 -0600 Subject: [PATCH 004/195] Fix carrier choice in the cart rule controller --- controllers/admin/AdminCartRulesController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index f7038946f012d..6f4d878839ace 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -640,9 +640,10 @@ public function renderForm() $groups = $current_object->getAssociatedRestrictions('group', false, true); $shops = $current_object->getAssociatedRestrictions('shop', false, false); $cart_rules = $current_object->getAssociatedRestrictions('cart_rule', false, true, 0, $limit); - $carriers = $current_object->getAssociatedRestrictions('carrier', true, false); + $carriers = $current_object->getAssociatedRestrictions('carrier', true, true); foreach ($carriers as &$carriers2) { foreach ($carriers2 as &$carrier) { + $carrier['name'] = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; foreach ($carrier as $field => &$value) { if ($field == 'name' && $value == '0') { $value = Configuration::get('PS_SHOP_NAME'); From 1e3b60b22922df5e1e2d6c7dda8b90b3aed13399 Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Tue, 5 Feb 2019 12:08:59 -0600 Subject: [PATCH 005/195] Fix of duplicate rows in the cart rule class --- classes/CartRule.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/classes/CartRule.php b/classes/CartRule.php index e0bafc771a548..144c4b3574526 100755 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -1483,6 +1483,7 @@ public function getAssociatedRestrictions( ' . (in_array($type, array('carrier', 'shop')) ? ' AND t.deleted = 0' : '') . ' ' . ($type == 'cart_rule' ? 'AND t.id_cart_rule != ' . (int) $this->id : '') . $shop_list . + ' GROUP BY t.id_' . $type . (in_array($type, array('carrier', 'shop')) ? ' ORDER BY t.name ASC ' : '') . (in_array($type, array('country', 'group', 'cart_rule')) && $i18n ? ' ORDER BY tl.name ASC ' : '') . $sql_limit); @@ -1497,8 +1498,9 @@ public function getAssociatedRestrictions( ' . ($i18n ? 'LEFT JOIN `' . _DB_PREFIX_ . $type . '_lang` tl ON (t.id_' . $type . ' = tl.id_' . $type . ' AND tl.id_lang = ' . (int) Context::getContext()->language->id . ')' : '') . ' LEFT JOIN (SELECT id_' . $type . ' FROM `' . _DB_PREFIX_ . 'cart_rule_' . $type . '` WHERE id_cart_rule = ' . (int) $this->id . ') crt ON t.id_' . ($type == 'carrier' ? 'reference' : $type) . ' = crt.id_' . $type . ' WHERE 1 ' . ($active_only ? ' AND t.active = 1' : '') . - $shop_list - . (in_array($type, array('carrier', 'shop')) ? ' AND t.deleted = 0' : '') . + $shop_list . + ' GROUP BY t.id_' . $type . + (in_array($type, array('carrier', 'shop')) ? ' AND t.deleted = 0' : '') . (in_array($type, array('carrier', 'shop')) ? ' ORDER BY t.name ASC ' : '') . (in_array($type, array('country', 'group')) && $i18n ? ' ORDER BY tl.name ASC ' : '') . $sql_limit, From 8d0cf05bd773d58291984227f88dee74657b63ce Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Thu, 18 Apr 2019 11:21:08 -0500 Subject: [PATCH 006/195] Fix to merge with current validations --- controllers/admin/AdminCartRulesController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index 6f4d878839ace..65edfe13a8ce7 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -643,10 +643,13 @@ public function renderForm() $carriers = $current_object->getAssociatedRestrictions('carrier', true, true); foreach ($carriers as &$carriers2) { foreach ($carriers2 as &$carrier) { - $carrier['name'] = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; foreach ($carrier as $field => &$value) { - if ($field == 'name' && $value == '0') { - $value = Configuration::get('PS_SHOP_NAME'); + if ($field == 'name') { + if ($value == '0') { + $value = Configuration::get('PS_SHOP_NAME'); + } else { + $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; + } } } } From 11f5d6e824d71bac0721e3ac2768e572f2176cad Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Thu, 18 Apr 2019 13:04:12 -0500 Subject: [PATCH 007/195] Fix to force the change of all the carriers name --- controllers/admin/AdminCartRulesController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index 65edfe13a8ce7..1c519f6a6ed77 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -647,9 +647,9 @@ public function renderForm() if ($field == 'name') { if ($value == '0') { $value = Configuration::get('PS_SHOP_NAME'); - } else { - $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; } + + $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; } } } From 08e798489c8d3b125afe69d3009ceb4f49cbf665 Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Thu, 18 Apr 2019 13:53:35 -0500 Subject: [PATCH 008/195] Revert commit 2ef06077cfcbbf1df9344a155e07ea7f3d058fa2 --- classes/CartRule.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/classes/CartRule.php b/classes/CartRule.php index 144c4b3574526..e0bafc771a548 100755 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -1483,7 +1483,6 @@ public function getAssociatedRestrictions( ' . (in_array($type, array('carrier', 'shop')) ? ' AND t.deleted = 0' : '') . ' ' . ($type == 'cart_rule' ? 'AND t.id_cart_rule != ' . (int) $this->id : '') . $shop_list . - ' GROUP BY t.id_' . $type . (in_array($type, array('carrier', 'shop')) ? ' ORDER BY t.name ASC ' : '') . (in_array($type, array('country', 'group', 'cart_rule')) && $i18n ? ' ORDER BY tl.name ASC ' : '') . $sql_limit); @@ -1498,9 +1497,8 @@ public function getAssociatedRestrictions( ' . ($i18n ? 'LEFT JOIN `' . _DB_PREFIX_ . $type . '_lang` tl ON (t.id_' . $type . ' = tl.id_' . $type . ' AND tl.id_lang = ' . (int) Context::getContext()->language->id . ')' : '') . ' LEFT JOIN (SELECT id_' . $type . ' FROM `' . _DB_PREFIX_ . 'cart_rule_' . $type . '` WHERE id_cart_rule = ' . (int) $this->id . ') crt ON t.id_' . ($type == 'carrier' ? 'reference' : $type) . ' = crt.id_' . $type . ' WHERE 1 ' . ($active_only ? ' AND t.active = 1' : '') . - $shop_list . - ' GROUP BY t.id_' . $type . - (in_array($type, array('carrier', 'shop')) ? ' AND t.deleted = 0' : '') . + $shop_list + . (in_array($type, array('carrier', 'shop')) ? ' AND t.deleted = 0' : '') . (in_array($type, array('carrier', 'shop')) ? ' ORDER BY t.name ASC ' : '') . (in_array($type, array('country', 'group')) && $i18n ? ' ORDER BY tl.name ASC ' : '') . $sql_limit, From 8ce468780e93b60360fa8058d6231ad91ff9501c Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Thu, 18 Apr 2019 13:56:28 -0500 Subject: [PATCH 009/195] Fix to avoid duplicate carriers in the carrier restriction of cart rules --- controllers/admin/AdminCartRulesController.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index 1c519f6a6ed77..b2f7521cf2a3c 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -642,7 +642,15 @@ public function renderForm() $cart_rules = $current_object->getAssociatedRestrictions('cart_rule', false, true, 0, $limit); $carriers = $current_object->getAssociatedRestrictions('carrier', true, true); foreach ($carriers as &$carriers2) { - foreach ($carriers2 as &$carrier) { + $prev_id_carrier = 0; + + foreach ($carriers2 as $key => &$carrier) { + if ($prev_id_carrier == $carrier['id_carrier']) { + unset($carriers2[$key]); + + continue; + } + foreach ($carrier as $field => &$value) { if ($field == 'name') { if ($value == '0') { @@ -652,6 +660,8 @@ public function renderForm() $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; } } + + $prev_id_carrier = $carrier['id_carrier']; } } From 5e7e682f78851b43c8b52b83ca897e33e991d9c6 Mon Sep 17 00:00:00 2001 From: Rolige eCommerce Solutions Date: Mon, 9 Sep 2019 08:27:27 -0500 Subject: [PATCH 010/195] Fix of overwritten variable --- controllers/admin/AdminCartRulesController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index b2f7521cf2a3c..b224169354a57 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -654,10 +654,10 @@ public function renderForm() foreach ($carrier as $field => &$value) { if ($field == 'name') { if ($value == '0') { - $value = Configuration::get('PS_SHOP_NAME'); + $value = $carrier['id_carrier'].' - '.Configuration::get('PS_SHOP_NAME'); + } else { + $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; } - - $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; } } From 5c475f21b5a918db1526a05c1cdc3f006dbf9597 Mon Sep 17 00:00:00 2001 From: Mathieu Ferment Date: Thu, 26 Sep 2019 18:33:03 +0200 Subject: [PATCH 011/195] Avoid issues for carrier IDs if no delay --- controllers/admin/AdminCartRulesController.php | 6 +++++- .../ChoiceProvider/CarrierByReferenceChoiceProvider.php | 8 +++++++- .../Form/Admin/Product/ProductShipping.php | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index b224169354a57..3f0ee65206c1b 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -641,6 +641,7 @@ public function renderForm() $shops = $current_object->getAssociatedRestrictions('shop', false, false); $cart_rules = $current_object->getAssociatedRestrictions('cart_rule', false, true, 0, $limit); $carriers = $current_object->getAssociatedRestrictions('carrier', true, true); + foreach ($carriers as &$carriers2) { $prev_id_carrier = 0; @@ -656,7 +657,10 @@ public function renderForm() if ($value == '0') { $value = $carrier['id_carrier'].' - '.Configuration::get('PS_SHOP_NAME'); } else { - $value = $carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'; + $value = $carrier['id_carrier'].' - '.$carrier['name']; + if ($carrier['name']) { + $value .= ' ('.$carrier['delay'].')'; + } } } } diff --git a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php index cf123a0adef2d..0032fd7d097c1 100644 --- a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php +++ b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php @@ -72,7 +72,13 @@ public function getChoices() ); foreach ($carriers as $carrier) { - $choices[$carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'] = $carrier['id_reference']; + + $choiceId = $carrier['id_carrier'].' - '.$carrier['name']; + if ($carrier['name']) { + $choiceId .= ' ('.$carrier['delay'].')'; + } + + $choices[$choiceId] = $carrier['id_reference']; } return $choices; diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php index 12a6a07a39aa9..6605f5b5fe833 100644 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php +++ b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php @@ -68,7 +68,12 @@ public function __construct($translator, $legacyContext, $warehouseDataProvider, ); $this->carriersChoices = []; foreach ($carriers as $carrier) { - $this->carriersChoices[$carrier['id_carrier'].' - '.$carrier['name'].' ('.$carrier['delay'].')'] = $carrier['id_reference']; + $choiceId = $carrier['id_carrier'].' - '.$carrier['name']; + if ($carrier['name']) { + $choiceId .= ' ('.$carrier['delay'].')'; + } + + $this->carriersChoices[$choiceId] = $carrier['id_reference']; } } From b47c43b66cd9486bc0875460f1c1ab65034fc1f3 Mon Sep 17 00:00:00 2001 From: Mathieu Ferment Date: Thu, 26 Sep 2019 22:09:13 +0200 Subject: [PATCH 012/195] Apply php-cs-fixer --- controllers/admin/AdminCartRulesController.php | 10 +++++----- .../CarrierByReferenceChoiceProvider.php | 5 ++--- .../Form/Admin/Product/ProductShipping.php | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index 3f0ee65206c1b..cb8c340d2c2de 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -655,12 +655,12 @@ public function renderForm() foreach ($carrier as $field => &$value) { if ($field == 'name') { if ($value == '0') { - $value = $carrier['id_carrier'].' - '.Configuration::get('PS_SHOP_NAME'); + $value = $carrier['id_carrier'] . ' - ' . Configuration::get('PS_SHOP_NAME'); } else { - $value = $carrier['id_carrier'].' - '.$carrier['name']; - if ($carrier['name']) { - $value .= ' ('.$carrier['delay'].')'; - } + $value = $carrier['id_carrier'] . ' - ' . $carrier['name']; + if ($carrier['name']) { + $value .= ' (' . $carrier['delay'] . ')'; + } } } } diff --git a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php index 0032fd7d097c1..7773bb30e0ad6 100644 --- a/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php +++ b/src/Core/Form/ChoiceProvider/CarrierByReferenceChoiceProvider.php @@ -72,10 +72,9 @@ public function getChoices() ); foreach ($carriers as $carrier) { - - $choiceId = $carrier['id_carrier'].' - '.$carrier['name']; + $choiceId = $carrier['id_carrier'] . ' - ' . $carrier['name']; if ($carrier['name']) { - $choiceId .= ' ('.$carrier['delay'].')'; + $choiceId .= ' (' . $carrier['delay'] . ')'; } $choices[$choiceId] = $carrier['id_reference']; diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php index 6605f5b5fe833..911291da719f7 100644 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php +++ b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php @@ -68,11 +68,11 @@ public function __construct($translator, $legacyContext, $warehouseDataProvider, ); $this->carriersChoices = []; foreach ($carriers as $carrier) { - $choiceId = $carrier['id_carrier'].' - '.$carrier['name']; + $choiceId = $carrier['id_carrier'] . ' - ' . $carrier['name']; if ($carrier['name']) { - $choiceId .= ' ('.$carrier['delay'].')'; + $choiceId .= ' (' . $carrier['delay'] . ')'; } - + $this->carriersChoices[$choiceId] = $carrier['id_reference']; } } From 5d43db977755319cb357e0c64215708bf2df9184 Mon Sep 17 00:00:00 2001 From: Justinas Date: Thu, 10 Oct 2019 22:11:06 +0300 Subject: [PATCH 013/195] Fixes issue Undefined index _route not sure why _route is expected to be part of caller parameters but it's not, the route is actually in the caller_route parameter, so we first merge parameters with defaults array to make sure _route key is set. + will not overwrite params with the same name --- src/PrestaShopBundle/Controller/Admin/CommonController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/CommonController.php b/src/PrestaShopBundle/Controller/Admin/CommonController.php index 9166f6b8a2ad2..b9544afa3418c 100644 --- a/src/PrestaShopBundle/Controller/Admin/CommonController.php +++ b/src/PrestaShopBundle/Controller/Admin/CommonController.php @@ -118,7 +118,7 @@ public function paginationAction(Request $request, $limit = 10, $offset = 0, $to unset($callerParameters[$k]); } } - $routeName = $request->attributes->get('caller_route', $request->attributes->get('caller_parameters', ['_route' => false])['_route']); + $routeName = $request->attributes->get('caller_route', ($callerParameters + ['_route' => false])['_route']); $nextPageUrl = (!$routeName || ($offset + $limit >= $total)) ? false : $this->generateUrl($routeName, array_merge( $callerParameters, array( From 6e7d0c4aea11ba9737f0ba152be79a1a94ffb1d5 Mon Sep 17 00:00:00 2001 From: Tomas Ilginis Date: Fri, 11 Oct 2019 15:54:54 +0300 Subject: [PATCH 014/195] adds kpis container so it is condensed not in full width but in container specific width - also put elements to behave with flex around properties --- .../views/Admin/Common/Kpi/kpi_row.html.twig | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig index d8a66ca7b0dc9..ec37fcd2af246 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig @@ -24,19 +24,19 @@ *#} {% block kpi_row %} -
+
- {% if kpiRow.allowRefresh %} -
- -
- {% endif %} +{# {% if kpiRow.allowRefresh %}#} +{#
#} +{# #} +{#
#} +{# {% endif %}#} -
+
{% for kpi in kpiRow.kpis %} -
+
{{ kpi|raw }}
{% endfor %} From c9d957a0e73ff308d181966fbd9cc5ab5ae0f60e Mon Sep 17 00:00:00 2001 From: Tomas Ilginis Date: Fri, 11 Oct 2019 15:57:29 +0300 Subject: [PATCH 015/195] returns back refresh icon functionality --- .../views/Admin/Common/Kpi/kpi_row.html.twig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig index ec37fcd2af246..47e9d7f06593e 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Common/Kpi/kpi_row.html.twig @@ -26,14 +26,6 @@ {% block kpi_row %}
-{# {% if kpiRow.allowRefresh %}#} -{#
#} -{# #} -{#
#} -{# {% endif %}#} -
{% for kpi in kpiRow.kpis %}
@@ -43,4 +35,12 @@
+ + {% if kpiRow.allowRefresh %} +
+ +
+ {% endif %} {% endblock %} From 3857f7a9d9656ba3ace313d674629d7235ae07df Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Mon, 22 Jul 2019 15:43:09 +0200 Subject: [PATCH 016/195] Add translation provider to allow modifying body contents, add a form in Email Theme page to access this edition --- .../Improve/Design/MailThemeController.php | 33 ++++++++++ .../MailTheme/TranslateMailsBodyType.php | 52 +++++++++++++++ .../admin/improve/design/mail_theme.yml | 8 +++ .../config/services/bundle/form/form_type.yml | 7 ++ .../config/services/bundle/translation.yml | 8 +++ .../translate_mails_body_form.html.twig | 63 ++++++++++++++++++ .../Improve/Design/MailTheme/index.html.twig | 6 ++ .../Provider/MailsBodyProvider.php | 65 +++++++++++++++++++ 8 files changed, 242 insertions(+) create mode 100644 src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php create mode 100644 src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig create mode 100644 src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php diff --git a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php index 1d5a2daf36726..e4419be5b5867 100644 --- a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php +++ b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php @@ -44,6 +44,7 @@ use PrestaShop\PrestaShop\Core\MailTemplate\Transformation\MailVariablesTransformation; use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\GenerateMailsType; +use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\TranslateMailsBodyType; use PrestaShopBundle\Security\Annotation\AdminSecurity; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\Request; @@ -72,6 +73,7 @@ public function indexAction(Request $request) { $legacyController = $request->attributes->get('_legacy_controller'); $generateThemeMailsForm = $this->createForm(GenerateMailsType::class); + $translateMailsBodyForm = $this->createForm(TranslateMailsBodyType::class); /** @var ThemeCatalogInterface $themeCatalog */ $themeCatalog = $this->get('prestashop.core.mail_template.theme_catalog'); $mailThemes = $themeCatalog->listThemes(); @@ -83,6 +85,7 @@ public function indexAction(Request $request) 'help_link' => $this->generateSidebarLink($legacyController), 'mailThemeConfigurationForm' => $this->getMailThemeFormHandler()->getForm()->createView(), 'generateMailsForm' => $generateThemeMailsForm->createView(), + 'translateMailsBodyForm' => $translateMailsBodyForm->createView(), 'mailThemes' => $mailThemes, ]); } @@ -353,6 +356,36 @@ public function sendTestMailAction($theme, $layout, $locale, $module = '') return $this->redirectToRoute('admin_mail_theme_preview', ['theme' => $theme]); } + /** + * @param Request $request + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + */ + public function translateBodyAction(Request $request) + { + $translateMailsBodyForm = $this->createForm(TranslateMailsBodyType::class); + $translateMailsBodyForm->handleRequest($request); + + if (!$translateMailsBodyForm->isSubmitted() || !$translateMailsBodyForm->isValid()) { + $this->addFlash( + 'error', + $this->trans( + 'Cannot send translate body contents for e-mail', + 'Admin.Notifications.Error' + ) + ); + + return $this->redirectToRoute('admin_mail_theme_index'); + } + $translateData = $translateMailsBodyForm->getData(); + + return $this->redirectToRoute('admin_international_translation_overview', [ + 'lang' => $translateData['language'], + 'type' => 'mails_body', + 'selected' => 'body', + ]); + } + /** * Preview a mail layout from a defined theme * diff --git a/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php b/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php new file mode 100644 index 0000000000000..be7e7e358b98c --- /dev/null +++ b/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php @@ -0,0 +1,52 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShopBundle\Form\Admin\Improve\Design\MailTheme; + +use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\FormBuilderInterface; + +/** + * Class TranslateMailsBodyType manages the form allowing to select a language + * and translate Emails body content. + */ +class TranslateMailsBodyType extends TranslatorAwareType +{ + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('language', ChoiceType::class, [ + 'placeholder' => $this->trans('Language', 'Admin.Global'), + 'choices' => $this->getLocaleChoices(), + 'choice_translation_domain' => false, + ]) + ; + } +} diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml index e5623184a9fad..9bf960adcd7f1 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml @@ -81,3 +81,11 @@ admin_mail_theme_send_test_module_mail: _controller: PrestaShopBundle:Admin\Improve\Design\MailTheme:sendTestMail _legacy_link: AdminMailTheme:rawModuleLayout _legacy_controller: AdminMailTheme + +admin_mail_theme_translate_body: + path: /translateBody + methods: [POST] + defaults: + _controller: PrestaShopBundle:Admin\Improve\Design\MailTheme:translateBody + _legacy_controller: AdminMailTheme + _legacy_link: AdminMailTheme:translateBody diff --git a/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml b/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml index beffb2c68b273..a2254ce1525ef 100644 --- a/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml +++ b/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml @@ -947,3 +947,10 @@ services: - '@prestashop.core.form.choice_provider.currency_by_id' tags: - { name: form.type } + + form.type.localization.translate_mails_body: + class: 'PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\TranslateMailsBodyType' + parent: 'form.type.translatable.aware' + public: true + tags: + - { name: form.type } diff --git a/src/PrestaShopBundle/Resources/config/services/bundle/translation.yml b/src/PrestaShopBundle/Resources/config/services/bundle/translation.yml index 2eef98f331a5e..3fd9d2e5000ad 100644 --- a/src/PrestaShopBundle/Resources/config/services/bundle/translation.yml +++ b/src/PrestaShopBundle/Resources/config/services/bundle/translation.yml @@ -43,6 +43,14 @@ services: tags: - { name: "ps.translation_provider" } + prestashop.translation.mails_body_provider: + class: PrestaShopBundle\Translation\Provider\MailsBodyProvider + arguments: + - "@prestashop.translation.database_loader" + - "%translations_dir%" + tags: + - { name: "ps.translation_provider" } + prestashop.translation.others_provider: class: PrestaShopBundle\Translation\Provider\OthersProvider arguments: diff --git a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig new file mode 100644 index 0000000000000..48eda680209a6 --- /dev/null +++ b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig @@ -0,0 +1,63 @@ +{#** + * 2007-2019 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to https://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + *#} + +{% block translateMailsBodyFormBlock %} +
+
+ {{ form_start(translateMailsBodyForm, {'action': path('admin_mail_theme_translate_body')}) }} +
+

+ email {{ 'Translate emails'|trans({}, 'Admin.Design.Feature') }} +

+ +
+
+ +
+ +
+ {{ form_errors(translateMailsBodyForm.language) }} + {{ form_widget(translateMailsBodyForm.language) }} +
+
+ +
+
+ + +
+ {{ form_end(translateMailsBodyForm) }} +
+
+{% endblock %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig index 9746a3310389d..360d7005794da 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig @@ -38,6 +38,12 @@ }) }} + {{ + include('@PrestaShop/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig', { + 'generateMailsForm': generateMailsForm + }) + }} + {{ include('@PrestaShop/Admin/Improve/Design/MailTheme/Blocks/list_mail_themes.html.twig', { 'mailThemes': mailThemes diff --git a/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php b/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php new file mode 100644 index 0000000000000..d137550ae10c3 --- /dev/null +++ b/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php @@ -0,0 +1,65 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShopBundle\Translation\Provider; + +/** + * Translation provider specific to email subjects. + */ +class MailsBodyProvider extends AbstractProvider implements UseDefaultCatalogueInterface +{ + /** + * {@inheritdoc} + */ + public function getTranslationDomains() + { + return ['EmailsBody*']; + } + + /** + * {@inheritdoc} + */ + public function getFilters() + { + return ['#EmailsBody*#']; + } + + /** + * {@inheritdoc} + */ + public function getIdentifier() + { + return 'mails_body'; + } + + /** + * {@inheritdoc} + */ + public function getDefaultResourceDirectory() + { + return $this->resourceDirectory . DIRECTORY_SEPARATOR . 'default'; + } +} From 380576ce42b4388381fbc14b9edea36a27fdd5d2 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Mon, 22 Jul 2019 16:55:46 +0200 Subject: [PATCH 017/195] Redirect with locale or the edition will not work --- .../Admin/Improve/Design/MailThemeController.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php index e4419be5b5867..1f57616b20973 100644 --- a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php +++ b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php @@ -46,6 +46,8 @@ use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\GenerateMailsType; use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\TranslateMailsBodyType; use PrestaShopBundle\Security\Annotation\AdminSecurity; +use PrestaShopBundle\Service\Hook\HookEvent; +use PrestaShopBundle\Service\TranslationService; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -377,10 +379,16 @@ public function translateBodyAction(Request $request) return $this->redirectToRoute('admin_mail_theme_index'); } + $translateData = $translateMailsBodyForm->getData(); + $language = $translateData['language']; + /** @var TranslationService $translationService */ + $translationService = $this->get('prestashop.service.translation'); + $locale = $translationService->langToLocale($language); return $this->redirectToRoute('admin_international_translation_overview', [ - 'lang' => $translateData['language'], + 'lang' => $language, + 'locale' => $locale, 'type' => 'mails_body', 'selected' => 'body', ]); From b48d05774319f20f4de8c6347518ff47d7865ac1 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Tue, 23 Jul 2019 10:04:24 +0200 Subject: [PATCH 018/195] Fix typo --- .../Controller/Admin/Improve/Design/MailThemeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php index 1f57616b20973..d16fbb618aa8c 100644 --- a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php +++ b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php @@ -372,7 +372,7 @@ public function translateBodyAction(Request $request) $this->addFlash( 'error', $this->trans( - 'Cannot send translate body contents for e-mail', + 'Cannot translate body contents for e-mail', 'Admin.Notifications.Error' ) ); From 07db2d7f8b42773934c81daeb6297240d54c133e Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Tue, 23 Jul 2019 15:49:05 +0200 Subject: [PATCH 019/195] Address feedback from PR --- .../Controller/Admin/Improve/Design/MailThemeController.php | 2 +- .../views/Admin/Improve/Design/MailTheme/index.html.twig | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php index d16fbb618aa8c..dfb8bf9da5fdd 100644 --- a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php +++ b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php @@ -372,7 +372,7 @@ public function translateBodyAction(Request $request) $this->addFlash( 'error', $this->trans( - 'Cannot translate body contents for e-mail', + 'Cannot translate emails body contents', 'Admin.Notifications.Error' ) ); diff --git a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig index 360d7005794da..a0e92fcd5294e 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/index.html.twig @@ -39,9 +39,9 @@ }} {{ - include('@PrestaShop/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig', { - 'generateMailsForm': generateMailsForm - }) + include('@PrestaShop/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig', { + 'generateMailsForm': generateMailsForm + }) }} {{ From 554d9587562cab5e5be3dbc5518f6cc68fdeb6a4 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Tue, 23 Jul 2019 18:14:43 +0200 Subject: [PATCH 020/195] Reformat mail theme urls and add redirections on former ones --- .../improve/design/_mail_theme_alias.yml | 22 +++++++++++++++++++ .../admin/improve/design/mail_theme.yml | 12 ++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml new file mode 100644 index 0000000000000..7c7e1c0f53224 --- /dev/null +++ b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml @@ -0,0 +1,22 @@ +# These are alias routes used to manage legacy urls that were badly formatted (camel case) +_admin_mail_theme_save_configuration_alias: + path: /saveConfiguration + defaults: + _controller: FrameworkBundle:Redirect:redirect + route: admin_mail_theme_save_configuration + permanent: true + +_admin_mail_theme_send_test_mail_alias: + path: /sendTestMail/{locale}/{theme}/{layout} + defaults: + _controller: FrameworkBundle:Redirect:redirect + route: admin_mail_theme_send_test_mail + permanent: true + +_admin_mail_theme_send_test_module_mail_mail: + path: /sendTestMail/{locale}/{theme}/{module}/{layout} + methods: [GET] + defaults: + _controller: FrameworkBundle:Redirect:redirect + route: admin_mail_theme_send_test_module_mail + permanent: true diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml index 9bf960adcd7f1..5900657bef506 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml @@ -1,3 +1,7 @@ +_mail_theme_alias: + resource: "_mail_theme_alias.yml" + prefix: / + admin_mail_theme_index: path: / methods: [GET] @@ -15,7 +19,7 @@ admin_mail_theme_generate: _legacy_link: AdminMailTheme:postGenerateMails admin_mail_theme_save_configuration: - path: /saveConfiguration + path: /save-configuration methods: [POST] defaults: _controller: PrestaShopBundle:Admin\Improve\Design\MailTheme:saveConfiguration @@ -66,7 +70,7 @@ admin_mail_theme_raw_module_layout: _legacy_controller: AdminMailTheme admin_mail_theme_send_test_mail: - path: /sendTestMail/{locale}/{theme}/{layout} + path: /send-test-mail/{locale}/{theme}/{layout} methods: [GET] defaults: _controller: PrestaShopBundle:Admin\Improve\Design\MailTheme:sendTestMail @@ -75,7 +79,7 @@ admin_mail_theme_send_test_mail: module: '' admin_mail_theme_send_test_module_mail: - path: /sendTestMail/{locale}/{theme}/{module}/{layout} + path: /send-test-mail/{locale}/{theme}/{module}/{layout} methods: [GET] defaults: _controller: PrestaShopBundle:Admin\Improve\Design\MailTheme:sendTestMail @@ -83,7 +87,7 @@ admin_mail_theme_send_test_module_mail: _legacy_controller: AdminMailTheme admin_mail_theme_translate_body: - path: /translateBody + path: /translate-body methods: [POST] defaults: _controller: PrestaShopBundle:Admin\Improve\Design\MailTheme:translateBody From 9ba85c3386cc0911c1cf05596c89c93eb9b59eb1 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Tue, 30 Jul 2019 16:18:45 +0200 Subject: [PATCH 021/195] Remove extra spaces --- .../Design/MailTheme/Blocks/translate_mails_body_form.html.twig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig index 48eda680209a6..e6752418b7dd9 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig @@ -34,7 +34,6 @@
-
-
From c2d387ffeec4ea8af00c39a6f929185140bf1fd6 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Mon, 16 Sep 2019 15:38:54 +0200 Subject: [PATCH 022/195] Fix translation tree API for emails subject, remove theme parameter from rediretcion so that the translations are correctly saved without a theme --- .../Improve/Design/MailThemeController.php | 2 -- .../Controller/Api/TranslationController.php | 22 +++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php index dfb8bf9da5fdd..e06938d963106 100644 --- a/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php +++ b/src/PrestaShopBundle/Controller/Admin/Improve/Design/MailThemeController.php @@ -46,7 +46,6 @@ use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\GenerateMailsType; use PrestaShopBundle\Form\Admin\Improve\Design\MailTheme\TranslateMailsBodyType; use PrestaShopBundle\Security\Annotation\AdminSecurity; -use PrestaShopBundle\Service\Hook\HookEvent; use PrestaShopBundle\Service\TranslationService; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\Request; @@ -390,7 +389,6 @@ public function translateBodyAction(Request $request) 'lang' => $language, 'locale' => $locale, 'type' => 'mails_body', - 'selected' => 'body', ]); } diff --git a/src/PrestaShopBundle/Controller/Api/TranslationController.php b/src/PrestaShopBundle/Controller/Api/TranslationController.php index 59ab74c49f2a6..7abde159e4101 100644 --- a/src/PrestaShopBundle/Controller/Api/TranslationController.php +++ b/src/PrestaShopBundle/Controller/Api/TranslationController.php @@ -150,11 +150,13 @@ public function listTreeAction(Request $request) break; case 'mails': - // when emails body will be implemented, it should be a different type - // because domain routes only support "type" & "selected/theme" as parameters $tree = $this->getMailsSubjectTree($lang, $search); break; + case 'mails_body': + $tree = $this->getMailsBodyTree($lang, $search); + break; + default: $tree = $this->getNormalTree($lang, $type, null, $search); break; @@ -371,6 +373,22 @@ private function getMailsSubjectTree($lang, $search = null) return $this->getCleanTree($treeBuilder, $catalogue, $theme, $search); } + /** + * @param string $lang Two-letter iso code + * @param null $search + * + * @return array + */ + private function getMailsBodyTree($lang, $search = null) + { + $theme = null; + + $treeBuilder = new TreeBuilder($this->translationService->langToLocale($lang), $theme); + $catalogue = $this->translationService->getTranslationsCatalogue($lang, 'mails_body', $theme, $search); + + return $this->getCleanTree($treeBuilder, $catalogue, $theme, $search); + } + /** * Make final tree. * From 9a86b6ea0686e69ad4a03f31bb8485d0782db322 Mon Sep 17 00:00:00 2001 From: Jonathan Lelievre Date: Mon, 16 Sep 2019 15:49:01 +0200 Subject: [PATCH 023/195] Fix headers in new files at least --- .../Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php | 2 +- .../Design/MailTheme/Blocks/translate_mails_body_form.html.twig | 2 +- src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php b/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php index be7e7e358b98c..cda736531b622 100644 --- a/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php +++ b/src/PrestaShopBundle/Form/Admin/Improve/Design/MailTheme/TranslateMailsBodyType.php @@ -16,7 +16,7 @@ * * Do not edit or add to this file if you wish to upgrade PrestaShop to newer * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to http://www.prestashop.com for more information. + * needs please refer to https://www.prestashop.com for more information. * * @author PrestaShop SA * @copyright 2007-2019 PrestaShop SA and Contributors diff --git a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig index e6752418b7dd9..255a3398c1804 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Improve/Design/MailTheme/Blocks/translate_mails_body_form.html.twig @@ -1,5 +1,5 @@ {#** - * 2007-2019 PrestaShop and Contributors + * 2007-2019 PrestaShop SA and Contributors * * NOTICE OF LICENSE * diff --git a/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php b/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php index d137550ae10c3..a597599c4a58d 100644 --- a/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php +++ b/src/PrestaShopBundle/Translation/Provider/MailsBodyProvider.php @@ -1,6 +1,6 @@ Date: Tue, 8 Oct 2019 12:06:13 +0200 Subject: [PATCH 024/195] Rename alias routes as deprecated, rename any routes starting with _ --- .../{_mail_theme_alias.yml => _mail_theme_deprecated.yml} | 6 +++--- .../config/routing/admin/improve/design/mail_theme.yml | 4 ++-- .../routing/admin/improve/international/translations.yml | 2 +- .../Translations/Blocks/modify_translations.html.twig | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/PrestaShopBundle/Resources/config/routing/admin/improve/design/{_mail_theme_alias.yml => _mail_theme_deprecated.yml} (81%) diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_deprecated.yml similarity index 81% rename from src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml rename to src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_deprecated.yml index 7c7e1c0f53224..5e46523fdca56 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_alias.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/_mail_theme_deprecated.yml @@ -1,19 +1,19 @@ # These are alias routes used to manage legacy urls that were badly formatted (camel case) -_admin_mail_theme_save_configuration_alias: +admin_mail_theme_save_configuration_deprecated: path: /saveConfiguration defaults: _controller: FrameworkBundle:Redirect:redirect route: admin_mail_theme_save_configuration permanent: true -_admin_mail_theme_send_test_mail_alias: +admin_mail_theme_send_test_mail_deprecated: path: /sendTestMail/{locale}/{theme}/{layout} defaults: _controller: FrameworkBundle:Redirect:redirect route: admin_mail_theme_send_test_mail permanent: true -_admin_mail_theme_send_test_module_mail_mail: +admin_mail_theme_send_test_module_mail_deprecated: path: /sendTestMail/{locale}/{theme}/{module}/{layout} methods: [GET] defaults: diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml index 5900657bef506..bb3d50aba7f29 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/improve/design/mail_theme.yml @@ -1,5 +1,5 @@ -_mail_theme_alias: - resource: "_mail_theme_alias.yml" +_mail_theme_deprecated: + resource: "_mail_theme_deprecated.yml" prefix: / admin_mail_theme_index: diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/improve/international/translations.yml b/src/PrestaShopBundle/Resources/config/routing/admin/improve/international/translations.yml index 374e273b77c70..c2b31b468cb5e 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/improve/international/translations.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/improve/international/translations.yml @@ -22,7 +22,7 @@ admin_international_translations_show_settings: _legacy_controller: AdminTranslations _legacy_link: AdminTranslations:settings -_admin_international_translations_modify: +admin_international_translations_modify: path: /modify methods: [GET] defaults: diff --git a/src/PrestaShopBundle/Resources/views/Admin/Improve/International/Translations/Blocks/modify_translations.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Improve/International/Translations/Blocks/modify_translations.html.twig index c9e5698ad3cb4..87eea3449a13d 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Improve/International/Translations/Blocks/modify_translations.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Improve/International/Translations/Blocks/modify_translations.html.twig @@ -26,7 +26,7 @@ {% trans_default_domain 'Admin.International.Feature' %} {% block modify_translations %} - {{ form_start(modifyTranslationsForm, {method: 'get', action: path('_admin_international_translations_modify')}) }} + {{ form_start(modifyTranslationsForm, {method: 'get', action: path('admin_international_translations_modify')}) }}

From 845a5ed23d176954549555f0d16c8eb102843ac6 Mon Sep 17 00:00:00 2001 From: Justinas Date: Mon, 14 Oct 2019 23:01:35 +0300 Subject: [PATCH 025/195] changed [] to array() for more BC --- src/PrestaShopBundle/Controller/Admin/CommonController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/CommonController.php b/src/PrestaShopBundle/Controller/Admin/CommonController.php index b9544afa3418c..466a691c9e63f 100644 --- a/src/PrestaShopBundle/Controller/Admin/CommonController.php +++ b/src/PrestaShopBundle/Controller/Admin/CommonController.php @@ -118,7 +118,7 @@ public function paginationAction(Request $request, $limit = 10, $offset = 0, $to unset($callerParameters[$k]); } } - $routeName = $request->attributes->get('caller_route', ($callerParameters + ['_route' => false])['_route']); + $routeName = $request->attributes->get('caller_route', ($callerParameters + array('_route' => false))['_route']); $nextPageUrl = (!$routeName || ($offset + $limit >= $total)) ? false : $this->generateUrl($routeName, array_merge( $callerParameters, array( From 8c695a430939e035125e1bccdfa8969171da3026 Mon Sep 17 00:00:00 2001 From: Justinas Date: Mon, 14 Oct 2019 23:13:42 +0300 Subject: [PATCH 026/195] fixed BC break with php 5.6 syntax --- src/PrestaShopBundle/Controller/Admin/CommonController.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/CommonController.php b/src/PrestaShopBundle/Controller/Admin/CommonController.php index 466a691c9e63f..5c160d38abd09 100644 --- a/src/PrestaShopBundle/Controller/Admin/CommonController.php +++ b/src/PrestaShopBundle/Controller/Admin/CommonController.php @@ -118,7 +118,9 @@ public function paginationAction(Request $request, $limit = 10, $offset = 0, $to unset($callerParameters[$k]); } } - $routeName = $request->attributes->get('caller_route', ($callerParameters + array('_route' => false))['_route']); + + $callerParameters += array('_route' => false); + $routeName = $request->attributes->get('caller_route', $callerParameters['_route']); $nextPageUrl = (!$routeName || ($offset + $limit >= $total)) ? false : $this->generateUrl($routeName, array_merge( $callerParameters, array( From 3b756572dd765428d7b13c5c39c5d2cb4cf88090 Mon Sep 17 00:00:00 2001 From: Justinas Date: Tue, 15 Oct 2019 11:24:32 +0300 Subject: [PATCH 027/195] Update src/PrestaShopBundle/Controller/Admin/CommonController.php Co-Authored-By: GoT --- src/PrestaShopBundle/Controller/Admin/CommonController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/CommonController.php b/src/PrestaShopBundle/Controller/Admin/CommonController.php index 5c160d38abd09..bf55fcf51c576 100644 --- a/src/PrestaShopBundle/Controller/Admin/CommonController.php +++ b/src/PrestaShopBundle/Controller/Admin/CommonController.php @@ -118,7 +118,6 @@ public function paginationAction(Request $request, $limit = 10, $offset = 0, $to unset($callerParameters[$k]); } } - $callerParameters += array('_route' => false); $routeName = $request->attributes->get('caller_route', $callerParameters['_route']); $nextPageUrl = (!$routeName || ($offset + $limit >= $total)) ? false : $this->generateUrl($routeName, array_merge( From fc48b612b2edbe81b2c6facc5bd83fa49f9ceb86 Mon Sep 17 00:00:00 2001 From: Ashish Sharawat Date: Thu, 17 Oct 2019 14:41:43 +0530 Subject: [PATCH 028/195] in.xml --- localization/in.xml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/localization/in.xml b/localization/in.xml index 37b183f2c8fa7..592c819057313 100644 --- a/localization/in.xml +++ b/localization/in.xml @@ -10,21 +10,26 @@ - - - - - + + + + + + - + - + + + + + From 64af7d63d5b5de68222849678d294e1eb856a735 Mon Sep 17 00:00:00 2001 From: Ashish Sharawat Date: Thu, 17 Oct 2019 21:31:27 +0530 Subject: [PATCH 029/195] Update localization/in.xml Co-Authored-By: LouiseBonnard <32565890+LouiseBonnard@users.noreply.github.com> --- localization/in.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/in.xml b/localization/in.xml index 592c819057313..97080fdb8e20b 100644 --- a/localization/in.xml +++ b/localization/in.xml @@ -10,7 +10,7 @@ - + From 5416de624875719cf95f795ce82992df1a8f9ac8 Mon Sep 17 00:00:00 2001 From: Ashish Sharawat Date: Thu, 17 Oct 2019 21:31:36 +0530 Subject: [PATCH 030/195] Update localization/in.xml Co-Authored-By: LouiseBonnard <32565890+LouiseBonnard@users.noreply.github.com> --- localization/in.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/in.xml b/localization/in.xml index 97080fdb8e20b..527bc721c30c5 100644 --- a/localization/in.xml +++ b/localization/in.xml @@ -12,7 +12,7 @@ - + From 36473032006a1dbb3bbe06eb0ef369a96b94c648 Mon Sep 17 00:00:00 2001 From: Ashish Sharawat Date: Thu, 17 Oct 2019 21:31:45 +0530 Subject: [PATCH 031/195] Update localization/in.xml Co-Authored-By: LouiseBonnard <32565890+LouiseBonnard@users.noreply.github.com> --- localization/in.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/in.xml b/localization/in.xml index 527bc721c30c5..c88b64cc03ea7 100644 --- a/localization/in.xml +++ b/localization/in.xml @@ -13,7 +13,7 @@ - + From e14026057e8e4a69c1958f022dcaf0e40acc0ded Mon Sep 17 00:00:00 2001 From: Ashish Sharawat Date: Thu, 17 Oct 2019 21:31:56 +0530 Subject: [PATCH 032/195] Update localization/in.xml Co-Authored-By: LouiseBonnard <32565890+LouiseBonnard@users.noreply.github.com> --- localization/in.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/in.xml b/localization/in.xml index c88b64cc03ea7..7517733fcdb5b 100644 --- a/localization/in.xml +++ b/localization/in.xml @@ -11,7 +11,7 @@ - + From ece7e5177eb0e4e8411b809195c177636c737ade Mon Sep 17 00:00:00 2001 From: Tomas Ilginis Date: Mon, 21 Oct 2019 09:26:29 +0300 Subject: [PATCH 033/195] changes icons --- src/Adapter/Kpi/AbandonedCartKpi.php | 4 ++-- src/Adapter/Kpi/AverageOrderValueKpi.php | 4 ++-- src/Adapter/Kpi/ConversionRateKpi.php | 2 +- src/Adapter/Kpi/NetProfitPerVisitKpi.php | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Adapter/Kpi/AbandonedCartKpi.php b/src/Adapter/Kpi/AbandonedCartKpi.php index d1b63514ee68a..fe6a40d7346bf 100644 --- a/src/Adapter/Kpi/AbandonedCartKpi.php +++ b/src/Adapter/Kpi/AbandonedCartKpi.php @@ -45,8 +45,8 @@ public function render() $helper = new HelperKpi(); $helper->id = 'box-carts'; - $helper->icon = 'shopping_cart'; - $helper->color = 'color2'; + $helper->icon = 'remove_shopping_cart'; + $helper->color = 'color1'; $helper->title = $translator->trans('Abandoned Carts', [], 'Admin.Global'); $helper->subtitle = $translator->trans('Today', [], 'Admin.Global'); $helper->href = Context::getContext()->link->getAdminLink('AdminCarts') . '&action=filterOnlyAbandonedCarts'; diff --git a/src/Adapter/Kpi/AverageOrderValueKpi.php b/src/Adapter/Kpi/AverageOrderValueKpi.php index 31c98a142adaf..a1c969c6191a6 100644 --- a/src/Adapter/Kpi/AverageOrderValueKpi.php +++ b/src/Adapter/Kpi/AverageOrderValueKpi.php @@ -45,8 +45,8 @@ public function render() $helper = new HelperKpi(); $helper->id = 'box-average-order'; - $helper->icon = 'money'; - $helper->color = 'color3'; + $helper->icon = 'account_balance_wallet'; + $helper->color = 'color1'; $helper->title = $translator->trans('Average Order Value', [], 'Admin.Global'); $helper->subtitle = $translator->trans('30 days', [], 'Admin.Global'); diff --git a/src/Adapter/Kpi/ConversionRateKpi.php b/src/Adapter/Kpi/ConversionRateKpi.php index fa318c588fed4..d903f88643e10 100644 --- a/src/Adapter/Kpi/ConversionRateKpi.php +++ b/src/Adapter/Kpi/ConversionRateKpi.php @@ -45,7 +45,7 @@ public function render() $helper = new HelperKpi(); $helper->id = 'box-conversion-rate'; - $helper->icon = 'show_chart'; + $helper->icon = 'assessment'; $helper->color = 'color1'; $helper->title = $translator->trans('Conversion Rate', [], 'Admin.Global'); $helper->subtitle = $translator->trans('30 days', [], 'Admin.Global'); diff --git a/src/Adapter/Kpi/NetProfitPerVisitKpi.php b/src/Adapter/Kpi/NetProfitPerVisitKpi.php index 09d9e42481b06..97c5bdc66e80a 100644 --- a/src/Adapter/Kpi/NetProfitPerVisitKpi.php +++ b/src/Adapter/Kpi/NetProfitPerVisitKpi.php @@ -45,8 +45,8 @@ public function render() $helper = new HelperKpi(); $helper->id = 'box-net-profit-visit'; - $helper->icon = 'person'; - $helper->color = 'color4'; + $helper->icon = 'account_box'; + $helper->color = 'color1'; $helper->title = $translator->trans('Net Profit per Visit', [], 'Admin.Orderscustomers.Feature'); $helper->subtitle = $translator->trans('30 days', [], 'Admin.Orderscustomers.Feature'); From 7e8b69ae2a0747869b7ee86f8cf601f1faeabbf4 Mon Sep 17 00:00:00 2001 From: Tomas Ilginis Date: Mon, 21 Oct 2019 10:12:11 +0300 Subject: [PATCH 034/195] adds specific kpis layout and colors on orders page only --- .../scss/components/layout/_kpi.scss | 32 ++++++++++++++----- .../new-theme/template/helpers/kpi/kpi.tpl | 6 ++-- .../Admin/Sell/Order/Order/index.html.twig | 2 +- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/admin-dev/themes/new-theme/scss/components/layout/_kpi.scss b/admin-dev/themes/new-theme/scss/components/layout/_kpi.scss index 41e4d035b803d..654bd6151fa21 100644 --- a/admin-dev/themes/new-theme/scss/components/layout/_kpi.scss +++ b/admin-dev/themes/new-theme/scss/components/layout/_kpi.scss @@ -33,43 +33,59 @@ } &.-color2 { - > .value, + .value, > .material-icons { color: $danger; } } &.-color3 { - > .value, + .value, > .material-icons { color: $success; } } - > .title, - > .subtitle, - > .value { + .title, + .subtitle, + .value { font-size: 0.75rem; padding-left: .125rem; color: $gray-dark; } - > .title { + .title { display: inline-block; } - > .subtitle { + .subtitle { text-transform: uppercase; color: $gray-medium; display: block; } - > .value { + .value { font-size: 1.25rem; color: $primary; display: block; } } +// specific design for orders page. If design tends to repeat use this for all kpi's in all pages +.orders-kpi { + .kpi-content { + .kpi-description { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: flex-end; + + .value { + color: $gray-dark; + } + } + } +} + .container-fluid { > .kpi-container { padding-left: 1rem; diff --git a/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl b/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl index 9b162e656fe3a..4e4e7c3f5821b 100644 --- a/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl +++ b/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl @@ -39,8 +39,10 @@

{/if} {$title|escape} - {$subtitle|escape} - {$value|escape|replace:'&':'&'} +
+
{$subtitle|escape}
+
{$value|escape|replace:'&':'&'}
+
{if isset($href) && $href} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/index.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/index.html.twig index 1a426f5775267..d50e91a9c6201 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/index.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/index.html.twig @@ -32,7 +32,7 @@
-
+
{{ render(controller( 'PrestaShopBundle:Admin\\Common:renderKpiRow', { 'kpiRow': orderKpi } From 167bae9f46bbd1f1639d9b919f0132fb3438de58 Mon Sep 17 00:00:00 2001 From: laurynas Sedys Date: Mon, 21 Oct 2019 15:44:00 +0300 Subject: [PATCH 035/195] Changed the context variable passed to bindSwapChange(context) The context variable is used inside bindSwapSave(context) function to find the selected values. Previously the variable passed to the function was referring to the button inside the .click() function. Now new variable "swap_container" is passed which refers to the found swap container. --- js/admin.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/admin.js b/js/admin.js index 5aaab57b8db92..aa5e6ad21cc12 100644 --- a/js/admin.js +++ b/js/admin.js @@ -899,6 +899,7 @@ $(document).ready(function() }); $('.swap-container').each(function() { + var swap_container = this; /** make sure that all the swap id is present in the dom to prevent mistake **/ if (typeof $('.addSwap', this) !== undefined && typeof $(".removeSwap", this) !== undefined && typeof $('.selectedSwap', this) !== undefined && typeof $('.availableSwap', this) !== undefined) @@ -907,7 +908,7 @@ $(document).ready(function() bindSwapButton('remove', 'selected', 'available', this); $('button:submit').click(function() { - bindSwapSave(this); + bindSwapSave(swap_container); }); } }); From a87cbf6b03b12af16219b06a5b451951cac0f37a Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Wed, 16 Oct 2019 16:55:09 +0200 Subject: [PATCH 036/195] Implements CQRS on Notifications --- ...EmployeeNotificationLastElementHandler.php | 47 ++++ .../GetNotificationLastElementsHandler.php | 77 ++++++ ...EmployeeNotificationLastElementCommand.php | 61 +++++ ...tionLastElementCommandHandlerInterface.php | 40 +++ .../Exception/NotificationException.php | 36 +++ .../Query/GetNotificationLastElements.php | 61 +++++ ...tificationLastElementsHandlerInterface.php | 43 ++++ .../QueryResult/NotificationResult.php | 233 ++++++++++++++++++ .../QueryResult/NotificationsResult.php | 88 +++++++ .../QueryResult/NotificationsResults.php | 86 +++++++ .../Domain/Notification/ValueObject/Type.php | 85 +++++++ .../Controller/Admin/CommonController.php | 19 +- .../config/services/adapter/notification.yml | 16 ++ 13 files changed, 888 insertions(+), 4 deletions(-) create mode 100644 src/Adapter/Notification/CommandHandler/UpdateEmployeeNotificationLastElementHandler.php create mode 100644 src/Adapter/Notification/QueryHandler/GetNotificationLastElementsHandler.php create mode 100644 src/Core/Domain/Notification/Command/UpdateEmployeeNotificationLastElementCommand.php create mode 100644 src/Core/Domain/Notification/CommandHandler/UpdateEmployeeNotificationLastElementCommandHandlerInterface.php create mode 100644 src/Core/Domain/Notification/Exception/NotificationException.php create mode 100644 src/Core/Domain/Notification/Query/GetNotificationLastElements.php create mode 100644 src/Core/Domain/Notification/QueryHandler/GetNotificationLastElementsHandlerInterface.php create mode 100644 src/Core/Domain/Notification/QueryResult/NotificationResult.php create mode 100644 src/Core/Domain/Notification/QueryResult/NotificationsResult.php create mode 100644 src/Core/Domain/Notification/QueryResult/NotificationsResults.php create mode 100644 src/Core/Domain/Notification/ValueObject/Type.php create mode 100644 src/PrestaShopBundle/Resources/config/services/adapter/notification.yml diff --git a/src/Adapter/Notification/CommandHandler/UpdateEmployeeNotificationLastElementHandler.php b/src/Adapter/Notification/CommandHandler/UpdateEmployeeNotificationLastElementHandler.php new file mode 100644 index 0000000000000..a36558735a869 --- /dev/null +++ b/src/Adapter/Notification/CommandHandler/UpdateEmployeeNotificationLastElementHandler.php @@ -0,0 +1,47 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Adapter\Notification\CommandHandler; + +use Notification; +use PrestaShop\PrestaShop\Core\Domain\Notification\Command\UpdateEmployeeNotificationLastElementCommand; +use PrestaShop\PrestaShop\Core\Domain\Notification\CommandHandler\UpdateEmployeeNotificationLastElementCommandHandlerInterface; + +/** + * Handle update employee's last notification element of a given type + * + * @internal + */ +final class UpdateEmployeeNotificationLastElementHandler implements UpdateEmployeeNotificationLastElementCommandHandlerInterface +{ + /** + * @param UpdateEmployeeNotificationLastElementCommand $command + */ + public function handle(UpdateEmployeeNotificationLastElementCommand $command) + { + (new Notification())->updateEmployeeLastElement($command->getType()); + } +} diff --git a/src/Adapter/Notification/QueryHandler/GetNotificationLastElementsHandler.php b/src/Adapter/Notification/QueryHandler/GetNotificationLastElementsHandler.php new file mode 100644 index 0000000000000..669a343ffdd06 --- /dev/null +++ b/src/Adapter/Notification/QueryHandler/GetNotificationLastElementsHandler.php @@ -0,0 +1,77 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Adapter\Notification\QueryHandler; + +use Notification; +use PrestaShop\PrestaShop\Core\Domain\Notification\Query\GetNotificationLastElements; +use PrestaShop\PrestaShop\Core\Domain\Notification\QueryHandler\GetNotificationLastElementsHandlerInterface; +use PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult\NotificationResult; +use PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult\NotificationsResult; +use PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult\NotificationsResults; + +/** + * Get employee last notification elements + * + * @internal + */ +final class GetNotificationLastElementsHandler implements GetNotificationLastElementsHandlerInterface +{ + /** + * @param GetNotificationLastElements $query + * + * @return NotificationsResults + * + * {@inheritdoc} + */ + public function handle(GetNotificationLastElements $query): NotificationsResults + { + $elements = (new Notification())->getLastElements(); + $results = []; + foreach ($elements as $type => $notifications) { + $notificationsResult = []; + foreach ($notifications['results'] as $notification) { + $notificationsResult[] = new NotificationResult( + $notification['id_order'], + $notification['id_customer'], + $notification['customer_name'], + $notification['id_customer_message'], + $notification['id_customer_thread'], + $notification['customer_view_url'], + $notification['total_paid'], + $notification['carrier'], + $notification['iso_code'], + $notification['company'], + $notification['status'], + $notification['date_add'] + ); + } + $results[] = new NotificationsResult($type, $notifications['total'], $notificationsResult); + } + + return new NotificationsResults($results); + } +} diff --git a/src/Core/Domain/Notification/Command/UpdateEmployeeNotificationLastElementCommand.php b/src/Core/Domain/Notification/Command/UpdateEmployeeNotificationLastElementCommand.php new file mode 100644 index 0000000000000..82885ca5d2909 --- /dev/null +++ b/src/Core/Domain/Notification/Command/UpdateEmployeeNotificationLastElementCommand.php @@ -0,0 +1,61 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\Command; + +use PrestaShop\PrestaShop\Core\Domain\Notification\Exception\TypeException; +use PrestaShop\PrestaShop\Core\Domain\Notification\ValueObject\Type; + +/** + * Updates the last notification element from a given type seen by the employee + */ +class UpdateEmployeeNotificationLastElementCommand +{ + /** + * @var Type + */ + private $type; + + /** + * UpdateEmployeeNotificationLastElementCommand constructor. + * + * @param string $type + * + * @throws TypeException + */ + public function __construct(string $type) + { + $this->type = new Type($type); + } + + /** + * @return Type + */ + public function getType() + { + return $this->type; + } +} diff --git a/src/Core/Domain/Notification/CommandHandler/UpdateEmployeeNotificationLastElementCommandHandlerInterface.php b/src/Core/Domain/Notification/CommandHandler/UpdateEmployeeNotificationLastElementCommandHandlerInterface.php new file mode 100644 index 0000000000000..84643c367cd22 --- /dev/null +++ b/src/Core/Domain/Notification/CommandHandler/UpdateEmployeeNotificationLastElementCommandHandlerInterface.php @@ -0,0 +1,40 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\CommandHandler; + +use PrestaShop\PrestaShop\Core\Domain\Notification\Command\UpdateEmployeeNotificationLastElementCommand; + +/** + * Interface for service that handles ACK employee notifications last elements + */ +interface UpdateEmployeeNotificationLastElementCommandHandlerInterface +{ + /** + * @param UpdateEmployeeNotificationLastElementCommand $command + */ + public function handle(UpdateEmployeeNotificationLastElementCommand $command); +} diff --git a/src/Core/Domain/Notification/Exception/NotificationException.php b/src/Core/Domain/Notification/Exception/NotificationException.php new file mode 100644 index 0000000000000..c7525ed6473ff --- /dev/null +++ b/src/Core/Domain/Notification/Exception/NotificationException.php @@ -0,0 +1,36 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\Exception; + +use PrestaShop\PrestaShop\Core\Domain\Exception\DomainException; + +/** + * Base exception for Notification sub-domain + */ +class NotificationException extends DomainException +{ +} diff --git a/src/Core/Domain/Notification/Query/GetNotificationLastElements.php b/src/Core/Domain/Notification/Query/GetNotificationLastElements.php new file mode 100644 index 0000000000000..6c96804ebce34 --- /dev/null +++ b/src/Core/Domain/Notification/Query/GetNotificationLastElements.php @@ -0,0 +1,61 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\Query; + +use PrestaShop\PrestaShop\Core\Domain\Employee\Exception\InvalidEmployeeIdException; +use PrestaShop\PrestaShop\Core\Domain\Employee\ValueObject\EmployeeId; + +/** + * This Query return the last Notifications elements + */ +class GetNotificationLastElements +{ + /** + * @var EmployeeId + */ + private $employeeId; + + /** + * GetNotificationLastElements constructor. + * + * @param $employeeId + * + * @throws InvalidEmployeeIdException + */ + public function __construct(int $employeeId) + { + $this->employeeId = new EmployeeId($employeeId); + } + + /** + * @return EmployeeId + */ + public function getEmployeeId(): EmployeeId + { + return $this->employeeId; + } +} diff --git a/src/Core/Domain/Notification/QueryHandler/GetNotificationLastElementsHandlerInterface.php b/src/Core/Domain/Notification/QueryHandler/GetNotificationLastElementsHandlerInterface.php new file mode 100644 index 0000000000000..97b15b63b8bb9 --- /dev/null +++ b/src/Core/Domain/Notification/QueryHandler/GetNotificationLastElementsHandlerInterface.php @@ -0,0 +1,43 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\QueryHandler; + +use PrestaShop\PrestaShop\Core\Domain\Notification\Query\GetNotificationLastElements; +use PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult\NotificationsResults; + +/** + * Interface for service that handles notifications last elements request + */ +interface GetNotificationLastElementsHandlerInterface +{ + /** + * @param GetNotificationLastElements $query + * + * @return NotificationsResults + */ + public function handle(GetNotificationLastElements $query): NotificationsResults; +} diff --git a/src/Core/Domain/Notification/QueryResult/NotificationResult.php b/src/Core/Domain/Notification/QueryResult/NotificationResult.php new file mode 100644 index 0000000000000..d1d5c09d812bd --- /dev/null +++ b/src/Core/Domain/Notification/QueryResult/NotificationResult.php @@ -0,0 +1,233 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult; + +/** + * NotificationResult contains the notification data + */ +class NotificationResult +{ + /** + * @var int + */ + private $orderId; + + /** + * @var int + */ + private $customerId; + + /** + * @var string + */ + private $customerName; + + /** + * @var int + */ + private $customerMessageId; + + /** + * @var int + */ + private $customerThreadId; + + /** + * @var string + */ + private $customerViewUrl; + + /** + * @var string + */ + private $totalPaid; + + /** + * @var string + */ + private $carrier; + + /** + * @var string + */ + private $isoCode; + + /** + * @var string + */ + private $company; + + /** + * @var string + */ + private $status; + + /** + * @var string + */ + private $dateAdd; + + /** + * NotificationResult constructor. + * + * @param int $orderId + * @param int $customerId + * @param string $customerName + * @param int $customerMessageId + * @param int $customerThreadId + * @param string $customerViewUrl + * @param string $totalPaid + * @param string $carrier + * @param string $isoCode + * @param string $company + * @param string $status + * @param string $dateAdd + */ + public function __construct( + int $orderId, + int $customerId, + string $customerName, + int $customerMessageId, + int $customerThreadId, + string $customerViewUrl, + string $totalPaid, + string $carrier, + string $isoCode, + string $company, + string $status, + string $dateAdd + ) { + $this->orderId = $orderId; + $this->customerId = $customerId; + $this->customerName = $customerName; + $this->customerMessageId = $customerMessageId; + $this->customerThreadId = $customerThreadId; + $this->customerViewUrl = $customerViewUrl; + $this->totalPaid = $totalPaid; + $this->carrier = $carrier; + $this->isoCode = $isoCode; + $this->company = $company; + $this->status = $status; + $this->dateAdd = $dateAdd; + } + + /** + * @return int + */ + public function getOrderId(): int + { + return $this->orderId; + } + + /** + * @return int + */ + public function getCustomerId(): int + { + return $this->customerId; + } + + /** + * @return string + */ + public function getCustomerName(): string + { + return $this->customerName; + } + + /** + * @return int + */ + public function getCustomerMessageId(): int + { + return $this->customerMessageId; + } + + /** + * @return int + */ + public function getCustomerThreadId(): int + { + return $this->customerThreadId; + } + + /** + * @return string + */ + public function getCustomerViewUrl(): string + { + return $this->customerViewUrl; + } + + /** + * @return string + */ + public function getTotalPaid(): string + { + return $this->totalPaid; + } + + /** + * @return string + */ + public function getCarrier(): string + { + return $this->carrier; + } + + /** + * @return string + */ + public function getIsoCode(): string + { + return $this->isoCode; + } + + /** + * @return string + */ + public function getCompany(): string + { + return $this->company; + } + + /** + * @return string + */ + public function getStatus(): string + { + return $this->status; + } + + /** + * @return string + */ + public function getDateAdd(): string + { + return $this->dateAdd; + } +} diff --git a/src/Core/Domain/Notification/QueryResult/NotificationsResult.php b/src/Core/Domain/Notification/QueryResult/NotificationsResult.php new file mode 100644 index 0000000000000..e33a89bac2d67 --- /dev/null +++ b/src/Core/Domain/Notification/QueryResult/NotificationsResult.php @@ -0,0 +1,88 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult; + +use PrestaShop\PrestaShop\Core\Domain\Notification\Exception\TypeException; +use PrestaShop\PrestaShop\Core\Domain\Notification\ValueObject\Type; + +/** + * NotificationsResult contains a collection of Notifications as well as their type and the total + */ +class NotificationsResult +{ + /** + * @var Type + */ + private $type; + + /** + * @var int + */ + private $total; + + /** + * @var NotificationResult[] + */ + private $notifications = []; + + /** + * NotificationsResult constructor. + * + * @param string $type + * @param int $total + * @param NotificationResult[] $notifications + * + * @throws TypeException + */ + public function __construct(string $type, int $total, array $notifications) + { + $this->type = new Type($type); + $this->total = $total; + $this->notifications = $notifications; + } + + public function getType(): Type + { + return $this->type; + } + + /** + * @return int + */ + public function getTotal(): int + { + return $this->total; + } + + /** + * @return NotificationResult[] + */ + public function getNotifications(): array + { + return $this->notifications; + } +} diff --git a/src/Core/Domain/Notification/QueryResult/NotificationsResults.php b/src/Core/Domain/Notification/QueryResult/NotificationsResults.php new file mode 100644 index 0000000000000..abc1a76c22554 --- /dev/null +++ b/src/Core/Domain/Notification/QueryResult/NotificationsResults.php @@ -0,0 +1,86 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult; + +/** + * NotificationsResults is a collection of NotificationsResult + */ +class NotificationsResults +{ + /** + * @var NotificationsResult[] + */ + private $notifications = []; + + /** + * NotificationsResults constructor. + * + * @param NotificationsResult[] $notifications + */ + public function __construct(array $notifications) + { + $this->notifications = $notifications; + } + + /** + * @return NotificationsResult[] + */ + public function getNotificationsResults() + { + return $this->notifications; + } + + public function getNotificationsResultsForJS() + { + $response = []; + foreach ($this->getNotificationsResults() as $element) { + $notifications = []; + foreach ($element->getNotifications() as $notification) { + $notifications[] = [ + 'id_order' => $notification->getOrderId(), + 'id_customer' => $notification->getCustomerId(), + 'id_customer_message' => $notification->getCustomerMessageId(), + 'id_customer_thread' => $notification->getCustomerThreadId(), + 'total_paid' => $notification->getTotalPaid(), + 'carrier' => $notification->getCarrier(), + 'iso_code' => $notification->getIsoCode(), + 'company' => $notification->getCompany(), + 'status' => $notification->getStatus(), + 'customer_name' => $notification->getCustomerName(), + 'date_add' => $notification->getDateAdd(), + 'customer_view_url' => $notification->getCustomerViewUrl(), + ]; + } + $response[($element->getType()->getValue())] = [ + 'total' => $element->getTotal(), + 'results' => $notifications, + ]; + } + + return $response; + } +} diff --git a/src/Core/Domain/Notification/ValueObject/Type.php b/src/Core/Domain/Notification/ValueObject/Type.php new file mode 100644 index 0000000000000..bc839a7a9d1c3 --- /dev/null +++ b/src/Core/Domain/Notification/ValueObject/Type.php @@ -0,0 +1,85 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Notification\ValueObject; + +use PrestaShop\PrestaShop\Core\Domain\Notification\Exception\NotificationException; + +/** + * Notifications types + */ +class Type +{ + const ORDER = 'order'; + + const CUSTOMER = 'customer'; + + const CUSTOMER_MESSAGE = 'customer_message'; + + /** + * @var string + */ + private $type; + + /** + * @param string $type + * + * @throws NotificationException + */ + public function __construct(string $type) + { + $this->assertIsValidType($type); + + $this->type = $type; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->type; + } + + /** + * @param string $type + * + * @throws NotificationException + */ + private function assertIsValidType(string $type) + { + $allowedTypes = [self::ORDER, self::CUSTOMER, self::CUSTOMER_MESSAGE]; + if (!in_array($type, $allowedTypes)) { + throw new NotificationException( + sprintf( + 'Type %s is invalid. Supported types are: %s', + var_export($type, true), + implode(', ', $allowedTypes) + ) + ); + } + } +} diff --git a/src/PrestaShopBundle/Controller/Admin/CommonController.php b/src/PrestaShopBundle/Controller/Admin/CommonController.php index 9166f6b8a2ad2..0e65d1b3534c3 100644 --- a/src/PrestaShopBundle/Controller/Admin/CommonController.php +++ b/src/PrestaShopBundle/Controller/Admin/CommonController.php @@ -26,9 +26,14 @@ namespace PrestaShopBundle\Controller\Admin; +use Context; use PrestaShop\PrestaShop\Adapter\Module\AdminModuleDataProvider; use PrestaShop\PrestaShop\Core\Addon\AddonsCollection; use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder; +use PrestaShop\PrestaShop\Core\Domain\Notification\Command\UpdateEmployeeNotificationLastElementCommand; +use PrestaShop\PrestaShop\Core\Domain\Notification\Exception\TypeException; +use PrestaShop\PrestaShop\Core\Domain\Notification\Query\GetNotificationLastElements; +use PrestaShop\PrestaShop\Core\Domain\Notification\QueryResult\NotificationsResults; use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\GridDefinitionFactoryInterface; use PrestaShop\PrestaShop\Core\Grid\Definition\GridDefinitionInterface; use PrestaShop\PrestaShop\Core\Kpi\Row\KpiRowInterface; @@ -56,20 +61,26 @@ class CommonController extends FrameworkBundleAdminController */ public function notificationsAction() { - // TODO: Use CQRS - return new JsonResponse((new \Notification())->getLastElements()); + $employeeId = Context::getContext()->employee->id; + /** @var NotificationsResults $elements */ + $elements = $this->getQueryBus()->handle(new GetNotificationLastElements($employeeId)); + + return new JsonResponse($elements->getNotificationsResultsForJS()); } /** * Update the last time a notification type has been seen. * * @param Request $request + * + * @throws TypeException */ public function notificationsAckAction(Request $request) { $type = $request->request->get('type'); - // TODO: Use CQRS - return new JsonResponse((new \Notification())->updateEmployeeLastElement($type)); + $this->getCommandBus()->handle(new UpdateEmployeeNotificationLastElementCommand($type)); + + return new JsonResponse(true); } /** diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/notification.yml b/src/PrestaShopBundle/Resources/config/services/adapter/notification.yml new file mode 100644 index 0000000000000..7c019e7016b06 --- /dev/null +++ b/src/PrestaShopBundle/Resources/config/services/adapter/notification.yml @@ -0,0 +1,16 @@ +services: + _defaults: + public: true + + + prestashop.adapter.notification.command_handler.update_employee_notification_last_element_handler: + class: 'PrestaShop\PrestaShop\Adapter\Notification\CommandHandler\UpdateEmployeeNotificationLastElementHandler' + tags: + - name: tactician.handler + command: 'PrestaShop\PrestaShop\Core\Domain\Notification\Command\UpdateEmployeeNotificationLastElementCommand' + + prestashop.adapter.notification.query_handler.get_notification_last_elements_handler: + class: 'PrestaShop\PrestaShop\Adapter\Notification\QueryHandler\GetNotificationLastElementsHandler' + tags: + - name: tactician.handler + command: 'PrestaShop\PrestaShop\Core\Domain\Notification\Query\GetNotificationLastElements' From 7a27234faf74e0e54c30617839808aae388dff77 Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Tue, 22 Oct 2019 11:33:30 +0200 Subject: [PATCH 037/195] Improve wording on order confirmation email --- classes/PaymentModule.php | 2 +- mails/themes/modern/core/order_conf.html.twig | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index 703a1cce47497..76da38c2e6606 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -615,7 +615,7 @@ public function validateOrder( '{order_name}' => $order->getUniqReference(), '{date}' => Tools::displayDate(date('Y-m-d H:i:s'), null, 1), '{carrier}' => ($virtual_product || !isset($carrier->name)) ? $this->trans('No carrier', array(), 'Admin.Payment.Notification') : $carrier->name, - '{payment}' => Tools::substr($order->payment, 0, 255), + '{payment}' => Tools::substr($order->payment, 0, 255) . ($order->hasBeenPaid() ? '' : ' ' . $this->trans('(waiting for validation)', array(), 'Emails.Body')), '{products}' => $product_list_html, '{products_txt}' => $product_list_txt, '{discounts}' => $cart_rules_list_html, diff --git a/mails/themes/modern/core/order_conf.html.twig b/mails/themes/modern/core/order_conf.html.twig index 1c470d94a6883..26d420e748b94 100644 --- a/mails/themes/modern/core/order_conf.html.twig +++ b/mails/themes/modern/core/order_conf.html.twig @@ -50,7 +50,7 @@
- {{ 'Thank you for shopping on [1]{shop_name}[/1]!'|trans({'[1]': '', '[/1]': ''}, 'Emails.Body', locale)|raw }} + {{ 'Thank you for your order on [1]{shop_name}[/1]!'|trans({'[1]': '', '[/1]': ''}, 'Emails.Body', locale)|raw }}
@@ -379,7 +379,7 @@ - {{ 'Total paid'|trans({}, 'Emails.Body', locale)|raw }} + {{ 'Total'|trans({}, 'Emails.Body', locale)|raw }} {total_paid} From cbf8f03e79771f71bd2e1d6cc66758f63097af5c Mon Sep 17 00:00:00 2001 From: 202 ecommerce <202-ecommerce@users.noreply.github.com> Date: Tue, 22 Oct 2019 18:10:57 +0200 Subject: [PATCH 038/195] Fix notice since console include config.inc.php See also #15515 https://github.com/PrestaShop/PrestaShop/pull/15515#issuecomment-536637885 --- src/PrestaShopBundle/Command/ModuleCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Command/ModuleCommand.php b/src/PrestaShopBundle/Command/ModuleCommand.php index c8825072985f5..fb740991bf6d6 100644 --- a/src/PrestaShopBundle/Command/ModuleCommand.php +++ b/src/PrestaShopBundle/Command/ModuleCommand.php @@ -83,7 +83,7 @@ protected function init(InputInterface $input, OutputInterface $output) $this->translator = $this->getContainer()->get('translator'); $this->input = $input; $this->output = $output; - require $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; + require_once $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; /** @var LegacyContext $legacyContext */ $legacyContext = $this->getContainer()->get('prestashop.adapter.legacy.context'); //We need to have an employee or the module hooks don't work From 473424d2039daf231d80d51d776eee596e8e10d4 Mon Sep 17 00:00:00 2001 From: 202 ecommerce <202-ecommerce@users.noreply.github.com> Date: Wed, 23 Oct 2019 10:26:58 +0200 Subject: [PATCH 039/195] remove require config.inc.php on module cli --- src/PrestaShopBundle/Command/ModuleCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PrestaShopBundle/Command/ModuleCommand.php b/src/PrestaShopBundle/Command/ModuleCommand.php index fb740991bf6d6..0a80594844324 100644 --- a/src/PrestaShopBundle/Command/ModuleCommand.php +++ b/src/PrestaShopBundle/Command/ModuleCommand.php @@ -83,7 +83,6 @@ protected function init(InputInterface $input, OutputInterface $output) $this->translator = $this->getContainer()->get('translator'); $this->input = $input; $this->output = $output; - require_once $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; /** @var LegacyContext $legacyContext */ $legacyContext = $this->getContainer()->get('prestashop.adapter.legacy.context'); //We need to have an employee or the module hooks don't work From 3fc03fff475b6eb8c687433f0c1fdee8a34a4785 Mon Sep 17 00:00:00 2001 From: 202 ecommerce <202-ecommerce@users.noreply.github.com> Date: Wed, 23 Oct 2019 10:29:07 +0200 Subject: [PATCH 040/195] remove config.inc.php on theme command --- src/PrestaShopBundle/Command/ThemeEnablerCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PrestaShopBundle/Command/ThemeEnablerCommand.php b/src/PrestaShopBundle/Command/ThemeEnablerCommand.php index e94f97becb44f..bcf493d8aba9a 100644 --- a/src/PrestaShopBundle/Command/ThemeEnablerCommand.php +++ b/src/PrestaShopBundle/Command/ThemeEnablerCommand.php @@ -51,8 +51,6 @@ final class ThemeEnablerCommand extends ContainerAwareCommand */ protected function init(InputInterface $input, OutputInterface $output) { - require $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; - Context::getContext()->employee = new Employee(); } From 3c8846f49a5d11303dbccb4f7f8f3ea455ab4317 Mon Sep 17 00:00:00 2001 From: 202 ecommerce <202-ecommerce@users.noreply.github.com> Date: Wed, 23 Oct 2019 10:30:29 +0200 Subject: [PATCH 041/195] remove config.inc.php on generate mail command line --- src/PrestaShopBundle/Command/GenerateMailTemplatesCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PrestaShopBundle/Command/GenerateMailTemplatesCommand.php b/src/PrestaShopBundle/Command/GenerateMailTemplatesCommand.php index 7daf1ef8c7e17..6bf5208f26dd3 100644 --- a/src/PrestaShopBundle/Command/GenerateMailTemplatesCommand.php +++ b/src/PrestaShopBundle/Command/GenerateMailTemplatesCommand.php @@ -96,7 +96,6 @@ protected function execute(InputInterface $input, OutputInterface $output) */ private function initContext() { - require_once $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; /** @var LegacyContext $legacyContext */ $legacyContext = $this->getContainer()->get('prestashop.adapter.legacy.context'); //We need to have an employee or the module hooks don't work From 0af0179d3d62b482d4e07d656404d2688e7531a7 Mon Sep 17 00:00:00 2001 From: 202 ecommerce <202-ecommerce@users.noreply.github.com> Date: Wed, 23 Oct 2019 10:32:03 +0200 Subject: [PATCH 042/195] remove config.inc.php on sql update command line --- .../Command/AppendHooksListForSqlUpgradeFileCommand.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/PrestaShopBundle/Command/AppendHooksListForSqlUpgradeFileCommand.php b/src/PrestaShopBundle/Command/AppendHooksListForSqlUpgradeFileCommand.php index 33f25d70433f6..19a93de2c6ea9 100644 --- a/src/PrestaShopBundle/Command/AppendHooksListForSqlUpgradeFileCommand.php +++ b/src/PrestaShopBundle/Command/AppendHooksListForSqlUpgradeFileCommand.php @@ -113,8 +113,6 @@ protected function execute(InputInterface $input, OutputInterface $output) */ private function initContext() { - require_once $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; - /** @var LegacyContext $legacyContext */ $legacyContext = $this->getContainer()->get('prestashop.adapter.legacy.context'); //We need to have an employee or the listing hooks don't work From 8f53621c1be4a321afbc0530b4d7f84e25fbb7b6 Mon Sep 17 00:00:00 2001 From: 202 ecommerce <202-ecommerce@users.noreply.github.com> Date: Wed, 23 Oct 2019 10:33:12 +0200 Subject: [PATCH 043/195] remove config.inc.php on configuration command --- .../Command/AppendConfigurationFileHooksListCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PrestaShopBundle/Command/AppendConfigurationFileHooksListCommand.php b/src/PrestaShopBundle/Command/AppendConfigurationFileHooksListCommand.php index a5667dc85cb39..1af3ad88a2ebb 100644 --- a/src/PrestaShopBundle/Command/AppendConfigurationFileHooksListCommand.php +++ b/src/PrestaShopBundle/Command/AppendConfigurationFileHooksListCommand.php @@ -91,7 +91,6 @@ protected function execute(InputInterface $input, OutputInterface $output) */ private function initContext() { - require_once $this->getContainer()->get('kernel')->getRootDir() . '/../config/config.inc.php'; /** @var LegacyContext $legacyContext */ $legacyContext = $this->getContainer()->get('prestashop.adapter.legacy.context'); //We need to have an employee or the listing hooks don't work From 56c70ecccf2f6b2be3d64be00a9ccc1cf3a849fc Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 23 Oct 2019 12:47:48 +0300 Subject: [PATCH 044/195] Update admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Šarūnas Jonušas --- admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl b/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl index 4e4e7c3f5821b..aa56f8c85d4df 100644 --- a/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl +++ b/admin-dev/themes/new-theme/template/helpers/kpi/kpi.tpl @@ -40,7 +40,7 @@ {/if} {$title|escape}
-
{$subtitle|escape}
+
{$subtitle|escape}
{$value|escape|replace:'&':'&'}
From 5efe849151b8b948a0eaf293acb5af69e2291e66 Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Tue, 22 Oct 2019 17:42:51 +0200 Subject: [PATCH 045/195] Change wording in FO when there is specific price by quantity --- classes/controller/FrontController.php | 4 ++-- .../classic/templates/catalog/_partials/product-discounts.tpl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/controller/FrontController.php b/classes/controller/FrontController.php index 18f78f4537b9f..ee30a800bd904 100644 --- a/classes/controller/FrontController.php +++ b/classes/controller/FrontController.php @@ -1535,8 +1535,8 @@ public function getTemplateVarConfiguration() 'quantity_discount' => array( 'type' => ($quantity_discount_price) ? 'price' : 'discount', 'label' => ($quantity_discount_price) - ? $this->getTranslator()->trans('Price', array(), 'Shop.Theme.Catalog') - : $this->getTranslator()->trans('Discount', array(), 'Shop.Theme.Catalog'), + ? $this->getTranslator()->trans('Unit price', array(), 'Shop.Theme.Catalog') + : $this->getTranslator()->trans('Unit discount', array(), 'Shop.Theme.Catalog'), ), 'voucher_enabled' => (int) CartRule::isFeatureActive(), 'return_enabled' => (int) Configuration::get('PS_ORDER_RETURN'), diff --git a/themes/classic/templates/catalog/_partials/product-discounts.tpl b/themes/classic/templates/catalog/_partials/product-discounts.tpl index ef10542da32b0..9530ee44c0230 100644 --- a/themes/classic/templates/catalog/_partials/product-discounts.tpl +++ b/themes/classic/templates/catalog/_partials/product-discounts.tpl @@ -39,7 +39,7 @@ {$quantity_discount.quantity} {$quantity_discount.discount} - {l s='Up to %discount%' d='Shop.Theme.Catalog' sprintf=['%discount%' => $quantity_discount.save]} + {$quantity_discount.save} {/foreach} From 693b11283517c8a95ed591de7eee3d50aa7434a9 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Wed, 23 Oct 2019 14:26:45 +0200 Subject: [PATCH 046/195] Fixed "Attempted to call an undefined method named 'formatPrice' of class 'PrestaShop\PrestaShop\Core\Localization\CLDR\Locale'" --- .../Presenter/Order/OrderDetailLazyArray.php | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/Adapter/Presenter/Order/OrderDetailLazyArray.php b/src/Adapter/Presenter/Order/OrderDetailLazyArray.php index e3dc6d34caef7..76b4628976534 100644 --- a/src/Adapter/Presenter/Order/OrderDetailLazyArray.php +++ b/src/Adapter/Presenter/Order/OrderDetailLazyArray.php @@ -29,12 +29,11 @@ use Cart; use Configuration; use Context; +use Currency; use HistoryController; use Order; -use PrestaShop\PrestaShop\Adapter\ContainerFinder; use PrestaShop\PrestaShop\Adapter\Presenter\AbstractLazyArray; use PrestaShop\PrestaShop\Core\Localization\Locale; -use PrestaShop\PrestaShop\Core\Localization\Locale\Repository as LocaleRepository; use PrestaShopBundle\Translation\TranslatorComponent; use PrestaShopException; use Tools; @@ -63,34 +62,18 @@ class OrderDetailLazyArray extends AbstractLazyArray /** * OrderDetailLazyArray constructor. + * + * @param Order $order */ public function __construct(Order $order) { $this->order = $order; $this->context = Context::getContext(); $this->translator = Context::getContext()->getTranslator(); - $this->locale = $this->getCldrLocaleRepository()->getLocale( - $this->context->language->getLocale() - ); + $this->locale = $this->context->getCurrentLocale(); parent::__construct(); } - /** - * @return LocaleRepository - * - * @throws \Exception - */ - protected function getCldrLocaleRepository() - { - $containerFinder = new ContainerFinder($this->context); - $container = $containerFinder->getContainer(); - - /** @var LocaleRepository $localeRepoCLDR */ - $localeRepoCLDR = $container->get('prestashop.core.localization.cldr.locale_repository'); - - return $localeRepoCLDR; - } - /** * @arrayAccess * @@ -249,7 +232,7 @@ public function getShipping() (!$order->getTaxCalculationMethod()) ? $shipping['shipping_cost_tax_excl'] : $shipping['shipping_cost_tax_incl']; $orderShipping[$shippingId]['shipping_cost'] = - ($shippingCost > 0) ? $this->locale->formatPrice($shippingCost, (\Currency::getIsoCodeById((int) $order->id_currency))) + ($shippingCost > 0) ? $this->locale->formatPrice($shippingCost, (Currency::getIsoCodeById((int) $order->id_currency))) : $this->translator->trans('Free', array(), 'Shop.Theme.Checkout'); $tracking_line = '-'; From 3d9b83c4f1d2e3c73dedf01133d58895dbbea834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 16:59:53 +0300 Subject: [PATCH 047/195] Implement Order header --- .../new-theme/scss/components/_font.scss | 3 ++ .../GetOrderForViewingHandler.php | 1 + .../Order/QueryResult/OrderForViewing.php | 17 +++++++ .../Admin/Sell/Order/OrderController.php | 2 +- .../routing/admin/sell/orders/orders.yml | 1 + .../Order/Order/Blocks/View/header.html.twig | 51 +++++++++++++++++++ .../Order/Blocks/View/products.html.twig | 2 +- .../Admin/Sell/Order/Order/view.html.twig | 4 ++ 8 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 admin-dev/themes/new-theme/scss/components/_font.scss create mode 100644 src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig diff --git a/admin-dev/themes/new-theme/scss/components/_font.scss b/admin-dev/themes/new-theme/scss/components/_font.scss new file mode 100644 index 0000000000000..c62bdb23a855b --- /dev/null +++ b/admin-dev/themes/new-theme/scss/components/_font.scss @@ -0,0 +1,3 @@ +.font-size-100 { + font-size: 100%; +} diff --git a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php index 262fee3c68463..ed2c959e03b6a 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php @@ -148,6 +148,7 @@ public function handle(GetOrderForViewing $query): OrderForViewing (bool) $order->valid, $order->hasInvoice(), $order->hasBeenDelivered(), + new DateTimeImmutable($order->date_add), $this->getOrderCustomer($order), $this->getOrderShippingAddress($order), $this->getOrderInvoiceAddress($order), diff --git a/src/Core/Domain/Order/QueryResult/OrderForViewing.php b/src/Core/Domain/Order/QueryResult/OrderForViewing.php index defb2765e4899..a822c31626a1e 100644 --- a/src/Core/Domain/Order/QueryResult/OrderForViewing.php +++ b/src/Core/Domain/Order/QueryResult/OrderForViewing.php @@ -26,6 +26,8 @@ namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; +use DateTimeImmutable; + /** * Contains data about order for viewing */ @@ -130,6 +132,11 @@ class OrderForViewing */ private $discounts; + /** + * @var DateTimeImmutable + */ + private $createdAt; + public function __construct( int $orderId, int $currencyId, @@ -139,6 +146,7 @@ public function __construct( bool $isValid, bool $hasInvoice, bool $isDelivered, + DateTimeImmutable $createdAt, OrderCustomerForViewing $customer, OrderShippingAddressForViewing $shippingAddress, OrderInvoiceAddressForViewing $invoiceAddress, @@ -172,6 +180,7 @@ public function __construct( $this->isTaxIncluded = $isTaxIncluded; $this->hasInvoice = $hasInvoice; $this->discounts = $discounts; + $this->createdAt = $createdAt; } /** @@ -333,4 +342,12 @@ public function getDiscounts(): OrderDiscountsForViewing { return $this->discounts; } + + /** + * @return DateTimeImmutable + */ + public function getCreatedAt(): DateTimeImmutable + { + return $this->createdAt; + } } diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php index c7f62dc538faf..9e8a8e925a3cd 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php @@ -278,7 +278,7 @@ public function viewAction(int $orderId): Response $updateOrderProductForm = $this->createForm(UpdateProductInOrderType::class); return $this->render('@PrestaShop/Admin/Sell/Order/Order/view.html.twig', [ - 'showContentHeader' => false, + 'showContentHeader' => true, 'orderForViewing' => $orderForViewing, 'addOrderCartRuleForm' => $addOrderCartRuleForm->createView(), 'updateOrderStatusForm' => $updateOrderStatusForm->createView(), diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml index 4f7e00531c2c5..2bfa46fc80402 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml @@ -55,6 +55,7 @@ admin_orders_view: path: /{orderId}/view defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Order:view + _legacy_controller: AdminOrders admin_orders_add_cart_rule: path: /{orderId}/cart-rules diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig new file mode 100644 index 0000000000000..b90d53056285b --- /dev/null +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig @@ -0,0 +1,51 @@ +{#** + * 2007-2019 PrestaShop SA and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to https://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + *#} + +
+

+ #{{ orderForViewing.id }} + {{ orderForViewing.reference }} +

+ +

+ {{ 'from'|trans({}, 'Admin.Global') }} + + {{ orderForViewing.customer.firstName }} + {{ orderForViewing.customer.lastName }} +

+ + + {{ orderForViewing.prices.totalAmountFormatted }} + + +

+ {{ orderForViewing.createdAt|date('Y-m-d') }} + + {{ 'at'|trans({}, 'Admin.Global') }} + + {{ orderForViewing.createdAt|date('H:i:s') }} + +

+
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig index f7a80129a3f47..4a976b0023b77 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig @@ -207,7 +207,7 @@

{{ 'Total'|trans({}, 'Admin.Global') }}

- {{ orderForViewing.prices.totalAmountFormatted }} + {{ orderForViewing.prices.totalAmountFormatted }}
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/view.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/view.html.twig index eaa5892d21af8..2fb23041d110b 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/view.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/view.html.twig @@ -25,6 +25,10 @@ {% extends '@PrestaShop/Admin/layout.html.twig' %} +{% block page_header_toolbar %} + {% include '@PrestaShop/Admin/Sell/Order/Order/Blocks/View/header.html.twig' %} +{% endblock %} + {% block content %}
From 9447b922ba50c405b98dded4a111359ede5aaee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 17:01:19 +0300 Subject: [PATCH 048/195] Customize legacy orders header --- .../template/controllers/orders/page_header_toolbar.tpl | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 admin-dev/themes/new-theme/template/controllers/orders/page_header_toolbar.tpl diff --git a/admin-dev/themes/new-theme/template/controllers/orders/page_header_toolbar.tpl b/admin-dev/themes/new-theme/template/controllers/orders/page_header_toolbar.tpl new file mode 100644 index 0000000000000..21ec097e5a462 --- /dev/null +++ b/admin-dev/themes/new-theme/template/controllers/orders/page_header_toolbar.tpl @@ -0,0 +1,8 @@ +
+
+ {literal} + {% block page_header_toolbar %}{% endblock %} + {/literal} +
+ {hook h='displayDashboardTop'} +
From efc3026ea0c2ed6d985cb3899648a3f3bfd2d79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 17:03:12 +0300 Subject: [PATCH 049/195] Include font component into theme scss --- admin-dev/themes/new-theme/scss/theme.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/admin-dev/themes/new-theme/scss/theme.scss b/admin-dev/themes/new-theme/scss/theme.scss index dcbb383a643ce..53bd7f7b9171b 100644 --- a/admin-dev/themes/new-theme/scss/theme.scss +++ b/admin-dev/themes/new-theme/scss/theme.scss @@ -43,6 +43,7 @@ @import "components/toggable_field"; @import "components/form_popover_error"; @import "components/button"; +@import "components/font"; // Pages specifics SCSS files @import "pages/product_page"; From 0f377580bf1fbff400f6438233ca841d32eaa6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 17:24:43 +0300 Subject: [PATCH 050/195] Add help link in order view header --- .../Controller/Admin/Sell/Order/OrderController.php | 3 ++- .../Admin/Sell/Order/Order/Blocks/View/header.html.twig | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php index 9e8a8e925a3cd..18cd6c0e24a9e 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php @@ -248,7 +248,7 @@ public function exportAction(OrderFilters $filters) ->setFileName('order_' . date('Y-m-d_His') . '.csv'); } - public function viewAction(int $orderId): Response + public function viewAction(int $orderId, Request $request): Response { /** @var OrderForViewing $orderForViewing */ $orderForViewing = $this->getQueryBus()->handle(new GetOrderForViewing($orderId)); @@ -279,6 +279,7 @@ public function viewAction(int $orderId): Response return $this->render('@PrestaShop/Admin/Sell/Order/Order/view.html.twig', [ 'showContentHeader' => true, + 'help_link' => $this->generateSidebarLink($request->attributes->get('_legacy_controller')), 'orderForViewing' => $orderForViewing, 'addOrderCartRuleForm' => $addOrderCartRuleForm->createView(), 'updateOrderStatusForm' => $updateOrderStatusForm->createView(), diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig index b90d53056285b..6d2520eebdb72 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/header.html.twig @@ -48,4 +48,13 @@ {{ orderForViewing.createdAt|date('H:i:s') }}

+ + + {{ 'Help'|trans({}, 'Admin.Global') }} +
From d9190575954dae15eb9a79e20c772a9a9b682b1c Mon Sep 17 00:00:00 2001 From: Prestaworks Date: Thu, 24 Oct 2019 10:24:42 +0200 Subject: [PATCH 051/195] Update AdminImagesController.php --- controllers/admin/AdminImagesController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/controllers/admin/AdminImagesController.php b/controllers/admin/AdminImagesController.php index fc582c0d2f1bc..88787725a8dce 100644 --- a/controllers/admin/AdminImagesController.php +++ b/controllers/admin/AdminImagesController.php @@ -534,8 +534,9 @@ protected function _regenerateNewImages($dir, $type, $productsImages = false) 'Admin.Design.Notification' ); } - - if ($generate_hight_dpi_images) { + } + if ($generate_hight_dpi_images) { + if (!file_exists($dir . $imageObj->getExistingImgPath() . '-' . stripslashes($imageType['name']) . '2x.jpg')) { if (!ImageManager::resize($existing_img, $dir . $imageObj->getExistingImgPath() . '-' . stripslashes($imageType['name']) . '2x.jpg', (int) $imageType['width'] * 2, (int) $imageType['height'] * 2)) { $this->errors[] = $this->trans( 'Original image is corrupt (%filename%) for product ID %id% or bad permission on folder.', From 7481ed1906781a30b65424ac88eceb0946c4b5e0 Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Mon, 21 Oct 2019 16:29:21 +0200 Subject: [PATCH 052/195] fix number of DL & expiration date being displayed in the download virtual product mail --- classes/order/OrderHistory.php | 9 +++++---- mails/_partials/download_product_virtual_products.tpl | 2 +- mails/_partials/download_product_virtual_products.txt | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/classes/order/OrderHistory.php b/classes/order/OrderHistory.php index cf888d4521cbb..1b8b093ce7b70 100644 --- a/classes/order/OrderHistory.php +++ b/classes/order/OrderHistory.php @@ -136,16 +136,17 @@ public function changeIdOrderState($new_order_state, $id_order, $use_existing_pa $customer = new Customer((int) $order->id_customer); $links = array(); foreach ($assign as $product) { - $text = Tools::htmlentitiesUTF8($product['name']); + $complementaryText = array(); if (isset($product['deadline'])) { - $text .= ' ' . $this->trans('expires on %s.', array($product['deadline']), 'Admin.Orderscustomers.Notification'); + $complementaryText[] = $this->trans('expires on %s.', array($product['deadline']), 'Admin.Orderscustomers.Notification'); } if (isset($product['downloadable'])) { - $text .= ' ' . $this->trans('downloadable %d time(s)', array((int) $product['downloadable']), 'Admin.Orderscustomers.Notification'); + $complementaryText[] = $this->trans('downloadable %d time(s)', array((int) $product['downloadable']), 'Admin.Orderscustomers.Notification'); } $links[] = array( - 'text' => $text, + 'text' => Tools::htmlentitiesUTF8($product['name']), 'url' => $product['link'], + 'complementary_text' => implode(' ', $complementaryText), ); } diff --git a/mails/_partials/download_product_virtual_products.tpl b/mails/_partials/download_product_virtual_products.tpl index 2ab35aca7be3d..30712565da347 100644 --- a/mails/_partials/download_product_virtual_products.tpl +++ b/mails/_partials/download_product_virtual_products.tpl @@ -24,6 +24,6 @@ *} diff --git a/mails/_partials/download_product_virtual_products.txt b/mails/_partials/download_product_virtual_products.txt index c3639fb9b6452..47a72639bb640 100644 --- a/mails/_partials/download_product_virtual_products.txt +++ b/mails/_partials/download_product_virtual_products.txt @@ -1,3 +1,3 @@ {foreach $list as $product} - [{$product.text}]({$product.url}) + [{$product.text}]({$product.url}) {$product.complementary_text} {/foreach} From e19fe63108736f2a924da30c77dffe1cb6b8f69d Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Mon, 21 Oct 2019 16:30:31 +0200 Subject: [PATCH 053/195] Display placeholder data instead of variables in download_product mail preview --- .../MailPreviewVariablesBuilder.php | 57 ++++++++++++++++++- .../config/services/adapter/mail_template.yml | 1 + 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Adapter/MailTemplate/MailPreviewVariablesBuilder.php b/src/Adapter/MailTemplate/MailPreviewVariablesBuilder.php index e5a61c47cbff9..a1fdc192b1f3f 100644 --- a/src/Adapter/MailTemplate/MailPreviewVariablesBuilder.php +++ b/src/Adapter/MailTemplate/MailPreviewVariablesBuilder.php @@ -31,6 +31,7 @@ use PrestaShop\PrestaShop\Core\Employee\ContextEmployeeProviderInterface; use PrestaShop\PrestaShop\Core\Localization\Locale; use PrestaShop\PrestaShop\Core\MailTemplate\Layout\LayoutInterface; +use Symfony\Component\Translation\TranslatorInterface; use Address; use AddressFormat; use Carrier; @@ -47,6 +48,8 @@ final class MailPreviewVariablesBuilder { const ORDER_CONFIRMATION = 'order_conf'; + const DOWNLOAD_PRODUCT = 'download_product'; + const EMAIL_ALERTS_MODULE = 'ps_emailalerts'; const NEW_ORDER = 'new_order'; const RETURN_SLIP = 'return_slip'; @@ -71,6 +74,11 @@ final class MailPreviewVariablesBuilder /** @var MailPartialTemplateRenderer */ private $mailPartialTemplateRenderer; + /** + * @var TranslatorInterface + */ + private $translator; + /** * MailPreviewVariablesBuilder constructor. * @@ -79,13 +87,15 @@ final class MailPreviewVariablesBuilder * @param ContextEmployeeProviderInterface $employeeProvider * @param MailPartialTemplateRenderer $mailPartialTemplateRenderer * @param Locale $locale + * @param TranslatorInterface $translatorComponent */ public function __construct( ConfigurationInterface $configuration, LegacyContext $legacyContext, ContextEmployeeProviderInterface $employeeProvider, MailPartialTemplateRenderer $mailPartialTemplateRenderer, - Locale $locale + Locale $locale, + TranslatorInterface $translatorComponent ) { $this->configuration = $configuration; $this->legacyContext = $legacyContext; @@ -93,6 +103,7 @@ public function __construct( $this->employeeProvider = $employeeProvider; $this->mailPartialTemplateRenderer = $mailPartialTemplateRenderer; $this->locale = $locale; + $this->translator = $translatorComponent; } /** @@ -136,6 +147,19 @@ public function buildTemplateVariables(LayoutInterface $mailLayout) return $templateVars; } + /** + * @param $id + * @param array $parameters + * @param null $domain + * @param null $local + * + * @return string + */ + protected function trans($id, $parameters = [], $domain = null, $local = null) + { + return $this->translator->trans($id, $parameters, $domain, $local); + } + /** * @return array * @@ -166,6 +190,15 @@ private function buildOrderVariables(LayoutInterface $mailLayout) '{discounts}' => $cartRulesListHtml, '{discounts_txt}' => $cartRulesListTxt, ]; + } elseif (self::DOWNLOAD_PRODUCT == $mailLayout->getName()) { + $virtualProductTemplateList = $this->getFakeVirtualProductList(); + $virtualProductListTxt = $this->mailPartialTemplateRenderer->render('download_product_virtual_products.txt', $this->context->language, $virtualProductTemplateList); + $virtualProductListHtml = $this->mailPartialTemplateRenderer->render('download_product_virtual_products.tpl', $this->context->language, $virtualProductTemplateList); + $productVariables = [ + '{nbProducts}' => count($virtualProductTemplateList), + '{virtualProducts}' => $virtualProductListHtml, + '{virtualProductsTxt}' => $virtualProductListTxt, + ]; } elseif (self::EMAIL_ALERTS_MODULE == $mailLayout->getModuleName() && self::NEW_ORDER == $mailLayout->getName()) { $productVariables = [ '{items}' => $this->getNewOrderItems($order), @@ -368,6 +401,28 @@ private function getProductList(Order $order) return $productTemplateList; } + /** + * @return array + */ + private function getFakeVirtualProductList() + { + $products = Product::getProducts($this->context->language->getId(), 0, 2, 'id_product', 'ASC'); + $results = []; + + foreach ($products as $productData) { + $product = new Product($productData['id_product']); + $results[] = [ + 'text' => Product::getProductName($productData['id_product']), + 'url' => $product->getLink(), + 'complementary_text' => '', + ]; + } + $results[1]['complementary_text'] = ' ' . $this->trans('expires on %s.', array(date('Y-m-d')), 'Admin.Orderscustomers.Notification'); + $results[1]['complementary_text'] .= ' ' . $this->trans('downloadable %d time(s)', array(10), 'Admin.Orderscustomers.Notification'); + + return $results; + } + /** * @param Address $address Address $the_address that needs to be txt formated * @param string $lineSeparator Line separator diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/mail_template.yml b/src/PrestaShopBundle/Resources/config/services/adapter/mail_template.yml index fe04a6a72f654..41c746588c97f 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/mail_template.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/mail_template.yml @@ -29,3 +29,4 @@ services: - '@prestashop.adapter.data_provider.employee' - "@prestashop.adapter.mail_template.partial_template_renderer" - "@prestashop.core.localization.locale.context_locale" + - "@translator" From e5bc041a47dc9122c2a507932ed80ef18b963d5a Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Thu, 24 Oct 2019 16:26:03 +0200 Subject: [PATCH 054/195] Checked that ShippingCostTax values are not null before being formatted --- src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php index 340b061d0245c..3e1ae061da868 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php @@ -584,9 +584,9 @@ private function getOrderShipping(Order $order): OrderShippingForViewing foreach ($shipping as $item) { if ($order->getTaxCalculationMethod() == PS_TAX_INC) { - $price = $this->locale->formatPrice($item['shipping_cost_tax_incl'], $currency->iso_code); + $price = !empty($item['shipping_cost_tax_incl']) ? $this->locale->formatPrice($item['shipping_cost_tax_incl'], $currency->iso_code) : ''; } else { - $price = $this->locale->formatPrice($item['shipping_cost_tax_excl'], $currency->iso_code); + $price = !empty($item['shipping_cost_tax_excl']) ? $this->locale->formatPrice($item['shipping_cost_tax_excl'], $currency->iso_code) : ''; } $trackingUrl = null; From 103e9ba968e6114c6d5c693dedfc22ccfdb62bd1 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Thu, 24 Oct 2019 17:29:24 +0200 Subject: [PATCH 055/195] Fixed default carrier name to empty --- src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php index 3e1ae061da868..a74256153b806 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php @@ -602,7 +602,7 @@ private function getOrderShipping(Order $order): OrderShippingForViewing $carriers[] = new OrderCarrierForViewing( (int) $item['id_order_carrier'], new DateTimeImmutable($item['date_add']), - $item['carrier_name'], + $item['carrier_name'] ?? '', $weight, (int) $item['id_carrier'], $price, From 87848fda60eabce501563ff68d945591b351811e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 09:41:38 +0300 Subject: [PATCH 056/195] Fix missing location for products with combinations --- src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php b/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php index 34a37f54a6a59..dca075e076cc3 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php @@ -44,6 +44,7 @@ use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId; use PrestaShop\PrestaShop\Core\Localization\Locale\Repository as LocaleRepository; use State; +use StockAvailable; use Validate; /** @@ -205,7 +206,11 @@ private function getProductDetails(Order $order): array $productDetails[] = new OrderPreviewProductDetail( $detail['product_name'], $detail['product_reference'], - $detail['location'], + StockAvailable::getLocation( + $detail['product_id'], + $detail['product_attribute_id'], + $detail['id_shop'] + ), (int) $detail['product_quantity'], $locale->formatPrice($unitPrice, $currency->iso_code), $locale->formatPrice($totalPrice, $currency->iso_code), From 40c6e6e1add62205134140d7d14a2c3f8640aa58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 09:50:37 +0300 Subject: [PATCH 057/195] Fix link to order view page from list --- .../Grid/Definition/Factory/OrderGridDefinitionFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/Grid/Definition/Factory/OrderGridDefinitionFactory.php b/src/Core/Grid/Definition/Factory/OrderGridDefinitionFactory.php index 1402bb9a53c59..0e876fcd7a0db 100644 --- a/src/Core/Grid/Definition/Factory/OrderGridDefinitionFactory.php +++ b/src/Core/Grid/Definition/Factory/OrderGridDefinitionFactory.php @@ -471,8 +471,8 @@ private function getRowActions(): RowActionCollection ->setName($this->trans('View', [], 'Admin.Actions')) ->setIcon('zoom_in') ->setOptions([ - 'route' => 'admin_orders_index', - 'route_param_name' => 'id_order', + 'route' => 'admin_orders_view', + 'route_param_name' => 'orderId', 'route_param_field' => 'id_order', 'use_inline_display' => true, ]) From d90908f5f27400536becd25b60ea1e99a12fdbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 10:36:56 +0300 Subject: [PATCH 058/195] Fix products without images display in view page --- .../QueryHandler/GetOrderForViewingHandler.php | 6 +++++- .../Order/QueryResult/OrderProductForViewing.php | 8 ++++---- .../Order/Order/Blocks/View/products.html.twig | 16 +++++++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php index a74256153b806..89d6b1114b1ab 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php @@ -400,6 +400,10 @@ private function getOrderProducts(Order $order): OrderProductsForViewing $unitPriceFormatted = $this->locale->formatPrice($unitPrice, $currency->iso_code); $totalPriceFormatted = $this->locale->formatPrice($totalPrice, $currency->iso_code); + $imagePath = isset($product['image_tag']) ? + $this->imageTagSourceParser->parse($product['image_tag']) : + null; + $productsForViewing[] = new OrderProductForViewing( $product['id_order_detail'], $product['product_id'], @@ -410,7 +414,7 @@ private function getOrderProducts(Order $order): OrderProductsForViewing $unitPriceFormatted, $totalPriceFormatted, $product['current_stock'], - $this->imageTagSourceParser->parse($product['image_tag']), + $imagePath, Tools::ps_round($product['unit_price_tax_excl'], 2), Tools::ps_round($product['unit_price_tax_incl'], 2) ); diff --git a/src/Core/Domain/Order/QueryResult/OrderProductForViewing.php b/src/Core/Domain/Order/QueryResult/OrderProductForViewing.php index 26f171f4c1398..ff91a60aec988 100644 --- a/src/Core/Domain/Order/QueryResult/OrderProductForViewing.php +++ b/src/Core/Domain/Order/QueryResult/OrderProductForViewing.php @@ -69,7 +69,7 @@ class OrderProductForViewing private $availableQuantity; /** - * @var string + * @var string|null */ private $imagePath; @@ -98,7 +98,7 @@ public function __construct( string $unitPrice, string $totalPrice, int $availableQuantity, - string $imagePath, + ?string $imagePath, float $unitPriceTaxExclRaw, float $unitPriceTaxInclRaw ) { @@ -189,9 +189,9 @@ public function getAvailableQuantity(): int } /** - * @return string + * @return string|null */ - public function getImagePath(): string + public function getImagePath(): ?string { return $this->imagePath; } diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig index 4a976b0023b77..e20bdee1b6a04 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/products.html.twig @@ -59,14 +59,20 @@ {% for product in orderForViewing.products.products %} - {{ product.name }} + + {% if product.imagePath %} + {{ product.name }} + {% endif %} +

{{ product.name }}

-

- {{ 'Reference number:'|trans({}, 'Admin.Orderscustomers.Feature') }} - {{ product.reference }} -

+ {% if product.reference %} +

+ {{ 'Reference number:'|trans({}, 'Admin.Orderscustomers.Feature') }} + {{ product.reference }} +

+ {% endif %} {% if product.supplierReference is not empty %}

{{ 'Supplier reference:'|trans({}, 'Admin.Orderscustomers.Feature') }} From 947a8678266207f2288edec163b377c5b65baa59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 10:43:49 +0300 Subject: [PATCH 059/195] Display address names instead of customer in order preview --- .../Order/QueryHandler/GetOrderPreviewHandler.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php b/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php index dca075e076cc3..5466993c0ac6e 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderPreviewHandler.php @@ -124,8 +124,8 @@ private function getInvoiceDetails(Order $order): OrderPreviewInvoiceDetails $stateName = Validate::isLoadedObject($state) ? $state->name : null; return new OrderPreviewInvoiceDetails( - $customer->firstname, - $customer->lastname, + $address->firstname, + $address->lastname, $address->company, $address->vat_number, $address->address1, @@ -135,8 +135,7 @@ private function getInvoiceDetails(Order $order): OrderPreviewInvoiceDetails $stateName, $country->name[$order->id_lang], $customer->email, - $address->phone, - $address->company + $address->phone ); } @@ -145,7 +144,6 @@ private function getInvoiceDetails(Order $order): OrderPreviewInvoiceDetails */ private function getShippingDetails(Order $order): OrderPreviewShippingDetails { - $customer = new Customer($order->id_customer); $address = new Address($order->id_address_delivery); $country = new Country($address->id_country); $carrier = new Carrier($order->id_carrier); @@ -162,8 +160,8 @@ private function getShippingDetails(Order $order): OrderPreviewShippingDetails $orderCarrier = new OrderCarrier($orderCarrierId); return new OrderPreviewShippingDetails( - $customer->firstname, - $customer->lastname, + $address->firstname, + $address->lastname, $address->company, $address->vat_number, $address->address1, From 6625c89338451f39e495b7e6b8059a4dea9e0c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Wed, 23 Oct 2019 11:16:03 +0300 Subject: [PATCH 060/195] Give more space to invoice data column --- .../Resources/views/Admin/Sell/Order/Order/preview.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/preview.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/preview.html.twig index f5a8831e3d0dc..674288d85d335 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/preview.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/preview.html.twig @@ -25,7 +25,7 @@ {% block order_preview %}

-
+
@@ -119,7 +119,7 @@
-
+
From 804713036d1d8911fee3f53fc682743bb658b80c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Fri, 18 Oct 2019 12:53:27 +0300 Subject: [PATCH 061/195] Add date formatting for order status --- .../Resources/config/services/bundle/twig.yml | 7 ++ .../Order/Order/Blocks/View/history.html.twig | 2 +- .../Twig/Extension/LocalizationExtension.php | 64 +++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/PrestaShopBundle/Twig/Extension/LocalizationExtension.php diff --git a/src/PrestaShopBundle/Resources/config/services/bundle/twig.yml b/src/PrestaShopBundle/Resources/config/services/bundle/twig.yml index 12255dfedc60d..5b97e092d3207 100644 --- a/src/PrestaShopBundle/Resources/config/services/bundle/twig.yml +++ b/src/PrestaShopBundle/Resources/config/services/bundle/twig.yml @@ -105,3 +105,10 @@ services: - '@=service("prestashop.user_provider").getUsername()' tags: - { name: twig.extension } + + prestashop.bundle.twig.extension.localization_extension: + class: 'PrestaShopBundle\Twig\Extension\LocalizationExtension' + arguments: + - '@=service("prestashop.adapter.legacy.context").getContext().language.date_format_full' + tags: + - { name: twig.extension } diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/history.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/history.html.twig index 80728f26745b1..e36092036c43c 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/history.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/history.html.twig @@ -40,7 +40,7 @@ {% endif %} + +
- {{ status.createdAt.format('Y-m-d H:i:s') }} + {{ status.createdAt|date_format_full }} {% if status.withEmail %} diff --git a/src/PrestaShopBundle/Twig/Extension/LocalizationExtension.php b/src/PrestaShopBundle/Twig/Extension/LocalizationExtension.php new file mode 100644 index 0000000000000..64a3e662a7b9b --- /dev/null +++ b/src/PrestaShopBundle/Twig/Extension/LocalizationExtension.php @@ -0,0 +1,64 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShopBundle\Twig\Extension; + +use DateTime; +use DateTimeInterface; +use Twig\Extension\AbstractExtension; +use Twig_SimpleFilter; + +class LocalizationExtension extends AbstractExtension +{ + /** + * @var string + */ + private $dateFormatFull; + + /** + * @param string $contextDateFormatFull + */ + public function __construct(string $contextDateFormatFull) + { + $this->dateFormatFull = $contextDateFormatFull; + } + + public function getFilters(): array + { + return [ + new Twig_SimpleFilter('date_format_full', [$this, 'dateFormatFull']), + ]; + } + + public function dateFormatFull($date): string + { + if (!$date instanceof DateTimeInterface) { + $date = new DateTime($date); + } + + return $date->format($this->dateFormatFull); + } +} From 8f95278d73eb16ae7ede56d4b6882f0894e06c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Fri, 18 Oct 2019 16:22:20 +0300 Subject: [PATCH 062/195] Add generating invoice & delivery slip from order view page --- .../new-theme/scss/components/_button.scss | 1 + .../GetOrderForViewingHandler.php | 12 ++++++- .../QueryResult/OrderDocumentsForViewing.php | 33 ++++++++++++++++++- .../Order/Blocks/View/order_actions.html.twig | 22 +++++++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) diff --git a/admin-dev/themes/new-theme/scss/components/_button.scss b/admin-dev/themes/new-theme/scss/components/_button.scss index a1eb62ee1b5f1..5b976bd05e7b8 100644 --- a/admin-dev/themes/new-theme/scss/components/_button.scss +++ b/admin-dev/themes/new-theme/scss/components/_button.scss @@ -1,5 +1,6 @@ .btn { &.btn-action { + color: black; background-color: white; border-radius: 4px; box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1); diff --git a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php index a74256153b806..60b62449b54f4 100644 --- a/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php +++ b/src/Adapter/Order/QueryHandler/GetOrderForViewingHandler.php @@ -562,7 +562,17 @@ private function getOrderDocuments(Order $order): OrderDocumentsForViewing ); } - return new OrderDocumentsForViewing($documentsForViewing); + $canGenerateInvoice = Configuration::get('PS_INVOICE') && + count($order->getInvoicesCollection()) && + $order->invoice_number; + + $canGenerateDeliverySlip = (bool) $order->delivery_number; + + return new OrderDocumentsForViewing( + $canGenerateInvoice, + $canGenerateDeliverySlip, + $documentsForViewing + ); } private function getOrderShipping(Order $order): OrderShippingForViewing diff --git a/src/Core/Domain/Order/QueryResult/OrderDocumentsForViewing.php b/src/Core/Domain/Order/QueryResult/OrderDocumentsForViewing.php index afffeba70357d..a8110f29537d8 100644 --- a/src/Core/Domain/Order/QueryResult/OrderDocumentsForViewing.php +++ b/src/Core/Domain/Order/QueryResult/OrderDocumentsForViewing.php @@ -34,13 +34,28 @@ class OrderDocumentsForViewing private $documents = []; /** + * @var bool + */ + private $canGenerateInvoice; + + /** + * @var bool + */ + private $canGenerateDeliverySlip; + + /** + * @param bool $canGenerateInvoice + * @param bool $canGenerateDeliverySlip * @param OrderDocumentForViewing[] $documents */ - public function __construct(array $documents) + public function __construct(bool $canGenerateInvoice, bool $canGenerateDeliverySlip, array $documents) { foreach ($documents as $document) { $this->add($document); } + + $this->canGenerateInvoice = $canGenerateInvoice; + $this->canGenerateDeliverySlip = $canGenerateDeliverySlip; } /** @@ -51,6 +66,22 @@ public function getDocuments(): array return $this->documents; } + /** + * @return bool + */ + public function canGenerateInvoice(): bool + { + return $this->canGenerateInvoice; + } + + /** + * @return bool + */ + public function canGenerateDeliverySlip(): bool + { + return $this->canGenerateDeliverySlip; + } + private function add(OrderDocumentForViewing $document): void { $this->documents[] = $document; diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/order_actions.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/order_actions.html.twig index c476e41a01429..5aa16e5b12f45 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/order_actions.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/View/order_actions.html.twig @@ -47,4 +47,26 @@ {{ form_rest(updateOrderStatusActionBarForm) }} {{ form_end(updateOrderStatusActionBarForm) }} + + {% if orderForViewing.documents.canGenerateInvoice %} +
+ +
+ {% endif %} + + {% if orderForViewing.documents.canGenerateDeliverySlip %} +
+ +
+ {% endif %} From f4ddfff3ac8c5b0544231c00418f00719a2c8807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Jonu=C5=A1as?= Date: Fri, 18 Oct 2019 16:55:25 +0300 Subject: [PATCH 063/195] Fix view for virtual orders --- .../Order/QueryResult/OrderCarrierForViewing.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/Domain/Order/QueryResult/OrderCarrierForViewing.php b/src/Core/Domain/Order/QueryResult/OrderCarrierForViewing.php index 4a5d146d39c42..d495e94e88609 100644 --- a/src/Core/Domain/Order/QueryResult/OrderCarrierForViewing.php +++ b/src/Core/Domain/Order/QueryResult/OrderCarrierForViewing.php @@ -78,10 +78,10 @@ class OrderCarrierForViewing /** * @param int $orderCarrierId * @param DateTimeImmutable $date - * @param string $name + * @param string $name Carrier name or null in case of virtual order * @param string $weight * @param int $carrierId - * @param string $price + * @param string $price Price or null in case of virtual order * @param string|null $trackingUrl * @param string|null $trackingNumber * @param bool $canEdit @@ -89,10 +89,10 @@ class OrderCarrierForViewing public function __construct( int $orderCarrierId, DateTimeImmutable $date, - string $name, + ?string $name, string $weight, int $carrierId, - string $price, + ?string $price, ?string $trackingUrl, ?string $trackingNumber, bool $canEdit @@ -127,7 +127,7 @@ public function getDate(): DateTimeImmutable /** * @return string */ - public function getName(): string + public function getName(): ?string { return $this->name; } @@ -143,7 +143,7 @@ public function getCarrierId(): int /** * @return string */ - public function getPrice(): string + public function getPrice(): ?string { return $this->price; } From fc65574905581b8236cab0962215d8d3f4b5b113 Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Thu, 24 Oct 2019 17:14:03 +0200 Subject: [PATCH 064/195] Add missing placeholder in the Private Note textarea --- .../Admin/Sell/Customer/PrivateNoteType.php | 19 +++++++++++++++++++ .../config/services/bundle/form/form_type.yml | 7 +++++++ 2 files changed, 26 insertions(+) diff --git a/src/PrestaShopBundle/Form/Admin/Sell/Customer/PrivateNoteType.php b/src/PrestaShopBundle/Form/Admin/Sell/Customer/PrivateNoteType.php index 2300ed3d5727e..74f48418a5aa5 100644 --- a/src/PrestaShopBundle/Form/Admin/Sell/Customer/PrivateNoteType.php +++ b/src/PrestaShopBundle/Form/Admin/Sell/Customer/PrivateNoteType.php @@ -29,12 +29,28 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Translation\TranslatorInterface; /** * Class PrivateNoteType is used to add private notes about customer. */ class PrivateNoteType extends AbstractType { + /** + * @var TranslatorInterface + */ + private $translator; + + /** + * PrivateNoteType constructor. + * + * @param TranslatorInterface $translator + */ + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + /** * {@inheritdoc} */ @@ -44,6 +60,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) ->add('note', TextareaType::class, [ 'required' => false, 'empty_data' => '', + 'attr' => [ + 'placeholder' => $this->translator->trans('Add a note on this customer. It will only be visible to you.', [], 'Admin.Orderscustomers.Feature'), + ], ]); } } diff --git a/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml b/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml index 0643d05624654..6f42e483ab58d 100644 --- a/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml +++ b/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml @@ -907,6 +907,13 @@ services: tags: - { name: form.type } + form.type.sell.customer.private_note: + class: 'PrestaShopBundle\Form\Admin\Sell\Customer\PrivateNoteType' + arguments: + - "@translator" + tags: + - { name: form.type } + form.type.order.add_order_cart_rule: class: 'PrestaShopBundle\Form\Admin\Sell\Order\AddOrderCartRuleType' arguments: From 9e53c4efd3294d91eb5a5ce97621b159a9df62d5 Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:33:39 +0200 Subject: [PATCH 065/195] Add demo file for brands and addresses --- tests/puppeteer/campaigns/data/demo/brands.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/puppeteer/campaigns/data/demo/brands.js diff --git a/tests/puppeteer/campaigns/data/demo/brands.js b/tests/puppeteer/campaigns/data/demo/brands.js new file mode 100644 index 0000000000000..517acb2dc04ed --- /dev/null +++ b/tests/puppeteer/campaigns/data/demo/brands.js @@ -0,0 +1,38 @@ +module.exports = { + demoBrands: { + first: { + id: '1', + name: 'Studio Design', + addresses: 1, + products: 9, + enabled: true, + }, + second: { + id: '2', + name: 'Graphic Corner', + addresses: 0, + products: 9, + enabled: true, + }, + }, + demoAddresses: { + first: { + id: '4', + brand: 'Studio Design', + firstName: 'manufacturer', + lastName: 'manufacturer', + postalCode: '10154', + city: 'New York', + country: 'United States', + }, + second: { + id: '3', + brand: '', + firstName: 'supplier', + lastName: 'supplier', + postalCode: '10153', + city: 'New York', + country: 'United States', + }, + }, +}; From ee2cbe41bb6f0cb4f5ab8675500f2e572887982c Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:35:11 +0200 Subject: [PATCH 066/195] Add faker For Brand and Address --- tests/puppeteer/campaigns/data/faker/brand.js | 21 +++++++++++++++++++ .../campaigns/data/faker/brandAddress.js | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/puppeteer/campaigns/data/faker/brand.js create mode 100644 tests/puppeteer/campaigns/data/faker/brandAddress.js diff --git a/tests/puppeteer/campaigns/data/faker/brand.js b/tests/puppeteer/campaigns/data/faker/brand.js new file mode 100644 index 0000000000000..3b40810fea538 --- /dev/null +++ b/tests/puppeteer/campaigns/data/faker/brand.js @@ -0,0 +1,21 @@ +const faker = require('faker'); + +module.exports = class Brand { + constructor(brandToCreate = {}) { + this.name = brandToCreate.name || faker.company.companyName(); + this.logo = `${this.name.replace(/[^\w\s]/gi, '')}.png`; + this.shortDescription = brandToCreate.shortDescription || faker.lorem.sentence(); + this.shortDescriptionFr = brandToCreate.shortDescriptionFr || this.shortDescription; + this.description = brandToCreate.description || faker.lorem.sentence(); + this.descriptionFr = brandToCreate.descriptionFr || this.description; + this.metaTitle = brandToCreate.metaTitle || this.name; + this.metaTitleFr = brandToCreate.metaTitleFr || this.metaTitle; + this.metaDescription = brandToCreate.metaDescription || faker.lorem.sentence(); + this.metaDescriptionFr = brandToCreate.metaDescriptionFr || this.metaDescription; + this.metaKeywords = brandToCreate.metaKeywords || [faker.lorem.word(), faker.lorem.word()]; + this.metaKeywordsFr = brandToCreate.metaKeywordsFr || this.metaKeywords; + this.enabled = brandToCreate.enabled === undefined ? true : brandToCreate.enabled; + this.addresses = brandToCreate.addresses || 0; + this.products = brandToCreate.products || 0; + } +}; diff --git a/tests/puppeteer/campaigns/data/faker/brandAddress.js b/tests/puppeteer/campaigns/data/faker/brandAddress.js new file mode 100644 index 0000000000000..436029cba9a58 --- /dev/null +++ b/tests/puppeteer/campaigns/data/faker/brandAddress.js @@ -0,0 +1,19 @@ +const faker = require('faker'); + +const countries = ['Italy', 'France', 'United States', 'Netherlands', 'United Kingdom', 'Germany']; + +module.exports = class branAddress { + constructor(brandAddressToCreate = {}) { + this.brandName = brandAddressToCreate.brandName || '--'; + this.firstName = brandAddressToCreate.firstName || faker.name.firstName(); + this.lastName = brandAddressToCreate.lastName || faker.name.lastName(); + this.address = brandAddressToCreate.address || faker.address.streetAddress(); + this.secondaryAddress = brandAddressToCreate.secondaryAddress || faker.address.secondaryAddress(); + this.postalCode = brandAddressToCreate.postalCode || faker.address.zipCode(); + this.city = brandAddressToCreate.city || faker.address.city(); + this.country = brandAddressToCreate.country || faker.random.arrayElement(countries); + this.homePhone = brandAddressToCreate.homePhone || faker.phone.phoneNumber('01########'); + this.mobilePhone = brandAddressToCreate.mobilePhone || faker.phone.phoneNumber('06########'); + this.other = brandAddressToCreate.other || ''; + } +}; From c73c790e4899ae86cf2f7d4ff363bca03a6d6e1d Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:36:38 +0200 Subject: [PATCH 067/195] Add new eslint rule --- tests/puppeteer/.eslintrc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/puppeteer/.eslintrc.js b/tests/puppeteer/.eslintrc.js index 152c8c76dca23..0f086edfff3e6 100755 --- a/tests/puppeteer/.eslintrc.js +++ b/tests/puppeteer/.eslintrc.js @@ -17,7 +17,8 @@ module.exports = { }, rules: { 'no-plusplus': [2, { allowForLoopAfterthoughts: true }], - "func-names": "off", + 'func-names': 'off', + 'class-methods-use-this': ['error', {'exceptMethods': ['replaceAll']}], 'max-len': [2, {code: 120}] }, }; From 528b23676656d0fa7825988b756ab6a730c82961 Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:38:44 +0200 Subject: [PATCH 068/195] Update function select by visible text for time saving --- tests/puppeteer/pages/commonPage.js | 44 +++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/tests/puppeteer/pages/commonPage.js b/tests/puppeteer/pages/commonPage.js index 4c4c660733709..d205d05adbd5f 100755 --- a/tests/puppeteer/pages/commonPage.js +++ b/tests/puppeteer/pages/commonPage.js @@ -171,17 +171,19 @@ module.exports = class CommonPage { */ async selectByVisibleText(selector, textValue) { let found = false; - const options = await this.page.$$(`${selector} option`); - for (let i = 0; i < options.length; i++) { - /*eslint-disable*/ - const elementText = await (await options[i].getProperty('textContent')).jsonValue(); - if (elementText === textValue) { - const elementValue = await (await options[i].getProperty('value')).jsonValue(); - await this.page.select(selector, elementValue); - found = true; - break; - } - /* eslint-enable */ + let options = await this.page.$$eval( + `${selector} option`, + all => all.map( + option => ({ + textContent: option.textContent, + value: option.value, + })), + ); + options = await options.filter(option => textValue === option.textContent); + if (options.length !== 0) { + const elementValue = await options[0].value; + await this.page.select(selector, elementValue); + found = true; } if (!found) throw new Error(`${textValue} was not found as option of select`); } @@ -210,4 +212,24 @@ module.exports = class CommonPage { this.page.waitForNavigation({waitUntil: 'networkidle0'}), ]); } + + /** + * Replace All occurrences in string + * @param str, string to update + * @param find, what to replace + * @param replace, value to replace with + * @return {Promise<*>} + */ + async replaceAll(str, find, replace) { + return str.replace(new RegExp(find, 'g'), replace); + } + + /** + * Navigate to the previous page in history + * @param waitUntil + * @return {Promise} + */ + async goToPreviousPage(waitUntil = 'networkidle0') { + await this.page.goBack({waitUntil}); + } }; From 87376e1e3f6e403505450c0cb045bfc6ef2850d5 Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:40:45 +0200 Subject: [PATCH 069/195] Add selectors for BO Menu --- tests/puppeteer/pages/BO/BObasePage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/puppeteer/pages/BO/BObasePage.js b/tests/puppeteer/pages/BO/BObasePage.js index f6f5ea11f6729..d9ce828717ec2 100644 --- a/tests/puppeteer/pages/BO/BObasePage.js +++ b/tests/puppeteer/pages/BO/BObasePage.js @@ -25,11 +25,16 @@ module.exports = class BOBasePage extends CommonPage { this.ordersParentLink = 'li#subtab-AdminParentOrders'; this.ordersLink = '#subtab-AdminOrders'; + // Catalog + // Products this.productsParentLink = 'li#subtab-AdminCatalog'; this.productsLink = '#subtab-AdminProducts'; - + // Categories this.categoriesLink = '#subtab-AdminCategories'; + // Brands And Suppliers + this.brandsAndSuppliersLink = '#subtab-AdminParentManufacturers'; + // Customers this.customersParentLink = 'li#subtab-AdminParentCustomer'; this.customersLink = '#subtab-AdminCustomers'; From d6446a4260aa5f1978cd99b6ab886167d7bf3eab Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:41:26 +0200 Subject: [PATCH 070/195] Change value of puppeteer slowMo --- tests/puppeteer/campaigns/utils/globals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/puppeteer/campaigns/utils/globals.js b/tests/puppeteer/campaigns/utils/globals.js index 1fb851414432c..0bb61c8d4a123 100755 --- a/tests/puppeteer/campaigns/utils/globals.js +++ b/tests/puppeteer/campaigns/utils/globals.js @@ -18,7 +18,7 @@ global.INSTALL = { global.BROWSER_CONFIG = { headless: JSON.parse(process.env.HEADLESS || true), timeout: 0, - slowMo: 25, + slowMo: 5, args: ['--start-maximized', '--no-sandbox', '--lang=en-GB'], defaultViewport: { width: 1680, From ce45c89a6ed1c4f40cc318a310cc1b5443763b11 Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:42:35 +0200 Subject: [PATCH 071/195] Add function to delete file --- tests/puppeteer/campaigns/utils/files.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/puppeteer/campaigns/utils/files.js diff --git a/tests/puppeteer/campaigns/utils/files.js b/tests/puppeteer/campaigns/utils/files.js new file mode 100644 index 0000000000000..2e53697590bea --- /dev/null +++ b/tests/puppeteer/campaigns/utils/files.js @@ -0,0 +1,12 @@ +const fs = require('fs'); + +module.exports = { + /** + * Delete File if exist + * @param pathToFile + * @return {Promise} + */ + async deleteFile(pathToFile) { + if (fs.existsSync(pathToFile)) fs.unlinkSync(pathToFile); + }, +}; From ab784acf12d487aadf663f2861a19ca4a09b3c60 Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:42:57 +0200 Subject: [PATCH 072/195] Add Brand Pages --- tests/puppeteer/pages/BO/addBrand.js | 108 +++++++ tests/puppeteer/pages/BO/addBrandAddress.js | 51 ++++ tests/puppeteer/pages/BO/brands.js | 303 ++++++++++++++++++++ tests/puppeteer/pages/BO/viewBrand.js | 22 ++ 4 files changed, 484 insertions(+) create mode 100644 tests/puppeteer/pages/BO/addBrand.js create mode 100644 tests/puppeteer/pages/BO/addBrandAddress.js create mode 100644 tests/puppeteer/pages/BO/brands.js create mode 100644 tests/puppeteer/pages/BO/viewBrand.js diff --git a/tests/puppeteer/pages/BO/addBrand.js b/tests/puppeteer/pages/BO/addBrand.js new file mode 100644 index 0000000000000..61fab1a500940 --- /dev/null +++ b/tests/puppeteer/pages/BO/addBrand.js @@ -0,0 +1,108 @@ +require('module-alias/register'); +const BOBasePage = require('@pages/BO/BObasePage'); + +module.exports = class AddBrand extends BOBasePage { + constructor(page) { + super(page); + + this.pageTitle = 'Add new • '; + this.pageTitleEdit = 'Edit:'; + + // Selectors + this.nameInput = '#manufacturer_name'; + this.shortDescriptionDiv = '#manufacturer_short_description'; + this.shortDescriptionLangLink = `${this.shortDescriptionDiv} li.nav-item a[data-locale='%LANG']`; + this.shortDescriptionIFrame = `${this.shortDescriptionDiv} #manufacturer_short_description_%ID_ifr`; + this.descriptionDiv = '#manufacturer_description'; + this.descriptionIFrame = `${this.descriptionDiv} #manufacturer_description_%ID_ifr`; + this.logoFileInput = '#manufacturer_logo'; + this.metaTitleInput = '#manufacturer_meta_title_%ID'; + this.metaDescriptionInput = '#manufacturer_meta_description_%ID'; + this.metaKeyworkdsInput = '#manufacturer_meta_keyword_%ID-tokenfield'; + this.enabledSwitchlabel = 'label[for=\'manufacturer_is_enabled_%ID\']'; + // Selectors for Meta keywords + this.taggableFieldDiv = 'div.input-group div.js-locale-%LANG'; + this.deleteKeywordLink = `${this.taggableFieldDiv} a.close`; + this.saveButton = '.card-footer button'; + } + + /* + Methods + */ + + /** + * Create or edit Brand + * @param brandData + * @return {Promise} + */ + async createEditBrand(brandData) { + // Fill Name + await this.setValue(this.nameInput, brandData.name); + // Fill information in english + await Promise.all([ + this.page.click(this.shortDescriptionLangLink.replace('%LANG', 'en')), + this.page.waitForSelector(`${this.shortDescriptionLangLink.replace('%LANG', 'en')}.active`, {visible: true}), + ]); + await this.setValueOnTinymceInput(this.shortDescriptionIFrame.replace('%ID', '1'), brandData.shortDescription); + await this.setValueOnTinymceInput(this.descriptionIFrame.replace('%ID', '1'), brandData.description); + await this.setValueOnTinymceInput(this.metaTitleInput.replace('%ID', '1'), brandData.metaTitle); + await this.setValueOnTinymceInput(this.metaDescriptionInput.replace('%ID', '1'), brandData.metaDescription); + await this.deleteKeywords('en'); + await this.addKeywords(brandData.metaKeywords, '1'); + + // Fill Information in french + await Promise.all([ + this.page.click(this.shortDescriptionLangLink.replace('%LANG', 'fr')), + this.page.waitForSelector(`${this.shortDescriptionLangLink.replace('%LANG', 'fr')}.active`, {visible: true}), + ]); + await this.setValueOnTinymceInput(this.shortDescriptionIFrame.replace('%ID', '2'), brandData.shortDescriptionFr); + await this.setValueOnTinymceInput(this.descriptionIFrame.replace('%ID', '2'), brandData.descriptionFr); + await this.setValueOnTinymceInput(this.metaTitleInput.replace('%ID', '2'), brandData.metaTitleFr); + await this.setValueOnTinymceInput(this.metaDescriptionInput.replace('%ID', '2'), brandData.metaDescriptionFr); + await this.deleteKeywords('fr'); + await this.addKeywords(brandData.metaKeywordsFr, '2'); + + // Add logo + await this.generateAndUploadImage(this.logoFileInput, brandData.logo); + + // Set Enabled value + if (brandData.enabled) { + await this.page.click(this.enabledSwitchlabel.replace('%ID', '1')); + } else { + await this.page.click(this.enabledSwitchlabel.replace('%ID', '0')); + } + // Save Created brand + await this.clickAndWaitForNavigation(this.saveButton); + await this.page.waitForSelector(this.alertSuccessBlockParagraph, {visible: true}); + return this.getTextContent(this.alertSuccessBlockParagraph); + } + + /** + * Delete all keywords + * @param lang, to specify which input to empty + * @return {Promise} + */ + async deleteKeywords(lang = 'en') { + const closeButtons = await this.page.$$(this.deleteKeywordLink.replace('%LANG', lang)); + /* eslint-disable no-await-in-loop, no-restricted-syntax */ + for (const closeButton of closeButtons) { + await closeButton.click(); + } + /* eslint-enable no-await-in-loop, no-restricted-syntax */ + } + + /** + * Add keywords + * @param keywords, array of keywords + * @param id, to choose which lang (1 for en, 2 for fr) + * @return {Promise} + */ + async addKeywords(keywords, id = '1') { + /* eslint-disable no-await-in-loop, no-restricted-syntax */ + for (const keyword of keywords) { + await this.page.type(this.metaKeyworkdsInput.replace('%ID', id), keyword); + await this.page.keyboard.press('Enter'); + } + /* eslint-enable no-await-in-loop, no-restricted-syntax */ + } +}; diff --git a/tests/puppeteer/pages/BO/addBrandAddress.js b/tests/puppeteer/pages/BO/addBrandAddress.js new file mode 100644 index 0000000000000..6ed99f2686dd6 --- /dev/null +++ b/tests/puppeteer/pages/BO/addBrandAddress.js @@ -0,0 +1,51 @@ +require('module-alias/register'); +const BOBasePage = require('@pages/BO/BObasePage'); + +module.exports = class AddBrandAddress extends BOBasePage { + constructor(page) { + super(page); + + this.pageTitle = 'Add new address • '; + + // Selectors + this.brandSelect = 'select#manufacturer_address_id_manufacturer'; + this.lastnameInput = 'input#manufacturer_address_last_name'; + this.firstnameInput = 'input#manufacturer_address_first_name'; + this.addressInput = 'input#manufacturer_address_address'; + this.secondaryAddressInput = 'input#manufacturer_address_address2'; + this.postalCodeInput = 'input#manufacturer_address_post_code'; + this.cityInput = 'input#manufacturer_address_city'; + this.countrySelect = 'select#manufacturer_address_id_country'; + this.homePhoneInput = 'input#manufacturer_address_home_phone'; + this.mobilePhoneInput = 'input#manufacturer_address_mobile_phone'; + this.otherInput = 'input#manufacturer_address_other'; + this.saveButton = '.card-footer button'; + } + + /* + Methods + */ + /** + * Create or edit Brand Address + * @param brandAddressData + * @return {Promise} + */ + async createEditBrandAddress(brandAddressData) { + // Fill information data + await this.selectByVisibleText(this.brandSelect, brandAddressData.brandName); + await this.setValue(this.lastnameInput, brandAddressData.lastName); + await this.setValue(this.firstnameInput, brandAddressData.firstName); + await this.setValue(this.addressInput, brandAddressData.address); + await this.setValue(this.secondaryAddressInput, brandAddressData.secondaryAddress); + await this.setValue(this.postalCodeInput, brandAddressData.postalCode); + await this.setValue(this.cityInput, brandAddressData.city); + await this.selectByVisibleText(this.countrySelect, brandAddressData.country); + await this.setValue(this.homePhoneInput, brandAddressData.homePhone); + await this.setValue(this.mobilePhoneInput, brandAddressData.mobilePhone); + await this.setValue(this.otherInput, brandAddressData.other); + // Click on Save button and successful message + await this.clickAndWaitForNavigation(this.saveButton); + await this.page.waitForSelector(this.alertSuccessBlockParagraph, {visible: true}); + return this.getTextContent(this.alertSuccessBlockParagraph); + } +}; diff --git a/tests/puppeteer/pages/BO/brands.js b/tests/puppeteer/pages/BO/brands.js new file mode 100644 index 0000000000000..4684795906472 --- /dev/null +++ b/tests/puppeteer/pages/BO/brands.js @@ -0,0 +1,303 @@ +require('module-alias/register'); +const BOBasePage = require('@pages/BO/BObasePage'); + +module.exports = class Brands extends BOBasePage { + constructor(page) { + super(page); + + this.pageTitle = 'Brands •'; + this.successfulUpdateStatusMessage = 'The status has been successfully updated.'; + + // Selectors + this.suppliersNavItemLink = '#subtab-AdminSuppliers'; + this.newBrandLink = '#page-header-desc-configuration-add_manufacturer'; + this.newBrandAddressLink = '#page-header-desc-configuration-add_manufacturer_address'; + + + // Selectors + this.gridPanel = '#%TABLE_grid_panel'; + this.gridTable = '#%TABLE_grid_table'; + this.gridHeaderTitle = `${this.gridPanel} h3.card-header-title`; + // Bulk Actions + this.selectAllRowsLabel = `${this.gridPanel} .md-checkbox label`; + this.bulkActionsToggleButton = `${this.gridPanel} button.js-bulk-actions-btn`; + // Filters + this.filterColumn = `${this.gridTable} #%TABLE_%FILTERBY`; + this.filterSearchButton = `${this.gridTable} button[name='%TABLE[actions][search]']`; + this.filterResetButton = `${this.gridTable} button[name='%TABLE[actions][reset]']`; + // Table rows and columns + this.tableBody = `${this.gridTable} tbody`; + this.tableRow = `${this.tableBody} tr:nth-child(%ROW)`; + this.tableColumn = `${this.tableRow} td.column-%COLUMN`; + // Actions buttons in Row + this.actionsColumn = `${this.tableRow} td.column-actions`; + this.dropdownToggleButton = `${this.actionsColumn} a.dropdown-toggle`; + this.dropdownToggleMenu = `${this.actionsColumn} div.dropdown-menu`; + this.deleteRowLink = `${this.dropdownToggleMenu} a[data-url*='/delete']`; + + // Brands list Selectors + this.brandsTableEnableColumn = `${this.tableColumn + .replace('%TABLE', 'manufacturer').replace('%COLUMN', 'active')}`; + this.brandsEnableColumnValidIcon = `${this.brandsTableEnableColumn} i.grid-toggler-icon-valid`; + this.brandsEnableColumnNotValidIcon = `${this.brandsTableEnableColumn} i.grid-toggler-icon-not-valid`; + this.viewBrandLink = `${this.actionsColumn} a[data-original-title='View']` + .replace('%TABLE', 'manufacturer'); + this.editBrandLink = `${this.dropdownToggleMenu} a[href*='/edit']`.replace('%TABLE', 'manufacturer'); + this.bulkActionsEnableButton = `${this.gridPanel} #manufacturer_grid_bulk_action_enable_selection` + .replace('%TABLE', 'manufacturer'); + this.bulkActionsDisableButton = `${this.gridPanel} #manufacturer_grid_bulk_action_disable_selection` + .replace('%TABLE', 'manufacturer'); + this.deleteBrandsButton = `${this.gridPanel} #manufacturer_grid_bulk_action_delete_selection` + .replace('%TABLE', 'manufacturer'); + + // Brand Addresses Selectors + this.editBrandAddressLink = `${this.actionsColumn} a[data-original-title='Edit']` + .replace('%TABLE', 'manufacturer_address'); + this.deleteAddressesButton = `${this.gridPanel} #manufacturer_address_grid_bulk_action_delete_manufacturer_address` + .replace('%TABLE', 'manufacturer_address'); + } + + /* + Methods + */ + + /** + * Go to Tab Suppliers + * @return {Promise} + */ + async goToSubTabSuppliers() { + await this.clickAndWaitForNavigation(this.suppliersNavItemLink); + } + + /* + Methods + */ + + /** + * Reset filters in table + * @param table, what table to reset + * @return {Promise} + */ + async resetFilters(table) { + const resetButton = await this.replaceAll(this.filterResetButton, '%TABLE', table); + if (await this.elementVisible(resetButton, 2000)) { + await this.clickAndWaitForNavigation(resetButton); + } + } + + /** + * Filter Table + * @param table, table to filter + * @param filterType, input / Select + * @param filterBy, which column + * @param value, value to put in filter + * @return {Promise} + */ + async filterTable(table, filterType, filterBy, value = '') { + const filterColumn = await this.replaceAll(this.filterColumn, '%TABLE', table); + const searchButton = await this.replaceAll(this.filterSearchButton, '%TABLE', table); + switch (filterType) { + case 'input': + await this.setValue(filterColumn.replace('%FILTERBY', filterBy), value); + break; + case 'select': + await this.selectByVisibleText(filterColumn.replace('%FILTERBY', filterBy), value); + break; + default: + // Do nothing + } + // click on search + await this.clickAndWaitForNavigation(searchButton); + } + + /** + * Filter Brands + * @param filterType, input / Select + * @param filterBy, which column + * @param value, value to put in filter + * @return {Promise} + */ + async filterBrands(filterType, filterBy, value = '') { + await this.filterTable('manufacturer', filterType, filterBy, value); + } + + /** + * Filter Addresses + * @param filterType, input / Select + * @param filterBy, which column + * @param value, value to put in filter + * @return {Promise} + */ + async filterAddresses(filterType, filterBy, value = '') { + await this.filterTable('manufacturer_address', filterType, filterBy, value); + } + + /** + * Get toggle column value for a row (Brands list) + * @param row + * @return {Promise} + */ + async getToggleColumnValue(row) { + return this.elementVisible( + this.brandsEnableColumnValidIcon.replace('%ROW', row), 100); + } + + /** + * Update Enable column for the value wanted in Brands list + * @param row + * @param valueWanted + * @return {Promise}, true if click has been performed + */ + async updateEnabledValue(row, valueWanted = true) { + if (await this.getToggleColumnValue(row, 'active') !== valueWanted) { + await this.clickAndWaitForNavigation(this.brandsTableEnableColumn.replace('%ROW', row)); + return true; + } + return false; + } + + /** + * Go to New Brand Page + * @return {Promise} + */ + async goToAddNewBrandPage() { + await this.clickAndWaitForNavigation(this.newBrandLink); + } + + /** + * Go to new Brand Address Page + * @return {Promise} + */ + async goToAddNewBrandAddressPage() { + await this.clickAndWaitForNavigation(this.newBrandAddressLink); + } + + /** + * View Brand + * @param row, Which row of the list + * @return {Promise} + */ + async viewBrand(row = '1') { + await this.clickAndWaitForNavigation(this.viewBrandLink.replace('%ROW', row)); + } + + async goToEditBrandPage(row = '1') { + await Promise.all([ + this.page.click(this.dropdownToggleButton.replace('%TABLE', 'manufacturer').replace('%ROW', row)), + this.page.waitForSelector( + `${this.dropdownToggleButton}[aria-expanded='true']` + .replace('%TABLE', 'manufacturer').replace('%ROW', row), + ), + ]); + await this.clickAndWaitForNavigation(this.editBrandLink.replace('%ROW', row)); + } + + /** + * + * @param row + * @return {Promise} + */ + async goToEditBrandAddressPage(row = '1') { + await this.clickAndWaitForNavigation(this.editBrandAddressLink.replace('%ROW', row)); + } + + /** + * Delete Row in table + * @param table, brand or address + * @param row, row to delete + * @return {Promise} + */ + async deleteRowInTable(table, row = '1') { + this.dialogListener(true); + await Promise.all([ + this.page.click(this.dropdownToggleButton.replace('%TABLE', table).replace('%ROW', row)), + this.page.waitForSelector( + `${this.dropdownToggleButton}[aria-expanded='true']` + .replace('%TABLE', table).replace('%ROW', row), + ), + ]); + await this.clickAndWaitForNavigation(this.deleteRowLink.replace('%TABLE', table).replace('%ROW', row)); + await this.page.waitForSelector(this.alertSuccessBlockParagraph, {visible: true}); + return this.getTextContent(this.alertSuccessBlockParagraph); + } + + /** + * Delete Brand + * @param row, row to delete + * @return {Promise} + */ + async deleteBrand(row = '1') { + return this.deleteRowInTable('manufacturer', row); + } + + /** + * Delete Brand Address + * @param row, row to delete + * @return {Promise} + */ + async deleteBrandAddress(row = '1') { + return this.deleteRowInTable('manufacturer_address', row); + } + + /** + * Enable / disable brands by Bulk Actions + * @param enable + * @return {Promise} + */ + async changeBrandsEnabledColumnBulkActions(enable = true) { + // Click on Select All + await Promise.all([ + this.page.click(this.selectAllRowsLabel.replace('%TABLE', 'manufacturer')), + this.page.waitForSelector( + `${this.selectAllRowsLabel}:not([disabled])`.replace('%TABLE', 'manufacturer'), + {visible: true}, + ), + ]); + // Click on Button Bulk actions + await Promise.all([ + this.page.click(this.bulkActionsToggleButton.replace('%TABLE', 'manufacturer')), + this.page.waitForSelector( + `${this.bulkActionsToggleButton}[aria-expanded='true']`.replace('%TABLE', 'manufacturer'), + {visible: true}, + ), + ]); + // Click on delete and wait for modal + await Promise.all([ + this.page.click(enable ? this.bulkActionsEnableButton : this.bulkActionsDisableButton), + this.page.waitForNavigation({waitUntil: 'networkidle0'}), + ]); + return this.getTextContent(this.alertSuccessBlockParagraph); + } + + /** + * Delete with bulk actions + * @param table, in which table + * @return {Promise} + */ + async deleteWithBulkActions(table) { + this.dialogListener(true); + // Click on Select All + await Promise.all([ + this.page.click(this.selectAllRowsLabel.replace('%TABLE', table)), + this.page.waitForSelector( + `${this.selectAllRowsLabel}:not([disabled])`.replace('%TABLE', table), + {visible: true}, + ), + ]); + // Click on Button Bulk actions + await Promise.all([ + this.page.click(this.bulkActionsToggleButton.replace('%TABLE', table)), + this.page.waitForSelector( + `${this.bulkActionsToggleButton}[aria-expanded='true']`.replace('%TABLE', table), + {visible: true}, + ), + ]); + // Click on delete and wait for modal + if (table === 'manufacturer') { + await this.clickAndWaitForNavigation(this.deleteBrandsButton); + } else if (table === 'manufacturer_address') { + await this.clickAndWaitForNavigation(this.deleteAddressesButton); + } + return this.getTextContent(this.alertSuccessBlockParagraph); + } +}; diff --git a/tests/puppeteer/pages/BO/viewBrand.js b/tests/puppeteer/pages/BO/viewBrand.js new file mode 100644 index 0000000000000..f74bd8d553e8f --- /dev/null +++ b/tests/puppeteer/pages/BO/viewBrand.js @@ -0,0 +1,22 @@ +require('module-alias/register'); +const BOBasePage = require('@pages/BO/BObasePage'); + +module.exports = class ViewBrand extends BOBasePage { + constructor(page) { + super(page); + + // Selectors + this.contentDiv = 'div.content-div'; + this.addressesGrid = `${this.contentDiv} > div.row > div > div.row:nth-of-type(2)`; + this.addressesGridHeader = `${this.addressesGrid} h3.card-header`; + this.addressesTableBody = `${this.addressesGrid} .card-body table tbody`; + this.addressesTableRow = `${this.addressesTableBody} tr:nth-of-type(%ROW)`; + this.addressesTableColumn = `${this.addressesTableRow} td:nth-of-type(%COLUMN)`; + this.productsGrid = `${this.contentDiv} > div.row > div > div.row:nth-of-type(3)`; + this.productsGridHeader = `${this.productsGrid} h3.card-header`; + } + + /* + Methods + */ +}; From 8903248aa905012f5861a46e3e9436b36978fbab Mon Sep 17 00:00:00 2001 From: Boubker BRIBRI Date: Fri, 25 Oct 2019 15:43:22 +0200 Subject: [PATCH 073/195] Add functional tests for brands and addresses --- .../brands/01_filterAndQuickEditBrands.js | 180 +++++++++ .../brands/02_filterBrandAddresses.js | 204 +++++++++++ .../brands/03_CRUDBrandAndAddress.js | 343 ++++++++++++++++++ .../brands/04_BulkActionsBrands.js | 193 ++++++++++ .../brands/05_BulkActionsBrandAddresses.js | 122 +++++++ 5 files changed, 1042 insertions(+) create mode 100644 tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/01_filterAndQuickEditBrands.js create mode 100644 tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/02_filterBrandAddresses.js create mode 100644 tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/03_CRUDBrandAndAddress.js create mode 100644 tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/04_BulkActionsBrands.js create mode 100644 tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/05_BulkActionsBrandAddresses.js diff --git a/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/01_filterAndQuickEditBrands.js b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/01_filterAndQuickEditBrands.js new file mode 100644 index 0000000000000..74dc71f21e2ae --- /dev/null +++ b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/01_filterAndQuickEditBrands.js @@ -0,0 +1,180 @@ +require('module-alias/register'); +// Using chai +const {expect} = require('chai'); +const helper = require('@utils/helpers'); +const loginCommon = require('@commonTests/loginBO'); +const {demoBrands} = require('@data/demo/brands'); +// Importing pages +const BOBasePage = require('@pages/BO/BObasePage'); +const LoginPage = require('@pages/BO/login'); +const DashboardPage = require('@pages/BO/dashboard'); +const BrandsPage = require('@pages/BO/brands'); + +let browser; +let page; +let numberOfBrands = 0; + +// Init objects needed +const init = async function () { + return { + boBasePage: new BOBasePage(page), + loginPage: new LoginPage(page), + dashboardPage: new DashboardPage(page), + brandsPage: new BrandsPage(page), + }; +}; + +// Filter And Quick Edit brands +describe('Filter And Quick Edit brands', async () => { + // before and after functions + before(async function () { + browser = await helper.createBrowser(); + page = await helper.newTab(browser); + this.pageObjects = await init(); + }); + after(async () => { + await helper.closeBrowser(browser); + }); + // Login into BO and go to brands page + loginCommon.loginBO(); + it('should go to brands page', async function () { + await this.pageObjects.boBasePage.goToSubMenu( + this.pageObjects.boBasePage.productsParentLink, + this.pageObjects.boBasePage.brandsAndSuppliersLink, + ); + await this.pageObjects.boBasePage.closeSfToolBar(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset all filters and get Number of brands in BO', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + numberOfBrands = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrands).to.be.above(0); + }); + // 1 : Filter brands + describe('Filter brands', async () => { + it('should filter by Id', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'id_manufacturer', demoBrands.first.id); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'id_manufacturer'), + ); + await expect(textColumn).to.contains(demoBrands.first.id); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.equal(numberOfBrands); + }); + it('should filter by brand name', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', demoBrands.first.name); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(demoBrands.first.name); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.equal(numberOfBrands); + }); + it('should filter by Enabled \'Yes\'', async function () { + await this.pageObjects.brandsPage.filterBrands( + 'select', + 'active', + demoBrands.first.enabled ? 'Yes' : 'No', + ); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + /* eslint-disable no-await-in-loop */ + for (let i = 1; i <= numberOfBrandsAfterFilter; i++) { + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'active'), + ); + await expect(textColumn).to.contains('check'); + } + /* eslint-enable no-await-in-loop */ + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.equal(numberOfBrands); + }); + }); + // 2 : Edit brands in list + describe('Quick Edit brands', async () => { + // Steps + it('should filter by brand name', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', demoBrands.first.name); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(demoBrands.first.name); + }); + it('should disable first brand', async function () { + const isActionPerformed = await this.pageObjects.brandsPage.updateEnabledValue( + '1', + false, + ); + if (isActionPerformed) { + const resultMessage = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.alertSuccessBlockParagraph, + ); + await expect(resultMessage).to.contains(this.pageObjects.brandsPage.successfulUpdateStatusMessage); + } + const isStatusChanged = await this.pageObjects.brandsPage.elementVisible( + this.pageObjects.brandsPage.brandsEnableColumnNotValidIcon.replace('%ROW', 1), + 100, + ); + await expect(isStatusChanged).to.be.true; + }); + it('should enable first brand', async function () { + const isActionPerformed = await this.pageObjects.brandsPage.updateEnabledValue('1', true); + if (isActionPerformed) { + const resultMessage = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.alertSuccessBlockParagraph, + ); + await expect(resultMessage).to.contains(this.pageObjects.brandsPage.successfulUpdateStatusMessage); + } + const isStatusChanged = await this.pageObjects.brandsPage.elementVisible( + this.pageObjects.brandsPage.brandsEnableColumnValidIcon.replace('%ROW', 1), + 100, + ); + await expect(isStatusChanged).to.be.true; + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.equal(numberOfBrands); + }); + }); +}); diff --git a/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/02_filterBrandAddresses.js b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/02_filterBrandAddresses.js new file mode 100644 index 0000000000000..57e4cbe5f81eb --- /dev/null +++ b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/02_filterBrandAddresses.js @@ -0,0 +1,204 @@ +require('module-alias/register'); +// Using chai +const {expect} = require('chai'); +const helper = require('@utils/helpers'); +const loginCommon = require('@commonTests/loginBO'); +const {demoAddresses} = require('@data/demo/brands'); +// Importing pages +const BOBasePage = require('@pages/BO/BObasePage'); +const LoginPage = require('@pages/BO/login'); +const DashboardPage = require('@pages/BO/dashboard'); +const BrandsPage = require('@pages/BO/brands'); + +let browser; +let page; +let numberOfBrandsAddresses = 0; + +// Init objects needed +const init = async function () { + return { + boBasePage: new BOBasePage(page), + loginPage: new LoginPage(page), + dashboardPage: new DashboardPage(page), + brandsPage: new BrandsPage(page), + }; +}; + +// Filter And Quick Edit Addresses +describe('Filter And Quick Edit Addresses', async () => { + // before and after functions + before(async function () { + browser = await helper.createBrowser(); + page = await helper.newTab(browser); + this.pageObjects = await init(); + }); + after(async () => { + await helper.closeBrowser(browser); + }); + // Login into BO and go to brands page + loginCommon.loginBO(); + it('should go to brands page', async function () { + await this.pageObjects.boBasePage.goToSubMenu( + this.pageObjects.boBasePage.productsParentLink, + this.pageObjects.boBasePage.brandsAndSuppliersLink, + ); + await this.pageObjects.boBasePage.closeSfToolBar(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset all filters and get Number of brands in BO', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + numberOfBrandsAddresses = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddresses).to.be.above(0); + }); + // 1 : Filter brands + describe('Filter brands addresses', async () => { + it('should filter by Id', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'id_address', demoAddresses.first.id); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1).replace('%COLUMN', 'id_address'), + ); + await expect(textColumn).to.contains(demoAddresses.first.id); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + it('should filter by brand name', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'name', demoAddresses.first.brand); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(demoAddresses.first.brand); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + it('should filter by Manufacturer firstname', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'firstname', demoAddresses.first.firstName); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'firstname'), + ); + await expect(textColumn).to.contains(demoAddresses.first.firstName); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + it('should filter by Manufacturer lastname', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'lastname', demoAddresses.first.lastName); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'lastname'), + ); + await expect(textColumn).to.contains(demoAddresses.first.lastName); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + it('should filter by Address postal code', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'postcode', demoAddresses.first.postalCode); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'postcode'), + ); + await expect(textColumn).to.contains(demoAddresses.first.postalCode); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + it('should filter by City', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'city', demoAddresses.first.city); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'city'), + ); + await expect(textColumn).to.contains(demoAddresses.first.city); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + it('should filter by Country', async function () { + await this.pageObjects.brandsPage.filterAddresses('select', 'country', demoAddresses.first.country); + const numberOfBrandsAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterFilter).to.be.at.most(numberOfBrandsAddresses); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'country'), + ); + await expect(textColumn).to.contains(demoAddresses.first.country); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.equal(numberOfBrandsAddresses); + }); + }); +}); diff --git a/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/03_CRUDBrandAndAddress.js b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/03_CRUDBrandAndAddress.js new file mode 100644 index 0000000000000..c25f83b84fed6 --- /dev/null +++ b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/03_CRUDBrandAndAddress.js @@ -0,0 +1,343 @@ +require('module-alias/register'); +const {expect} = require('chai'); +const helper = require('@utils/helpers'); +const files = require('@utils/files'); +const BrandFaker = require('@data/faker/brand'); +const BrandAddressFaker = require('@data/faker/brandAddress'); +const loginCommon = require('@commonTests/loginBO'); +// Importing pages +const BOBasePage = require('@pages/BO/BObasePage'); +const LoginPage = require('@pages/BO/login'); +const DashboardPage = require('@pages/BO/dashboard'); +const BrandsPage = require('@pages/BO/brands'); +const AddBrandPage = require('@pages/BO/addBrand'); +const ViewBrandPage = require('@pages/BO/viewBrand'); +const AddBrandAddressPage = require('@pages/BO/addBrandAddress'); + +let browser; +let page; +let numberOfBrands = 0; +let numberOfBrandsAddresses = 0; +let createBrandData; +let editBrandData; +let createBrandAddressData; +let editBrandAddressData; + +// Init objects needed +const init = async function () { + return { + boBasePage: new BOBasePage(page), + loginPage: new LoginPage(page), + dashboardPage: new DashboardPage(page), + brandsPage: new BrandsPage(page), + addBrandPage: new AddBrandPage(page), + viewBrandPage: new ViewBrandPage(page), + addBrandAddressPage: new AddBrandAddressPage(page), + }; +}; + +// CRUD Brand And Address +describe('Create, Update and Delete Brand and Address', async () => { + // before and after functions + before(async function () { + browser = await helper.createBrowser(); + page = await helper.newTab(browser); + this.pageObjects = await init(); + createBrandData = await (new BrandFaker()); + editBrandData = await (new BrandFaker()); + createBrandAddressData = await (new BrandAddressFaker({brandName: createBrandData.name})); + editBrandAddressData = await (new BrandAddressFaker({brandName: editBrandData.name})); + }); + after(async () => { + await helper.closeBrowser(browser); + await Promise.all([ + files.deleteFile(createBrandData.logo), + files.deleteFile(editBrandData.logo), + ]); + }); + // Login into BO and go to brands page + loginCommon.loginBO(); + it('should go to brands page', async function () { + await this.pageObjects.boBasePage.goToSubMenu( + this.pageObjects.boBasePage.productsParentLink, + this.pageObjects.boBasePage.brandsAndSuppliersLink, + ); + await this.pageObjects.boBasePage.closeSfToolBar(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + numberOfBrands = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrands).to.be.above(0); + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + numberOfBrandsAddresses = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddresses).to.be.above(0); + }); + // 1: Create Brand + describe('Create Brand', async () => { + it('should go to new brand page', async function () { + await this.pageObjects.brandsPage.goToAddNewBrandPage(); + const pageTitle = await this.pageObjects.addBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandPage.pageTitle); + }); + it('should create brand', async function () { + const result = await this.pageObjects.addBrandPage.createEditBrand(createBrandData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulCreationMessage); + const numberOfBrandsAfterCreation = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterCreation).to.be.equal(numberOfBrands + 1); + }); + }); + // 2: Create Address for this Brand + describe('Create Address associated to created Brand', async () => { + it('should go to new brand address page', async function () { + await this.pageObjects.brandsPage.goToAddNewBrandAddressPage(); + const pageTitle = await this.pageObjects.addBrandAddressPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandAddressPage.pageTitle); + }); + it('should create brand address', async function () { + const result = await this.pageObjects.addBrandAddressPage.createEditBrandAddress(createBrandAddressData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulCreationMessage); + const numberOfBrandsAddressesAfterCreation = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + createBrandData.addresses += 1; + await expect(numberOfBrandsAddressesAfterCreation).to.be.equal(numberOfBrandsAddresses + 1); + }); + }); + // 3 : View Brand and check Address Value in list + describe('View Brand and check Address Value in list', async () => { + it('should filter Brand list by name of brand created', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', createBrandData.name); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(createBrandData.name); + }); + it('should view brand', async function () { + await this.pageObjects.brandsPage.viewBrand('1'); + const pageTitle = await this.pageObjects.viewBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(createBrandData.name); + }); + it('should check existence of the associated address', async function () { + const numberOfAddressesInGrid = await this.pageObjects.viewBrandPage.getNumberFromText( + this.pageObjects.viewBrandPage.addressesGridHeader, + ); + await expect(numberOfAddressesInGrid).to.equal(createBrandData.addresses); + const textColumn = await this.pageObjects.viewBrandPage.getTextContent( + this.pageObjects.viewBrandPage.addressesTableColumn.replace('%ROW', '1').replace('%COLUMN', '1'), + ); + await expect(textColumn).to.contains(`${createBrandAddressData.firstName} ${createBrandAddressData.lastName}`); + }); + it('should return brands Page', async function () { + await this.pageObjects.viewBrandPage.goToPreviousPage(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset brands filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.be.equal(numberOfBrands + 1); + }); + }); + // 4: Update Brand And Verify Brand in Address List + describe('Update Brand And Verify Brand in Address List', async () => { + it('should filter Brand list by name of brand created', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', createBrandData.name); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(createBrandData.name); + }); + it('should go to edit brand page', async function () { + await this.pageObjects.brandsPage.goToEditBrandPage('1'); + const pageTitle = await this.pageObjects.addBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandPage.pageTitleEdit); + }); + it('should edit brand', async function () { + const result = await this.pageObjects.addBrandPage.createEditBrand(editBrandData); + editBrandData.addresses += 1; + await expect(result).to.equal(this.pageObjects.brandsPage.successfulUpdateMessage); + }); + it('should check the update in Brand Address List', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'name', editBrandData.name); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(editBrandData.name); + }); + it('should reset all filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.be.equal(numberOfBrands + 1); + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.be.equal(numberOfBrandsAddresses + 1); + }); + }); + // 5: Update Address + describe('Update Address', async () => { + it('should filter Brand Address list by name of edited brand', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'name', editBrandData.name); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(editBrandData.name); + }); + it('should go to edit brand address page', async function () { + await this.pageObjects.brandsPage.goToEditBrandAddressPage('1'); + const pageTitle = await this.pageObjects.addBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should edit brand address', async function () { + const result = await this.pageObjects.addBrandAddressPage.createEditBrandAddress(editBrandAddressData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulUpdateMessage); + }); + it('should reset Brand Addresses filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.be.equal(numberOfBrandsAddresses + 1); + }); + }); + // 6 : View Brand and check Address Value in list + describe('View Brand and check Address Value in list', async () => { + it('should filter Brand list by name of brand created', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', editBrandData.name); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(editBrandData.name); + }); + it('should view brand', async function () { + await this.pageObjects.brandsPage.viewBrand('1'); + const pageTitle = await this.pageObjects.viewBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(editBrandData.name); + }); + it('should check existence of the associated address', async function () { + const numberOfAddressesInGrid = await this.pageObjects.viewBrandPage.getNumberFromText( + this.pageObjects.viewBrandPage.addressesGridHeader, + ); + await expect(numberOfAddressesInGrid).to.equal(editBrandData.addresses); + const textColumn = await this.pageObjects.viewBrandPage.getTextContent( + this.pageObjects.viewBrandPage.addressesTableColumn.replace('%ROW', '1').replace('%COLUMN', '1'), + ); + await expect(textColumn).to.contains(`${editBrandAddressData.firstName} ${editBrandAddressData.lastName}`); + }); + it('should return brands Page', async function () { + await this.pageObjects.viewBrandPage.goToPreviousPage(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset brands filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.be.equal(numberOfBrands + 1); + }); + }); + // 7 : Delete Brand And Verify that Address has no Brand associated + describe('Delete Brand And Verify that Address has no Brand associated', async () => { + it('should filter Brand list by name of edited brand', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', editBrandData.name); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn.replace('%TABLE', 'manufacturer') + .replace('%ROW', 1).replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains(editBrandData.name); + }); + it('should Delete brand', async function () { + const result = await this.pageObjects.brandsPage.deleteBrand('1'); + await expect(result).to.be.equal(this.pageObjects.brandsPage.successfulDeleteMessage); + }); + it('should check the delete in Brand Address List', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'firstname', editBrandAddressData.firstName); + await this.pageObjects.brandsPage.filterAddresses('input', 'lastname', editBrandAddressData.lastName); + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains('--'); + }); + it('should reset Brand filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.be.equal(numberOfBrands); + }); + }); + // 8 : Delete Address + describe('Delete brand Address', async () => { + it('should filter Brand Address list by firstName and lastName', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'firstname', editBrandAddressData.firstName); + await this.pageObjects.brandsPage.filterAddresses('input', 'lastname', editBrandAddressData.lastName); + const textColumnFirstName = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'firstname'), + ); + await expect(textColumnFirstName).to.contains(editBrandAddressData.firstName); + const textColumnLastName = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'lastname'), + ); + await expect(textColumnLastName).to.contains(editBrandAddressData.lastName); + }); + it('should Delete Brand Address', async function () { + const result = await this.pageObjects.brandsPage.deleteBrandAddress('1'); + await expect(result).to.be.equal(this.pageObjects.brandsPage.successfulDeleteMessage); + }); + it('should reset Brand Addresses filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandsAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandsAddressesAfterReset).to.be.equal(numberOfBrandsAddresses); + }); + }); +}); diff --git a/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/04_BulkActionsBrands.js b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/04_BulkActionsBrands.js new file mode 100644 index 0000000000000..d9e06a928de99 --- /dev/null +++ b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/04_BulkActionsBrands.js @@ -0,0 +1,193 @@ +require('module-alias/register'); +const {expect} = require('chai'); +const helper = require('@utils/helpers'); +const files = require('@utils/files'); +const BrandFaker = require('@data/faker/brand'); +const loginCommon = require('@commonTests/loginBO'); +// Importing pages +const BOBasePage = require('@pages/BO/BObasePage'); +const LoginPage = require('@pages/BO/login'); +const DashboardPage = require('@pages/BO/dashboard'); +const BrandsPage = require('@pages/BO/brands'); +const AddBrandPage = require('@pages/BO/addBrand'); + +let browser; +let page; +let numberOfBrands = 0; +let firstBrandData; +let secondBrandData; + +// Init objects needed +const init = async function () { + return { + boBasePage: new BOBasePage(page), + loginPage: new LoginPage(page), + dashboardPage: new DashboardPage(page), + brandsPage: new BrandsPage(page), + addBrandPage: new AddBrandPage(page), + }; +}; + +// Create 2 brands, Enable, disable and delete with bulk actions +describe('Create 2 brands, Enable, disable and delete with bulk actions', async () => { + // before and after functions + before(async function () { + browser = await helper.createBrowser(); + page = await helper.newTab(browser); + this.pageObjects = await init(); + firstBrandData = await (new BrandFaker({name: 'BrandToDelete'})); + secondBrandData = await (new BrandFaker({name: 'BrandToDelete2'})); + }); + after(async () => { + await helper.closeBrowser(browser); + await Promise.all([ + files.deleteFile(firstBrandData.logo), + files.deleteFile(secondBrandData.logo), + ]); + }); + // Login into BO and go to brands page + loginCommon.loginBO(); + it('should go to brands page', async function () { + await this.pageObjects.boBasePage.goToSubMenu( + this.pageObjects.boBasePage.productsParentLink, + this.pageObjects.boBasePage.brandsAndSuppliersLink, + ); + await this.pageObjects.boBasePage.closeSfToolBar(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset all Brands filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + numberOfBrands = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrands).to.be.above(0); + }); + // 1: Create 2 Brands + describe('Create 2 Brands', async () => { + it('should go to new brand page', async function () { + await this.pageObjects.brandsPage.goToAddNewBrandPage(); + const pageTitle = await this.pageObjects.addBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandPage.pageTitle); + }); + it('should create first brand', async function () { + const result = await this.pageObjects.addBrandPage.createEditBrand(firstBrandData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulCreationMessage); + const numberOfBrandsAfterCreation = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterCreation).to.be.equal(numberOfBrands + 1); + }); + it('should go to new brand page', async function () { + await this.pageObjects.brandsPage.goToAddNewBrandPage(); + const pageTitle = await this.pageObjects.addBrandPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandPage.pageTitle); + }); + it('should create second brand', async function () { + const result = await this.pageObjects.addBrandPage.createEditBrand(secondBrandData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulCreationMessage); + const numberOfBrandsAfterCreation = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterCreation).to.be.equal(numberOfBrands + 2); + }); + }); + // 2 : Disable, enable Brands + describe('Disable, enable created Brands', async () => { + it('should filter Brand list by name of brand created', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', 'BrandToDelete'); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + /* eslint-disable no-await-in-loop */ + for (let i = 1; i <= numberOfBrandsAfterFilter; i++) { + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains('BrandToDelete'); + } + /* eslint-enable no-await-in-loop */ + }); + it('should disable brands', async function () { + const disableTextResult = await this.pageObjects.brandsPage.changeBrandsEnabledColumnBulkActions(false); + await expect(disableTextResult).to.be.equal(this.pageObjects.brandsPage.successfulUpdateStatusMessage); + const numberOfBrandsInGrid = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsInGrid).to.be.at.most(numberOfBrands); + /* eslint-disable no-await-in-loop */ + for (let i = 1; i <= numberOfBrandsInGrid; i++) { + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer') + .replace('%ROW', 1) + .replace('%COLUMN', 'active'), + ); + await expect(textColumn).to.contains('clear'); + } + /* eslint-enable no-await-in-loop */ + }); + it('should enable brands', async function () { + const disableTextResult = await this.pageObjects.brandsPage.changeBrandsEnabledColumnBulkActions(true); + await expect(disableTextResult).to.be.equal(this.pageObjects.brandsPage.successfulUpdateStatusMessage); + const numberOfBrandsInGrid = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsInGrid).to.be.at.most(numberOfBrands); + /* eslint-disable no-await-in-loop */ + for (let i = 1; i <= numberOfBrandsInGrid; i++) { + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer') + .replace('%ROW', 1) + .replace('%COLUMN', 'active'), + ); + await expect(textColumn).to.contains('check'); + } + /* eslint-enable no-await-in-loop */ + }); + it('should reset Brand filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.be.equal(numberOfBrands + 2); + }); + }); + // 3 : Delete Brands created with bulk actions + describe('Delete Brands with Bulk Actions', async () => { + it('should filter Brand list by name of brand created', async function () { + await this.pageObjects.brandsPage.filterBrands('input', 'name', 'BrandToDelete'); + const numberOfBrandsAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterFilter).to.be.at.most(numberOfBrands); + /* eslint-disable no-await-in-loop */ + for (let i = 1; i <= numberOfBrandsAfterFilter; i++) { + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer') + .replace('%ROW', 1) + .replace('%COLUMN', 'name'), + ); + await expect(textColumn).to.contains('BrandToDelete'); + } + /* eslint-enable no-await-in-loop */ + }); + it('should delete Brands with Bulk Actions and check Result', async function () { + const deleteTextResult = await this.pageObjects.brandsPage.deleteWithBulkActions('manufacturer'); + await expect(deleteTextResult).to.be.equal(this.pageObjects.brandsPage.successfulDeleteMessage); + }); + it('should reset Brand filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer'); + const numberOfBrandsAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer'), + ); + await expect(numberOfBrandsAfterReset).to.be.equal(numberOfBrands); + }); + }); +}); diff --git a/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/05_BulkActionsBrandAddresses.js b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/05_BulkActionsBrandAddresses.js new file mode 100644 index 0000000000000..2a879a3003c35 --- /dev/null +++ b/tests/puppeteer/campaigns/functional/BO/catalog/brandsAndSuppliers/brands/05_BulkActionsBrandAddresses.js @@ -0,0 +1,122 @@ +require('module-alias/register'); +const {expect} = require('chai'); +const helper = require('@utils/helpers'); +const BrandAddressFaker = require('@data/faker/brandAddress'); +const loginCommon = require('@commonTests/loginBO'); +// Importing pages +const BOBasePage = require('@pages/BO/BObasePage'); +const LoginPage = require('@pages/BO/login'); +const DashboardPage = require('@pages/BO/dashboard'); +const BrandsPage = require('@pages/BO/brands'); +const AddBrandAddressPage = require('@pages/BO/addBrandAddress'); + +let browser; +let page; +let numberOfBrandAddresses = 0; +let firstAddressData; +let secondAddressData; + +// Init objects needed +const init = async function () { + return { + boBasePage: new BOBasePage(page), + loginPage: new LoginPage(page), + dashboardPage: new DashboardPage(page), + brandsPage: new BrandsPage(page), + addBrandAddressPage: new AddBrandAddressPage(page), + }; +}; + +// Create 2 brands, Enable, disable and delete with bulk actions +describe('Create 2 brand Addresses and delete with bulk actions', async () => { + // before and after functions + before(async function () { + browser = await helper.createBrowser(); + page = await helper.newTab(browser); + this.pageObjects = await init(); + firstAddressData = await (new BrandAddressFaker({firstName: 'AddressToDelete'})); + secondAddressData = await (new BrandAddressFaker({firstName: 'AddressToDeleteTwo'})); + }); + after(async () => { + await helper.closeBrowser(browser); + }); + // Login into BO and go to brands page + loginCommon.loginBO(); + it('should go to brands page', async function () { + await this.pageObjects.boBasePage.goToSubMenu( + this.pageObjects.boBasePage.productsParentLink, + this.pageObjects.boBasePage.brandsAndSuppliersLink, + ); + await this.pageObjects.boBasePage.closeSfToolBar(); + const pageTitle = await this.pageObjects.brandsPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.brandsPage.pageTitle); + }); + it('should reset all Addresses filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + numberOfBrandAddresses = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandAddresses).to.be.above(0); + }); + // 1: Create 2 Addresses + describe('Create 2 Addresses', async () => { + it('should go to new brand Address page', async function () { + await this.pageObjects.brandsPage.goToAddNewBrandAddressPage(); + const pageTitle = await this.pageObjects.addBrandAddressPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandAddressPage.pageTitle); + }); + it('should create first brand', async function () { + const result = await this.pageObjects.addBrandAddressPage.createEditBrandAddress(firstAddressData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulCreationMessage); + const numberOfBrandAddressesAfterCreation = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandAddressesAfterCreation).to.be.equal(numberOfBrandAddresses + 1); + }); + it('should go to new brand Address page', async function () { + await this.pageObjects.brandsPage.goToAddNewBrandAddressPage(); + const pageTitle = await this.pageObjects.addBrandAddressPage.getPageTitle(); + await expect(pageTitle).to.contains(this.pageObjects.addBrandAddressPage.pageTitle); + }); + it('should create second brand', async function () { + const result = await this.pageObjects.addBrandAddressPage.createEditBrandAddress(secondAddressData); + await expect(result).to.equal(this.pageObjects.brandsPage.successfulCreationMessage); + const numberOfBrandAddressesAfterCreation = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandAddressesAfterCreation).to.be.equal(numberOfBrandAddresses + 2); + }); + }); + // 2 : Delete Brand Addresses created with bulk actions + describe('Delete Addresses with Bulk Actions', async () => { + it('should filter Addresses list by firstName', async function () { + await this.pageObjects.brandsPage.filterAddresses('input', 'firstname', 'AddressToDelete'); + const numberOfBrandAddressesAfterFilter = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandAddressesAfterFilter).to.be.at.most(numberOfBrandAddresses); + /* eslint-disable no-await-in-loop */ + for (let i = 1; i <= numberOfBrandAddressesAfterFilter; i++) { + const textColumn = await this.pageObjects.brandsPage.getTextContent( + this.pageObjects.brandsPage.tableColumn + .replace('%TABLE', 'manufacturer_address') + .replace('%ROW', 1) + .replace('%COLUMN', 'firstname'), + ); + await expect(textColumn).to.contains('AddressToDelete'); + } + /* eslint-enable no-await-in-loop */ + }); + it('should delete Addresses with Bulk Actions and check Result', async function () { + const deleteTextResult = await this.pageObjects.brandsPage.deleteWithBulkActions('manufacturer_address'); + await expect(deleteTextResult).to.be.equal(this.pageObjects.brandsPage.successfulDeleteMessage); + }); + it('should reset Addresses filters', async function () { + await this.pageObjects.brandsPage.resetFilters('manufacturer_address'); + const numberOfBrandAddressesAfterReset = await this.pageObjects.brandsPage.getNumberFromText( + this.pageObjects.brandsPage.gridHeaderTitle.replace('%TABLE', 'manufacturer_address'), + ); + await expect(numberOfBrandAddressesAfterReset).to.be.equal(numberOfBrandAddresses); + }); + }); +}); From 57b0e4c4685f76c09012f81bd9486c8ce21c7cdc Mon Sep 17 00:00:00 2001 From: Thomas Baccelli Date: Fri, 25 Oct 2019 16:14:31 +0200 Subject: [PATCH 074/195] Change checkout button wording --- themes/classic/templates/checkout/_partials/steps/payment.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/classic/templates/checkout/_partials/steps/payment.tpl b/themes/classic/templates/checkout/_partials/steps/payment.tpl index 2f14dfdaf4935..27641f02b0895 100644 --- a/themes/classic/templates/checkout/_partials/steps/payment.tpl +++ b/themes/classic/templates/checkout/_partials/steps/payment.tpl @@ -128,7 +128,7 @@
{if $show_final_summary}
- @@ -44,40 +44,42 @@
-
-
- {{ 'Product'|trans({}, 'Admin.Global') }} -
-
- -
-
-
-
- {{ 'Combination'|trans({}, 'Admin.Global') }} -
-
- -
-
-
-
- {{ 'Quantity'|trans({}, 'Admin.Global') }} +
+
+
+ {{ 'Product'|trans({}, 'Admin.Global') }} +
+
+ +
-
- + +
+
+ {{ 'Quantity'|trans({}, 'Admin.Global') }} +
+
+ + + {{ 'In stock'|trans({}, 'Admin.Orderscustomers.Feature') }} + + +
-
-
-
- +
+
+ +
-
-
-
-
+
+
+
+
+
@@ -88,11 +90,12 @@ + - + +
{{ 'Price per unit'|trans({}, 'Admin.Catalog.Feature') }} {{ 'Quantity'|trans({}, 'Admin.Global') }} {{ 'Price'|trans({}, 'Admin.Global') }}

Hummingbird printed t-shirt

S- White

@@ -104,11 +107,14 @@
- + 45 + +
@@ -154,3 +160,60 @@
+ +
+
+
+ +
+
+
+ + + + + + + + + + From 32529a3ff73d76c9d9ac81aaccf353f60ac7688e Mon Sep 17 00:00:00 2001 From: Raimondas Date: Mon, 14 Oct 2019 10:05:56 +0300 Subject: [PATCH 094/195] Updated handler php doc --- .../QueryHandler/SearchProductsForOrderCreationHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php b/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php index 280b9d0de5a46..f45ebd3b220af 100644 --- a/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php +++ b/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php @@ -42,7 +42,7 @@ use Product; /** - * Handles product search for order creation + * Handles product for order creation search */ final class SearchProductsForOrderCreationHandler implements SearchProductsForOrderCreationHandlerInterface { From d73f89ea9121da0a706b359b9110857b4501cbbb Mon Sep 17 00:00:00 2001 From: Raimondas Date: Mon, 14 Oct 2019 10:06:15 +0300 Subject: [PATCH 095/195] order product component assets --- .../js/pages/order/create-order-page.js | 351 ++++++++++++++++++ .../js/pages/order/create/create-order-map.js | 22 ++ .../order/create/order-product-component.js | 313 ++++++++++++++++ .../new-theme/public/order_create.bundle.js | 2 +- 4 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 admin-dev/themes/new-theme/js/pages/order/create-order-page.js create mode 100644 admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js diff --git a/admin-dev/themes/new-theme/js/pages/order/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create-order-page.js new file mode 100644 index 0000000000000..40e8cfd28d758 --- /dev/null +++ b/admin-dev/themes/new-theme/js/pages/order/create-order-page.js @@ -0,0 +1,351 @@ +/** + * 2007-2019 PrestaShop and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to https://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +import createOrderPageMap from './create-order-map'; +import CustomerSearcherComponent from './customer-searcher-component'; +import ShippingRenderer from './shipping-renderer'; +import OrderProductComponent from './create/order-product-component'; + +const $ = window.$; + +/** + * Page Object for "Create order" page + */ +export default class CreateOrderPage { + constructor() { + this.data = {}; + this.$container = $(createOrderPageMap.orderCreationContainer); + + this.customerSearcher = new CustomerSearcherComponent(); + this.shippingRenderer = new ShippingRenderer(); + this.orderProducts = new OrderProductComponent(); + + return { + listenForCustomerSearch: () => this._handleCustomerSearch(), + listenForCustomerChooseForOrderCreation: () => this._handleCustomerChooseForOrderCreation(), + }; + } + + /** + * Searches for customer + * + * @private + */ + _handleCustomerSearch() { + this.$container.on('input', createOrderPageMap.customerSearchInput, () => { + this.customerSearcher.onCustomerSearch(); + }); + } + + /** + * Chooses customer for which order is being created + * + * @private + */ + _handleCustomerChooseForOrderCreation() { + this.$container.on('click', createOrderPageMap.chooseCustomerBtn, (event) => { + this.data.customer_id = this.customerSearcher.onCustomerChooseForOrderCreation(event); + + this._loadCartSummaryAfterChoosingCustomer(); + }); + + this.$container.on('click', createOrderPageMap.changeCustomerBtn, () => this.customerSearcher.onCustomerChange()); + this.$container.on('change', createOrderPageMap.addressSelect, () => this._changeCartAddresses()); + + this.$container.on('click', '.js-use-cart-btn', () => { + const cartId = $(event.target).data('cart-id'); + + this._choosePreviousCart(cartId); + }); + } + + /** + * Loads cart summary with customer's carts & orders history. + * + * @private + */ + _loadCartSummaryAfterChoosingCustomer() { + $.ajax(this.$container.data('last-empty-cart-url'), { + method: 'POST', + data: { + id_customer: this.data.customer_id, + }, + dataType: 'json', + }).then((response) => { + this.data.cart_id = response.cart.id_cart; + + const checkoutHistory = { + carts: typeof response.carts !== 'undefined' ? response.carts : [], + orders: typeof response.orders !== 'undefined' ? response.orders : [], + }; + + this._renderCheckoutHistory(checkoutHistory); + this._renderCartSummary(response); + }); + } + + /** + * Renders previous Carts & Orders from customer history + * + * @param {Object} checkoutHistory + * + * @private + */ + _renderCheckoutHistory(checkoutHistory) { + this._renderCustomerCarts(checkoutHistory.carts); + this._renderCustomerOrders(checkoutHistory.orders); + + $(createOrderPageMap.customerCheckoutHistory).removeClass('d-none'); + } + + /** + * Renders customer carts from checkout history + * + * @param {Object} carts + * + * @private + */ + _renderCustomerCarts(carts) { + const $cartsTable = $(createOrderPageMap.customerCartsTable); + const $cartsTableRowTemplate = $($(createOrderPageMap.customerCartsTableRowTemplate).html()); + + $cartsTable.find('tbody').empty(); + + if (!carts) { + return; + } + + for (const key in carts) { + if (!carts.hasOwnProperty(key)) { + continue; + } + + const cart = carts[key]; + const $template = $cartsTableRowTemplate.clone(); + + $template.find('.js-cart-id').text(cart.id_cart); + $template.find('.js-cart-date').text(cart.date_add); + $template.find('.js-cart-total').text(cart.total_price); + + $template.find('.js-use-cart-btn').data('cart-id', cart.id_cart); + + $cartsTable.find('tbody').append($template); + } + + $(createOrderPageMap.customerCheckoutHistory).removeClass('d-none'); + } + + /** + * Renders cart summary on the page + * + * @param {Object} cartSummary + * + * @private + */ + _renderCartSummary(cartSummary) { + this._renderAddressesSelect(cartSummary); + + // render Summary block when at least 1 product is in cart + // and delivery options are available + + this._showCartSummary(); + } + + /** + * Renders customer orders + * + * @param {Object} orders + * + * @private + */ + _renderCustomerOrders(orders) { + const $ordersTable = $(createOrderPageMap.customerOrdersTable); + const $rowTemplate = $($(createOrderPageMap.customerOrdersTableRowTemplate).html()); + + $ordersTable.find('tbody').empty(); + + if (!orders) { + return; + } + + for (const key in Object.keys(orders)) { + if (!orders.hasOwnProperty(key)) { + continue; + } + + const order = orders[key]; + const $template = $rowTemplate.clone(); + + $template.find('.js-order-id').text(order.id_order); + $template.find('.js-order-date').text(order.date_add); + $template.find('.js-order-products').text(order.nb_products); + $template.find('.js-order-total-paid').text(order.total_paid_real); + $template.find('.js-order-status').text(order.order_state); + + $ordersTable.find('tbody').append($template); + } + } + + /** + * Shows Cart, Vouchers, Addresses blocks + * + * @private + */ + _showCartSummary() { + $(createOrderPageMap.cartBlock).removeClass('d-none'); + $(createOrderPageMap.vouchersBlock).removeClass('d-none'); + $(createOrderPageMap.addressesBlock).removeClass('d-none'); + } + + /** + * Renders Delivery & Invoice addresses select + * + * @param {Object} cartSummary + * + * @private + */ + _renderAddressesSelect(cartSummary) { + let deliveryAddressDetailsContent = ''; + let invoiceAddressDetailsContent = ''; + + const $deliveryAddressDetails = $(createOrderPageMap.deliveryAddressDetails); + const $invoiceAddressDetails = $(createOrderPageMap.invoiceAddressDetails); + const $deliveryAddressSelect = $(createOrderPageMap.deliveryAddressSelect); + const $invoiceAddressSelect = $(createOrderPageMap.invoiceAddressSelect); + + const $addressesContent = $(createOrderPageMap.addressesContent); + const $addressesWarningContent = $(createOrderPageMap.addressesWarning); + + $deliveryAddressDetails.empty(); + $invoiceAddressDetails.empty(); + $deliveryAddressSelect.empty(); + $invoiceAddressSelect.empty(); + + if (cartSummary.addresses.length === 0) { + $addressesWarningContent.removeClass('d-none'); + $addressesContent.addClass('d-none'); + + return; + } + + $addressesContent.removeClass('d-none'); + $addressesWarningContent.addClass('d-none'); + + for (const key in Object.keys(cartSummary.addresses)) { + if (!cartSummary.addresses.hasOwnProperty(key)) { + continue; + } + + const address = cartSummary.addresses[key]; + + const deliveryAddressOption = { + value: address.id_address, + text: address.alias, + }; + + const invoiceAddressOption = { + value: address.id_address, + text: address.alias, + }; + + if (parseInt(cartSummary.cart.id_address_delivery) === parseInt(address.id_address)) { + deliveryAddressDetailsContent = address.formated_address; + deliveryAddressOption.selected = 'selected'; + } + + if (parseInt(cartSummary.cart.id_address_invoice) === parseInt(address.id_address)) { + invoiceAddressDetailsContent = address.formated_address; + invoiceAddressOption.selected = 'selected'; + } + + $deliveryAddressSelect.append($('
- From 195116c9bd57eafbd8c239f36b3c127606058108 Mon Sep 17 00:00:00 2001 From: Raimondas Date: Wed, 16 Oct 2019 16:44:35 +0300 Subject: [PATCH 097/195] Add product to cart action --- .../Command/AddCustomizationFieldsCommand.php | 32 +++++++++++++++++ .../Admin/Sell/Order/OrderController.php | 36 ++++++++++++++++++- .../routing/admin/sell/orders/orders.yml | 8 ++++- .../Order/Order/Blocks/Create/cart.html.twig | 2 +- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php diff --git a/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php b/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php new file mode 100644 index 0000000000000..9250a45e77bc6 --- /dev/null +++ b/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php @@ -0,0 +1,32 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Cart\Command; + + +class AddCustomizationFieldsCommand +{ +} diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php index 38f4f8b0f99f2..722fc1f289fec 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php @@ -51,6 +51,7 @@ use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPreview; use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductsForOrderCreation; use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId; +use PrestaShop\PrestaShop\Core\Exception\ProductException; use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\OrderGridDefinitionFactory; use PrestaShop\PrestaShop\Core\Search\Filters\OrderFilters; use PrestaShopBundle\Component\CsvResponse; @@ -75,6 +76,8 @@ */ class OrderController extends FrameworkBundleAdminController { + const PRODUCT_CUSTOMIZATION_FIELDS_REQUEST_KEY = 'customization'; + /** * Shows list of orders * @@ -603,6 +606,25 @@ public function searchProductsAction(Request $request): Response } } + /** + * @param Request $request + * @return Response + */ + public function addProductAction(Request $request): Response + { + try { + $this->addCustomizationFields($request); + + return new Response(); + } catch (Exception $e) { + return $this->json([ + 'message' => $this->getErrorMessageForException($e, []), + ], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + } + /** * @param Exception $e * @@ -624,7 +646,7 @@ private function getErrorMessages(Exception $e) ), OrderConstraintException::class => [ OrderConstraintException::INVALID_PRODUCT_SEARCH_PHRASE => $this->trans('Inval') - ] + ], ]; } @@ -661,4 +683,16 @@ private function handleChangeOrderStatusException(ChangeOrderStatusException $e) ); } } + + /** + * @param Request $request + * @return int|null + */ + private function addCustomizationFields(Request $request): ?int + { + $customizationFields = $request->request->get(self::PRODUCT_CUSTOMIZATION_FIELDS_REQUEST_KEY); + $customizationFiles = $request->files->get(self::PRODUCT_CUSTOMIZATION_FIELDS_REQUEST_KEY); + + return null; + } } diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml index 6f59769c8db14..2cb348d5cee48 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml @@ -11,7 +11,7 @@ admin_orders_create: defaults: _controller: PrestaShopBundle:Admin\Sell\Order\Order:create _legacy_controller: AdminOrders - _legacy_link: AdminOrders:addorder +# _legacy_link: AdminOrders:addorder admin_orders_search: path: / @@ -152,3 +152,9 @@ admin_search_product_for_order_creation: methods: [GET] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Order:searchProducts + +admin_add_product_for_order_creation: + path: /add-product + methods: [POST, GET] + defaults: + _controller: PrestaShopBundle:Admin/Sell/Order/Order:addProduct diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig index 1419cb0183fc4..233d4a99e8ee0 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig @@ -68,7 +68,7 @@
-
From 4fa916374daffad6e5bd1f138d8f41a730733d98 Mon Sep 17 00:00:00 2001 From: Raimondas Date: Wed, 16 Oct 2019 16:44:54 +0300 Subject: [PATCH 098/195] Add product to cart submission assets --- .../js/pages/order/create-order-page.js | 351 ------------------ .../js/pages/order/create/create-order-map.js | 3 + .../pages/order/create/create-order-page.js | 2 + .../order/create/order-product-component.js | 150 ++++++-- 4 files changed, 122 insertions(+), 384 deletions(-) delete mode 100644 admin-dev/themes/new-theme/js/pages/order/create-order-page.js diff --git a/admin-dev/themes/new-theme/js/pages/order/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create-order-page.js deleted file mode 100644 index 40e8cfd28d758..0000000000000 --- a/admin-dev/themes/new-theme/js/pages/order/create-order-page.js +++ /dev/null @@ -1,351 +0,0 @@ -/** - * 2007-2019 PrestaShop and Contributors - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.txt. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -import createOrderPageMap from './create-order-map'; -import CustomerSearcherComponent from './customer-searcher-component'; -import ShippingRenderer from './shipping-renderer'; -import OrderProductComponent from './create/order-product-component'; - -const $ = window.$; - -/** - * Page Object for "Create order" page - */ -export default class CreateOrderPage { - constructor() { - this.data = {}; - this.$container = $(createOrderPageMap.orderCreationContainer); - - this.customerSearcher = new CustomerSearcherComponent(); - this.shippingRenderer = new ShippingRenderer(); - this.orderProducts = new OrderProductComponent(); - - return { - listenForCustomerSearch: () => this._handleCustomerSearch(), - listenForCustomerChooseForOrderCreation: () => this._handleCustomerChooseForOrderCreation(), - }; - } - - /** - * Searches for customer - * - * @private - */ - _handleCustomerSearch() { - this.$container.on('input', createOrderPageMap.customerSearchInput, () => { - this.customerSearcher.onCustomerSearch(); - }); - } - - /** - * Chooses customer for which order is being created - * - * @private - */ - _handleCustomerChooseForOrderCreation() { - this.$container.on('click', createOrderPageMap.chooseCustomerBtn, (event) => { - this.data.customer_id = this.customerSearcher.onCustomerChooseForOrderCreation(event); - - this._loadCartSummaryAfterChoosingCustomer(); - }); - - this.$container.on('click', createOrderPageMap.changeCustomerBtn, () => this.customerSearcher.onCustomerChange()); - this.$container.on('change', createOrderPageMap.addressSelect, () => this._changeCartAddresses()); - - this.$container.on('click', '.js-use-cart-btn', () => { - const cartId = $(event.target).data('cart-id'); - - this._choosePreviousCart(cartId); - }); - } - - /** - * Loads cart summary with customer's carts & orders history. - * - * @private - */ - _loadCartSummaryAfterChoosingCustomer() { - $.ajax(this.$container.data('last-empty-cart-url'), { - method: 'POST', - data: { - id_customer: this.data.customer_id, - }, - dataType: 'json', - }).then((response) => { - this.data.cart_id = response.cart.id_cart; - - const checkoutHistory = { - carts: typeof response.carts !== 'undefined' ? response.carts : [], - orders: typeof response.orders !== 'undefined' ? response.orders : [], - }; - - this._renderCheckoutHistory(checkoutHistory); - this._renderCartSummary(response); - }); - } - - /** - * Renders previous Carts & Orders from customer history - * - * @param {Object} checkoutHistory - * - * @private - */ - _renderCheckoutHistory(checkoutHistory) { - this._renderCustomerCarts(checkoutHistory.carts); - this._renderCustomerOrders(checkoutHistory.orders); - - $(createOrderPageMap.customerCheckoutHistory).removeClass('d-none'); - } - - /** - * Renders customer carts from checkout history - * - * @param {Object} carts - * - * @private - */ - _renderCustomerCarts(carts) { - const $cartsTable = $(createOrderPageMap.customerCartsTable); - const $cartsTableRowTemplate = $($(createOrderPageMap.customerCartsTableRowTemplate).html()); - - $cartsTable.find('tbody').empty(); - - if (!carts) { - return; - } - - for (const key in carts) { - if (!carts.hasOwnProperty(key)) { - continue; - } - - const cart = carts[key]; - const $template = $cartsTableRowTemplate.clone(); - - $template.find('.js-cart-id').text(cart.id_cart); - $template.find('.js-cart-date').text(cart.date_add); - $template.find('.js-cart-total').text(cart.total_price); - - $template.find('.js-use-cart-btn').data('cart-id', cart.id_cart); - - $cartsTable.find('tbody').append($template); - } - - $(createOrderPageMap.customerCheckoutHistory).removeClass('d-none'); - } - - /** - * Renders cart summary on the page - * - * @param {Object} cartSummary - * - * @private - */ - _renderCartSummary(cartSummary) { - this._renderAddressesSelect(cartSummary); - - // render Summary block when at least 1 product is in cart - // and delivery options are available - - this._showCartSummary(); - } - - /** - * Renders customer orders - * - * @param {Object} orders - * - * @private - */ - _renderCustomerOrders(orders) { - const $ordersTable = $(createOrderPageMap.customerOrdersTable); - const $rowTemplate = $($(createOrderPageMap.customerOrdersTableRowTemplate).html()); - - $ordersTable.find('tbody').empty(); - - if (!orders) { - return; - } - - for (const key in Object.keys(orders)) { - if (!orders.hasOwnProperty(key)) { - continue; - } - - const order = orders[key]; - const $template = $rowTemplate.clone(); - - $template.find('.js-order-id').text(order.id_order); - $template.find('.js-order-date').text(order.date_add); - $template.find('.js-order-products').text(order.nb_products); - $template.find('.js-order-total-paid').text(order.total_paid_real); - $template.find('.js-order-status').text(order.order_state); - - $ordersTable.find('tbody').append($template); - } - } - - /** - * Shows Cart, Vouchers, Addresses blocks - * - * @private - */ - _showCartSummary() { - $(createOrderPageMap.cartBlock).removeClass('d-none'); - $(createOrderPageMap.vouchersBlock).removeClass('d-none'); - $(createOrderPageMap.addressesBlock).removeClass('d-none'); - } - - /** - * Renders Delivery & Invoice addresses select - * - * @param {Object} cartSummary - * - * @private - */ - _renderAddressesSelect(cartSummary) { - let deliveryAddressDetailsContent = ''; - let invoiceAddressDetailsContent = ''; - - const $deliveryAddressDetails = $(createOrderPageMap.deliveryAddressDetails); - const $invoiceAddressDetails = $(createOrderPageMap.invoiceAddressDetails); - const $deliveryAddressSelect = $(createOrderPageMap.deliveryAddressSelect); - const $invoiceAddressSelect = $(createOrderPageMap.invoiceAddressSelect); - - const $addressesContent = $(createOrderPageMap.addressesContent); - const $addressesWarningContent = $(createOrderPageMap.addressesWarning); - - $deliveryAddressDetails.empty(); - $invoiceAddressDetails.empty(); - $deliveryAddressSelect.empty(); - $invoiceAddressSelect.empty(); - - if (cartSummary.addresses.length === 0) { - $addressesWarningContent.removeClass('d-none'); - $addressesContent.addClass('d-none'); - - return; - } - - $addressesContent.removeClass('d-none'); - $addressesWarningContent.addClass('d-none'); - - for (const key in Object.keys(cartSummary.addresses)) { - if (!cartSummary.addresses.hasOwnProperty(key)) { - continue; - } - - const address = cartSummary.addresses[key]; - - const deliveryAddressOption = { - value: address.id_address, - text: address.alias, - }; - - const invoiceAddressOption = { - value: address.id_address, - text: address.alias, - }; - - if (parseInt(cartSummary.cart.id_address_delivery) === parseInt(address.id_address)) { - deliveryAddressDetailsContent = address.formated_address; - deliveryAddressOption.selected = 'selected'; - } - - if (parseInt(cartSummary.cart.id_address_invoice) === parseInt(address.id_address)) { - invoiceAddressDetailsContent = address.formated_address; - invoiceAddressOption.selected = 'selected'; - } - - $deliveryAddressSelect.append($('').attr('value', value.product_id).text(name).attr('data-index', index) + $('').attr('value', product.product_id).text(name).attr('data-index', index) ); } @@ -138,7 +151,7 @@ export default class OrderProductComponent { * @param product * @private */ - _fillRelatedProductFields(product) { + _fillFieldsRelatedToProduct(product) { this._fillCombinations(product.combinations); this._resolveCustomizationFields(product.customization_fields); } @@ -150,10 +163,10 @@ export default class OrderProductComponent { * @private */ _handleProductChange(event) { - const index = $(event.target).find(':selected').data('index'); + const index = $(event.currentTarget).find(':selected').data('index'); const product = this.products[index]; - this._fillRelatedProductFields(product); + this._fillFieldsRelatedToProduct(product); if (product.combinations === null) { this.combinations = []; @@ -170,7 +183,7 @@ export default class OrderProductComponent { * @private */ _handleCombinationChange(event) { - const index = $(event.target).find(':selected').data('index'); + const index = $(event.currentTarget).find(':selected').val(); const combination = this.combinations[index]; this._updateStock(combination.stock); @@ -196,7 +209,8 @@ export default class OrderProductComponent { $(createOrderPageMap.productSelectRow).after($combinationsTemplate); } - $(createOrderPageMap.combinationsSelect).empty(); + const $combinationsSelect = $(createOrderPageMap.combinationsSelect); + $combinationsSelect.empty(); const entries = Object.entries(combinations.combinations); @@ -208,14 +222,11 @@ export default class OrderProductComponent { } const name = combination.attribute + ' - ' + combination.formatted_price; - $(createOrderPageMap.combinationsSelect).append($('') + $combinationsSelect.append($('') .attr('value', id).text(name)); i += 1; } - - $(createOrderPageMap.combinationsSelect) - .on('change', (event) => this._handleCombinationChange(event)); } /** @@ -240,18 +251,24 @@ export default class OrderProductComponent { $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); - $.each(customizationFields.product_customization_fields, function(index, value) { - const $fieldTemplate = $($(createOrderPageMap.customizedFieldTypes[value.type]).html()); + $.each(customizationFields.product_customization_fields, function(index, field) { + const $fieldTemplate = $($(createOrderPageMap.customizedFieldTypes[field.type]).html()); + $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - if (value.required) { + if (field.required) { $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) .append('*'); } $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - .append(value.name); - $customizedFieldTemplateContent.find(createOrderPageMap.customizedFieldInputWrapper) - .append($fieldTemplate); + .append(field.name); + + const $fieldWrapper = $customizedFieldTemplateContent + .find(createOrderPageMap.customizedFieldInputWrapper); + + $fieldWrapper.append($fieldTemplate); + $fieldWrapper.find(createOrderPageMap.customizedFieldInput) + .attr('data-customization-field-id', field.customization_field_id); $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); }); @@ -281,7 +298,7 @@ export default class OrderProductComponent { * @private */ _showNotFoundProducts() { - const $emptyResultTemplate = $($('#productSearchEmptyResultTemplate').html()); + const $emptyResultTemplate = $($(createOrderPageMap.productSearchEmptyResultTemplate).html()); this._hideResultBlock(); @@ -310,4 +327,71 @@ export default class OrderProductComponent { _hideResultBlock() { $(createOrderPageMap.productResultBlock).hide(); } + + /** + * Adds selected product to current cart + * + * @private + */ + _addProductToCart() { + $.ajax($(createOrderPageMap.addToCartButton).data('add-product-url'), { + method: 'POST', + data: this._getProductData(), + processData: false, + contentType: false, + cache: false, + }).then((response) => { + console.log(response); + }).catch((response) => { + console.log('labai nepasiseke'); + }); + } + + /** + * Retrieves product data from product search result block fields + * + * @returns {FormData} + * @private + */ + _getProductData() { + const formData = new FormData(); + const productId = $(createOrderPageMap.productSelect).find(':selected').val(); + + formData.append('product_id', productId); + + if ($(createOrderPageMap.combinationsSelect).length !== 0) { + const combinationId = $(createOrderPageMap.combinationsSelect).find(':selected').val(); + formData.append('combination_id', combinationId); + } + + this._resolveCustomizationValuesForAddProduct(formData); + + return formData; + } + + /** + * Resolves product customization fields to be added to formData object + * + * @param {FormData} formData + * @returns {FormData} + * @private + */ + _resolveCustomizationValuesForAddProduct(formData) { + const customizationKey = 'customization'; + const customizedFields = $(createOrderPageMap.customizedFieldInput); + + $.each(customizedFields, (index, field) => { + const customizationFieldId = $(field).data('customization-field-id'); + const formKey = `${customizationKey}[${customizationFieldId}]`; + if ($(field).attr('type') === 'file') { + formData.append(formKey, $(field)[0].files[0]); + + return; + } + + formData.append(formKey, $(field).val()); + }); + + return formData; + } } From 60df5e386c2cf138c6faa38e527ffbc36c524b16 Mon Sep 17 00:00:00 2001 From: Raimondas Date: Mon, 21 Oct 2019 12:33:59 +0300 Subject: [PATCH 099/195] add product handling --- .../Query/SearchProductsForOrderCreation.php | 2 +- .../Admin/Sell/Order/CartController.php | 46 +++++++++++++++++++ .../Admin/Sell/Order/OrderController.php | 15 ------ .../routing/admin/sell/orders/carts.yml | 6 +++ .../routing/admin/sell/orders/orders.yml | 6 --- 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php b/src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php index d7b2fde37ecc3..48bde31af0519 100644 --- a/src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php +++ b/src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php @@ -26,7 +26,6 @@ namespace PrestaShop\PrestaShop\Core\Domain\Order\Query; - use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductException; class SearchProductsForOrderCreation @@ -57,6 +56,7 @@ public function getPhrase() /** * @param string $phrase + * * @throws ProductException */ private function assertIsNotEmptyString(string $phrase): void diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php index daa8e5f09a187..8e2d687defcd3 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php @@ -33,12 +33,14 @@ use PrestaShop\PrestaShop\Core\Domain\Cart\Command\SetFreeShippingToCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\UpdateCartAddressesCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\UpdateCartCarrierCommand; +use PrestaShop\PrestaShop\Core\Domain\Cart\Command\UpdateProductQuantityInCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartConstraintException; use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartNotFoundException; use PrestaShop\PrestaShop\Core\Domain\Cart\Query\GetCartForViewing; use PrestaShop\PrestaShop\Core\Domain\Cart\Query\GetCartInformation; use PrestaShop\PrestaShop\Core\Domain\Cart\QueryResult\CartInformation; use PrestaShop\PrestaShop\Core\Domain\CartRule\Exception\CartRuleValidityException; +use PrestaShop\PrestaShop\Core\Domain\Cart\ValueObject\QuantityAction; use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; use PrestaShopBundle\Security\Annotation\AdminSecurity; use Symfony\Component\HttpFoundation\JsonResponse; @@ -261,6 +263,29 @@ public function removeCartRuleFromCartAction(int $cartId, int $cartRuleId) } } + /** + * @param Request $request + * + * @return Response + */ + public function addProductAction(Request $request): Response + { + $cartId = (int) $request->get('cart_id'); + + try { + $addProductToCartCommand = $this->getAddProductToCartCommand($request, $cartId); + $this->getCommandBus()->handle($addProductToCartCommand); + + return $this->json($this->getCartInfo($cartId)); + } catch (Exception $e) { + return $this->json([ + 'message' => $this->getErrorMessageForException($e, []), + ], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + } + /** * @param int $cartId * @@ -283,4 +308,25 @@ private function getErrorMessages(Exception $e) CartRuleValidityException::class => $e->getMessage(), ]; } + + /** + * @param Request $request + * @param int $cartId + * + * @return UpdateProductQuantityInCartCommand + */ + private function getAddProductToCartCommand(Request $request, int $cartId): UpdateProductQuantityInCartCommand + { + $productId = $request->get('product_id'); + $quantity = $request->get('quantity'); + $combinationId = $request->get('combination_id'); + + return new UpdateProductQuantityInCartCommand( + $cartId, + (int) $productId, + (int) $quantity, + QuantityAction::INCREASE_PRODUCT_QUANTITY, + $combinationId !== null ? (int) $combinationId : null + ); + } } diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php index 722fc1f289fec..efb05c807f2da 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php @@ -51,7 +51,6 @@ use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPreview; use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductsForOrderCreation; use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId; -use PrestaShop\PrestaShop\Core\Exception\ProductException; use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\OrderGridDefinitionFactory; use PrestaShop\PrestaShop\Core\Search\Filters\OrderFilters; use PrestaShopBundle\Component\CsvResponse; @@ -76,8 +75,6 @@ */ class OrderController extends FrameworkBundleAdminController { - const PRODUCT_CUSTOMIZATION_FIELDS_REQUEST_KEY = 'customization'; - /** * Shows list of orders * @@ -683,16 +680,4 @@ private function handleChangeOrderStatusException(ChangeOrderStatusException $e) ); } } - - /** - * @param Request $request - * @return int|null - */ - private function addCustomizationFields(Request $request): ?int - { - $customizationFields = $request->request->get(self::PRODUCT_CUSTOMIZATION_FIELDS_REQUEST_KEY); - $customizationFiles = $request->files->get(self::PRODUCT_CUSTOMIZATION_FIELDS_REQUEST_KEY); - - return null; - } } diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml index 1093411907f2b..fe9d3fbebfd15 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml @@ -85,3 +85,9 @@ admin_carts_delete_rule: _legacy_controller: AdminCarts options: expose: true + +admin_add_product_for_order_creation: + path: /add-product + methods: [POST, GET] + defaults: + _controller: PrestaShopBundle:Admin/Sell/Order/Cart:addProduct diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml index 2cb348d5cee48..917064b9f5945 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/orders.yml @@ -152,9 +152,3 @@ admin_search_product_for_order_creation: methods: [GET] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Order:searchProducts - -admin_add_product_for_order_creation: - path: /add-product - methods: [POST, GET] - defaults: - _controller: PrestaShopBundle:Admin/Sell/Order/Order:addProduct From 0777615051a9a5f0466120710efc7a0547b7bf42 Mon Sep 17 00:00:00 2001 From: Raimondas Date: Mon, 21 Oct 2019 12:34:23 +0300 Subject: [PATCH 100/195] Add product frontend handling to submit product data --- .../themes/new-theme/js/pages/order/create.js | 1 + .../pages/order/create/create-order-page.js | 11 +++++++ .../order/create/order-product-component.js | 25 +++++++++------- .../themes/new-theme/public/order.bundle.js | 28 ++--------------- .../new-theme/public/order_create.bundle.js | 30 +++++++++++++++++-- 5 files changed, 56 insertions(+), 39 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create.js b/admin-dev/themes/new-theme/js/pages/order/create.js index 6b05b636ce4ba..c48c55b3be8e0 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create.js +++ b/admin-dev/themes/new-theme/js/pages/order/create.js @@ -38,4 +38,5 @@ $(document).ready(() => { createOrderPage.listenForCartRuleSearch(); createOrderPage.listenForCartRuleSelect(); createOrderPage.listenForCartRuleRemove(); + createOrderPage.listenForAddToCartClick(); }); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index fe45ab25ff5f7..aca983c8e38ad 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -59,6 +59,7 @@ export default class CreateOrderPage { this.cartEditor = new CartEditor(); this.cartRuleManager = new CartRuleManager(); this.orderProducts = new OrderProductComponent(); + this.cartProducts = new OrderProductComponent(); return { listenForCustomerSearch: () => this._handleCustomerSearch(), @@ -70,6 +71,7 @@ export default class CreateOrderPage { listenForCartRuleSearch: () => this._handleCartRuleSearch(), listenForCartRuleSelect: () => this._handleCartRuleSelect(), listenForCartRuleRemove: () => this._handleCartRuleRemove(), + listenForAddToCartClick: () => this._handleAddProductToCart(), }; } @@ -115,6 +117,15 @@ export default class CreateOrderPage { this.$container.on('click', createOrderPageMap.changeCustomerBtn, () => this.customerManager.onCustomerChange()); } + /** + * Add chosen product to cart + * + * @private + */ + _handleAddProductToCart() { + $(createOrderPageMap.addToCartButton).on('click', () => this.cartProducts.onAddProductToCart(this.data.cart_id)); + } + /** * Handles use case when cart is selected for order creation * diff --git a/admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js b/admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js index a8f5d71e289d8..22189a83f93fa 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js @@ -37,7 +37,11 @@ export default class OrderProductComponent { this._initEvents(); - return {}; + return { + onAddProductToCart: (cartId) => { + this._addProductToCart(cartId); + }, + }; } /** @@ -53,8 +57,6 @@ export default class OrderProductComponent { createOrderPageMap.combinationsSelect, (event) => this._handleCombinationChange(event) ); - - $(createOrderPageMap.addToCartButton).on('click', () => this._addProductToCart()); } /** @@ -333,17 +335,19 @@ export default class OrderProductComponent { * * @private */ - _addProductToCart() { + _addProductToCart(cartId) { $.ajax($(createOrderPageMap.addToCartButton).data('add-product-url'), { method: 'POST', - data: this._getProductData(), + data: this._getProductData(cartId), processData: false, contentType: false, cache: false, }).then((response) => { - console.log(response); + //TODO add product to list }).catch((response) => { - console.log('labai nepasiseke'); + if (typeof response.responseJSON !== 'undefined') { + showErrorMessage(response.responseJSON.message); + } }); } @@ -353,11 +357,12 @@ export default class OrderProductComponent { * @returns {FormData} * @private */ - _getProductData() { + _getProductData(cartId) { const formData = new FormData(); - const productId = $(createOrderPageMap.productSelect).find(':selected').val(); - formData.append('product_id', productId); + formData.append('cart_id', cartId); + formData.append('product_id', $(createOrderPageMap.productSelect).find(':selected').val()); + formData.append('quantity', $(createOrderPageMap.quantityInput).val()); if ($(createOrderPageMap.combinationsSelect).length !== 0) { const combinationId = $(createOrderPageMap.combinationsSelect).find(':selected').val(); diff --git a/admin-dev/themes/new-theme/public/order.bundle.js b/admin-dev/themes/new-theme/public/order.bundle.js index ac8b48cd209ce..0899836ffcf3e 100644 --- a/admin-dev/themes/new-theme/public/order.bundle.js +++ b/admin-dev/themes/new-theme/public/order.bundle.js @@ -1,4 +1,4 @@ -window.order=function(n){function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var t={};return e.m=n,e.c=t,e.i=function(n){return n},e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{configurable:!1,enumerable:!0,get:r})},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},e.p="",e(e.s=361)}({0:function(n,e){var t;t=function(){return this}();try{t=t||Function("return this")()||(0,eval)("this")}catch(n){"object"==typeof window&&(t=window)}n.exports=t},10:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ -(0,window.$)(function(){var n=new i.default("order");n.addExtension(new l.default),n.addExtension(new s.default),n.addExtension(new u.default),n.addExtension(new b.default),n.addExtension(new h.default),n.addExtension(new y.default),n.addExtension(new g.default),n.addExtension(new w.default),n.addExtension(new x.default),n.addExtension(new C.default)})},4:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t0?e._enableBulkActionsBtn(n):e._disableBulkActionsBtn(n)})}},{key:"_enableBulkActionsBtn",value:function(n){n.getContainer().find(".js-bulk-actions-btn").prop("disabled",!1)}},{key:"_disableBulkActionsBtn",value:function(n){n.getContainer().find(".js-bulk-actions-btn").prop("disabled",!0)}}]),n}();e.default=a},7:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t ")),e=e.concat(o)}),e}}]),n}();e.default=a},8:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t",{action:n,method:"POST"}).append(o("",{name:"value",value:t,type:"hidden"}));r.appendTo("body"),r.submit(),this._lock(n)}}},{key:"_isLocked",value:function(n){return this.lock.includes(n)}},{key:"_lock",value:function(n){this.lock.push(n)}}]),n}();e.default=i}).call(e,t(0))},248:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t0?e._enableBulkActionsBtn(n):e._disableBulkActionsBtn(n)})}},{key:"_enableBulkActionsBtn",value:function(n){n.getContainer().find(".js-bulk-actions-btn").prop("disabled",!1)}},{key:"_disableBulkActionsBtn",value:function(n){n.getContainer().find(".js-bulk-actions-btn").prop("disabled",!0)}}]),n}();e.default=a},7:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t ")),e=e.concat(o)}),e}}]),n}();e.default=a},8:function(n,e,t){"use strict";function r(n,e){if(!(n instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var o=function(){function n(n,e){for(var t=0;t0&&u.length>a&&!u.warned){u.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+u.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=u.length,n(c)}return e}function s(){for(var e=[],t=0;t0&&(i=t[0]),i instanceof Error)throw i;var a=new Error("Unhandled error."+(i?" ("+i.message+")":""));throw a.context=i,a}var s=o[e];if(void 0===s)return!1;if("function"==typeof s)v(s,this,t);else for(var u=s.length,c=l(s,u),r=0;r=0;i--)if(r[i]===t||r[i].listener===t){a=r[i].listener,o=i;break}if(o<0)return this;0===o?r.shift():f(r,o),1===r.length&&(n[e]=r[0]),void 0!==n.removeListener&&this.emit("removeListener",e,a||t)}return this},o.prototype.off=o.prototype.removeListener,o.prototype.removeAllListeners=function(e){var t,r,n;if(void 0===(r=this._events))return this;if(void 0===r.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==r[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete r[e]),this;if(0===arguments.length){var o,i=Object.keys(r);for(n=0;n=0;n--)this.removeListener(e,t[n]);return this},o.prototype.listeners=function(e){return c(this,e,!0)},o.prototype.rawListeners=function(e){return c(this,e,!1)},o.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):d.call(e,t)},o.prototype.listenerCount=d,o.prototype.eventNames=function(){return this._eventsCount>0?m(this._events):[]}},268:function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r",h)),i.append(u("
- @@ -44,13 +44,13 @@
-
+
{{ 'Product'|trans({}, 'Admin.Global') }}
- +
@@ -59,7 +59,7 @@ {{ 'Quantity'|trans({}, 'Admin.Global') }}
- + {{ 'In stock'|trans({}, 'Admin.Orderscustomers.Feature') }} @@ -68,7 +68,7 @@
-
@@ -161,7 +161,7 @@
-
+
- - - - - From 4bc62752b7618cfd9c4668e04204669fe55d3725 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Mon, 21 Oct 2019 15:02:45 +0300 Subject: [PATCH 102/195] Implements products list rendering after adding to cart --- .../themes/new-theme/js/pages/order/create.js | 3 - .../js/pages/order/create/create-order-map.js | 11 ++- .../pages/order/create/create-order-page.js | 24 ++--- ...roduct-component.js => product-manager.js} | 22 +++-- .../order/create/products-list-renderer.js | 91 +++++++++++++++++++ .../Order/Order/Blocks/Create/cart.html.twig | 61 +++++++------ 6 files changed, 153 insertions(+), 59 deletions(-) rename admin-dev/themes/new-theme/js/pages/order/create/{order-product-component.js => product-manager.js} (93%) create mode 100644 admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js diff --git a/admin-dev/themes/new-theme/js/pages/order/create.js b/admin-dev/themes/new-theme/js/pages/order/create.js index c48c55b3be8e0..30ce499d964f2 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create.js +++ b/admin-dev/themes/new-theme/js/pages/order/create.js @@ -36,7 +36,4 @@ $(document).ready(() => { createOrderPage.listenForCartEdit(); createOrderPage.listenForCartLoading(); createOrderPage.listenForCartRuleSearch(); - createOrderPage.listenForCartRuleSelect(); - createOrderPage.listenForCartRuleRemove(); - createOrderPage.listenForAddToCartClick(); }); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js index e15fa63b4805c..3bb1e4d46dee8 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js @@ -92,7 +92,7 @@ export default { totalShippingField: '.js-total-shipping', freeShippingSwitch: '.js-free-shipping-switch', - // selectors related to products cart block + // selectors related to cart products block productSearch: '#product-search', combinationsSelect: '#combination-select', productResultBlock: '#product-search-results', @@ -116,4 +116,13 @@ export default { 0: '#cart-product-customized-field-input-template-0', 1: '#cart-product-customized-field-input-template-1', }, + productsTable: '#products-table', + productsTableRowTemplate: '#products-table-row-template', + productImageField: '.js-product-image', + productNameField: '.js-product-name', + productAttrField: '.js-product-attr', + productReferenceField: '.js-product-ref', + productUnitPriceInput: '.js-product-unit-input', + productTotalPriceField: '.js-product-total-price', + productRemoveBtn: '.js-product-remove-btn', }; diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index aca983c8e38ad..f178a793d815f 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -36,7 +36,7 @@ import {EventEmitter} from '../../../components/event-emitter'; import CartEditor from './cart-editor'; import eventMap from './event-map'; import CartRuleManager from './cart-rule-manager'; -import OrderProductComponent from './order-product-component'; +import ProductManager from './product-manager'; const $ = window.$; @@ -58,8 +58,7 @@ export default class CreateOrderPage { this.router = new Router(); this.cartEditor = new CartEditor(); this.cartRuleManager = new CartRuleManager(); - this.orderProducts = new OrderProductComponent(); - this.cartProducts = new OrderProductComponent(); + this.cartProducts = new ProductManager(); return { listenForCustomerSearch: () => this._handleCustomerSearch(), @@ -69,9 +68,6 @@ export default class CreateOrderPage { listenForCartEdit: () => this._handleCartEdit(), listenForCartLoading: () => this._onCartLoaded(), listenForCartRuleSearch: () => this._handleCartRuleSearch(), - listenForCartRuleSelect: () => this._handleCartRuleSelect(), - listenForCartRuleRemove: () => this._handleCartRuleRemove(), - listenForAddToCartClick: () => this._handleAddProductToCart(), }; } @@ -117,15 +113,6 @@ export default class CreateOrderPage { this.$container.on('click', createOrderPageMap.changeCustomerBtn, () => this.customerManager.onCustomerChange()); } - /** - * Add chosen product to cart - * - * @private - */ - _handleAddProductToCart() { - $(createOrderPageMap.addToCartButton).on('click', () => this.cartProducts.onAddProductToCart(this.data.cart_id)); - } - /** * Handles use case when cart is selected for order creation * @@ -160,6 +147,9 @@ export default class CreateOrderPage { this.$container.on('change', createOrderPageMap.addressSelect, () => this._changeCartAddresses()); this.$container.on('change', createOrderPageMap.deliveryOptionSelect, e => this._changeDeliveryOption(e)); this.$container.on('change', createOrderPageMap.freeShippingSwitch, e => this._setFreeShipping(e)); + this.$container.on('click', createOrderPageMap.addToCartButton, () => this.cartProducts.onAddProductToCart(this.data.cart_id)); + this._selectCartRule(); + this._removeCartRule(); } /** @@ -181,7 +171,7 @@ export default class CreateOrderPage { * * @private */ - _handleCartRuleSelect() { + _selectCartRule() { this.$container.on('mousedown', createOrderPageMap.foundCartRuleListItem, (event) => { // prevent blur event to allow selecting cart rule event.preventDefault(); @@ -199,7 +189,7 @@ export default class CreateOrderPage { * * @private */ - _handleCartRuleRemove() { + _removeCartRule() { this.$container.on('click', createOrderPageMap.cartRuleDeleteBtn, (event) => { this.cartRuleManager.onCartRuleRemove( $(event.currentTarget).data('cart-rule-id'), diff --git a/admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js similarity index 93% rename from admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js rename to admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 22189a83f93fa..874d4af651a25 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/order-product-component.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -24,16 +24,18 @@ */ import createOrderPageMap from './create-order-map'; +import ProductsListRenderer from './products-list-renderer'; const $ = window.$; /** * Product component Object for "Create order" page */ -export default class OrderProductComponent { +export default class ProductManager { constructor() { this.products = []; this.combinations = []; + this.renderer = new ProductsListRenderer(); this._initEvents(); @@ -50,12 +52,12 @@ export default class OrderProductComponent { * @private */ _initEvents() { - $(createOrderPageMap.productSearch).on('input', (event) => this._handleProductSearch(event)); - $(createOrderPageMap.productSelect).on('change', (event) => this._handleProductChange(event)); + $(createOrderPageMap.productSearch).on('input', event => this._handleProductSearch(event)); + $(createOrderPageMap.productSelect).on('change', event => this._handleProductChange(event)); $(createOrderPageMap.productResultBlock).on( 'change', createOrderPageMap.combinationsSelect, - (event) => this._handleCombinationChange(event) + event => this._handleCombinationChange(event), ); } @@ -111,7 +113,7 @@ export default class OrderProductComponent { const combinationsCollection = product.combinations; if (combinationsCollection === null) { - name += ' - ' + product.formatted_price; + name += ` - ${ product.formatted_price}`; } const shouldUpdateStockValue = combinationsCollection === null && index === 0; @@ -121,12 +123,12 @@ export default class OrderProductComponent { } $(createOrderPageMap.productSelect).append( - $('').attr('value', product.product_id).text(name).attr('data-index', index) + $('').attr('value', product.product_id).text(name).attr('data-index', index), ); } const $noRecordsRow = $(createOrderPageMap.noRecordsFound).find( - createOrderPageMap.noRecordsFoundRow + createOrderPageMap.noRecordsFoundRow, ); if ($noRecordsRow.length !== 0) { @@ -223,7 +225,7 @@ export default class OrderProductComponent { this._updateStock(combination.stock); } - const name = combination.attribute + ' - ' + combination.formatted_price; + const name = `${combination.attribute } - ${ combination.formatted_price}`; $combinationsSelect.append($('') .attr('value', id).text(name)); @@ -253,7 +255,7 @@ export default class OrderProductComponent { $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); - $.each(customizationFields.product_customization_fields, function(index, field) { + $.each(customizationFields.product_customization_fields, (index, field) => { const $fieldTemplate = $($(createOrderPageMap.customizedFieldTypes[field.type]).html()); $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); @@ -343,7 +345,7 @@ export default class OrderProductComponent { contentType: false, cache: false, }).then((response) => { - //TODO add product to list + this.renderer.render(response.products); }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js new file mode 100644 index 0000000000000..12ce9123f12cb --- /dev/null +++ b/admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js @@ -0,0 +1,91 @@ +/** + * 2007-2019 PrestaShop SA and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to https://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +import createOrderMap from './create-order-map'; + +const $ = window.$; + +export default class ProductsListRenderer { + constructor() { + this.$productsTable = $(createOrderMap.productsTable); + } + render(products) { + if (products.length === 0) { + this._hideProductsList(); + + return; + } + this._renderList(products); + } + + _renderList(products) { + this._cleanProductsList(); + const $productsTableRowTemplate = $($(createOrderMap.productsTableRowTemplate).html()); + + for (const key in products) { + const product = products[key]; + const $template = $productsTableRowTemplate.clone(); + + $template.find(createOrderMap.productImageField).text(product.imageLink); + $template.find(createOrderMap.productNameField).text(product.name); + $template.find(createOrderMap.productAttrField).text(product.attribute); + $template.find(createOrderMap.productReferenceField).text(product.reference); + $template.find(createOrderMap.productUnitPriceInput).text(product.unitPrice); + $template.find(createOrderMap.productTotalPriceField).text(product.price); + $template.find(createOrderMap.productRemoveBtn).data('product-id', product.productId); + + this.$productsTable.find('tbody').append($template); + } + + this._showProductsList(); + } + + /** + * Shows products list + * + * @private + */ + _showProductsList() { + this.$productsTable.removeClass('d-none'); + } + + /** + * Hides products list + * + * @private + */ + _hideProductsList() { + this.$productsTable.addClass('d-none'); + } + + /** + * Emptes products list + * + * @private + */ + _cleanProductsList() { + this.$productsTable.find('tbody').empty(); + } +} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig index c963e43a26057..77a2a40186fb4 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig @@ -81,8 +81,9 @@
- - +
+
+ @@ -92,32 +93,10 @@ - - - - - - - - - - - - -
{{ 'Product'|trans({}, 'Admin.Global') }} {{ 'Description'|trans({}, 'Admin.Global') }}{{ 'Price'|trans({}, 'Admin.Global') }}
-

Hummingbird printed t-shirt

-

S- White

-
- demo_1 - - - - - - 45 - - -
+
+
@@ -217,3 +196,29 @@
+ + From 6aacb5dfd5a6cac08f59ee8ea74b6a918c549dd5 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Mon, 21 Oct 2019 18:20:16 +0300 Subject: [PATCH 103/195] Rename product searcher and move to product controller. WIP refactoring js --- .../themes/new-theme/js/fos_js_routes.json | 2 +- .../js/pages/order/create/product-manager.js | 421 +++++++++--------- .../js/pages/order/create/product-renderer.js | 304 +++++++++++++ .../order/create/products-list-renderer.js | 91 ---- classes/Product.php | 6 +- .../SearchProductsForOrderCreationHandler.php | 205 --------- .../QueryHandler/SearchProductsHandler.php | 193 ++++++++ .../ProductCombinationsForOrderCreation.php | 61 --- .../ProductCustomizationFields.php | 64 --- .../QueryResult/ProductsForOrderCreation.php | 61 --- .../Query/SearchProducts.php} | 24 +- .../SearchProductsHandlerInterface.php} | 17 +- .../QueryResult/FoundProduct.php} | 51 +-- .../QueryResult/ProductCombination.php} | 7 +- .../QueryResult/ProductCustomizationField.php | 2 +- .../Controller/Admin/ProductController.php | 26 ++ .../Admin/Sell/Order/OrderController.php | 7 +- .../admin/sell/catalog/products/product.yml | 8 + .../config/services/adapter/order.yml | 1 - .../config/services/adapter/product.yml | 10 + .../Order/Order/Blocks/Create/cart.html.twig | 2 +- 21 files changed, 817 insertions(+), 746 deletions(-) create mode 100644 admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js delete mode 100644 admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js delete mode 100644 src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php create mode 100644 src/Adapter/Product/QueryHandler/SearchProductsHandler.php delete mode 100644 src/Core/Domain/Order/QueryResult/ProductCombinationsForOrderCreation.php delete mode 100644 src/Core/Domain/Order/QueryResult/ProductCustomizationFields.php delete mode 100644 src/Core/Domain/Order/QueryResult/ProductsForOrderCreation.php rename src/Core/Domain/{Order/Query/SearchProductsForOrderCreation.php => Product/Query/SearchProducts.php} (79%) rename src/Core/Domain/{Order/QueryHandler/SearchProductsForOrderCreationHandlerInterface.php => Product/QueryHandler/SearchProductsHandlerInterface.php} (69%) rename src/Core/Domain/{Order/QueryResult/ProductForOrderCreation.php => Product/QueryResult/FoundProduct.php} (71%) rename src/Core/Domain/{Order/QueryResult/ProductCombinationForOrderCreation.php => Product/QueryResult/ProductCombination.php} (94%) rename src/Core/Domain/{Order => Product}/QueryResult/ProductCustomizationField.php (97%) diff --git a/admin-dev/themes/new-theme/js/fos_js_routes.json b/admin-dev/themes/new-theme/js/fos_js_routes.json index b0065ac8f1cb3..74097ea9acad2 100644 --- a/admin-dev/themes/new-theme/js/fos_js_routes.json +++ b/admin-dev/themes/new-theme/js/fos_js_routes.json @@ -1 +1 @@ -{"base_url":"","routes":{"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_rule":{"tokens":[["text","\/rules\/add"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file +{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_rule":{"tokens":[["text","\/rules\/add"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 874d4af651a25..f7823b2703a44 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -24,7 +24,8 @@ */ import createOrderPageMap from './create-order-map'; -import ProductsListRenderer from './products-list-renderer'; +import ProductRenderer from './product-renderer'; +import Router from '../../../components/router'; const $ = window.$; @@ -35,7 +36,8 @@ export default class ProductManager { constructor() { this.products = []; this.combinations = []; - this.renderer = new ProductsListRenderer(); + this.renderer = new ProductRenderer(); + this.router = new Router(); this._initEvents(); @@ -53,11 +55,14 @@ export default class ProductManager { */ _initEvents() { $(createOrderPageMap.productSearch).on('input', event => this._handleProductSearch(event)); - $(createOrderPageMap.productSelect).on('change', event => this._handleProductChange(event)); + $(createOrderPageMap.productSelect).on('change', event => { + const index = $(event.currentTarget).find(':selected').data('index'); + this.renderer._handleProductChange(this.products[index]) + }); $(createOrderPageMap.productResultBlock).on( 'change', createOrderPageMap.combinationsSelect, - event => this._handleCombinationChange(event), + event => this.renderer._handleCombinationChange(event), ); } @@ -75,11 +80,8 @@ export default class ProductManager { return; } - $.ajax($productSearchInput.data('product-search-url'), { - method: 'GET', - data: { - product_search_phrase: name, - }, + $.get(this.router.generate('admin_products_search'), { + search_phrase: name, }).then((response) => { this.products = JSON.parse(response); if (this.products.length === 0) { @@ -88,213 +90,212 @@ export default class ProductManager { return; } - this._renderProductSearchResult(); + this.renderer.renderSearchResults(this.products); }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); } }); } - - /** - * Renders product result block - * - * @private - */ - _renderProductSearchResult() { - $(createOrderPageMap.productSelect).empty(); - - for (const [index, product] of this.products.entries()) { - if (index === 0) { - this._fillFieldsRelatedToProduct(product); - } - - let name = product.name; - const combinationsCollection = product.combinations; - - if (combinationsCollection === null) { - name += ` - ${ product.formatted_price}`; - } - - const shouldUpdateStockValue = combinationsCollection === null && index === 0; - - if (shouldUpdateStockValue) { - this._updateStock(product.stock); - } - - $(createOrderPageMap.productSelect).append( - $('').attr('value', product.product_id).text(name).attr('data-index', index), - ); - } - - const $noRecordsRow = $(createOrderPageMap.noRecordsFound).find( - createOrderPageMap.noRecordsFoundRow, - ); - - if ($noRecordsRow.length !== 0) { - $noRecordsRow.remove(); - } - - this._showResultBlock(); - } - - /** - * Updates stock text helper value - * - * @param stock - * @private - */ - _updateStock(stock) { - $(createOrderPageMap.inStockCounter).text(stock); - $(createOrderPageMap.quantityInput).attr('max', stock); - } - - /** - * Adds available fields related to selected product - * - * @param product - * @private - */ - _fillFieldsRelatedToProduct(product) { - this._fillCombinations(product.combinations); - this._resolveCustomizationFields(product.customization_fields); - } - - /** - * Handles product select change - * - * @param event - * @private - */ - _handleProductChange(event) { - const index = $(event.currentTarget).find(':selected').data('index'); - const product = this.products[index]; - - this._fillFieldsRelatedToProduct(product); - - if (product.combinations === null) { - this.combinations = []; - this._updateStock(product.stock); - } - - this._fillCombinations(product.combinations); - } - - /** - * Handles combination select change - * - * @param event - * @private - */ - _handleCombinationChange(event) { - const index = $(event.currentTarget).find(':selected').val(); - const combination = this.combinations[index]; - - this._updateStock(combination.stock); - } - - /** - * Fills combination select with options - * - * @param combinations - * @private - */ - _fillCombinations(combinations) { - if (combinations === null) { - this._removeCombinationSelect(); - - return; - } - - this.combinations = combinations.combinations; - - if ($(createOrderPageMap.combinationsRow).length === 0) { - const $combinationsTemplate = $($(createOrderPageMap.combinationsTemplate).html()); - $(createOrderPageMap.productSelectRow).after($combinationsTemplate); - } - - const $combinationsSelect = $(createOrderPageMap.combinationsSelect); - $combinationsSelect.empty(); - - const entries = Object.entries(combinations.combinations); - - let i = 0; // This is needed because index in this case is attribute combination id - - for (const [id, combination] of entries) { - if (i === 0) { - this._updateStock(combination.stock); - } - - const name = `${combination.attribute } - ${ combination.formatted_price}`; - $combinationsSelect.append($('') - .attr('value', id).text(name)); - - i += 1; - } - } - - /** - * Resolves weather to add customization fields to result block and adds them if needed - * - * @param customizationFields - * @private - */ - _resolveCustomizationFields(customizationFields) { - this._removeCustomizedFields(); - if (customizationFields === null) { - return; - } - - const $customizedFieldTemplate = $(createOrderPageMap.cartProductCustomizedFieldTemplate); - let $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - - const customizationLabel = $(createOrderPageMap.productSelect).data('customization-label'); - - $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - .text(customizationLabel); - - $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); - - $.each(customizationFields.product_customization_fields, (index, field) => { - const $fieldTemplate = $($(createOrderPageMap.customizedFieldTypes[field.type]).html()); - - $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - if (field.required) { - $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - .append('*'); - } - - $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - .append(field.name); - - const $fieldWrapper = $customizedFieldTemplateContent - .find(createOrderPageMap.customizedFieldInputWrapper); - - $fieldWrapper.append($fieldTemplate); - $fieldWrapper.find(createOrderPageMap.customizedFieldInput) - .attr('data-customization-field-id', field.customization_field_id); - - $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); - }); - } - - /** - * Removes combination select for products with no combinations - * - * @private - */ - _removeCombinationSelect() { - $(createOrderPageMap.combinationsRow).remove(); - } - - /** - * Removes customized fields select for products with no customized fields - * - * @private - */ - _removeCustomizedFields() { - $(createOrderPageMap.customizedFieldContainer).remove(); - } + // /** + // * Renders product result block + // * + // * @private + // */ + // _renderProductSearchResult() { + // $(createOrderPageMap.productSelect).empty(); + // + // for (const [index, product] of this.products.entries()) { + // if (index === 0) { + // this._fillFieldsRelatedToProduct(product); + // } + // + // let name = product.name; + // const combinationsCollection = product.combinations; + // + // if (combinationsCollection === null) { + // name += ` - ${ product.formatted_price}`; + // } + // + // const shouldUpdateStockValue = combinationsCollection === null && index === 0; + // + // if (shouldUpdateStockValue) { + // this._updateStock(product.stock); + // } + // + // $(createOrderPageMap.productSelect).append( + // $('').attr('value', product.product_id).text(name).attr('data-index', index), + // ); + // } + // + // const $noRecordsRow = $(createOrderPageMap.noRecordsFound).find( + // createOrderPageMap.noRecordsFoundRow, + // ); + // + // if ($noRecordsRow.length !== 0) { + // $noRecordsRow.remove(); + // } + // + // this._showResultBlock(); + // } + + // /** + // * Updates stock text helper value + // * + // * @param stock + // * @private + // */ + // _updateStock(stock) { + // $(createOrderPageMap.inStockCounter).text(stock); + // $(createOrderPageMap.quantityInput).attr('max', stock); + // } + + // /** + // * Adds available fields related to selected product + // * + // * @param product + // * @private + // */ + // _fillFieldsRelatedToProduct(product) { + // this._fillCombinations(product.combinations); + // this._resolveCustomizationFields(product.customization_fields); + // } + + // /** + // * Handles product select change + // * + // * @param event + // * @private + // */ + // _handleProductChange(event) { + // const index = $(event.currentTarget).find(':selected').data('index'); + // const product = this.products[index]; + // + // this._fillFieldsRelatedToProduct(product); + // + // if (product.combinations === null) { + // this.combinations = []; + // this._updateStock(product.stock); + // } + // + // this._fillCombinations(product.combinations); + // } + // + // /** + // * Handles combination select change + // * + // * @param event + // * @private + // */ + // _handleCombinationChange(event) { + // const index = $(event.currentTarget).find(':selected').val(); + // const combination = this.combinations[index]; + // + // this._updateStock(combination.stock); + // } + // + // /** + // * Fills combination select with options + // * + // * @param combinations + // * @private + // */ + // _fillCombinations(combinations) { + // if (combinations === null) { + // this._removeCombinationSelect(); + // + // return; + // } + // + // this.combinations = combinations.combinations; + // + // if ($(createOrderPageMap.combinationsRow).length === 0) { + // const $combinationsTemplate = $($(createOrderPageMap.combinationsTemplate).html()); + // $(createOrderPageMap.productSelectRow).after($combinationsTemplate); + // } + // + // const $combinationsSelect = $(createOrderPageMap.combinationsSelect); + // $combinationsSelect.empty(); + // + // const entries = Object.entries(combinations.combinations); + // + // let i = 0; // This is needed because index in this case is attribute combination id + // + // for (const [id, combination] of entries) { + // if (i === 0) { + // this._updateStock(combination.stock); + // } + // + // const name = `${combination.attribute } - ${ combination.formatted_price}`; + // $combinationsSelect.append($('') + // .attr('value', id).text(name)); + // + // i += 1; + // } + // } + // + // /** + // * Resolves weather to add customization fields to result block and adds them if needed + // * + // * @param customizationFields + // * @private + // */ + // _resolveCustomizationFields(customizationFields) { + // this._removeCustomizedFields(); + // if (customizationFields === null) { + // return; + // } + // + // const $customizedFieldTemplate = $(createOrderPageMap.cartProductCustomizedFieldTemplate); + // let $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); + // + // const customizationLabel = $(createOrderPageMap.productSelect).data('customization-label'); + // + // $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) + // .text(customizationLabel); + // + // $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); + // + // $.each(customizationFields.product_customization_fields, (index, field) => { + // const $fieldTemplate = $($(createOrderPageMap.customizedFieldTypes[field.type]).html()); + // + // $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); + // if (field.required) { + // $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) + // .append('*'); + // } + // + // $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) + // .append(field.name); + // + // const $fieldWrapper = $customizedFieldTemplateContent + // .find(createOrderPageMap.customizedFieldInputWrapper); + // + // $fieldWrapper.append($fieldTemplate); + // $fieldWrapper.find(createOrderPageMap.customizedFieldInput) + // .attr('data-customization-field-id', field.customization_field_id); + // + // $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); + // }); + // } + + // /** + // * Removes combination select for products with no combinations + // * + // * @private + // */ + // _removeCombinationSelect() { + // $(createOrderPageMap.combinationsRow).remove(); + // } + // + // /** + // * Removes customized fields select for products with no customized fields + // * + // * @private + // */ + // _removeCustomizedFields() { + // $(createOrderPageMap.customizedFieldContainer).remove(); + // } /** * Shows empty result when product is not found diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js new file mode 100644 index 0000000000000..c103aa55a68f1 --- /dev/null +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js @@ -0,0 +1,304 @@ +/** + * 2007-2019 PrestaShop SA and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to https://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +import createOrderMap from './create-order-map'; + +const $ = window.$; + +export default class ProductRenderer { + constructor() { + this.$productsTable = $(createOrderMap.productsTable); + } + render(products) { + if (products.length === 0) { + this._hideProductsList(); + + return; + } + this._renderList(products); + } + + _renderList(products) { + this._cleanProductsList(); + const $productsTableRowTemplate = $($(createOrderMap.productsTableRowTemplate).html()); + + for (const key in products) { + const product = products[key]; + const $template = $productsTableRowTemplate.clone(); + + $template.find(createOrderMap.productImageField).text(product.imageLink); + $template.find(createOrderMap.productNameField).text(product.name); + $template.find(createOrderMap.productAttrField).text(product.attribute); + $template.find(createOrderMap.productReferenceField).text(product.reference); + $template.find(createOrderMap.productUnitPriceInput).text(product.unitPrice); + $template.find(createOrderMap.productTotalPriceField).text(product.price); + $template.find(createOrderMap.productRemoveBtn).data('product-id', product.productId); + + this.$productsTable.find('tbody').append($template); + } + + this._showProductsList(); + } + + renderSearchResults(foundProducts) { + $(createOrderMap.productSelect).empty(); + + for (const [index, product] of foundProducts.entries()) { + if (index === 0) { + this._fillFieldsRelatedToProduct(product); + } + + let name = product.name; + const combinationsCollection = product.combinations; + + if (combinationsCollection === null) { + name += ` - ${ product.formatted_price}`; + } + + const shouldUpdateStockValue = combinationsCollection === null && index === 0; + + if (shouldUpdateStockValue) { + this._updateStock(product.stock); + } + + $(createOrderMap.productSelect).append( + $('').attr('value', product.product_id).text(name).attr('data-index', index), + ); + } + + const $noRecordsRow = $(createOrderMap.noRecordsFound).find( + createOrderMap.noRecordsFoundRow, + ); + + if ($noRecordsRow.length !== 0) { + $noRecordsRow.remove(); + } + + this._showResultBlock(); + } + + + /** + * Fills combination select with options + * + * @param combinations + * @private + */ + _fillCombinations(combinations) { + if (combinations === null) { + this._removeCombinationSelect(); + + return; + } + + this.combinations = combinations; + + if ($(createOrderMap.combinationsRow).length === 0) { + const $combinationsTemplate = $($(createOrderMap.combinationsTemplate).html()); + $(createOrderMap.productSelectRow).after($combinationsTemplate); + } + + const $combinationsSelect = $(createOrderMap.combinationsSelect); + $combinationsSelect.empty(); + + const entries = Object.entries(combinations); + + let i = 0; // This is needed because index in this case is attribute combination id + + for (const [id, combination] of entries) { + if (i === 0) { + this._updateStock(combination.stock); + } + + const name = `${combination.attribute } - ${ combination.formatted_price}`; + $combinationsSelect.append($('') + .attr('value', id).text(name)); + + i += 1; + } + } + + /** + * Resolves weather to add customization fields to result block and adds them if needed + * + * @param customizationFields + * @private + */ + _resolveCustomizationFields(customizationFields) { + this._removeCustomizedFields(); + if (customizationFields === null) { + return; + } + + const $customizedFieldTemplate = $(createOrderMap.cartProductCustomizedFieldTemplate); + let $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); + + const customizationLabel = $(createOrderMap.productSelect).data('customization-label'); + + $customizedFieldTemplateContent.find(createOrderMap.customizedLabelClass) + .text(customizationLabel); + + $(createOrderMap.quantityRow).before($customizedFieldTemplateContent); + + $.each(customizationFields.product_customization_fields, (index, field) => { + const $fieldTemplate = $($(createOrderMap.customizedFieldTypes[field.type]).html()); + + $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); + if (field.required) { + $customizedFieldTemplateContent.find(createOrderMap.customizedLabelClass) + .append('*'); + } + + $customizedFieldTemplateContent.find(createOrderMap.customizedLabelClass) + .append(field.name); + + const $fieldWrapper = $customizedFieldTemplateContent + .find(createOrderMap.customizedFieldInputWrapper); + + $fieldWrapper.append($fieldTemplate); + $fieldWrapper.find(createOrderMap.customizedFieldInput) + .attr('data-customization-field-id', field.customization_field_id); + + $(createOrderMap.quantityRow).before($customizedFieldTemplateContent); + }); + } + + /** + * Adds available fields related to selected product + * + * @param product + * @private + */ + _fillFieldsRelatedToProduct(product) { + this._fillCombinations(product.combinations); + this._resolveCustomizationFields(product.customization_fields); + } + + /** + * Removes combination select for products with no combinations + * + * @private + */ + _removeCombinationSelect() { + $(createOrderMap.combinationsRow).remove(); + } + + /** + * Removes customized fields select for products with no customized fields + * + * @private + */ + _removeCustomizedFields() { + $(createOrderMap.customizedFieldContainer).remove(); + } + + /** + * Handles product select change + * + * @param event + * @private + */ + _handleProductChange(product) { + this._fillFieldsRelatedToProduct(product); + + if (product.combinations === null) { + this.combinations = []; + this._updateStock(product.stock); + } + + this._fillCombinations(product.combinations); + } + + /** + * Handles combination select change + * + * @param event + * @private + */ + _handleCombinationChange(event) { + const index = $(event.currentTarget).find(':selected').val(); + const combination = this.combinations[index]; + + this._updateStock(combination.stock); + } + + /** + * Updates stock text helper value + * + * @param stock + * @private + */ + _updateStock(stock) { + $(createOrderMap.inStockCounter).text(stock); + $(createOrderMap.quantityInput).attr('max', stock); + } + + /** + * Shows result block + * + * @private + */ + _showResultBlock() { + $(createOrderMap.productResultBlock).show(); + $(createOrderMap.productResultBlock).removeClass('d-none'); + } + + /** + * Hides result block + * + * @private + */ + _hideResultBlock() { + $(createOrderMap.productResultBlock).hide(); + } + + + /** + * Shows products list + * + * @private + */ + _showProductsList() { + this.$productsTable.removeClass('d-none'); + } + + /** + * Hides products list + * + * @private + */ + _hideProductsList() { + this.$productsTable.addClass('d-none'); + } + + /** + * Emptes products list + * + * @private + */ + _cleanProductsList() { + this.$productsTable.find('tbody').empty(); + } +} diff --git a/admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js deleted file mode 100644 index 12ce9123f12cb..0000000000000 --- a/admin-dev/themes/new-theme/js/pages/order/create/products-list-renderer.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * 2007-2019 PrestaShop SA and Contributors - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.txt. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -import createOrderMap from './create-order-map'; - -const $ = window.$; - -export default class ProductsListRenderer { - constructor() { - this.$productsTable = $(createOrderMap.productsTable); - } - render(products) { - if (products.length === 0) { - this._hideProductsList(); - - return; - } - this._renderList(products); - } - - _renderList(products) { - this._cleanProductsList(); - const $productsTableRowTemplate = $($(createOrderMap.productsTableRowTemplate).html()); - - for (const key in products) { - const product = products[key]; - const $template = $productsTableRowTemplate.clone(); - - $template.find(createOrderMap.productImageField).text(product.imageLink); - $template.find(createOrderMap.productNameField).text(product.name); - $template.find(createOrderMap.productAttrField).text(product.attribute); - $template.find(createOrderMap.productReferenceField).text(product.reference); - $template.find(createOrderMap.productUnitPriceInput).text(product.unitPrice); - $template.find(createOrderMap.productTotalPriceField).text(product.price); - $template.find(createOrderMap.productRemoveBtn).data('product-id', product.productId); - - this.$productsTable.find('tbody').append($template); - } - - this._showProductsList(); - } - - /** - * Shows products list - * - * @private - */ - _showProductsList() { - this.$productsTable.removeClass('d-none'); - } - - /** - * Hides products list - * - * @private - */ - _hideProductsList() { - this.$productsTable.addClass('d-none'); - } - - /** - * Emptes products list - * - * @private - */ - _cleanProductsList() { - this.$productsTable.find('tbody').empty(); - } -} diff --git a/classes/Product.php b/classes/Product.php index 0fc089e2214c1..1a3b1a3bcde18 100644 --- a/classes/Product.php +++ b/classes/Product.php @@ -4236,7 +4236,7 @@ public static function cacheFrontFeatures($product_ids, $id_lang) * * @return array Matching products */ - public static function searchByName($id_lang, $query, Context $context = null) + public static function searchByName($id_lang, $query, Context $context = null, $limit = null) { if (!$context) { $context = Context::getContext(); @@ -4265,6 +4265,10 @@ public static function searchByName($id_lang, $query, Context $context = null) $sql->orderBy('pl.`name` ASC'); + if ($limit) { + $sql->limit($limit); + } + if (Combination::isFeatureActive()) { $where .= ' OR EXISTS(SELECT * FROM `' . _DB_PREFIX_ . 'product_attribute` `pa` WHERE pa.`id_product` = p.`id_product` AND (pa.`reference` LIKE \'%' . pSQL($query) . '%\' OR pa.`supplier_reference` LIKE \'%' . pSQL($query) . '%\' diff --git a/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php b/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php deleted file mode 100644 index 4fff777a56864..0000000000000 --- a/src/Adapter/Order/QueryHandler/SearchProductsForOrderCreationHandler.php +++ /dev/null @@ -1,205 +0,0 @@ - - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -namespace PrestaShop\PrestaShop\Adapter\Order\QueryHandler; - -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Core\Domain\Order\Query\SearchProductsForOrderCreation; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryHandler\SearchProductsForOrderCreationHandlerInterface; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductCustomizationField; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductCustomizationFields; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductForOrderCreation; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductCombinationForOrderCreation; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductCombinationsForOrderCreation; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductsForOrderCreation; -use PrestaShop\PrestaShop\Core\Localization\Exception\LocalizationException; -use PrestaShop\PrestaShop\Core\Localization\Locale; -use PrestaShop\PrestaShop\Core\Localization\Locale\RepositoryInterface; -use PrestaShopException; -use Product; - -/** - * Handles product for order creation search - */ -final class SearchProductsForOrderCreationHandler implements SearchProductsForOrderCreationHandlerInterface -{ - /** - * @var int - */ - private $langId; - - /** - * @var string - */ - private $currencyCode; - - /** - * @var RepositoryInterface - */ - private $localeRepository; - - /** - * @var string - */ - private $locale; - - /** - * @param LegacyContext $context - * @param RepositoryInterface $localeRepository - * @param string $locale - */ - public function __construct(LegacyContext $context, RepositoryInterface $localeRepository, string $locale) - { - $this->langId = $context->getLanguage()->getId(); - $this->currencyCode = $context->getContext()->currency->iso_code; - $this->localeRepository = $localeRepository; - $this->locale = $locale; - } - - /** - * {@inheritdoc} - * - * @throws LocalizationException - * @throws PrestaShopException - */ - public function handle(SearchProductsForOrderCreation $query): ProductsForOrderCreation - { - $products = Product::searchByName($this->langId, $query->getPhrase()); - - $productForOrderCreation = []; - - if ($products) { - foreach ($products as $product) { - $productForOrderCreation[] = $this->getProductForOrderCreation(new Product($product['id_product'])); - } - } - - return new ProductsForOrderCreation($productForOrderCreation); - } - - /** - * @param Product $product - * - * @return ProductForOrderCreation - * - * @throws LocalizationException - */ - private function getProductForOrderCreation(Product $product): ProductForOrderCreation - { - /** @var Locale $locale */ - $locale = $this->localeRepository->getLocale($this->locale); - $priceTaxExcluded = Product::getPriceStatic($product->id, false); - - $customizationFields = is_array($product->getCustomizationFields()) ? - $this->getProductCustomizationFields($product->getCustomizationFields()) : - null - ; - - $combinations = !empty($product->getAttributeCombinations()) ? - $this->getProductCombinations($product->getAttributeCombinations(), $product->id) : - null; - - $productForOrderCreation = new ProductForOrderCreation( - $product->id, - $product->name[$this->langId], - $locale->formatPrice($priceTaxExcluded, $this->currencyCode), - Product::getQuantity($product->id) - ); - - if (null !== $customizationFields) { - $productForOrderCreation->setCustomizationFields($customizationFields); - } - - if (null !== $combinations) { - $productForOrderCreation->setCombinations($combinations); - } - - return $productForOrderCreation; - } - - /** - * @param array $fields - * - * @return ProductCustomizationFields|null - */ - private function getProductCustomizationFields(array $fields): ?ProductCustomizationFields - { - $fieldsForProductOrderCreations = []; - - foreach ($fields as $typeId => $typeFields) { - foreach ($typeFields as $field) { - $customizationField = new ProductCustomizationField( - (int) $field[$this->langId]['id_customization_field'], - (int) $typeId, - $field[$this->langId]['name'], - (bool) $field[$this->langId]['required'] - ); - - $fieldsForProductOrderCreations[] = $customizationField; - } - } - - return new ProductCustomizationFields($fieldsForProductOrderCreations); - } - - /** - * @param array $combinations - * @param int $productId - * - * @return ProductCombinationsForOrderCreation|null - * - * @throws LocalizationException - */ - private function getProductCombinations(array $combinations, int $productId): ?ProductCombinationsForOrderCreation - { - $productCombinations = new ProductCombinationsForOrderCreation(); - - /** @var Locale $locale */ - $locale = $this->localeRepository->getLocale($this->locale); - - foreach ($combinations as $combination) { - $productAttributeId = (int) $combination['id_product_attribute']; - if ($productCombination = $productCombinations->getCombination($productAttributeId)) { - $productCombination->appendAttributeName($combination['attribute_name']); - - continue; - } - - $priceTaxExcluded = Product::getPriceStatic($productId, false, $productAttributeId); - - $productCombination = new ProductCombinationForOrderCreation( - $productAttributeId, - $combination['attribute_name'], - $combination['quantity'], - $locale->formatPrice($priceTaxExcluded, $this->currencyCode) - ); - - $productCombinations->addCombination($productCombination); - } - - return $productCombinations; - } -} diff --git a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php new file mode 100644 index 0000000000000..cd610948ed546 --- /dev/null +++ b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php @@ -0,0 +1,193 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Adapter\Product\QueryHandler; + +use PrestaShop\PrestaShop\Adapter\LegacyContext; +use PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts; +use PrestaShop\PrestaShop\Core\Domain\Product\QueryHandler\SearchProductsHandlerInterface; +use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\FoundProduct; +use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\ProductCombination; +use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\ProductCustomizationField; +use PrestaShop\PrestaShop\Core\Localization\Exception\LocalizationException; +use PrestaShop\PrestaShop\Core\Localization\Locale; +use PrestaShop\PrestaShop\Core\Localization\Locale\RepositoryInterface; +use PrestaShopException; +use Product; + +/** + * Handles products search using legacy object model + */ +final class SearchProductsHandler implements SearchProductsHandlerInterface +{ + /** + * @var int + */ + private $langId; + + /** + * @var string + */ + private $currencyCode; + + /** + * @var RepositoryInterface + */ + private $localeRepository; + + /** + * @var string + */ + private $locale; + + /** + * @param LegacyContext $context + * @param RepositoryInterface $localeRepository + * @param string $locale + */ + public function __construct(LegacyContext $context, RepositoryInterface $localeRepository, string $locale) + { + $this->langId = $context->getLanguage()->getId(); + $this->currencyCode = $context->getContext()->currency->iso_code; + $this->localeRepository = $localeRepository; + $this->locale = $locale; + } + + /** + * {@inheritdoc} + * + * @throws LocalizationException + * @throws PrestaShopException + */ + public function handle(SearchProducts $query): array + { + $products = Product::searchByName($this->langId, $query->getPhrase(), null, $query->getResultsLimit()); + + $foundProducts = []; + + if ($products) { + foreach ($products as $product) { + $foundProducts[] = $this->createFoundProductFromLegacy(new Product($product['id_product'])); + } + } + + return $foundProducts; + } + + /** + * @param Product $product + * + * @return FoundProduct + * + * @throws LocalizationException + */ + private function createFoundProductFromLegacy(Product $product): FoundProduct + { + /** @var Locale $locale */ + $locale = $this->localeRepository->getLocale($this->locale); + $priceTaxExcluded = Product::getPriceStatic($product->id, false); + + $foundProduct = new FoundProduct( + $product->id, + $product->name[$this->langId], + $locale->formatPrice($priceTaxExcluded, $this->currencyCode), + Product::getQuantity($product->id), + $this->getProductCombinations($product), + $this->getProductCustomizationFields($product) + ); + + return $foundProduct; + } + + /** + * @param Product $product + * + * @return ProductCustomizationField[] + */ + private function getProductCustomizationFields(Product $product): array + { + $fields = $product->getCustomizationFields(); + $customizationFields = []; + + if (false !== $fields) { + foreach ($fields as $typeId => $typeFields) { + foreach ($typeFields as $field) { + $customizationField = new ProductCustomizationField( + (int) $field[$this->langId]['id_customization_field'], + (int) $typeId, + $field[$this->langId]['name'], + (bool) $field[$this->langId]['required'] + ); + + $customizationFields[] = $customizationField; + } + } + } + + return $customizationFields; + } + + /** + * @param Product $product + * + * @return ProductCombination[] + * + * @throws LocalizationException + */ + private function getProductCombinations(Product $product): array + { + $productCombinations = []; + $combinations = $product->getAttributeCombinations(); + + if (false !== $combinations) { + /** @var Locale $locale */ + $locale = $this->localeRepository->getLocale($this->locale); + + foreach ($combinations as $combination) { + $productAttributeId = (int) $combination['id_product_attribute']; + $attribute = $combination['attribute_name']; + + if (isset($productCombinations[$productAttributeId])) { + $existingAttribute = $productCombinations[$productAttributeId]->getAttribute(); + $attribute = $existingAttribute . ' - ' . $attribute; + } + + $priceTaxExcluded = Product::getPriceStatic((int) $product->id, false, $productAttributeId); + + $productCombination = new ProductCombination( + $productAttributeId, + $attribute, + $combination['quantity'], + $locale->formatPrice($priceTaxExcluded, $this->currencyCode) + ); + + $productCombinations[$productAttributeId] = $productCombination; + } + } + + return $productCombinations; + } +} diff --git a/src/Core/Domain/Order/QueryResult/ProductCombinationsForOrderCreation.php b/src/Core/Domain/Order/QueryResult/ProductCombinationsForOrderCreation.php deleted file mode 100644 index 39f7d917f3bb1..0000000000000 --- a/src/Core/Domain/Order/QueryResult/ProductCombinationsForOrderCreation.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; - -class ProductCombinationsForOrderCreation -{ - /** - * @var ProductCombinationForOrderCreation[] - */ - private $combinations = []; - - /** - * @param ProductCombinationForOrderCreation $combination - */ - public function addCombination(ProductCombinationForOrderCreation $combination): void - { - $this->combinations[$combination->getAttributeCombinationId()] = $combination; - } - - /** - * @param int $id - * - * @return ProductCombinationForOrderCreation|null - */ - public function getCombination(int $id): ?ProductCombinationForOrderCreation - { - return isset($this->combinations[$id]) ? $this->combinations[$id] : null; - } - - /** - * @return ProductCombinationForOrderCreation[] - */ - public function getCombinations(): array - { - return $this->combinations; - } -} diff --git a/src/Core/Domain/Order/QueryResult/ProductCustomizationFields.php b/src/Core/Domain/Order/QueryResult/ProductCustomizationFields.php deleted file mode 100644 index f446243bec997..0000000000000 --- a/src/Core/Domain/Order/QueryResult/ProductCustomizationFields.php +++ /dev/null @@ -1,64 +0,0 @@ - - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; - -/** - * Holds and provides product customization fields - */ -class ProductCustomizationFields -{ - /** - * @var ProductCustomizationField[] - */ - private $fields = []; - - /** - * @param ProductCustomizationField[] $fields - */ - public function __construct(array $fields) - { - foreach ($fields as $field) { - $this->addProductCustomizationField($field); - } - } - - /** - * @return ProductCustomizationField[] - */ - public function getProductCustomizationFields(): array - { - return $this->fields; - } - - /** - * @param ProductCustomizationField $productCustomizationField - */ - private function addProductCustomizationField(ProductCustomizationField $productCustomizationField): void - { - $this->fields[] = $productCustomizationField; - } -} diff --git a/src/Core/Domain/Order/QueryResult/ProductsForOrderCreation.php b/src/Core/Domain/Order/QueryResult/ProductsForOrderCreation.php deleted file mode 100644 index 137f3e03c9754..0000000000000 --- a/src/Core/Domain/Order/QueryResult/ProductsForOrderCreation.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; - -class ProductsForOrderCreation -{ - /** - * @var ProductForOrderCreation[] - */ - private $products = []; - - /** - * @param ProductForOrderCreation[] $products - */ - public function __construct(array $products) - { - foreach ($products as $product) { - $this->addProductForOrderCreation($product); - } - } - - /** - * @param ProductForOrderCreation $productForOrderCreation - */ - private function addProductForOrderCreation(ProductForOrderCreation $productForOrderCreation): void - { - $this->products[] = $productForOrderCreation; - } - - /** - * @return ProductForOrderCreation[] - */ - public function getProducts(): array - { - return $this->products; - } -} diff --git a/src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php b/src/Core/Domain/Product/Query/SearchProducts.php similarity index 79% rename from src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php rename to src/Core/Domain/Product/Query/SearchProducts.php index 48bde31af0519..5b7df0a25b484 100644 --- a/src/Core/Domain/Order/Query/SearchProductsForOrderCreation.php +++ b/src/Core/Domain/Product/Query/SearchProducts.php @@ -24,26 +24,36 @@ * International Registered Trademark & Property of PrestaShop SA */ -namespace PrestaShop\PrestaShop\Core\Domain\Order\Query; +namespace PrestaShop\PrestaShop\Core\Domain\Product\Query; use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductException; -class SearchProductsForOrderCreation +/** + * Queries for products by provided search phrase + */ +class SearchProducts { /** * @var string */ private $phrase; + /** + * @var int + */ + private $resultsLimit; + /** * @param string $phrase + * @param int $resultsLimit * * @throws ProductException */ - public function __construct(string $phrase) + public function __construct(string $phrase, int $resultsLimit) { $this->assertIsNotEmptyString($phrase); $this->phrase = $phrase; + $this->resultsLimit = $resultsLimit; } /** @@ -54,6 +64,14 @@ public function getPhrase() return $this->phrase; } + /** + * @return int + */ + public function getResultsLimit(): int + { + return $this->resultsLimit; + } + /** * @param string $phrase * diff --git a/src/Core/Domain/Order/QueryHandler/SearchProductsForOrderCreationHandlerInterface.php b/src/Core/Domain/Product/QueryHandler/SearchProductsHandlerInterface.php similarity index 69% rename from src/Core/Domain/Order/QueryHandler/SearchProductsForOrderCreationHandlerInterface.php rename to src/Core/Domain/Product/QueryHandler/SearchProductsHandlerInterface.php index 0c328840da648..7ddde63ff6037 100644 --- a/src/Core/Domain/Order/QueryHandler/SearchProductsForOrderCreationHandlerInterface.php +++ b/src/Core/Domain/Product/QueryHandler/SearchProductsHandlerInterface.php @@ -24,17 +24,20 @@ * International Registered Trademark & Property of PrestaShop SA */ -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryHandler; +namespace PrestaShop\PrestaShop\Core\Domain\Product\QueryHandler; -use PrestaShop\PrestaShop\Core\Domain\Order\Query\SearchProductsForOrderCreation; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductsForOrderCreation; +use PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts; +use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\FoundProduct; -interface SearchProductsForOrderCreationHandlerInterface +/** + * Interface for handling SearchProducts query + */ +interface SearchProductsHandlerInterface { /** - * @param SearchProductsForOrderCreation $query + * @param SearchProducts $query * - * @return ProductsForOrderCreation + * @return FoundProduct[] */ - public function handle(SearchProductsForOrderCreation $query): ProductsForOrderCreation; + public function handle(SearchProducts $query): array; } diff --git a/src/Core/Domain/Order/QueryResult/ProductForOrderCreation.php b/src/Core/Domain/Product/QueryResult/FoundProduct.php similarity index 71% rename from src/Core/Domain/Order/QueryResult/ProductForOrderCreation.php rename to src/Core/Domain/Product/QueryResult/FoundProduct.php index a80944a8c89f5..7584c7f06522e 100644 --- a/src/Core/Domain/Order/QueryResult/ProductForOrderCreation.php +++ b/src/Core/Domain/Product/QueryResult/FoundProduct.php @@ -24,9 +24,12 @@ * International Registered Trademark & Property of PrestaShop SA */ -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; +namespace PrestaShop\PrestaShop\Core\Domain\Product\QueryResult; -class ProductForOrderCreation +/** + * DTO for product that was found by search + */ +class FoundProduct { /** * @var int @@ -49,12 +52,12 @@ class ProductForOrderCreation private $stock; /** - * @var ProductCombinationsForOrderCreation|null + * @var ProductCombination[] */ private $combinations; /** - * @var ProductCustomizationFields|null + * @var ProductCustomizationField[] */ private $customizationFields; @@ -63,17 +66,23 @@ class ProductForOrderCreation * @param string $name * @param string $formattedPrice * @param int $stock + * @param ProductCombination[] $combinations + * @param ProductCustomizationField[] $customizationFields */ public function __construct( int $productId, string $name, string $formattedPrice, - int $stock + int $stock, + array $combinations = [], + array $customizationFields = [] ) { $this->productId = $productId; $this->name = $name; $this->formattedPrice = $formattedPrice; $this->stock = $stock; + $this->combinations = $combinations; + $this->customizationFields = $customizationFields; } /** @@ -109,42 +118,18 @@ public function getStock(): int } /** - * @return ProductCombinationsForOrderCreation|null + * @return ProductCombination[] */ - public function getCombinations(): ?ProductCombinationsForOrderCreation + public function getCombinations(): array { return $this->combinations; } /** - * @param ProductCombinationsForOrderCreation $combinations - * - * @return ProductForOrderCreation - */ - public function setCombinations(ProductCombinationsForOrderCreation $combinations): ProductForOrderCreation - { - $this->combinations = $combinations; - - return $this; - } - - /** - * @return ProductCustomizationFields|null + * @return ProductCustomizationField[] */ - public function getCustomizationFields(): ?ProductCustomizationFields + public function getCustomizationFields(): array { return $this->customizationFields; } - - /** - * @param ProductCustomizationFields $customizationFields - * - * @return ProductForOrderCreation - */ - public function setCustomizationFields(ProductCustomizationFields $customizationFields): ProductForOrderCreation - { - $this->customizationFields = $customizationFields; - - return $this; - } } diff --git a/src/Core/Domain/Order/QueryResult/ProductCombinationForOrderCreation.php b/src/Core/Domain/Product/QueryResult/ProductCombination.php similarity index 94% rename from src/Core/Domain/Order/QueryResult/ProductCombinationForOrderCreation.php rename to src/Core/Domain/Product/QueryResult/ProductCombination.php index 1082a0fc20cbc..63430565d77ff 100644 --- a/src/Core/Domain/Order/QueryResult/ProductCombinationForOrderCreation.php +++ b/src/Core/Domain/Product/QueryResult/ProductCombination.php @@ -24,9 +24,12 @@ * International Registered Trademark & Property of PrestaShop SA */ -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; +namespace PrestaShop\PrestaShop\Core\Domain\Product\QueryResult; -class ProductCombinationForOrderCreation +/** + * Holds product combination data + */ +class ProductCombination { /** * @var int diff --git a/src/Core/Domain/Order/QueryResult/ProductCustomizationField.php b/src/Core/Domain/Product/QueryResult/ProductCustomizationField.php similarity index 97% rename from src/Core/Domain/Order/QueryResult/ProductCustomizationField.php rename to src/Core/Domain/Product/QueryResult/ProductCustomizationField.php index d858687295d05..91133fdadb6de 100644 --- a/src/Core/Domain/Order/QueryResult/ProductCustomizationField.php +++ b/src/Core/Domain/Product/QueryResult/ProductCustomizationField.php @@ -24,7 +24,7 @@ * International Registered Trademark & Property of PrestaShop SA */ -namespace PrestaShop\PrestaShop\Core\Domain\Order\QueryResult; +namespace PrestaShop\PrestaShop\Core\Domain\Product\QueryResult; /** * Holds data of product customization field diff --git a/src/PrestaShopBundle/Controller/Admin/ProductController.php b/src/PrestaShopBundle/Controller/Admin/ProductController.php index 5cd0fd07dcd10..fd3832d6cc9fd 100644 --- a/src/PrestaShopBundle/Controller/Admin/ProductController.php +++ b/src/PrestaShopBundle/Controller/Admin/ProductController.php @@ -31,6 +31,8 @@ use PrestaShop\PrestaShop\Adapter\Product\ListParametersUpdater; use PrestaShop\PrestaShop\Adapter\Tax\TaxRuleDataProvider; use PrestaShop\PrestaShop\Adapter\Warehouse\WarehouseDataProvider; +use PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts; +use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\FoundProduct; use PrestaShopBundle\Component\CsvResponse; use PrestaShopBundle\Entity\AdminFilter; use PrestaShopBundle\Exception\UpdateProductException; @@ -1253,4 +1255,28 @@ public function renderFieldAction($productId, $step, $fieldName) 'formId' => $step . '_' . $fieldName . '_rendered', ]); } + + /** + * @param Request $request + * + * @return Response + */ + public function searchProductsAction(Request $request): Response + { + try { + $searchPhrase = $request->query->get('search_phrase'); + + /** @var FoundProduct[] $foundProducts */ + $foundProducts = $this->getQueryBus()->handle(new SearchProducts($searchPhrase, 5)); + + $serializer = $this->get('prestashop.bundle.snake_case_serializer_json'); + + return new Response($serializer->serialize($foundProducts, 'json')); + } catch (Exception $e) { + return $this->json( + ['message' => $this->getErrorMessageForException($e, [])], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + } } diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php index 830fbfb4c8ed3..7ee3ea88ba5df 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php @@ -31,9 +31,9 @@ use PrestaShop\PrestaShop\Core\Domain\Cart\Query\GetCartInformation; use PrestaShop\PrestaShop\Core\Domain\Order\Command\AddCartRuleToOrderCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Command\BulkChangeOrderStatusCommand; -use PrestaShop\PrestaShop\Core\Domain\Order\Command\DuplicateOrderCartCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Command\ChangeOrderCurrencyCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Command\DeleteCartRuleFromOrderCommand; +use PrestaShop\PrestaShop\Core\Domain\Order\Command\DuplicateOrderCartCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Command\ResendOrderEmailCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Command\UpdateOrderStatusCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Exception\CannotEditDeliveredOrderProductException; @@ -44,7 +44,6 @@ use PrestaShop\PrestaShop\Core\Domain\Order\Payment\Command\AddPaymentCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Product\Command\UpdateProductInOrderCommand; use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderForViewing; -use PrestaShop\PrestaShop\Core\Domain\Order\Query\SearchProductsForOrderCreation; use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderForViewing; use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderPreview; use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPreview; @@ -56,12 +55,12 @@ use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; use PrestaShopBundle\Form\Admin\Sell\Customer\PrivateNoteType; use PrestaShopBundle\Form\Admin\Sell\Order\AddOrderCartRuleType; -use PrestaShopBundle\Form\Admin\Sell\Order\ChangeOrderCurrencyType; use PrestaShopBundle\Form\Admin\Sell\Order\AddProductToOrderType; +use PrestaShopBundle\Form\Admin\Sell\Order\ChangeOrderCurrencyType; use PrestaShopBundle\Form\Admin\Sell\Order\ChangeOrdersStatusType; use PrestaShopBundle\Form\Admin\Sell\Order\OrderPaymentType; -use PrestaShopBundle\Form\Admin\Sell\Order\UpdateProductInOrderType; use PrestaShopBundle\Form\Admin\Sell\Order\UpdateOrderStatusType; +use PrestaShopBundle\Form\Admin\Sell\Order\UpdateProductInOrderType; use PrestaShopBundle\Security\Annotation\AdminSecurity; use PrestaShopBundle\Service\Grid\ResponseBuilder; use Symfony\Component\HttpFoundation\JsonResponse; diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml index c8fcac334b45a..186c85278c5fe 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml @@ -88,3 +88,11 @@ admin_product_image_delete: idImage: 0 requirements: idImage: \d+ + +admin_products_search: + path: /search + methods: [GET] + defaults: + _controller: PrestaShopBundle:Admin/Product:searchProducts + options: + expose: true diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/order.yml b/src/PrestaShopBundle/Resources/config/services/adapter/order.yml index 6a5e6f1e61a86..9134fbc1a35a2 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/order.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/order.yml @@ -140,7 +140,6 @@ services: - '@prestashop.adapter.legacy.context' - '@prestashop.core.localization.locale.repository' - "@=service('prestashop.adapter.legacy.context').getContext().language.getLocale()" - tags: - name: tactician.handler command: PrestaShop\PrestaShop\Core\Domain\Order\Query\SearchProductsForOrderCreation diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/product.yml b/src/PrestaShopBundle/Resources/config/services/adapter/product.yml index 60f8ae1d3b437..15b76b576b8c9 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/product.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/product.yml @@ -40,3 +40,13 @@ services: public: true tags: - { name: tactician.handler, command: PrestaShop\PrestaShop\Core\Domain\Product\Command\AssignProductToCategoryCommand } + + prestashop.adapter.product.query_handler.search_products: + class: PrestaShop\PrestaShop\Adapter\Product\QueryHandler\SearchProductsHandler + arguments: + - '@prestashop.adapter.legacy.context' + - '@prestashop.core.localization.locale.repository' + - "@=service('prestashop.adapter.legacy.context').getContext().language.getLocale()" + tags: + - name: tactician.handler + command: PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig index 77a2a40186fb4..260e444da5497 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig @@ -31,7 +31,7 @@ {{ 'Search for a product'|trans({}, 'Admin.Orderscustomers.Feature') }}
- From f2a4796f80efeff55407696b601ca6c25c467263 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Tue, 22 Oct 2019 17:43:09 +0300 Subject: [PATCH 104/195] Refactoring products block js --- .../js/pages/order/create/create-order-map.js | 20 +- .../js/pages/order/create/product-manager.js | 261 ++------------ .../js/pages/order/create/product-renderer.js | 319 +++++++++++------- .../QueryHandler/SearchProductsHandler.php | 2 +- .../Controller/Admin/ProductController.php | 2 +- .../Order/Order/Blocks/Create/cart.html.twig | 104 +++--- 6 files changed, 266 insertions(+), 442 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js index 3bb1e4d46dee8..1a75cd0902a24 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-map.js @@ -98,24 +98,18 @@ export default { productResultBlock: '#product-search-results', productSelect: '#product-select', quantityInput: '#quantity-input', - noRecordsFound: '.js-no-records-found', - noRecordsFoundRow: '.js-no-records-found-row', inStockCounter: '.js-in-stock-counter', combinationsTemplate: '#combinations-template', combinationsRow: '.js-combinations-row', productSelectRow: '.js-product-select-row', - customizedFieldContainer: '.js-customized-field-container', + productCustomFieldsContainer: '#js-custom-fields-container', + productCustomizationContainer: '#js-customization-container', + productCustomFileTemplate: '#js-product-custom-file-template', + productCustomTextTemplate: '#js-product-custom-text-template', + productCustomInputLabel: '.js-product-custom-input-label', + productCustomInput: '.js-product-custom-input', quantityRow: '.js-quantity-row', - customizedLabelClass: '.js-customized-field-label', - cartProductCustomizedFieldTemplate: '#cart-product-customized-field-template', - customizedFieldInputWrapper: '.js-customized-field-input-wrapper', - customizedFieldInput: '.js-customized-field-input', - productSearchEmptyResultTemplate: '#product-search-empty-result-template', addToCartButton: '#add-product-to-cart-btn', - customizedFieldTypes: { - 0: '#cart-product-customized-field-input-template-0', - 1: '#cart-product-customized-field-input-template-1', - }, productsTable: '#products-table', productsTableRowTemplate: '#products-table-row-template', productImageField: '.js-product-image', @@ -125,4 +119,6 @@ export default { productUnitPriceInput: '.js-product-unit-input', productTotalPriceField: '.js-product-total-price', productRemoveBtn: '.js-product-remove-btn', + productTaxWarning: '.js-tax-warning', + noProductsFoundWarning: '.js-no-products-found', }; diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index f7823b2703a44..1438e156d1232 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -35,7 +35,8 @@ const $ = window.$; export default class ProductManager { constructor() { this.products = []; - this.combinations = []; + this.selectedProduct = {}; + this.combinations = {}; this.renderer = new ProductRenderer(); this.router = new Router(); @@ -55,15 +56,8 @@ export default class ProductManager { */ _initEvents() { $(createOrderPageMap.productSearch).on('input', event => this._handleProductSearch(event)); - $(createOrderPageMap.productSelect).on('change', event => { - const index = $(event.currentTarget).find(':selected').data('index'); - this.renderer._handleProductChange(this.products[index]) - }); - $(createOrderPageMap.productResultBlock).on( - 'change', - createOrderPageMap.combinationsSelect, - event => this.renderer._handleCombinationChange(event), - ); + $(createOrderPageMap.productSelect).on('change', event => this._handleProductSelect(event)); + $(createOrderPageMap.combinationsSelect).on('change', event => this._handleCombinationSelect(event)); } /** @@ -81,256 +75,43 @@ export default class ProductManager { } $.get(this.router.generate('admin_products_search'), { - search_phrase: name, + search_phrase: name, }).then((response) => { this.products = JSON.parse(response); - if (this.products.length === 0) { - this._showNotFoundProducts(); - - return; - } - this.renderer.renderSearchResults(this.products); + this.combinations = this.products[0].combinations; }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); } }); } - // /** - // * Renders product result block - // * - // * @private - // */ - // _renderProductSearchResult() { - // $(createOrderPageMap.productSelect).empty(); - // - // for (const [index, product] of this.products.entries()) { - // if (index === 0) { - // this._fillFieldsRelatedToProduct(product); - // } - // - // let name = product.name; - // const combinationsCollection = product.combinations; - // - // if (combinationsCollection === null) { - // name += ` - ${ product.formatted_price}`; - // } - // - // const shouldUpdateStockValue = combinationsCollection === null && index === 0; - // - // if (shouldUpdateStockValue) { - // this._updateStock(product.stock); - // } - // - // $(createOrderPageMap.productSelect).append( - // $('').attr('value', product.product_id).text(name).attr('data-index', index), - // ); - // } - // - // const $noRecordsRow = $(createOrderPageMap.noRecordsFound).find( - // createOrderPageMap.noRecordsFoundRow, - // ); - // - // if ($noRecordsRow.length !== 0) { - // $noRecordsRow.remove(); - // } - // - // this._showResultBlock(); - // } - - // /** - // * Updates stock text helper value - // * - // * @param stock - // * @private - // */ - // _updateStock(stock) { - // $(createOrderPageMap.inStockCounter).text(stock); - // $(createOrderPageMap.quantityInput).attr('max', stock); - // } - - // /** - // * Adds available fields related to selected product - // * - // * @param product - // * @private - // */ - // _fillFieldsRelatedToProduct(product) { - // this._fillCombinations(product.combinations); - // this._resolveCustomizationFields(product.customization_fields); - // } - - // /** - // * Handles product select change - // * - // * @param event - // * @private - // */ - // _handleProductChange(event) { - // const index = $(event.currentTarget).find(':selected').data('index'); - // const product = this.products[index]; - // - // this._fillFieldsRelatedToProduct(product); - // - // if (product.combinations === null) { - // this.combinations = []; - // this._updateStock(product.stock); - // } - // - // this._fillCombinations(product.combinations); - // } - // - // /** - // * Handles combination select change - // * - // * @param event - // * @private - // */ - // _handleCombinationChange(event) { - // const index = $(event.currentTarget).find(':selected').val(); - // const combination = this.combinations[index]; - // - // this._updateStock(combination.stock); - // } - // - // /** - // * Fills combination select with options - // * - // * @param combinations - // * @private - // */ - // _fillCombinations(combinations) { - // if (combinations === null) { - // this._removeCombinationSelect(); - // - // return; - // } - // - // this.combinations = combinations.combinations; - // - // if ($(createOrderPageMap.combinationsRow).length === 0) { - // const $combinationsTemplate = $($(createOrderPageMap.combinationsTemplate).html()); - // $(createOrderPageMap.productSelectRow).after($combinationsTemplate); - // } - // - // const $combinationsSelect = $(createOrderPageMap.combinationsSelect); - // $combinationsSelect.empty(); - // - // const entries = Object.entries(combinations.combinations); - // - // let i = 0; // This is needed because index in this case is attribute combination id - // - // for (const [id, combination] of entries) { - // if (i === 0) { - // this._updateStock(combination.stock); - // } - // - // const name = `${combination.attribute } - ${ combination.formatted_price}`; - // $combinationsSelect.append($('') - // .attr('value', id).text(name)); - // - // i += 1; - // } - // } - // - // /** - // * Resolves weather to add customization fields to result block and adds them if needed - // * - // * @param customizationFields - // * @private - // */ - // _resolveCustomizationFields(customizationFields) { - // this._removeCustomizedFields(); - // if (customizationFields === null) { - // return; - // } - // - // const $customizedFieldTemplate = $(createOrderPageMap.cartProductCustomizedFieldTemplate); - // let $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - // - // const customizationLabel = $(createOrderPageMap.productSelect).data('customization-label'); - // - // $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - // .text(customizationLabel); - // - // $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); - // - // $.each(customizationFields.product_customization_fields, (index, field) => { - // const $fieldTemplate = $($(createOrderPageMap.customizedFieldTypes[field.type]).html()); - // - // $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - // if (field.required) { - // $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - // .append('*'); - // } - // - // $customizedFieldTemplateContent.find(createOrderPageMap.customizedLabelClass) - // .append(field.name); - // - // const $fieldWrapper = $customizedFieldTemplateContent - // .find(createOrderPageMap.customizedFieldInputWrapper); - // - // $fieldWrapper.append($fieldTemplate); - // $fieldWrapper.find(createOrderPageMap.customizedFieldInput) - // .attr('data-customization-field-id', field.customization_field_id); - // - // $(createOrderPageMap.quantityRow).before($customizedFieldTemplateContent); - // }); - // } - - // /** - // * Removes combination select for products with no combinations - // * - // * @private - // */ - // _removeCombinationSelect() { - // $(createOrderPageMap.combinationsRow).remove(); - // } - // - // /** - // * Removes customized fields select for products with no customized fields - // * - // * @private - // */ - // _removeCustomizedFields() { - // $(createOrderPageMap.customizedFieldContainer).remove(); - // } /** - * Shows empty result when product is not found + * Handles use case when product is selected from search results * - * @private - */ - _showNotFoundProducts() { - const $emptyResultTemplate = $($(createOrderPageMap.productSearchEmptyResultTemplate).html()); - - this._hideResultBlock(); - - const $noRecordsElement = $(createOrderPageMap.noRecordsFound); - - if ($noRecordsElement.find(createOrderPageMap.noRecordsFoundRow).length === 0) { - $noRecordsElement.append($emptyResultTemplate); - } - } - - /** - * Shows result block + * @param event * * @private */ - _showResultBlock() { - $(createOrderPageMap.productResultBlock).show(); - $(createOrderPageMap.productResultBlock).removeClass('d-none'); + _handleProductSelect(event) { + const index = $(event.currentTarget).find(':selected').data('index'); + this.renderer.renderProductMetadata(this.products[index]); + this.combinations = this.products[index].combinations; } /** - * Hides result block + * Handles use case when new combination is selected + * + * @param event * * @private */ - _hideResultBlock() { - $(createOrderPageMap.productResultBlock).hide(); + _handleCombinationSelect(event) { + const combinationIndex = $(event.currentTarget).find(':selected').data('index'); + const combination = this.combinations[combinationIndex]; + debugger; + this.renderer.renderStock(combination.stock); } /** @@ -346,7 +127,7 @@ export default class ProductManager { contentType: false, cache: false, }).then((response) => { - this.renderer.render(response.products); + this.renderer.renderList(response.products); }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js index c103aa55a68f1..513f0cf797f68 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js @@ -31,17 +31,21 @@ export default class ProductRenderer { constructor() { this.$productsTable = $(createOrderMap.productsTable); } - render(products) { + + /** + * Renders cart products list + * + * @param products + */ + renderList(products) { + this._cleanProductsList(); + if (products.length === 0) { this._hideProductsList(); return; } - this._renderList(products); - } - _renderList(products) { - this._cleanProductsList(); const $productsTableRowTemplate = $($(createOrderMap.productsTableRowTemplate).html()); for (const key in products) { @@ -59,200 +63,197 @@ export default class ProductRenderer { this.$productsTable.find('tbody').append($template); } + this._showTaxWarning(); this._showProductsList(); } + /** + * Renders cart products search results block + * + * @param foundProducts + */ renderSearchResults(foundProducts) { - $(createOrderMap.productSelect).empty(); + this._cleanSearchResults(); + if (foundProducts.length === 0) { + this._showNotFound(); + this._hideTaxWarning(); - for (const [index, product] of foundProducts.entries()) { - if (index === 0) { - this._fillFieldsRelatedToProduct(product); - } + return; + } - let name = product.name; - const combinationsCollection = product.combinations; + this._renderFoundProducts(foundProducts); + this.renderProductMetadata(foundProducts[0]); - if (combinationsCollection === null) { - name += ` - ${ product.formatted_price}`; - } + this._hideNotFound(); + this._showTaxWarning(); + this._showResultBlock(); + } - const shouldUpdateStockValue = combinationsCollection === null && index === 0; + /** + * Renders available fields related to selected product + * + * @param product + */ + renderProductMetadata(product) { + this._renderCombinations(product.combinations); + this._renderCustomizations(product.customization_fields); + this.renderStock(product.stock); + } - if (shouldUpdateStockValue) { - this._updateStock(product.stock); - } + /** + * Updates stock text helper value + * + * @param stock + */ + renderStock(stock) { + $(createOrderMap.inStockCounter).text(stock); + $(createOrderMap.quantityInput).attr('max', stock); + } + + /** + * Renders found products select + * + * @param foundProducts + * + * @private + */ + _renderFoundProducts(foundProducts) { + for (const key in foundProducts) { + const product = foundProducts[key]; $(createOrderMap.productSelect).append( - $('').attr('value', product.product_id).text(name).attr('data-index', index), + ``, ); } - - const $noRecordsRow = $(createOrderMap.noRecordsFound).find( - createOrderMap.noRecordsFoundRow, - ); - - if ($noRecordsRow.length !== 0) { - $noRecordsRow.remove(); - } - - this._showResultBlock(); } + /** + * Cleans product search result fields + * + * @private + */ + _cleanSearchResults() { + $(createOrderMap.productSelect).empty(); + $(createOrderMap.combinationsSelect).empty(); + $(createOrderMap.quantityInput).empty(); + } /** - * Fills combination select with options + * Renders combinations row with select options + * + * @param {Array} combinations * - * @param combinations * @private */ - _fillCombinations(combinations) { - if (combinations === null) { - this._removeCombinationSelect(); + _renderCombinations(combinations) { + this._cleanCombinations(); + + if (combinations.length === 0) { + this._hideCombinations(); return; } - this.combinations = combinations; + for (const key in combinations) { + const combination = combinations[key]; + this.renderStock(combination.stock); - if ($(createOrderMap.combinationsRow).length === 0) { - const $combinationsTemplate = $($(createOrderMap.combinationsTemplate).html()); - $(createOrderMap.productSelectRow).after($combinationsTemplate); + $(createOrderMap.combinationsSelect).append( + ``, + ); } - const $combinationsSelect = $(createOrderMap.combinationsSelect); - $combinationsSelect.empty(); - - const entries = Object.entries(combinations); - - let i = 0; // This is needed because index in this case is attribute combination id - - for (const [id, combination] of entries) { - if (i === 0) { - this._updateStock(combination.stock); - } - - const name = `${combination.attribute } - ${ combination.formatted_price}`; - $combinationsSelect.append($('') - .attr('value', id).text(name)); - - i += 1; - } + this._showCombinations(); } /** * Resolves weather to add customization fields to result block and adds them if needed * * @param customizationFields + * * @private */ - _resolveCustomizationFields(customizationFields) { - this._removeCustomizedFields(); - if (customizationFields === null) { + _renderCustomizations(customizationFields) { + this._cleanCustomizations(); + if (customizationFields.length === 0) { + this._hideCustomizations(); + return; } - const $customizedFieldTemplate = $(createOrderMap.cartProductCustomizedFieldTemplate); - let $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - - const customizationLabel = $(createOrderMap.productSelect).data('customization-label'); - - $customizedFieldTemplateContent.find(createOrderMap.customizedLabelClass) - .text(customizationLabel); - - $(createOrderMap.quantityRow).before($customizedFieldTemplateContent); + const $customFieldsContainer = $(createOrderMap.productCustomFieldsContainer); - $.each(customizationFields.product_customization_fields, (index, field) => { - const $fieldTemplate = $($(createOrderMap.customizedFieldTypes[field.type]).html()); + const $fileInputTemplate = $($(createOrderMap.productCustomFileTemplate).html()); + const $textInputTemplate = $($(createOrderMap.productCustomTextTemplate).html()); - $customizedFieldTemplateContent = $($customizedFieldTemplate.html()); - if (field.required) { - $customizedFieldTemplateContent.find(createOrderMap.customizedLabelClass) - .append('*'); - } + const templateTypeMap = { + 0: $fileInputTemplate, + 1: $textInputTemplate, + }; - $customizedFieldTemplateContent.find(createOrderMap.customizedLabelClass) - .append(field.name); + for (const key in customizationFields) { + const customField = customizationFields[key]; + const $template = templateTypeMap[customField.type].clone(); - const $fieldWrapper = $customizedFieldTemplateContent - .find(createOrderMap.customizedFieldInputWrapper); + $template.find(createOrderMap.productCustomInput) + .attr('name', `customization[${customField.customization_field_id}]`); + $template.find(createOrderMap.productCustomInputLabel) + .attr('for', `customization[${customField.customization_field_id}]`) + .text(customField.name); - $fieldWrapper.append($fieldTemplate); - $fieldWrapper.find(createOrderMap.customizedFieldInput) - .attr('data-customization-field-id', field.customization_field_id); + $customFieldsContainer.append($template); + } - $(createOrderMap.quantityRow).before($customizedFieldTemplateContent); - }); + this._showCustomizations(); } /** - * Adds available fields related to selected product + * Shows product customization container * - * @param product * @private */ - _fillFieldsRelatedToProduct(product) { - this._fillCombinations(product.combinations); - this._resolveCustomizationFields(product.customization_fields); + _showCustomizations() { + $(createOrderMap.productCustomizationContainer).removeClass('d-none'); } /** - * Removes combination select for products with no combinations + * Hides product customization container * * @private */ - _removeCombinationSelect() { - $(createOrderMap.combinationsRow).remove(); + _hideCustomizations() { + $(createOrderMap.productCustomizationContainer).addClass('d-none'); } /** - * Removes customized fields select for products with no customized fields + * Empties customization fields container * * @private */ - _removeCustomizedFields() { - $(createOrderMap.customizedFieldContainer).remove(); - } - - /** - * Handles product select change - * - * @param event - * @private - */ - _handleProductChange(product) { - this._fillFieldsRelatedToProduct(product); - - if (product.combinations === null) { - this.combinations = []; - this._updateStock(product.stock); - } - - this._fillCombinations(product.combinations); + _cleanCustomizations() { + $(createOrderMap.productCustomFieldsContainer).empty(); } /** * Handles combination select change * * @param event + * * @private */ _handleCombinationChange(event) { const index = $(event.currentTarget).find(':selected').val(); const combination = this.combinations[index]; - this._updateStock(combination.stock); - } - - /** - * Updates stock text helper value - * - * @param stock - * @private - */ - _updateStock(stock) { - $(createOrderMap.inStockCounter).text(stock); - $(createOrderMap.quantityInput).attr('max', stock); + this.renderStock(combination.stock); } /** @@ -261,7 +262,6 @@ export default class ProductRenderer { * @private */ _showResultBlock() { - $(createOrderMap.productResultBlock).show(); $(createOrderMap.productResultBlock).removeClass('d-none'); } @@ -271,7 +271,7 @@ export default class ProductRenderer { * @private */ _hideResultBlock() { - $(createOrderMap.productResultBlock).hide(); + $(createOrderMap.productResultBlock).addClass('d-none'); } @@ -294,11 +294,74 @@ export default class ProductRenderer { } /** - * Emptes products list + * Empties products list * * @private */ _cleanProductsList() { this.$productsTable.find('tbody').empty(); } + + /** + * Empties combinations select + * + * @private + */ + _cleanCombinations() { + $(createOrderMap.combinationsSelect).empty(); + } + + /** + * Shows combinations row + * + * @private + */ + _showCombinations() { + $(createOrderMap.combinationsRow).removeClass('d-none'); + } + + /** + * Hides combinations row + * + * @private + */ + _hideCombinations() { + $(createOrderMap.combinationsRow).addClass('d-none'); + } + + /** + * Shows warning of tax included/excluded + * + * @private + */ + _showTaxWarning() { + $(createOrderMap.productTaxWarning).removeClass('d-none'); + } + + /** + * Hides warning of tax included/excluded + * + * @private + */ + _hideTaxWarning() { + $(createOrderMap.productTaxWarning).addClass('d-none'); + } + + /** + * Shows product not found warning + * + * @private + */ + _showNotFound() { + $(createOrderMap.noProductsFoundWarning).removeClass('d-none'); + } + + /** + * Hides product not found warning + * + * @private + */ + _hideNotFound() { + $(createOrderMap.noProductsFoundWarning).addClass('d-none'); + } } diff --git a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php index cd610948ed546..36ebb111ed9f3 100644 --- a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php +++ b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php @@ -188,6 +188,6 @@ private function getProductCombinations(Product $product): array } } - return $productCombinations; + return array_values($productCombinations); } } diff --git a/src/PrestaShopBundle/Controller/Admin/ProductController.php b/src/PrestaShopBundle/Controller/Admin/ProductController.php index fd3832d6cc9fd..3b397b78bade3 100644 --- a/src/PrestaShopBundle/Controller/Admin/ProductController.php +++ b/src/PrestaShopBundle/Controller/Admin/ProductController.php @@ -1267,7 +1267,7 @@ public function searchProductsAction(Request $request): Response $searchPhrase = $request->query->get('search_phrase'); /** @var FoundProduct[] $foundProducts */ - $foundProducts = $this->getQueryBus()->handle(new SearchProducts($searchPhrase, 5)); + $foundProducts = $this->getQueryBus()->handle(new SearchProducts($searchPhrase, 10)); $serializer = $this->get('prestashop.bundle.snake_case_serializer_json'); diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig index 260e444da5497..e42836b9dba22 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig @@ -40,6 +40,14 @@
+
+
+ +
+
+
@@ -54,6 +62,25 @@
+
+
+ {{ 'Combination'|trans({}, 'Admin.Global') }} +
+
+ +
+
+ + {#@todo: max height and verical scroll#} +
+
+ + {{ 'Customization'|trans({}, 'Admin.Global') }} + +
+
+
+
{{ 'Quantity'|trans({}, 'Admin.Global') }} @@ -98,7 +125,7 @@
-
+
@@ -140,59 +167,25 @@
-
-
+ - - - - - - - @@ -200,25 +193,16 @@ From 037d36802f98c7ced36d9c091c4f4eb657f190fe Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Wed, 23 Oct 2019 13:22:42 +0300 Subject: [PATCH 105/195] Switch js products and combination to object with id instead of array --- .../pages/order/create/create-order-page.js | 25 ++++++++-------- .../js/pages/order/create/product-manager.js | 20 ++++++------- .../js/pages/order/create/product-renderer.js | 29 ++++++++++--------- .../QueryHandler/SearchProductsHandler.php | 9 +++--- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index f178a793d815f..0874a747db7db 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -78,9 +78,9 @@ export default class CreateOrderPage { */ _onCartLoaded() { EventEmitter.on(eventMap.cartLoaded, (cartInfo) => { - this.data.cart_id = cartInfo.cartId; + this.data.cartId = cartInfo.cartId; this._renderCartInfo(cartInfo); - this._loadCustomerCarts(this.data.customer_id); + this._loadCustomerCarts(this.data.customerId); }); } @@ -103,7 +103,7 @@ export default class CreateOrderPage { _handleCustomerChooseForOrderCreation() { this.$container.on('click', createOrderPageMap.chooseCustomerBtn, (event) => { const customerId = this.customerManager.onCustomerChooseForOrderCreation(event); - this.data.customer_id = customerId; + this.data.customerId = customerId; this.cartProvider.loadEmptyCart(customerId); this._loadCustomerCarts(customerId); @@ -143,11 +143,10 @@ export default class CreateOrderPage { * @private */ _handleCartEdit() { - // @todo: add other actions this.$container.on('change', createOrderPageMap.addressSelect, () => this._changeCartAddresses()); this.$container.on('change', createOrderPageMap.deliveryOptionSelect, e => this._changeDeliveryOption(e)); this.$container.on('change', createOrderPageMap.freeShippingSwitch, e => this._setFreeShipping(e)); - this.$container.on('click', createOrderPageMap.addToCartButton, () => this.cartProducts.onAddProductToCart(this.data.cart_id)); + this.$container.on('click', createOrderPageMap.addToCartButton, () => this.cartProducts.onAddProductToCart(this.data.cartId)); this._selectCartRule(); this._removeCartRule(); } @@ -176,7 +175,7 @@ export default class CreateOrderPage { // prevent blur event to allow selecting cart rule event.preventDefault(); const cartRuleId = $(event.currentTarget).data('cart-rule-id'); - this.cartRuleManager.onCartRuleSelect(cartRuleId, this.data.cart_id); + this.cartRuleManager.onCartRuleSelect(cartRuleId, this.data.cartId); // manually fire blur event after cart rule is selected. }).on('click', createOrderPageMap.foundCartRuleListItem, () => { @@ -193,7 +192,7 @@ export default class CreateOrderPage { this.$container.on('click', createOrderPageMap.cartRuleDeleteBtn, (event) => { this.cartRuleManager.onCartRuleRemove( $(event.currentTarget).data('cart-rule-id'), - this.data.cart_id + this.data.cartId ); EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => { this.cartRulesRenderer.render(cartInfo.cartRules, cartInfo.products.length === 0); @@ -213,7 +212,7 @@ export default class CreateOrderPage { EventEmitter.on(eventMap.customerCartsLoaded, (cartInfo) => { this.cartsRenderer.render({ carts: cartInfo.carts, - currentCartId: this.data.cart_id, + currentCartId: this.data.cartId, }); }); } @@ -260,7 +259,7 @@ export default class CreateOrderPage { invoice_address_id: $(createOrderPageMap.invoiceAddressSelect).val(), }; - this.cartEditor.changeCartAddresses(this.data.cart_id, addresses); + this.cartEditor.changeCartAddresses(this.data.cartId, addresses); EventEmitter.on(eventMap.cartAddressesChanged, (cartInfo) => { this.addressesRenderer.render(cartInfo.addresses); this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); @@ -275,7 +274,7 @@ export default class CreateOrderPage { * @private */ _changeDeliveryOption(event) { - this.cartEditor.changeDeliveryOption(this.data.cart_id, event.currentTarget.value); + this.cartEditor.changeDeliveryOption(this.data.cartId, event.currentTarget.value); EventEmitter.on(eventMap.cartDeliveryOptionChanged, (cartInfo) => { this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); @@ -289,7 +288,7 @@ export default class CreateOrderPage { * @private */ _setFreeShipping(event) { - this.cartEditor.setFreeShipping(this.data.cart_id, event.currentTarget.value); + this.cartEditor.setFreeShipping(this.data.cartId, event.currentTarget.value); EventEmitter.on(eventMap.cartFreeShippingSet, (cartInfo) => { this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); @@ -304,7 +303,7 @@ export default class CreateOrderPage { * @private */ _persistCartInfoData(cartInfo) { - this.data.cart_id = cartInfo.cart.id; + this.data.cartId = cartInfo.cart.id; this.data.delivery_address_id = cartInfo.cart.id_address_delivery; this.data.invoice_address_id = cartInfo.cart.id_address_invoice; } @@ -322,7 +321,7 @@ export default class CreateOrderPage { method: 'POST', data: { id_cart: cartId, - id_customer: this.data.customer_id, + id_customer: this.data.customerId, }, }).then((response) => { this._persistCartInfoData(response); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 1438e156d1232..1cfdd34de25e7 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -34,9 +34,8 @@ const $ = window.$; */ export default class ProductManager { constructor() { - this.products = []; - this.selectedProduct = {}; - this.combinations = {}; + this.products = {}; + this.currentProductId = {}; this.renderer = new ProductRenderer(); this.router = new Router(); @@ -78,8 +77,7 @@ export default class ProductManager { search_phrase: name, }).then((response) => { this.products = JSON.parse(response); - this.renderer.renderSearchResults(this.products); - this.combinations = this.products[0].combinations; + this.currentProductId = this.renderer.renderSearchResults(this.products); }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); @@ -95,9 +93,10 @@ export default class ProductManager { * @private */ _handleProductSelect(event) { - const index = $(event.currentTarget).find(':selected').data('index'); - this.renderer.renderProductMetadata(this.products[index]); - this.combinations = this.products[index].combinations; + const id = Number($(event.currentTarget).find(':selected').val()); + this.renderer.renderProductMetadata(this.products[id]); + + this.currentProductId = id; } /** @@ -108,9 +107,8 @@ export default class ProductManager { * @private */ _handleCombinationSelect(event) { - const combinationIndex = $(event.currentTarget).find(':selected').data('index'); - const combination = this.combinations[combinationIndex]; - debugger; + const combinationId = Number($(event.currentTarget).find(':selected').val()); + const combination = this.products[this.currentProductId].combinations[combinationId]; this.renderer.renderStock(combination.stock); } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js index 513f0cf797f68..ccd966464fbc6 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js @@ -78,15 +78,19 @@ export default class ProductRenderer { this._showNotFound(); this._hideTaxWarning(); - return; + return null; } this._renderFoundProducts(foundProducts); - this.renderProductMetadata(foundProducts[0]); + // first product from found products is selected on rendering + const selectedProductId = Object.keys(foundProducts)[0]; + this.renderProductMetadata(foundProducts[selectedProductId]); this._hideNotFound(); this._showTaxWarning(); this._showResultBlock(); + + return selectedProductId; } /** @@ -95,9 +99,9 @@ export default class ProductRenderer { * @param product */ renderProductMetadata(product) { - this._renderCombinations(product.combinations); this._renderCustomizations(product.customization_fields); this.renderStock(product.stock); + this._renderCombinations(product.combinations); } /** @@ -121,13 +125,12 @@ export default class ProductRenderer { for (const key in foundProducts) { const product = foundProducts[key]; - $(createOrderMap.productSelect).append( - ``, - ); + let name = product.name; + if (product.combinations.length === 0) { + name += ` - ${product.formatted_price}`; + } + + $(createOrderMap.productSelect).append(``); } } @@ -160,17 +163,17 @@ export default class ProductRenderer { for (const key in combinations) { const combination = combinations[key]; - this.renderStock(combination.stock); $(createOrderMap.combinationsSelect).append( ``, ); } + // render stock of first combination which is the selected one + this.renderStock(combinations[Object.keys(combinations)[0]].stock); this._showCombinations(); } diff --git a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php index 36ebb111ed9f3..d734c524ef759 100644 --- a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php +++ b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php @@ -90,7 +90,8 @@ public function handle(SearchProducts $query): array if ($products) { foreach ($products as $product) { - $foundProducts[] = $this->createFoundProductFromLegacy(new Product($product['id_product'])); + $foundProduct = $this->createFoundProductFromLegacy(new Product($product['id_product'])); + $foundProducts[$foundProduct->getProductId()] = $foundProduct; } } @@ -142,7 +143,7 @@ private function getProductCustomizationFields(Product $product): array (bool) $field[$this->langId]['required'] ); - $customizationFields[] = $customizationField; + $customizationFields[$customizationField->getCustomizationFieldId()] = $customizationField; } } } @@ -184,10 +185,10 @@ private function getProductCombinations(Product $product): array $locale->formatPrice($priceTaxExcluded, $this->currencyCode) ); - $productCombinations[$productAttributeId] = $productCombination; + $productCombinations[$productCombination->getAttributeCombinationId()] = $productCombination; } } - return array_values($productCombinations); + return $productCombinations; } } From 132806b78e4a4af42e4c5d15409c4e3576a0e4f1 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Wed, 23 Oct 2019 17:00:40 +0300 Subject: [PATCH 106/195] refactoring customization fields. Added todo --- .../js/pages/order/create/product-manager.js | 116 ++++++++++++------ .../js/pages/order/create/product-renderer.js | 23 +--- .../Admin/Sell/Order/CartController.php | 16 ++- 3 files changed, 94 insertions(+), 61 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 1cfdd34de25e7..5807648cd2dc8 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -35,7 +35,9 @@ const $ = window.$; export default class ProductManager { constructor() { this.products = {}; - this.currentProductId = {}; + this.selectedProductId = null; + this.selectedCombinationId = null; + this.renderer = new ProductRenderer(); this.router = new Router(); @@ -54,9 +56,15 @@ export default class ProductManager { * @private */ _initEvents() { - $(createOrderPageMap.productSearch).on('input', event => this._handleProductSearch(event)); - $(createOrderPageMap.productSelect).on('change', event => this._handleProductSelect(event)); - $(createOrderPageMap.combinationsSelect).on('change', event => this._handleCombinationSelect(event)); + $(createOrderPageMap.productSearch).on('input', event => this._search(event)); + $(createOrderPageMap.productSelect).on('change', (event) => { + const productId = Number($(event.currentTarget).find(':selected').val()); + this._selectProduct(productId); + }); + $(createOrderPageMap.combinationsSelect).on('change', (event) => { + const combinationId = Number($(event.currentTarget).find(':selected').val()); + this.selectCombination(combinationId); + }); } /** @@ -64,7 +72,7 @@ export default class ProductManager { * * @private */ - _handleProductSearch(event) { + _search(event) { const minSearchPhraseLength = 3; const $productSearchInput = $(event.currentTarget); const name = $productSearchInput.val(); @@ -77,7 +85,8 @@ export default class ProductManager { search_phrase: name, }).then((response) => { this.products = JSON.parse(response); - this.currentProductId = this.renderer.renderSearchResults(this.products); + this.renderer.renderSearchResults(this.products); + this._selectFirstResult(); }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); @@ -86,30 +95,71 @@ export default class ProductManager { } /** - * Handles use case when product is selected from search results + * Initiate first result dataset after search * - * @param event + * @private + */ + _selectFirstResult() { + this._unsetProduct(); + + if (this.products.length !== 0) { + this._selectProduct(Object.keys(this.products)[0]); + } + } + + /** + * Handles use case when product is selected from search results * * @private + * + * @param productId */ - _handleProductSelect(event) { - const id = Number($(event.currentTarget).find(':selected').val()); - this.renderer.renderProductMetadata(this.products[id]); + _selectProduct(productId) { + this._unsetCombination(); + + this.selectedProductId = productId; + const product = this.products[productId]; - this.currentProductId = id; + this.renderer.renderProductMetadata(product); + + // if product has combinations select the first else leave it null + if (product.combinations.length !== 0) { + this.selectCombination(Object.keys(product.combinations)[0]); + } + + return product; } /** * Handles use case when new combination is selected * - * @param event + * @param combinationId + */ + selectCombination(combinationId) { + const combination = this.products[this.selectedProductId].combinations[combinationId]; + + this.selectedCombinationId = combinationId; + this.renderer.renderStock(combination.stock); + + return combination; + } + + /** + * Sets the selected combination id to null + * + * @private + */ + _unsetCombination() { + this.selectedCombinationId = null; + } + + /** + * Sets the selected product id to null * * @private */ - _handleCombinationSelect(event) { - const combinationId = Number($(event.currentTarget).find(':selected').val()); - const combination = this.products[this.currentProductId].combinations[combinationId]; - this.renderer.renderStock(combination.stock); + _unsetProduct() { + this.selectedProductId = null; } /** @@ -143,15 +193,11 @@ export default class ProductManager { const formData = new FormData(); formData.append('cart_id', cartId); - formData.append('product_id', $(createOrderPageMap.productSelect).find(':selected').val()); + formData.append('product_id', this.selectedProductId); formData.append('quantity', $(createOrderPageMap.quantityInput).val()); + formData.append('combination_id', this.selectedCombinationId); - if ($(createOrderPageMap.combinationsSelect).length !== 0) { - const combinationId = $(createOrderPageMap.combinationsSelect).find(':selected').val(); - formData.append('combination_id', combinationId); - } - - this._resolveCustomizationValuesForAddProduct(formData); + this._getCustomFieldsData(formData); return formData; } @@ -160,23 +206,23 @@ export default class ProductManager { * Resolves product customization fields to be added to formData object * * @param {FormData} formData + * * @returns {FormData} + * * @private */ - _resolveCustomizationValuesForAddProduct(formData) { - const customizationKey = 'customization'; - const customizedFields = $(createOrderPageMap.customizedFieldInput); + _getCustomFieldsData(formData) { + const $customFields = $(createOrderPageMap.productCustomInput); - $.each(customizedFields, (index, field) => { - const customizationFieldId = $(field).data('customization-field-id'); - const formKey = `${customizationKey}[${customizationFieldId}]`; - if ($(field).attr('type') === 'file') { - formData.append(formKey, $(field)[0].files[0]); + $customFields.each((key, field) => { + const $field = $(field); + const name = $field.attr('name'); - return; + if ($field.attr('type') === 'file') { + formData.append(name, $field[0].files[0]); + } else { + formData.append(name, $field.val()); } - - formData.append(formKey, $(field).val()); }); return formData; diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js index ccd966464fbc6..20f6261cbe63d 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js @@ -82,15 +82,10 @@ export default class ProductRenderer { } this._renderFoundProducts(foundProducts); - // first product from found products is selected on rendering - const selectedProductId = Object.keys(foundProducts)[0]; - this.renderProductMetadata(foundProducts[selectedProductId]); this._hideNotFound(); this._showTaxWarning(); this._showResultBlock(); - - return selectedProductId; } /** @@ -99,9 +94,9 @@ export default class ProductRenderer { * @param product */ renderProductMetadata(product) { - this._renderCustomizations(product.customization_fields); this.renderStock(product.stock); this._renderCombinations(product.combinations); + this._renderCustomizations(product.customization_fields); } /** @@ -172,8 +167,6 @@ export default class ProductRenderer { ); } - // render stock of first combination which is the selected one - this.renderStock(combinations[Object.keys(combinations)[0]].stock); this._showCombinations(); } @@ -245,20 +238,6 @@ export default class ProductRenderer { $(createOrderMap.productCustomFieldsContainer).empty(); } - /** - * Handles combination select change - * - * @param event - * - * @private - */ - _handleCombinationChange(event) { - const index = $(event.currentTarget).find(':selected').val(); - const combination = this.combinations[index]; - - this.renderStock(combination.stock); - } - /** * Shows result block * diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php index 736f56c52f2b0..6fb982376b25c 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php @@ -316,16 +316,24 @@ private function getErrorMessages(Exception $e) */ private function getAddProductToCartCommand(Request $request, int $cartId): UpdateProductQuantityInCartCommand { - $productId = $request->get('product_id'); - $quantity = $request->get('quantity'); - $combinationId = $request->get('combination_id'); + $productId = $request->request->getInt('product_id'); + $quantity = $request->request->getInt('quantity'); + $combinationId = $request->request->getInt('combination_id'); + + if ($request->request->get('customization')) { + //@todo: Add updateCustomizationsCommand + //check AdminCartsController::jaxProcessUpdateCustomizationFields + // index is id of customization_field + //id_customization is always empty(0) until it reaches Cart::_updateCustomizationQuantity where it gets linked to product + //check Cart::addTextFieldToProduct Cart::addPictureToProduct + } return new UpdateProductQuantityInCartCommand( $cartId, (int) $productId, (int) $quantity, QuantityAction::INCREASE_PRODUCT_QUANTITY, - $combinationId !== null ? (int) $combinationId : null + $combinationId ?: null ); } } From 241ab2df6530b3a80cd682ffe9e1a542e68a192f Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Wed, 23 Oct 2019 17:44:41 +0300 Subject: [PATCH 107/195] mvp of customization fields adding --- .../AddCustomizationFieldsHandler.php | 79 +++++++++++++++++++ .../QueryHandler/SearchProductsHandler.php | 2 + .../Command/AddCustomizationFieldsCommand.php | 56 +++++++++++++ ...AddCustomizationFieldsHandlerInterface.php | 37 +++++++++ .../Admin/Sell/Order/CartController.php | 4 +- .../config/services/adapter/cart.yml | 6 ++ 6 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php create mode 100644 src/Core/Domain/Cart/CommandHandler/AddCustomizationFieldsHandlerInterface.php diff --git a/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php b/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php new file mode 100644 index 0000000000000..09c49d19e3589 --- /dev/null +++ b/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php @@ -0,0 +1,79 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Adapter\Cart\CommandHandler; + +use PrestaShop\PrestaShop\Adapter\Cart\AbstractCartHandler; +use PrestaShop\PrestaShop\Core\Domain\Cart\Command\AddCustomizationFieldsCommand; +use PrestaShop\PrestaShop\Core\Domain\Cart\CommandHandler\AddCustomizationFieldsHandlerInterface; +use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartNotFoundException; +use PrestaShopException; +use Product; + +final class AddCustomizationFieldsHandler extends AbstractCartHandler implements AddCustomizationFieldsHandlerInterface +{ + /** + * @param AddCustomizationFieldsCommand $command + * + * @throws PrestaShopException + * @throws CartNotFoundException + */ + public function handle(AddCustomizationFieldsCommand $command) + { + //@todo: exceptions handling + $productId = $command->getProductId()->getValue(); + + $cart = $this->getCart($command->getCartId()); + $product = new Product($productId); + + $customizationFields = $product->getCustomizationFieldIds(); + $customizations = $command->getCustomizations(); + + foreach ($customizationFields as $customizationField) { + $customizationId = (int) $customizationField['id_customization_field']; + //@todo validation + if (isset($customizations[$customizationId])) { + if ($customizationField['type'] == Product::CUSTOMIZE_TEXTFIELD) { + $cart->addTextFieldToProduct( + $productId, + $customizationId, + Product::CUSTOMIZE_TEXTFIELD, + $customizations[$customizationId] + ); + continue; + } + + //@todo: file validation + $cart->addPictureToProduct( + $productId, + $customizationId, + Product::CUSTOMIZE_TEXTFIELD, + $customizations[$customizationId] + ); + } + } + } +} diff --git a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php index d734c524ef759..80bb8fea28fa1 100644 --- a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php +++ b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php @@ -107,6 +107,8 @@ public function handle(SearchProducts $query): array */ private function createFoundProductFromLegacy(Product $product): FoundProduct { + //@todo: sort products alphabetically + /** @var Locale $locale */ $locale = $this->localeRepository->getLocale($this->locale); $priceTaxExcluded = Product::getPriceStatic($product->id, false); diff --git a/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php b/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php index 522a5d7046952..974dcfe2faa8a 100644 --- a/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php +++ b/src/Core/Domain/Cart/Command/AddCustomizationFieldsCommand.php @@ -26,6 +26,62 @@ namespace PrestaShop\PrestaShop\Core\Domain\Cart\Command; +use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartConstraintException; +use PrestaShop\PrestaShop\Core\Domain\Cart\ValueObject\CartId; +use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId; + class AddCustomizationFieldsCommand { + /** + * @var CartId + */ + private $cartId; + + /** + * @var ProductId + */ + private $productId; + + /** + * @var array + */ + private $customizations; + + /** + * @param int $cartId + * @param int $productId + * @param array $customizations + * + * @throws CartConstraintException + */ + public function __construct(int $cartId, int $productId, array $customizations) + { + $this->cartId = new CartId($cartId); + $this->productId = new ProductId($productId); + $this->customizations = $customizations; + } + + /** + * @return CartId + */ + public function getCartId(): CartId + { + return $this->cartId; + } + + /** + * @return ProductId + */ + public function getProductId(): ProductId + { + return $this->productId; + } + + /** + * @return array + */ + public function getCustomizations(): array + { + return $this->customizations; + } } diff --git a/src/Core/Domain/Cart/CommandHandler/AddCustomizationFieldsHandlerInterface.php b/src/Core/Domain/Cart/CommandHandler/AddCustomizationFieldsHandlerInterface.php new file mode 100644 index 0000000000000..be47d98846b18 --- /dev/null +++ b/src/Core/Domain/Cart/CommandHandler/AddCustomizationFieldsHandlerInterface.php @@ -0,0 +1,37 @@ + + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +namespace PrestaShop\PrestaShop\Core\Domain\Cart\CommandHandler; + +use PrestaShop\PrestaShop\Core\Domain\Cart\Command\AddCustomizationFieldsCommand; + +interface AddCustomizationFieldsHandlerInterface +{ + /** + * @param AddCustomizationFieldsCommand $command + */ + public function handle(AddCustomizationFieldsCommand $command); +} diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php index 6fb982376b25c..4516a030e700b 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php @@ -28,6 +28,7 @@ use Exception; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\AddCartRuleToCartCommand; +use PrestaShop\PrestaShop\Core\Domain\Cart\Command\AddCustomizationFieldsCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\CreateEmptyCustomerCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\RemoveCartRuleFromCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\SetFreeShippingToCartCommand; @@ -320,7 +321,8 @@ private function getAddProductToCartCommand(Request $request, int $cartId): Upda $quantity = $request->request->getInt('quantity'); $combinationId = $request->request->getInt('combination_id'); - if ($request->request->get('customization')) { + if ($customizations = $request->request->get('customization')) { + $this->getCommandBus()->handle(new AddCustomizationFieldsCommand($cartId, $productId, $customizations)); //@todo: Add updateCustomizationsCommand //check AdminCartsController::jaxProcessUpdateCustomizationFields // index is id of customization_field diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/cart.yml b/src/PrestaShopBundle/Resources/config/services/adapter/cart.yml index 332d1530f1c80..f76fc67b8dfe3 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/cart.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/cart.yml @@ -102,3 +102,9 @@ services: tags: - name: tactician.handler command: 'PrestaShop\PrestaShop\Core\Domain\Cart\Query\GetCartInformation' + + prestashop.adapter.command_handler.add_customization_fields_handler: + class: 'PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\AddCustomizationFieldsHandler' + tags: + - name: tactician.handler + command: 'PrestaShop\PrestaShop\Core\Domain\Cart\Command\AddCustomizationFieldsCommand' From 9e16f485a98022b8bd1c69db576a1c3512c0a414 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 10:54:28 +0300 Subject: [PATCH 108/195] Moves cartrules rendering parts to renderer --- .../pages/order/create/cart-rule-manager.js | 115 ++-------------- .../pages/order/create/cart-rules-renderer.js | 123 ++++++++++++++++-- .../pages/order/create/create-order-page.js | 4 +- 3 files changed, 122 insertions(+), 120 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js index aadae856a3806..176e2c5ce3650 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js @@ -38,8 +38,7 @@ export default class CartRuleManager { constructor() { this.router = new Router(); this.$searchInput = $(createOrderMap.cartRuleSearchInput); - this.$searchResultBox = $(createOrderMap.cartRulesSearchResultBox); - this.cartRulesRenderer = new CartRulesRenderer(); + this.renderer = new CartRulesRenderer(); return { onCartRuleSearch: () => { @@ -49,7 +48,7 @@ export default class CartRuleManager { this._addCartRuleToCart(cartRuleId, cartId); }, onDoneSearchingCartRule: () => { - this._hideResultsDropdown(); + this.renderer.hideResultsDropdown(); }, onCartRuleRemove: (cartRuleId, cartId) => { this._removeCartRuleFromCart(cartRuleId, cartId); @@ -71,9 +70,9 @@ export default class CartRuleManager { $.get(this.router.generate('admin_cart_rules_search'), { search_phrase: searchPhrase, }).then((cartRules) => { - this._renderSearchResults(cartRules); + this.renderer.renderSearchResults(cartRules); }).catch((e) => { - showErrorMessage(e.responseJSON.message); + this._showUnexpectedError(e.responseJSON.message); }); } @@ -89,9 +88,9 @@ export default class CartRuleManager { $.post(this.router.generate('admin_carts_add_rule', {cartId}), { cart_rule_id: cartRuleId, }).then((cartInfo) => { - this.cartRulesRenderer.render(cartInfo.cartRules, cartInfo.products.length === 0); + this.renderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); }).catch((e) => { - this._displayErrorMessage(e.responseJSON.message); + this.renderer.displayErrorMessage(e.responseJSON.message); }); } @@ -110,110 +109,16 @@ export default class CartRuleManager { })).then((cartInfo) => { EventEmitter.emit(eventMap.cartRuleRemoved, cartInfo); }).catch((e) => { - showErrorMessage(e.responseJSON.message); + this._showUnexpectedError(e.responseJSON.message); }); } /** - * Displays error message - * - * @param message - * - * @private - */ - _displayErrorMessage(message) { - $(createOrderMap.cartRuleErrorText).text(message); - this._showErrorBlock(); - } - - /** - * Shows error block - * - * @private - */ - _showErrorBlock() { - $(createOrderMap.cartRuleErrorBlock).removeClass('d-none'); - } - - /** - * Responsible for rendering search results dropdown - * - * @param searchResults - * - * @private - */ - _renderSearchResults(searchResults) { - this._clearSearchResults(); - if (searchResults.cart_rules.length === 0) { - this._renderNotFound(); - - return; - } - this._renderFoundCartRules(searchResults.cart_rules); - } - - /** - * Renders found cart rules after search - * - * @param cartRules - * - * @private - */ - _renderFoundCartRules(cartRules) { - const $cartRuleTemplate = $($(createOrderMap.foundCartRuleTemplate).html()); - for (const key in cartRules) { - const $template = $cartRuleTemplate.clone(); - const cartRule = cartRules[key]; - - let cartRuleName = cartRule.name; - if (cartRule.code !== '') { - cartRuleName = `${cartRule.name} - ${cartRule.code}`; - } - - $template.text(cartRuleName); - $template.data('cart-rule-id', cartRule.cartRuleId); - this.$searchResultBox.append($template); - } - - this._showResultsDropdown(); - } - - /** - * Renders warning that no cart rule was found - * - * @private - */ - _renderNotFound() { - const $template = $($(createOrderMap.cartRulesNotFoundTemplate).html()).clone(); - this.$searchResultBox.html($template); - - this._showResultsDropdown(); - } - - /** - * Empties cart rule search results block - * - * @private - */ - _clearSearchResults() { - this.$searchResultBox.empty(); - } - - /** - * Displays cart rules search result dropdown - * - * @private - */ - _showResultsDropdown() { - this.$searchResultBox.removeClass('d-none'); - } - - /** - * Hides cart rules search result dropdown + * Wrapper for error message when ajax request fails * * @private */ - _hideResultsDropdown() { - this.$searchResultBox.addClass('d-none'); + _showUnexpectedError(message) { + showErrorMessage(message); } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-rules-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-rules-renderer.js index 45d514820fe77..2f096f9e87678 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-rules-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-rules-renderer.js @@ -23,7 +23,7 @@ * International Registered Trademark & Property of PrestaShop SA */ -import createOrderPageMap from './create-order-map'; +import createOrderMap from './create-order-map'; const $ = window.$; @@ -32,8 +32,9 @@ const $ = window.$; */ export default class CartRulesRenderer { constructor() { - this.$cartRulesBlock = $(createOrderPageMap.cartRulesBlock); - this.$cartRulesTable = $(createOrderPageMap.cartRulesTable); + this.$cartRulesBlock = $(createOrderMap.cartRulesBlock); + this.$cartRulesTable = $(createOrderMap.cartRulesTable); + this.$searchResultBox = $(createOrderMap.cartRulesSearchResultBox); } /** @@ -42,8 +43,8 @@ export default class CartRulesRenderer { * @param {Array} cartRules * @param {Boolean} emptyCart */ - render(cartRules, emptyCart) { - this._hideError(); + renderCartRulesBlock(cartRules, emptyCart) { + this._hideErrorBlock(); // do not render cart rules block at all if cart has no products if (emptyCart) { this._hideCartRulesBlock(); @@ -61,6 +62,93 @@ export default class CartRulesRenderer { this._renderList(cartRules); } + /** + * Responsible for rendering search results dropdown + * + * @param searchResults + */ + renderSearchResults(searchResults) { + this._clearSearchResults(); + + if (searchResults.cart_rules.length === 0) { + this._renderNotFound(); + } else { + this._renderFoundCartRules(searchResults.cart_rules); + } + + this._showResultsDropdown(); + } + + /** + * Displays error message bellow search input + * + * @param message + */ + displayErrorMessage(message) { + $(createOrderMap.cartRuleErrorText).text(message); + this._showErrorBlock(); + } + + /** + * Hides cart rules search result dropdown + */ + hideResultsDropdown() { + this.$searchResultBox.addClass('d-none'); + } + + /** + * Displays cart rules search result dropdown + * + * @private + */ + _showResultsDropdown() { + this.$searchResultBox.removeClass('d-none'); + } + + /** + * Renders warning that no cart rule was found + * + * @private + */ + _renderNotFound() { + const $template = $($(createOrderMap.cartRulesNotFoundTemplate).html()).clone(); + this.$searchResultBox.html($template); + } + + + /** + * Empties cart rule search results block + * + * @private + */ + _clearSearchResults() { + this.$searchResultBox.empty(); + } + + /** + * Renders found cart rules after search + * + * @param cartRules + * + * @private + */ + _renderFoundCartRules(cartRules) { + const $cartRuleTemplate = $($(createOrderMap.foundCartRuleTemplate).html()); + for (const key in cartRules) { + const $template = $cartRuleTemplate.clone(); + const cartRule = cartRules[key]; + + let cartRuleName = cartRule.name; + if (cartRule.code !== '') { + cartRuleName = `${cartRule.name} - ${cartRule.code}`; + } + + $template.text(cartRuleName); + $template.data('cart-rule-id', cartRule.cartRuleId); + this.$searchResultBox.append($template); + } + } + /** * Responsible for rendering the list of cart rules * @@ -70,16 +158,16 @@ export default class CartRulesRenderer { */ _renderList(cartRules) { this._cleanCartRulesList(); - const $cartRulesTableRowTemplate = $($(createOrderPageMap.cartRulesTableRowTemplate).html()); + const $cartRulesTableRowTemplate = $($(createOrderMap.cartRulesTableRowTemplate).html()); for (const key in cartRules) { const cartRule = cartRules[key]; const $template = $cartRulesTableRowTemplate.clone(); - $template.find(createOrderPageMap.cartRuleNameField).text(cartRule.name); - $template.find(createOrderPageMap.cartRuleDescriptionField).text(cartRule.description); - $template.find(createOrderPageMap.cartRuleValueField).text(cartRule.value); - $template.find(createOrderPageMap.cartRuleDeleteBtn).data('cart-rule-id', cartRule.cartRuleId); + $template.find(createOrderMap.cartRuleNameField).text(cartRule.name); + $template.find(createOrderMap.cartRuleDescriptionField).text(cartRule.description); + $template.find(createOrderMap.cartRuleValueField).text(cartRule.value); + $template.find(createOrderMap.cartRuleDeleteBtn).data('cart-rule-id', cartRule.cartRuleId); this.$cartRulesTable.find('tbody').append($template); } @@ -88,12 +176,21 @@ export default class CartRulesRenderer { } /** - * Hides error block which can be visible after cart rules search + * Shows error block + * + * @private + */ + _showErrorBlock() { + $(createOrderMap.cartRuleErrorBlock).removeClass('d-none'); + } + + /** + * Hides error block * * @private */ - _hideError() { - $(createOrderPageMap.cartRuleErrorBlock).addClass('d-none'); + _hideErrorBlock() { + $(createOrderMap.cartRuleErrorBlock).addClass('d-none'); } /** diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index 0874a747db7db..522bdef6be13c 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -195,7 +195,7 @@ export default class CreateOrderPage { this.data.cartId ); EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => { - this.cartRulesRenderer.render(cartInfo.cartRules, cartInfo.products.length === 0); + this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); }); }); } @@ -240,7 +240,7 @@ export default class CreateOrderPage { */ _renderCartInfo(cartInfo) { this.addressesRenderer.render(cartInfo.addresses); - this.cartRulesRenderer.render(cartInfo.cartRules, cartInfo.products.length === 0); + this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); // @todo: render Summary block when at least 1 product is in cart // and delivery options are available From f9f61445242b49ffbaa9936fba8e86df864b4808 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 11:25:19 +0300 Subject: [PATCH 109/195] Remove unused methods --- .../Admin/Sell/Order/OrderController.php | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php index 7ee3ea88ba5df..2d27ed0e846ea 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/OrderController.php @@ -47,7 +47,6 @@ use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderForViewing; use PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderPreview; use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\OrderPreview; -use PrestaShop\PrestaShop\Core\Domain\Order\QueryResult\ProductsForOrderCreation; use PrestaShop\PrestaShop\Core\Domain\Order\ValueObject\OrderId; use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\OrderGridDefinitionFactory; use PrestaShop\PrestaShop\Core\Search\Filters\OrderFilters; @@ -576,49 +575,6 @@ public function resendEmailAction(int $orderId, int $orderStatusId, int $orderHi ]); } - /** - * @param Request $request - * - * @return Response - */ - public function searchProductsAction(Request $request): Response - { - try { - $searchPhrase = $request->query->get('product_search_phrase'); - - /** @var ProductsForOrderCreation $productsForOrderCreation */ - $productsForOrderCreation = $this->getQueryBus()->handle(new SearchProductsForOrderCreation($searchPhrase)); - - $serializer = $this->get('prestashop.bundle.snake_case_serializer_json'); - - return new Response($serializer->serialize($productsForOrderCreation->getProducts(), 'json')); - } catch (Exception $e) { - return $this->json( - ['message' => $this->getErrorMessageForException($e, [])], - Response::HTTP_INTERNAL_SERVER_ERROR - ); - } - } - - /** - * @param Request $request - * @return Response - */ - public function addProductAction(Request $request): Response - { - try { - $this->addCustomizationFields($request); - - return new Response(); - } catch (Exception $e) { - return $this->json([ - 'message' => $this->getErrorMessageForException($e, []), - ], - Response::HTTP_INTERNAL_SERVER_ERROR - ); - } - } - /** * @param Exception $e * From 451ba7e4eb527cc35de3c21500eefd9922ffbe3b Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 12:25:14 +0300 Subject: [PATCH 110/195] implement events on addProduct action --- .../themes/new-theme/js/fos_js_routes.json | 2 +- .../js/pages/order/create/cart-editor.js | 50 +++++++++++++++++++ .../pages/order/create/cart-rule-manager.js | 39 ++++++--------- .../pages/order/create/create-order-page.js | 3 -- .../js/pages/order/create/customer-manager.js | 2 +- .../js/pages/order/create/event-map.js | 6 +++ .../js/pages/order/create/product-manager.js | 20 +++----- .../Admin/Sell/Order/CartController.php | 7 +-- .../routing/admin/sell/orders/carts.yml | 7 ++- .../Order/Order/Blocks/Create/cart.html.twig | 2 +- 10 files changed, 91 insertions(+), 47 deletions(-) diff --git a/admin-dev/themes/new-theme/js/fos_js_routes.json b/admin-dev/themes/new-theme/js/fos_js_routes.json index 74097ea9acad2..290f5b242045b 100644 --- a/admin-dev/themes/new-theme/js/fos_js_routes.json +++ b/admin-dev/themes/new-theme/js/fos_js_routes.json @@ -1 +1 @@ -{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_rule":{"tokens":[["text","\/rules\/add"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file +{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_rule":{"tokens":[["text","\/rules\/add"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/add-product"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js index 468722ff178a5..37aef1a610670 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js @@ -76,4 +76,54 @@ export default class CartEditor { EventEmitter.emit(eventMap.cartFreeShippingSet, cartInfo); }); } + + /** + * Adds cart rule to cart + * + * @param cartRuleId + * @param cartId + */ + addCartRuleToCart(cartRuleId, cartId) { + $.post(this.router.generate('admin_carts_add_rule', {cartId}), { + cart_rule_id: cartRuleId, + }).then((cartInfo) => { + EventEmitter.emit(eventMap.cartRuleAdded, cartInfo); + }).catch((response) => { + EventEmitter.emit(eventMap.cartRuleFailedToAdd, response.responseJSON.message); + }); + } + + /** + * Removes cart rule from cart + * + * @param cartRuleId + * @param cartId + */ + removeCartRuleFromCart(cartRuleId, cartId) { + $.post(this.router.generate('admin_carts_delete_rule', { + cartId, + cartRuleId, + })).then((cartInfo) => { + EventEmitter.emit(eventMap.cartRuleRemoved, cartInfo); + }).catch((response) => { + showErrorMessage(response.responseJSON.message); + }); + } + + /** + * Adds product to cart + */ + addProduct(cartId, product) { + $.ajax(this.router.generate('admin_carts_add_product', {cartId}), { + method: 'POST', + data: product, + processData: false, + contentType: false, + cache: false, + }).then((cartInfo) => { + EventEmitter.emit(eventMap.productAddedToCart, cartInfo); + }).catch((response) => { + showErrorMessage(response.responseJSON.message); + }); + } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js index 176e2c5ce3650..06ceec80036b5 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js @@ -23,11 +23,12 @@ * International Registered Trademark & Property of PrestaShop SA */ -import Router from '../../../components/router'; -import createOrderMap from './create-order-map'; +import CartEditor from './cart-editor'; import CartRulesRenderer from './cart-rules-renderer'; +import createOrderMap from './create-order-map'; import {EventEmitter} from '../../../components/event-emitter'; import eventMap from './event-map'; +import Router from '../../../components/router'; const $ = window.$; @@ -39,6 +40,7 @@ export default class CartRuleManager { this.router = new Router(); this.$searchInput = $(createOrderMap.cartRuleSearchInput); this.renderer = new CartRulesRenderer(); + this.cartEditor = new CartEditor(); return { onCartRuleSearch: () => { @@ -72,7 +74,7 @@ export default class CartRuleManager { }).then((cartRules) => { this.renderer.renderSearchResults(cartRules); }).catch((e) => { - this._showUnexpectedError(e.responseJSON.message); + showErrorMessage(e.responseJSON.message); }); } @@ -85,12 +87,13 @@ export default class CartRuleManager { * @private */ _addCartRuleToCart(cartRuleId, cartId) { - $.post(this.router.generate('admin_carts_add_rule', {cartId}), { - cart_rule_id: cartRuleId, - }).then((cartInfo) => { + this.cartEditor.addCartRuleToCart(cartRuleId, cartId); + + EventEmitter.on(eventMap.cartRuleAdded, (cartInfo) => { this.renderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); - }).catch((e) => { - this.renderer.displayErrorMessage(e.responseJSON.message); + }); + EventEmitter.on(eventMap.cartRuleFailedToAdd, (message) => { + this.renderer.displayErrorMessage(message); }); } @@ -103,22 +106,10 @@ export default class CartRuleManager { * @private */ _removeCartRuleFromCart(cartRuleId, cartId) { - $.post(this.router.generate('admin_carts_delete_rule', { - cartId, - cartRuleId, - })).then((cartInfo) => { - EventEmitter.emit(eventMap.cartRuleRemoved, cartInfo); - }).catch((e) => { - this._showUnexpectedError(e.responseJSON.message); - }); - } + this.cartEditor.removeCartRuleFromCart(cartRuleId, cartId); - /** - * Wrapper for error message when ajax request fails - * - * @private - */ - _showUnexpectedError(message) { - showErrorMessage(message); + EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => { + this.renderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); + }); } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index 522bdef6be13c..85af88364f509 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -194,9 +194,6 @@ export default class CreateOrderPage { $(event.currentTarget).data('cart-rule-id'), this.data.cartId ); - EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => { - this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); - }); }); } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js index 50e1c11ddb3fc..c5fa9530350f8 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js @@ -111,7 +111,7 @@ export default class CustomerManager { /** * Searches for customers - * + *@todo: fix showing not found customers and rerender after change customer * @private */ _doSearch() { diff --git a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js index 00bf6b050e2c1..4b53ae095d273 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js @@ -41,4 +41,10 @@ export default { cartFreeShippingSet: 'cartFreeShippingSet', // when cart rule is removed from cart cartRuleRemoved: 'cartRuleRemoved', + // when cart rule is added to cart + cartRuleAdded: 'cartRuleAdded', + // when cart rule cannot be added to cart + cartRuleFailedToAdd: 'cartRuleFailedToAdd', + // when product is added to cart + productAddedToCart: 'productAddedToCart', }; diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 5807648cd2dc8..8e6c6767260ca 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -23,7 +23,10 @@ * International Registered Trademark & Property of PrestaShop SA */ +import CartEditor from './cart-editor'; import createOrderPageMap from './create-order-map'; +import eventMap from './event-map'; +import {EventEmitter} from '../../../components/event-emitter'; import ProductRenderer from './product-renderer'; import Router from '../../../components/router'; @@ -40,6 +43,7 @@ export default class ProductManager { this.renderer = new ProductRenderer(); this.router = new Router(); + this.cartEditor = new CartEditor(); this._initEvents(); @@ -168,18 +172,10 @@ export default class ProductManager { * @private */ _addProductToCart(cartId) { - $.ajax($(createOrderPageMap.addToCartButton).data('add-product-url'), { - method: 'POST', - data: this._getProductData(cartId), - processData: false, - contentType: false, - cache: false, - }).then((response) => { - this.renderer.renderList(response.products); - }).catch((response) => { - if (typeof response.responseJSON !== 'undefined') { - showErrorMessage(response.responseJSON.message); - } + this.cartEditor.addProduct(cartId, this._getProductData(cartId)); + + EventEmitter.on(eventMap.productAddedToCart, (cartInfo) => { + this.renderer.renderList(cartInfo.products); }); } diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php index 4516a030e700b..b803cea9f94ea 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php @@ -265,14 +265,15 @@ public function removeCartRuleFromCartAction(int $cartId, int $cartRuleId) } /** + * @AdminSecurity("is_granted('update', request.get('_legacy_controller'))") + * * @param Request $request + * @param int $cartId * * @return Response */ - public function addProductAction(Request $request): Response + public function addProductAction(Request $request, int $cartId): Response { - $cartId = $request->request->getInt('cart_id'); - try { $addProductToCartCommand = $this->getAddProductToCartCommand($request, $cartId); $this->getCommandBus()->handle($addProductToCartCommand); diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml index 4e9d577993b09..857221296fddd 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml @@ -86,8 +86,11 @@ admin_carts_delete_rule: options: expose: true -admin_add_product_for_order_creation: - path: /add-product +admin_carts_add_product: + path: /{cartId}/add-product methods: [POST] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Cart:addProduct + _legacy_controller: AdminCarts + options: + expose: true diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig index e42836b9dba22..50b8bb0152c9d 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig @@ -95,7 +95,7 @@
-
From 9c8323a1a19bd73b298382103dd77ea1434a14c6 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 15:02:49 +0300 Subject: [PATCH 111/195] Adds customer renderer. --- .../js/pages/order/create/cart-editor.js | 5 +- .../js/pages/order/create/carts-renderer.js | 79 ------- .../pages/order/create/create-order-page.js | 68 ++---- .../js/pages/order/create/customer-manager.js | 142 +++--------- .../pages/order/create/customer-renderer.js | 215 ++++++++++++++++++ .../js/pages/order/create/event-map.js | 4 - .../js/pages/order/create/orders-renderer.js | 75 ------ 7 files changed, 264 insertions(+), 324 deletions(-) delete mode 100644 admin-dev/themes/new-theme/js/pages/order/create/carts-renderer.js create mode 100644 admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js delete mode 100644 admin-dev/themes/new-theme/js/pages/order/create/orders-renderer.js diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js index 37aef1a610670..f0891fe9c9034 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js @@ -1,5 +1,3 @@ -import createOrderPageMap from "./create-order-map"; - /** * 2007-2019 PrestaShop SA and Contributors * @@ -112,6 +110,9 @@ export default class CartEditor { /** * Adds product to cart + * + * @param {Number} cartId + * @param {FormData} product */ addProduct(cartId, product) { $.ajax(this.router.generate('admin_carts_add_product', {cartId}), { diff --git a/admin-dev/themes/new-theme/js/pages/order/create/carts-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/carts-renderer.js deleted file mode 100644 index 179300ed74c6e..0000000000000 --- a/admin-dev/themes/new-theme/js/pages/order/create/carts-renderer.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * 2007-2019 PrestaShop SA and Contributors - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.txt. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -import createOrderPageMap from './create-order-map'; - -const $ = window.$; - -/** - * Renders customer carts list - */ -export default class CartsRenderer { - - /** - * Renders customer carts from checkout history - * - * @param {Array} carts - * @param {Int} currentCartId - */ - render({carts, currentCartId}) { - const $cartsTable = $(createOrderPageMap.customerCartsTable); - const $cartsTableRowTemplate = $($(createOrderPageMap.customerCartsTableRowTemplate).html()); - - $cartsTable.find('tbody').empty(); - - if (!carts) { - return; - } - - this._showCheckoutHistoryBlock(); - - for (const key in carts) { - const cart = carts[key]; - // do not render current cart - if (cart.cartId === currentCartId) { - continue; - } - const $template = $cartsTableRowTemplate.clone(); - - $template.find('.js-cart-id').text(cart.cartId); - $template.find('.js-cart-date').text(cart.creationDate); - $template.find('.js-cart-total').text(cart.totalPrice); - - $template.find('.js-use-cart-btn').data('cart-id', cart.cartId); - - $cartsTable.find('tbody').append($template); - } - } - - /** - * Shows checkout history block where carts and orders are rendered - * - * @private - */ - _showCheckoutHistoryBlock() { - $(createOrderPageMap.customerCheckoutHistory).removeClass('d-none'); - } -} diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index 85af88364f509..dc294f0282a0f 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -27,8 +27,6 @@ import createOrderPageMap from './create-order-map'; import CustomerManager from './customer-manager'; import ShippingRenderer from './shipping-renderer'; import CartProvider from './cart-provider'; -import CartsRenderer from './carts-renderer'; -import OrdersRenderer from './orders-renderer'; import AddressesRenderer from './addresses-renderer'; import CartRulesRenderer from './cart-rules-renderer'; import Router from '../../../components/router'; @@ -45,24 +43,22 @@ const $ = window.$; */ export default class CreateOrderPage { constructor() { - this.data = {}; + this.cartId = null; this.$container = $(createOrderPageMap.orderCreationContainer); this.cartProvider = new CartProvider(); this.customerManager = new CustomerManager(); this.shippingRenderer = new ShippingRenderer(); - this.cartsRenderer = new CartsRenderer(); - this.ordersRenderer = new OrdersRenderer(); this.addressesRenderer = new AddressesRenderer(); this.cartRulesRenderer = new CartRulesRenderer(); this.router = new Router(); this.cartEditor = new CartEditor(); this.cartRuleManager = new CartRuleManager(); - this.cartProducts = new ProductManager(); + this.productManager = new ProductManager(); return { listenForCustomerSearch: () => this._handleCustomerSearch(), - listenForCustomerSelect: () => this._handleCustomerChooseForOrderCreation(), + listenForCustomerSelect: () => this._handleCustomerSelect(), listenForCartSelect: () => this._handleUseCartForOrderCreation(), listenForOrderSelect: () => this._handleDuplicateOrderCart(), listenForCartEdit: () => this._handleCartEdit(), @@ -78,9 +74,10 @@ export default class CreateOrderPage { */ _onCartLoaded() { EventEmitter.on(eventMap.cartLoaded, (cartInfo) => { - this.data.cartId = cartInfo.cartId; + this.cartId = cartInfo.cartId; this._renderCartInfo(cartInfo); - this._loadCustomerCarts(this.data.customerId); + this.customerManager.loadCustomerCarts(this.cartId); + this.customerManager.loadCustomerOrders(); }); } @@ -100,14 +97,11 @@ export default class CreateOrderPage { * * @private */ - _handleCustomerChooseForOrderCreation() { + _handleCustomerSelect() { this.$container.on('click', createOrderPageMap.chooseCustomerBtn, (event) => { - const customerId = this.customerManager.onCustomerChooseForOrderCreation(event); - this.data.customerId = customerId; + const customerId = this.customerManager.onCustomerSelect(event); this.cartProvider.loadEmptyCart(customerId); - this._loadCustomerCarts(customerId); - this._loadCustomerOrders(customerId); }); this.$container.on('click', createOrderPageMap.changeCustomerBtn, () => this.customerManager.onCustomerChange()); @@ -146,7 +140,7 @@ export default class CreateOrderPage { this.$container.on('change', createOrderPageMap.addressSelect, () => this._changeCartAddresses()); this.$container.on('change', createOrderPageMap.deliveryOptionSelect, e => this._changeDeliveryOption(e)); this.$container.on('change', createOrderPageMap.freeShippingSwitch, e => this._setFreeShipping(e)); - this.$container.on('click', createOrderPageMap.addToCartButton, () => this.cartProducts.onAddProductToCart(this.data.cartId)); + this.$container.on('click', createOrderPageMap.addToCartButton, () => this.productManager.onAddProductToCart(this.cartId)); this._selectCartRule(); this._removeCartRule(); } @@ -175,7 +169,7 @@ export default class CreateOrderPage { // prevent blur event to allow selecting cart rule event.preventDefault(); const cartRuleId = $(event.currentTarget).data('cart-rule-id'); - this.cartRuleManager.onCartRuleSelect(cartRuleId, this.data.cartId); + this.cartRuleManager.onCartRuleSelect(cartRuleId, this.cartId); // manually fire blur event after cart rule is selected. }).on('click', createOrderPageMap.foundCartRuleListItem, () => { @@ -190,41 +184,7 @@ export default class CreateOrderPage { */ _removeCartRule() { this.$container.on('click', createOrderPageMap.cartRuleDeleteBtn, (event) => { - this.cartRuleManager.onCartRuleRemove( - $(event.currentTarget).data('cart-rule-id'), - this.data.cartId - ); - }); - } - - /** - * Gets and renders customer carts - * - * @param customerId - * - * @private - */ - _loadCustomerCarts(customerId) { - this.customerManager.getCustomerCarts(customerId); - EventEmitter.on(eventMap.customerCartsLoaded, (cartInfo) => { - this.cartsRenderer.render({ - carts: cartInfo.carts, - currentCartId: this.data.cartId, - }); - }); - } - - /** - * Gets and renders customer orders - * - * @param customerId - * - * @private - */ - _loadCustomerOrders(customerId) { - this.customerManager.getCustomerOrders(customerId); - EventEmitter.on(eventMap.customerOrdersLoaded, (cartInfo) => { - this.ordersRenderer.render(cartInfo.orders); + this.cartRuleManager.onCartRuleRemove($(event.currentTarget).data('cart-rule-id'), this.cartId); }); } @@ -256,7 +216,7 @@ export default class CreateOrderPage { invoice_address_id: $(createOrderPageMap.invoiceAddressSelect).val(), }; - this.cartEditor.changeCartAddresses(this.data.cartId, addresses); + this.cartEditor.changeCartAddresses(this.cartId, addresses); EventEmitter.on(eventMap.cartAddressesChanged, (cartInfo) => { this.addressesRenderer.render(cartInfo.addresses); this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); @@ -271,7 +231,7 @@ export default class CreateOrderPage { * @private */ _changeDeliveryOption(event) { - this.cartEditor.changeDeliveryOption(this.data.cartId, event.currentTarget.value); + this.cartEditor.changeDeliveryOption(this.cartId, event.currentTarget.value); EventEmitter.on(eventMap.cartDeliveryOptionChanged, (cartInfo) => { this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); @@ -285,7 +245,7 @@ export default class CreateOrderPage { * @private */ _setFreeShipping(event) { - this.cartEditor.setFreeShipping(this.data.cartId, event.currentTarget.value); + this.cartEditor.setFreeShipping(this.cartId, event.currentTarget.value); EventEmitter.on(eventMap.cartFreeShippingSet, (cartInfo) => { this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js index c5fa9530350f8..fe1cb0785ed64 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js @@ -24,35 +24,37 @@ */ import createOrderPageMap from './create-order-map'; +import CustomerRenderer from './customer-renderer'; import Router from '../../../components/router'; -import {EventEmitter} from '../../../components/event-emitter'; -import eventMap from './event-map'; const $ = window.$; /** - * Searches customers for which order is being created + * Responsible for customers managing. (search, select, get customer info etc.) */ export default class CustomerManager { constructor() { + this.customerId = null; + this.router = new Router(); this.$container = $(createOrderPageMap.customerSearchBlock); this.$searchInput = $(createOrderPageMap.customerSearchInput); this.$customerSearchResultBlock = $(createOrderPageMap.customerSearchResultsBlock); + this.renderer = new CustomerRenderer(); return { onCustomerSearch: () => { - this._doSearch(); + this._search(); }, - onCustomerChooseForOrderCreation: event => this._chooseCustomerForOrderCreation(event), + onCustomerSelect: event => this._selectCustomer(event), onCustomerChange: () => { - this._showCustomerSearch(); + this.renderer.showCustomerSearch(); }, - getCustomerCarts: (customerId) => { - this._getCustomerCarts(customerId); + loadCustomerCarts: (currentCartId) => { + this._loadCustomerCarts(currentCartId); }, - getCustomerOrders: (customerId) => { - this._getCustomerOrders(customerId); + loadCustomerOrders: () => { + this._loadCustomerOrders(); }, }; } @@ -61,11 +63,13 @@ export default class CustomerManager { * Gets customer carts * After Request is complete, emits event providing carts list * - * @param customerId + * @param currentCartId */ - _getCustomerCarts(customerId) { - $.get(this.router.generate('admin_customers_carts', {customerId})).then((carts) => { - EventEmitter.emit(eventMap.customerCartsLoaded, carts); + _loadCustomerCarts(currentCartId) { + const customerId = this.customerId; + + $.get(this.router.generate('admin_customers_carts', {customerId})).then((response) => { + this.renderer.renderCarts(response.carts, currentCartId); }).catch((e) => { showErrorMessage(e.responseJSON.message); }); @@ -74,39 +78,29 @@ export default class CustomerManager { /** * Gets customer carts * After Request is complete, emits event providing orders list - * - * @param customerId */ - _getCustomerOrders(customerId) { - $.get(this.router.generate('admin_customers_orders', {customerId})).then((orders) => { - EventEmitter.emit(eventMap.customerOrdersLoaded, orders); + _loadCustomerOrders() { + const customerId = this.customerId; + + $.get(this.router.generate('admin_customers_orders', {customerId})).then((response) => { + this.renderer.renderOrders(response.orders); }).catch((e) => { showErrorMessage(e.responseJSON.message); }); } /** - * * @param {Event} chooseCustomerEvent * * @return {Number} */ - _chooseCustomerForOrderCreation(chooseCustomerEvent) { + _selectCustomer(chooseCustomerEvent) { const $chooseBtn = $(chooseCustomerEvent.currentTarget); - const $customerCard = $chooseBtn.closest('.card'); + this.customerId = $chooseBtn.data('customer-id'); - $chooseBtn.addClass('d-none'); + this.renderer.displaySelectedCustomerBlock($chooseBtn); - $customerCard.addClass('border-success'); - $customerCard.find(createOrderPageMap.changeCustomerBtn).removeClass('d-none'); - - this.$container.find(createOrderPageMap.customerSearchRow).addClass('d-none'); - this.$container.find(createOrderPageMap.notSelectedCustomerSearchResults) - .closest(createOrderPageMap.customerSearchResultColumn) - .remove() - ; - - return $chooseBtn.data('customer-id'); + return this.customerId; } /** @@ -114,90 +108,18 @@ export default class CustomerManager { *@todo: fix showing not found customers and rerender after change customer * @private */ - _doSearch() { - const name = this.$searchInput.val(); + _search() { + const searchPhrase = this.$searchInput.val(); - if (name.length < 4) { + if (searchPhrase.length < 3) { return; } $.get(this.router.generate('admin_customers_search'), { - customer_search: name, + customer_search: searchPhrase, }).then((response) => { - this._clearShownCustomers(); - - if (!response.found) { - this._showNotFoundCustomers(); - - return; - } - - for (const customerId in response.customers) { - const customerResult = response.customers[customerId]; - const customer = { - id: customerId, - first_name: customerResult.firstname, - last_name: customerResult.lastname, - email: customerResult.email, - birthday: customerResult.birthday !== '0000-00-00' ? customerResult.birthday : ' ', - }; - - this._showCustomer(customer); - } + this.renderer.renderSearchResults(response.customers); }); } - - /** - * Get template as jQuery object with customer data - * - * @param {Object} customer - * - * @return {jQuery} - * - * @private - */ - _showCustomer(customer) { - const $customerSearchResultTemplate = $($(createOrderPageMap.customerSearchResultTemplate).html()); - const $template = $customerSearchResultTemplate.clone(); - - $template.find(createOrderPageMap.customerSearchResultName).text(`${customer.first_name} ${customer.last_name}`); - $template.find(createOrderPageMap.customerSearchResultEmail).text(customer.email); - $template.find(createOrderPageMap.customerSearchResultId).text(customer.id); - $template.find(createOrderPageMap.customerSearchResultBirthday).text(customer.birthday); - - $template.find(createOrderPageMap.customerDetailsBtn).data('customer-id', customer.id); - $template.find(createOrderPageMap.chooseCustomerBtn).data('customer-id', customer.id); - - return this.$customerSearchResultBlock.append($template); - } - - /** - * Shows empty result when customer is not found - * - * @private - */ - _showNotFoundCustomers() { - const $emptyResultTemplate = $($('#customerSearchEmptyResultTemplate').html()); - - this.$customerSearchResultBlock.append($emptyResultTemplate); - } - - /** - * Clears shown customers - * - * @private - */ - _clearShownCustomers() { - this.$customerSearchResultBlock.empty(); - } - - /** - * Shows customer search block - * - * @private - */ - _showCustomerSearch() { - this.$container.find(createOrderPageMap.customerSearchRow).removeClass('d-none'); - } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js new file mode 100644 index 0000000000000..0c8d0a3a6fe59 --- /dev/null +++ b/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js @@ -0,0 +1,215 @@ +import createOrderMap from './create-order-map'; + +/** + * 2007-2019 PrestaShop SA and Contributors + * + * NOTICE OF LICENSE + * + * This source file is subject to the Open Software License (OSL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/OSL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to https://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2019 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ + +const $ = window.$; + +/** + * Responsible for customer information rendering + */ +export default class CustomerRenderer { + constructor() { + this.$container = $(createOrderMap.customerSearchBlock); + this.$customerSearchResultBlock = $(createOrderMap.customerSearchResultsBlock); + } + + /** + * Renders customer search results + * + * @param foundCustomers + */ + renderSearchResults(foundCustomers) { + this._clearShownCustomers(); + + if (foundCustomers.length === 0) { + this._showNotFoundCustomers(); + + return; + } + + for (const customerId in foundCustomers) { + const customerResult = foundCustomers[customerId]; + const customer = { + id: customerId, + first_name: customerResult.firstname, + last_name: customerResult.lastname, + email: customerResult.email, + birthday: customerResult.birthday !== '0000-00-00' ? customerResult.birthday : ' ', + }; + + this._renderFoundCustomer(customer); + } + } + + /** + * Responsible for displaying customer block after customer select + * + * @param $targetedBtn + */ + displaySelectedCustomerBlock($targetedBtn) { + $targetedBtn.addClass('d-none'); + + const $customerCard = $targetedBtn.closest('.card'); + + $customerCard.addClass('border-success'); + $customerCard.find(createOrderMap.changeCustomerBtn).removeClass('d-none'); + + this.$container.find(createOrderMap.customerSearchRow).addClass('d-none'); + this.$container.find(createOrderMap.notSelectedCustomerSearchResults) + .closest(createOrderMap.customerSearchResultColumn) + .remove() + ; + } + + /** + * Shows customer search block + */ + showCustomerSearch() { + this.$container.find(createOrderMap.customerSearchRow).removeClass('d-none'); + } + + /** + * Renders customer carts list + * + * @param {Array} carts + * @param {Int} currentCartId + */ + renderCarts(carts, currentCartId) { + const $cartsTable = $(createOrderMap.customerCartsTable); + const $cartsTableRowTemplate = $($(createOrderMap.customerCartsTableRowTemplate).html()); + + $cartsTable.find('tbody').empty(); + + if (carts.length === 0) { + return; + } + + this._showCheckoutHistoryBlock(); + + for (const key in carts) { + const cart = carts[key]; + // do not render current cart + if (cart.cartId === currentCartId) { + continue; + } + const $template = $cartsTableRowTemplate.clone(); + + $template.find('.js-cart-id').text(cart.cartId); + $template.find('.js-cart-date').text(cart.creationDate); + $template.find('.js-cart-total').text(cart.totalPrice); + + $template.find('.js-use-cart-btn').data('cart-id', cart.cartId); + + $cartsTable.find('tbody').append($template); + } + } + + /** + * Renders customer orders list + * + * @param {Array} orders + */ + renderOrders(orders) { + const $ordersTable = $(createOrderMap.customerOrdersTable); + const $rowTemplate = $($(createOrderMap.customerOrdersTableRowTemplate).html()); + + $ordersTable.find('tbody').empty(); + + if (orders.length === 0) { + return; + } + + this._showCheckoutHistoryBlock(); + + for (const key in Object.keys(orders)) { + const order = orders[key]; + const $template = $rowTemplate.clone(); + + $template.find('.js-order-id').text(order.orderId); + $template.find('.js-order-date').text(order.orderPlacedDate); + $template.find('.js-order-products').text(order.totalProductsCount); + $template.find('.js-order-total-paid').text(order.totalPaid); + $template.find('.js-order-status').text(order.orderStatus); + + $template.find('.js-use-order-btn').data('order-id', order.orderId); + + $ordersTable.find('tbody').append($template); + } + } + + /** + * Renders customer information after search action + * + * @param {Object} customer + * + * @return {jQuery} + * + * @private + */ + _renderFoundCustomer(customer) { + const $customerSearchResultTemplate = $($(createOrderMap.customerSearchResultTemplate).html()); + const $template = $customerSearchResultTemplate.clone(); + + $template.find(createOrderMap.customerSearchResultName).text(`${customer.first_name} ${customer.last_name}`); + $template.find(createOrderMap.customerSearchResultEmail).text(customer.email); + $template.find(createOrderMap.customerSearchResultId).text(customer.id); + $template.find(createOrderMap.customerSearchResultBirthday).text(customer.birthday); + + $template.find(createOrderMap.customerDetailsBtn).data('customer-id', customer.id); + $template.find(createOrderMap.chooseCustomerBtn).data('customer-id', customer.id); + + return this.$customerSearchResultBlock.append($template); + } + + /** + * Shows checkout history block where carts and orders are rendered + * + * @private + */ + _showCheckoutHistoryBlock() { + $(createOrderMap.customerCheckoutHistory).removeClass('d-none'); + } + + /** + * Clears shown customers + * + * @private + */ + _clearShownCustomers() { + this.$customerSearchResultBlock.empty(); + } + + /** + * Shows empty result when customer is not found + * + * @private + */ + _showNotFoundCustomers() { + const $emptyResultTemplate = $($('#customerSearchEmptyResultTemplate').html()); + + this.$customerSearchResultBlock.append($emptyResultTemplate); + } +} diff --git a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js index 4b53ae095d273..0d9d6df07ad45 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js @@ -29,10 +29,6 @@ export default { // when new cart is loaded, no matter if its empty, selected from carts list or duplicated by order. cartLoaded: 'cartLoaded', - // when customer carts list is loaded. - customerCartsLoaded: 'customerCartsLoaded', - // when customer orders list is loaded - customerOrdersLoaded: 'customerOrdersLoaded', // when cart addresses information has been changed cartAddressesChanged: 'cartAddressesChanged', // when cart delivery option has been changed diff --git a/admin-dev/themes/new-theme/js/pages/order/create/orders-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/orders-renderer.js deleted file mode 100644 index 471f57c5065c9..0000000000000 --- a/admin-dev/themes/new-theme/js/pages/order/create/orders-renderer.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 2007-2019 PrestaShop SA and Contributors - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.txt. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://www.prestashop.com for more information. - * - * @author PrestaShop SA - * @copyright 2007-2019 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - * International Registered Trademark & Property of PrestaShop SA - */ - -import createOrderPageMap from './create-order-map'; - -const $ = window.$; - -/** - * Renders customer orders list - */ -export default class OrdersRenderer { - /** - * Renders customer orders - * - * @param {Array} orders - */ - render(orders) { - const $ordersTable = $(createOrderPageMap.customerOrdersTable); - const $rowTemplate = $($(createOrderPageMap.customerOrdersTableRowTemplate).html()); - - $ordersTable.find('tbody').empty(); - - if (!orders) { - return; - } - - this._showCheckoutHistoryBlock(); - - for (const key in Object.keys(orders)) { - const order = orders[key]; - const $template = $rowTemplate.clone(); - - $template.find('.js-order-id').text(order.orderId); - $template.find('.js-order-date').text(order.orderPlacedDate); - $template.find('.js-order-products').text(order.totalProductsCount); - $template.find('.js-order-total-paid').text(order.totalPaid); - $template.find('.js-order-status').text(order.orderStatus); - - $template.find('.js-use-order-btn').data('order-id', order.orderId); - - $ordersTable.find('tbody').append($template); - } - } - - /** - * Shows checkout history block where carts and orders are rendered - * - * @private - */ - _showCheckoutHistoryBlock() { - $(createOrderPageMap.customerCheckoutHistory).removeClass('d-none'); - } -} From 2e24c95d9380fc3d0dd8f564bec1c44efab44423 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 15:17:54 +0300 Subject: [PATCH 112/195] Trigger cart info update after product is added to cart. --- .../new-theme/js/pages/order/create/create-order-page.js | 3 +++ .../themes/new-theme/js/pages/order/create/product-manager.js | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index dc294f0282a0f..7fd4e70f78b82 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -35,6 +35,7 @@ import CartEditor from './cart-editor'; import eventMap from './event-map'; import CartRuleManager from './cart-rule-manager'; import ProductManager from './product-manager'; +import ProductRenderer from './product-renderer'; const $ = window.$; @@ -55,6 +56,7 @@ export default class CreateOrderPage { this.cartEditor = new CartEditor(); this.cartRuleManager = new CartRuleManager(); this.productManager = new ProductManager(); + this.productRenderer = new ProductRenderer(); return { listenForCustomerSearch: () => this._handleCustomerSearch(), @@ -199,6 +201,7 @@ export default class CreateOrderPage { this.addressesRenderer.render(cartInfo.addresses); this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); + this.productRenderer.renderList(cartInfo.products); // @todo: render Summary block when at least 1 product is in cart // and delivery options are available diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 8e6c6767260ca..02fa7c05e5337 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -173,9 +173,8 @@ export default class ProductManager { */ _addProductToCart(cartId) { this.cartEditor.addProduct(cartId, this._getProductData(cartId)); - EventEmitter.on(eventMap.productAddedToCart, (cartInfo) => { - this.renderer.renderList(cartInfo.products); + EventEmitter.emit(eventMap.cartLoaded, cartInfo); }); } From f4aebb35471e170f1597172bd8bb5a9718eacda4 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 15:20:54 +0300 Subject: [PATCH 113/195] Fix private method --- .../new-theme/js/pages/order/create/product-manager.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 02fa7c05e5337..e822f18dc03a9 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -67,7 +67,7 @@ export default class ProductManager { }); $(createOrderPageMap.combinationsSelect).on('change', (event) => { const combinationId = Number($(event.currentTarget).find(':selected').val()); - this.selectCombination(combinationId); + this._selectCombination(combinationId); }); } @@ -128,7 +128,7 @@ export default class ProductManager { // if product has combinations select the first else leave it null if (product.combinations.length !== 0) { - this.selectCombination(Object.keys(product.combinations)[0]); + this._selectCombination(Object.keys(product.combinations)[0]); } return product; @@ -138,8 +138,10 @@ export default class ProductManager { * Handles use case when new combination is selected * * @param combinationId + * + * @private */ - selectCombination(combinationId) { + _selectCombination(combinationId) { const combination = this.products[this.selectedProductId].combinations[combinationId]; this.selectedCombinationId = combinationId; From 8708d9bdec3c5643139008b3b9e90bd11516b772 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 15:54:16 +0300 Subject: [PATCH 114/195] Modifies api route names --- admin-dev/themes/new-theme/js/fos_js_routes.json | 2 +- .../themes/new-theme/js/pages/order/create/cart-editor.js | 4 ++-- .../Resources/config/routing/admin/sell/orders/carts.yml | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/admin-dev/themes/new-theme/js/fos_js_routes.json b/admin-dev/themes/new-theme/js/fos_js_routes.json index 290f5b242045b..69c6b6ebe30bc 100644 --- a/admin-dev/themes/new-theme/js/fos_js_routes.json +++ b/admin-dev/themes/new-theme/js/fos_js_routes.json @@ -1 +1 @@ -{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_rule":{"tokens":[["text","\/rules\/add"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/add-product"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file +{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js index f0891fe9c9034..23be4a2c5a655 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js @@ -82,7 +82,7 @@ export default class CartEditor { * @param cartId */ addCartRuleToCart(cartRuleId, cartId) { - $.post(this.router.generate('admin_carts_add_rule', {cartId}), { + $.post(this.router.generate('admin_carts_add_cart_rule', {cartId}), { cart_rule_id: cartRuleId, }).then((cartInfo) => { EventEmitter.emit(eventMap.cartRuleAdded, cartInfo); @@ -98,7 +98,7 @@ export default class CartEditor { * @param cartId */ removeCartRuleFromCart(cartRuleId, cartId) { - $.post(this.router.generate('admin_carts_delete_rule', { + $.post(this.router.generate('admin_carts_delete_cart_rule', { cartId, cartRuleId, })).then((cartInfo) => { diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml index 857221296fddd..8fc156f6568d6 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml @@ -68,8 +68,8 @@ admin_carts_set_free_shipping: options: expose: true -admin_carts_add_rule: - path: /{cartId}/rules/add +admin_carts_add_cart_rule: + path: /{cartId}/cart-rules methods: [POST] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Cart:addCartRuleToCart @@ -77,7 +77,7 @@ admin_carts_add_rule: options: expose: true -admin_carts_delete_rule: +admin_carts_delete_cart_rule: path: /{cartId}/rules/{cartRuleId}/delete methods: [POST] defaults: @@ -87,7 +87,7 @@ admin_carts_delete_rule: expose: true admin_carts_add_product: - path: /{cartId}/add-product + path: /{cartId}/products methods: [POST] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Cart:addProduct From e5411fa797440dacd623c6c2629d70625c045742 Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Thu, 24 Oct 2019 18:15:24 +0300 Subject: [PATCH 115/195] Adds remove product from cart action --- .../themes/new-theme/js/fos_js_routes.json | 2 +- .../js/pages/order/create/cart-editor.js | 32 +++++++++++++++---- .../pages/order/create/create-order-page.js | 18 +++++++++-- .../js/pages/order/create/event-map.js | 2 ++ .../js/pages/order/create/product-manager.js | 30 +++++++++++++---- .../Admin/Sell/Order/CartController.php | 31 ++++++++++++++++-- .../routing/admin/sell/orders/carts.yml | 17 +++++++++- 7 files changed, 114 insertions(+), 18 deletions(-) diff --git a/admin-dev/themes/new-theme/js/fos_js_routes.json b/admin-dev/themes/new-theme/js/fos_js_routes.json index 69c6b6ebe30bc..2176f3780fc65 100644 --- a/admin-dev/themes/new-theme/js/fos_js_routes.json +++ b/admin-dev/themes/new-theme/js/fos_js_routes.json @@ -1 +1 @@ -{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file +{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_product":{"tokens":[["text","\/delete"],["variable","\/","\\d+","productId"],["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js index 23be4a2c5a655..dd8abe980dafb 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js @@ -40,6 +40,9 @@ export default class CartEditor { /** * Changes cart addresses + * + * @param {Number} cartId + * @param {Object} addresses */ changeCartAddresses(cartId, addresses) { $.post(this.router.generate('admin_carts_edit_addresses', {cartId}), addresses).then((cartInfo) => { @@ -50,8 +53,8 @@ export default class CartEditor { /** * Modifies cart delivery option * - * @param cartId - * @param value + * @param {Number} cartId + * @param {Number} value */ changeDeliveryOption(cartId, value) { $.post(this.router.generate('admin_carts_edit_carrier', {cartId}), { @@ -78,8 +81,8 @@ export default class CartEditor { /** * Adds cart rule to cart * - * @param cartRuleId - * @param cartId + * @param {Number} cartRuleId + * @param {Number} cartId */ addCartRuleToCart(cartRuleId, cartId) { $.post(this.router.generate('admin_carts_add_cart_rule', {cartId}), { @@ -94,8 +97,8 @@ export default class CartEditor { /** * Removes cart rule from cart * - * @param cartRuleId - * @param cartId + * @param {Number} cartRuleId + * @param {Number} cartId */ removeCartRuleFromCart(cartRuleId, cartId) { $.post(this.router.generate('admin_carts_delete_cart_rule', { @@ -127,4 +130,21 @@ export default class CartEditor { showErrorMessage(response.responseJSON.message); }); } + + /** + * Removes product from cart + * + * @param {Number} cartId + * @param {Number} productId + */ + removeProductFromCart(cartId, productId) { + $.post(this.router.generate('admin_carts_delete_product', { + cartId, + productId, + })).then((cartInfo) => { + EventEmitter.emit(eventMap.productRemovedFromCart, cartInfo); + }).catch((response) => { + showErrorMessage(response.responseJSON.message); + }); + } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index 7fd4e70f78b82..2c4baeab19804 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -65,7 +65,7 @@ export default class CreateOrderPage { listenForOrderSelect: () => this._handleDuplicateOrderCart(), listenForCartEdit: () => this._handleCartEdit(), listenForCartLoading: () => this._onCartLoaded(), - listenForCartRuleSearch: () => this._handleCartRuleSearch(), + listenForCartRuleSearch: () => this._searchCartRule(), }; } @@ -143,6 +143,7 @@ export default class CreateOrderPage { this.$container.on('change', createOrderPageMap.deliveryOptionSelect, e => this._changeDeliveryOption(e)); this.$container.on('change', createOrderPageMap.freeShippingSwitch, e => this._setFreeShipping(e)); this.$container.on('click', createOrderPageMap.addToCartButton, () => this.productManager.onAddProductToCart(this.cartId)); + this.$container.on('click', createOrderPageMap.productRemoveBtn, e => this._removeProductFromCart(e)); this._selectCartRule(); this._removeCartRule(); } @@ -152,7 +153,7 @@ export default class CreateOrderPage { * * @private */ - _handleCartRuleSearch() { + _searchCartRule() { this.$container.on('input', createOrderPageMap.cartRuleSearchInput, () => { this.cartRuleManager.onCartRuleSearch(); }); @@ -161,6 +162,19 @@ export default class CreateOrderPage { }); } + /** + * Triggers removing product from cart + * + * @param {Object} event + * + * @private + */ + _removeProductFromCart(event) { + const productId = Number($(event.currentTarget).data('product-id')); + + this.productManager.onRemoveProductFromCart(this.cartId, productId); + } + /** * Triggers cart rule select * diff --git a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js index 0d9d6df07ad45..b062b36d78e89 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js @@ -43,4 +43,6 @@ export default { cartRuleFailedToAdd: 'cartRuleFailedToAdd', // when product is added to cart productAddedToCart: 'productAddedToCart', + // when product is removed from cart + productRemovedFromCart: 'productRemovedFromCart', }; diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index e822f18dc03a9..3d9aa1297ddf1 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -24,7 +24,7 @@ */ import CartEditor from './cart-editor'; -import createOrderPageMap from './create-order-map'; +import createOrderMap from './create-order-map'; import eventMap from './event-map'; import {EventEmitter} from '../../../components/event-emitter'; import ProductRenderer from './product-renderer'; @@ -51,6 +51,9 @@ export default class ProductManager { onAddProductToCart: (cartId) => { this._addProductToCart(cartId); }, + onRemoveProductFromCart: (cartId, productId) => { + this._removeProductFromCart(cartId, productId); + } }; } @@ -60,12 +63,12 @@ export default class ProductManager { * @private */ _initEvents() { - $(createOrderPageMap.productSearch).on('input', event => this._search(event)); - $(createOrderPageMap.productSelect).on('change', (event) => { + $(createOrderMap.productSearch).on('input', event => this._search(event)); + $(createOrderMap.productSelect).on('change', (event) => { const productId = Number($(event.currentTarget).find(':selected').val()); this._selectProduct(productId); }); - $(createOrderPageMap.combinationsSelect).on('change', (event) => { + $(createOrderMap.combinationsSelect).on('change', (event) => { const combinationId = Number($(event.currentTarget).find(':selected').val()); this._selectCombination(combinationId); }); @@ -180,6 +183,21 @@ export default class ProductManager { }); } + /** + * Removes product from cart + * + * @param {Number} cartId + * @param {Number} productId + * + * @private + */ + _removeProductFromCart(cartId, productId) { + this.cartEditor.removeProductFromCart(productId, productId); + EventEmitter.on(eventMap.productRemovedFromCart, (cartInfo) => { + EventEmitter.emit(eventMap.cartLoaded, cartInfo); + }); + } + /** * Retrieves product data from product search result block fields * @@ -191,7 +209,7 @@ export default class ProductManager { formData.append('cart_id', cartId); formData.append('product_id', this.selectedProductId); - formData.append('quantity', $(createOrderPageMap.quantityInput).val()); + formData.append('quantity', $(createOrderMap.quantityInput).val()); formData.append('combination_id', this.selectedCombinationId); this._getCustomFieldsData(formData); @@ -209,7 +227,7 @@ export default class ProductManager { * @private */ _getCustomFieldsData(formData) { - const $customFields = $(createOrderPageMap.productCustomInput); + const $customFields = $(createOrderMap.productCustomInput); $customFields.each((key, field) => { const $field = $(field); diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php index b803cea9f94ea..2b8419c09fb5f 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php @@ -31,6 +31,7 @@ use PrestaShop\PrestaShop\Core\Domain\Cart\Command\AddCustomizationFieldsCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\CreateEmptyCustomerCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\RemoveCartRuleFromCartCommand; +use PrestaShop\PrestaShop\Core\Domain\Cart\Command\RemoveProductFromCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\SetFreeShippingToCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\UpdateCartAddressesCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\Command\UpdateCartCarrierCommand; @@ -265,14 +266,16 @@ public function removeCartRuleFromCartAction(int $cartId, int $cartRuleId) } /** + * Adds product to cart + * * @AdminSecurity("is_granted('update', request.get('_legacy_controller'))") * * @param Request $request * @param int $cartId * - * @return Response + * @return JsonResponse */ - public function addProductAction(Request $request, int $cartId): Response + public function addProductAction(Request $request, int $cartId): JsonResponse { try { $addProductToCartCommand = $this->getAddProductToCartCommand($request, $cartId); @@ -287,6 +290,30 @@ public function addProductAction(Request $request, int $cartId): Response } } + /** + * Deletes product from cart + * + * @AdminSecurity("is_granted('delete', request.get('_legacy_controller'))") + * + * @param int $cartId + * @param int $productId + * + * @return JsonResponse + */ + public function deleteProductAction(int $cartId, int $productId): JsonResponse + { + try { + $this->getCommandBus()->handle(new RemoveProductFromCartCommand($cartId, $productId)); + + return $this->json($this->getCartInfo($cartId)); + } catch (Exception $e) { + return $this->json( + ['message' => $this->getErrorMessageForException($e, $this->getErrorMessages($e))], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } + } + /** * @param int $cartId * diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml index 8fc156f6568d6..c1961e32db998 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml @@ -78,7 +78,7 @@ admin_carts_add_cart_rule: expose: true admin_carts_delete_cart_rule: - path: /{cartId}/rules/{cartRuleId}/delete + path: /{cartId}/cart-rules/{cartRuleId}/delete methods: [POST] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Cart:removeCartRuleFromCart @@ -94,3 +94,18 @@ admin_carts_add_product: _legacy_controller: AdminCarts options: expose: true + requirements: + cartId: \d+ + productId: \d+ + +admin_carts_delete_product: + path: /{cartId}/products/{productId}/delete + methods: [POST] + defaults: + _controller: PrestaShopBundle:Admin/Sell/Order/Cart:deleteProduct + _legacy_controller: AdminCarts + options: + expose: true + requirements: + cartId: \d+ + productId: \d+ From 8c29ecbc6f9f203713558a822858cfc1a384256e Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Fri, 25 Oct 2019 14:21:34 +0300 Subject: [PATCH 116/195] Initialize event listeners instead of calling them multiple times --- .../themes/new-theme/js/pages/order/create.js | 10 +- .../js/pages/order/create/cart-editor.js | 5 +- .../pages/order/create/cart-rule-manager.js | 94 ++++--- .../pages/order/create/create-order-page.js | 255 ++++++++---------- .../js/pages/order/create/customer-manager.js | 101 ++++--- .../js/pages/order/create/event-map.js | 8 + .../js/pages/order/create/product-manager.js | 132 +++++---- .../js/pages/order/create/product-renderer.js | 4 +- .../Sell/Customer/CustomerController.php | 9 +- 9 files changed, 345 insertions(+), 273 deletions(-) diff --git a/admin-dev/themes/new-theme/js/pages/order/create.js b/admin-dev/themes/new-theme/js/pages/order/create.js index 30ce499d964f2..700082784703a 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create.js +++ b/admin-dev/themes/new-theme/js/pages/order/create.js @@ -27,13 +27,5 @@ import CreateOrderPage from './create/create-order-page'; const $ = window.$; $(document).ready(() => { - const createOrderPage = new CreateOrderPage(); - - createOrderPage.listenForCustomerSearch(); - createOrderPage.listenForCustomerSelect(); - createOrderPage.listenForCartSelect(); - createOrderPage.listenForOrderSelect(); - createOrderPage.listenForCartEdit(); - createOrderPage.listenForCartLoading(); - createOrderPage.listenForCartRuleSearch(); + new CreateOrderPage(); }); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js index dd8abe980dafb..423d647c31ddb 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js @@ -138,10 +138,7 @@ export default class CartEditor { * @param {Number} productId */ removeProductFromCart(cartId, productId) { - $.post(this.router.generate('admin_carts_delete_product', { - cartId, - productId, - })).then((cartInfo) => { + $.post(this.router.generate('admin_carts_delete_product', {cartId, productId})).then((cartInfo) => { EventEmitter.emit(eventMap.productRemovedFromCart, cartInfo); }).catch((response) => { showErrorMessage(response.responseJSON.message); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js index 06ceec80036b5..d2b8e6b9d26b0 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js @@ -39,77 +39,99 @@ export default class CartRuleManager { constructor() { this.router = new Router(); this.$searchInput = $(createOrderMap.cartRuleSearchInput); - this.renderer = new CartRulesRenderer(); + this.cartRulesRenderer = new CartRulesRenderer(); this.cartEditor = new CartEditor(); + this._initListeners(); + return { - onCartRuleSearch: () => { + search: () => { this._search(); }, - onCartRuleSelect: (cartRuleId, cartId) => { - this._addCartRuleToCart(cartRuleId, cartId); + stopSearching: () => { + this.cartRulesRenderer.hideResultsDropdown(); }, - onDoneSearchingCartRule: () => { - this.renderer.hideResultsDropdown(); + addCartRuleToCart: (cartRuleId, cartId) => { + this.cartEditor.addCartRuleToCart(cartRuleId, cartId); }, - onCartRuleRemove: (cartRuleId, cartId) => { - this._removeCartRuleFromCart(cartRuleId, cartId); + removeCartRuleFromCart: (cartRuleId, cartId) => { + this.cartEditor.removeCartRuleFromCart(cartRuleId, cartId); }, }; } /** - * Searches for cart rules by search phrase + * Initiates event listeners for cart rule actions * * @private */ - _search() { - const searchPhrase = this.$searchInput.val(); - if (searchPhrase.length < 3) { - return; - } + _initListeners() { + this._onCartRuleSearch(); + this._onAddCartRuleToCart(); + this._onAddCartRuleToCartFailure(); + this._onRemoveCartRuleFromCart(); + } - $.get(this.router.generate('admin_cart_rules_search'), { - search_phrase: searchPhrase, - }).then((cartRules) => { - this.renderer.renderSearchResults(cartRules); - }).catch((e) => { - showErrorMessage(e.responseJSON.message); + /** + * Listens for cart rule search action + * + * @private + */ + _onCartRuleSearch() { + EventEmitter.on(eventMap.cartRuleSearched, (cartRules) => { + this.cartRulesRenderer.renderSearchResults(cartRules); }); } /** - * Adds cart rule to cart - * - * @param cartRuleId - * @param cartId + * Listens event of add cart rule to cart action * * @private */ - _addCartRuleToCart(cartRuleId, cartId) { - this.cartEditor.addCartRuleToCart(cartRuleId, cartId); - + _onAddCartRuleToCart() { EventEmitter.on(eventMap.cartRuleAdded, (cartInfo) => { - this.renderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); + this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); }); + } + + /** + * Listens event when add cart rule to cart fails + * + * @private + */ + _onAddCartRuleToCartFailure() { EventEmitter.on(eventMap.cartRuleFailedToAdd, (message) => { - this.renderer.displayErrorMessage(message); + this.cartRulesRenderer.displayErrorMessage(message); }); } /** - * Removes cart rule from cart + * Listens event for remove cart rule from cart action * - * @param cartRuleId - * @param cartId + * @private + */ + _onRemoveCartRuleFromCart() { + EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => { + this.cartRulesRenderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); + }); + } + + /** + * Searches for cart rules by search phrase * * @private */ - _removeCartRuleFromCart(cartRuleId, cartId) { - this.cartEditor.removeCartRuleFromCart(cartRuleId, cartId); + _search(searchPhrase) { + if (searchPhrase.length < 3) { + return; + } - EventEmitter.on(eventMap.cartRuleRemoved, (cartInfo) => { - this.renderer.renderCartRulesBlock(cartInfo.cartRules, cartInfo.products.length === 0); + $.get(this.router.generate('admin_cart_rules_search'), { + search_phrase: searchPhrase, + }).then((cartRules) => { + EventEmitter.emit(eventMap.cartRuleSearched, cartRules); + }).catch((e) => { + showErrorMessage(e.responseJSON.message); }); } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index 2c4baeab19804..5e0d88c590f24 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -23,7 +23,7 @@ * International Registered Trademark & Property of PrestaShop SA */ -import createOrderPageMap from './create-order-map'; +import createOrderMap from './create-order-map'; import CustomerManager from './customer-manager'; import ShippingRenderer from './shipping-renderer'; import CartProvider from './cart-provider'; @@ -45,7 +45,7 @@ const $ = window.$; export default class CreateOrderPage { constructor() { this.cartId = null; - this.$container = $(createOrderPageMap.orderCreationContainer); + this.$container = $(createOrderMap.orderCreationContainer); this.cartProvider = new CartProvider(); this.customerManager = new CustomerManager(); @@ -58,19 +58,58 @@ export default class CreateOrderPage { this.productManager = new ProductManager(); this.productRenderer = new ProductRenderer(); - return { - listenForCustomerSearch: () => this._handleCustomerSearch(), - listenForCustomerSelect: () => this._handleCustomerSelect(), - listenForCartSelect: () => this._handleUseCartForOrderCreation(), - listenForOrderSelect: () => this._handleDuplicateOrderCart(), - listenForCartEdit: () => this._handleCartEdit(), - listenForCartLoading: () => this._onCartLoaded(), - listenForCartRuleSearch: () => this._searchCartRule(), - }; + this._initListeners(); } /** - * Handles event when cart is loaded. + * Initializes event listeners + * + * @private + */ + _initListeners() { + this.$container.on('input', createOrderMap.customerSearchInput, e => this._initCustomerSearch(e)); + this.$container.on('click', createOrderMap.chooseCustomerBtn, e => this._initCustomerSelect(e)); + this.$container.on('click', createOrderMap.useCartBtn, e => this._initCartSelect(e)); + this.$container.on('click', createOrderMap.useOrderBtn, e => this._initDuplicateOrderCart(e)); + this.$container.on('input', createOrderMap.productSearch, e => this._initProductSearch(e)); + this.$container.on('input', createOrderMap.cartRuleSearchInput, e => this._initCartRuleSearch(e)); + this.$container.on('blur', createOrderMap.cartRuleSearchInput, () => this.cartRuleManager.stopSearching()); + this._initCartEditing(); + this._onCartLoaded(); + this._onCartAddressesChanged(); + } + + /** + * Delegates actions to events associated with cart update (e.g. change cart address) + * + * @private + */ + _initCartEditing() { + this.$container.on('change', createOrderMap.addressSelect, () => this._changeCartAddresses()); + + this.$container.on('change', createOrderMap.deliveryOptionSelect, e => + this.cartEditor.changeDeliveryOption(this.cartId, e.currentTarget.value) + ); + + this.$container.on('change', createOrderMap.freeShippingSwitch, e => + this.cartEditor.setFreeShipping(this.cartId, e.currentTarget.value) + ); + + this.$container.on('click', createOrderMap.addToCartButton, () => + this.productManager.addProductToCart(this.cartId) + ); + + this.$container.on('click', createOrderMap.productRemoveBtn, (e) => { + const productId = Number($(e.currentTarget).data('product-id')); + this.productManager.removeProductFromCart(this.cartId, productId); + }); + + this._addCartRuleToCart(); + this._removeCartRuleFromCart(); + } + + /** + * Listens for event when cart is loaded * * @private */ @@ -84,95 +123,92 @@ export default class CreateOrderPage { } /** - * Searches for customer + * Listens for cart addresses update event * * @private */ - _handleCustomerSearch() { - this.$container.on('input', createOrderPageMap.customerSearchInput, () => { - this.customerManager.onCustomerSearch(); + _onCartAddressesChanged() { + EventEmitter.on(eventMap.cartAddressesChanged, (cartInfo) => { + this.addressesRenderer.render(cartInfo.addresses); + this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); } /** - * Chooses customer for which order is being created + * Listens for cart delivery option update event * * @private */ - _handleCustomerSelect() { - this.$container.on('click', createOrderPageMap.chooseCustomerBtn, (event) => { - const customerId = this.customerManager.onCustomerSelect(event); - - this.cartProvider.loadEmptyCart(customerId); + _onDeliveryOptionChanged() { + EventEmitter.on(eventMap.cartDeliveryOptionChanged, (cartInfo) => { + this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); - - this.$container.on('click', createOrderPageMap.changeCustomerBtn, () => this.customerManager.onCustomerChange()); } /** - * Handles use case when cart is selected for order creation + * Listens for cart free shipping update event * * @private */ - _handleUseCartForOrderCreation() { - this.$container.on('click', createOrderPageMap.useCartBtn, (e) => { - const cartId = $(e.currentTarget).data('cart-id'); - this.cartProvider.getCart(cartId); + _onFreeShippingChanged() { + EventEmitter.on(eventMap.cartFreeShippingSet, (cartInfo) => { + this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); }); } /** - * Handles use case when order is selected for cart duplication + * Init customer searching + * + * @param event * * @private */ - _handleDuplicateOrderCart() { - this.$container.on('click', createOrderPageMap.useOrderBtn, (e) => { - const orderId = $(e.currentTarget).data('order-id'); - this.cartProvider.duplicateOrderCart(orderId); - }); + _initCustomerSearch(event) { + this.customerManager.search($(event.currentTarget).val()); } /** - * Delegates actions to events associated with cart update (e.g. change cart address) + * Init selecting customer for which order is being created + * + * @param event * * @private */ - _handleCartEdit() { - this.$container.on('change', createOrderPageMap.addressSelect, () => this._changeCartAddresses()); - this.$container.on('change', createOrderPageMap.deliveryOptionSelect, e => this._changeDeliveryOption(e)); - this.$container.on('change', createOrderPageMap.freeShippingSwitch, e => this._setFreeShipping(e)); - this.$container.on('click', createOrderPageMap.addToCartButton, () => this.productManager.onAddProductToCart(this.cartId)); - this.$container.on('click', createOrderPageMap.productRemoveBtn, e => this._removeProductFromCart(e)); - this._selectCartRule(); - this._removeCartRule(); + _initCustomerSelect(event) { + const customerId = this.customerManager.selectCustomer(event); + this.cartProvider.loadEmptyCart(customerId); } /** - * Triggers cart rule searching + * Inits selecting cart to load + * + * @param event * * @private */ - _searchCartRule() { - this.$container.on('input', createOrderPageMap.cartRuleSearchInput, () => { - this.cartRuleManager.onCartRuleSearch(); - }); - this.$container.on('blur', createOrderPageMap.cartRuleSearchInput, () => { - this.cartRuleManager.onDoneSearchingCartRule(); - }); + _initCartSelect(event) { + const cartId = $(event.currentTarget).data('cart-id'); + this.cartProvider.getCart(cartId); } /** - * Triggers removing product from cart - * - * @param {Object} event + * Inits duplicating order cart * * @private */ - _removeProductFromCart(event) { - const productId = Number($(event.currentTarget).data('product-id')); + _initDuplicateOrderCart(event) { + const orderId = $(event.currentTarget).data('order-id'); + this.cartProvider.duplicateOrderCart(orderId); + } - this.productManager.onRemoveProductFromCart(this.cartId, productId); + /** + * Triggers cart rule searching + * + * @private + */ + _initCartRuleSearch(event) { + const searchPhrase = event.currentTarget.value; + this.cartRuleManager.search(searchPhrase); } /** @@ -180,16 +216,16 @@ export default class CreateOrderPage { * * @private */ - _selectCartRule() { - this.$container.on('mousedown', createOrderPageMap.foundCartRuleListItem, (event) => { + _addCartRuleToCart() { + this.$container.on('mousedown', createOrderMap.foundCartRuleListItem, (event) => { // prevent blur event to allow selecting cart rule event.preventDefault(); const cartRuleId = $(event.currentTarget).data('cart-rule-id'); - this.cartRuleManager.onCartRuleSelect(cartRuleId, this.cartId); + this.cartRuleManager.addCartRuleToCart(cartRuleId, this.cartId); // manually fire blur event after cart rule is selected. - }).on('click', createOrderPageMap.foundCartRuleListItem, () => { - $(createOrderPageMap.cartRuleSearchInput).blur(); + }).on('click', createOrderMap.foundCartRuleListItem, () => { + $(createOrderMap.cartRuleSearchInput).blur(); }); } @@ -198,12 +234,26 @@ export default class CreateOrderPage { * * @private */ - _removeCartRule() { - this.$container.on('click', createOrderPageMap.cartRuleDeleteBtn, (event) => { - this.cartRuleManager.onCartRuleRemove($(event.currentTarget).data('cart-rule-id'), this.cartId); + _removeCartRuleFromCart() { + this.$container.on('click', createOrderMap.cartRuleDeleteBtn, (event) => { + this.cartRuleManager.removeCartRuleFromCart($(event.currentTarget).data('cart-rule-id'), this.cartId); }); } + /** + * Inits product searching + * + * @param event + * + * @private + */ + _initProductSearch(event) { + const $productSearchInput = $(event.currentTarget); + const searchPhrase = $productSearchInput.val(); + + this.productManager.search(searchPhrase); + } + /** * Renders cart summary on the page * @@ -219,7 +269,7 @@ export default class CreateOrderPage { // @todo: render Summary block when at least 1 product is in cart // and delivery options are available - $(createOrderPageMap.cartBlock).removeClass('d-none'); + $(createOrderMap.cartBlock).removeClass('d-none'); } /** @@ -229,77 +279,10 @@ export default class CreateOrderPage { */ _changeCartAddresses() { const addresses = { - delivery_address_id: $(createOrderPageMap.deliveryAddressSelect).val(), - invoice_address_id: $(createOrderPageMap.invoiceAddressSelect).val(), + delivery_address_id: $(createOrderMap.deliveryAddressSelect).val(), + invoice_address_id: $(createOrderMap.invoiceAddressSelect).val(), }; this.cartEditor.changeCartAddresses(this.cartId, addresses); - EventEmitter.on(eventMap.cartAddressesChanged, (cartInfo) => { - this.addressesRenderer.render(cartInfo.addresses); - this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); - }); - } - - /** - * Modifies cart delivery option - * - * @param event - * - * @private - */ - _changeDeliveryOption(event) { - this.cartEditor.changeDeliveryOption(this.cartId, event.currentTarget.value); - EventEmitter.on(eventMap.cartDeliveryOptionChanged, (cartInfo) => { - this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); - }); - } - - /** - * Sets free shipping value of cart - * - * @param event - * - * @private - */ - _setFreeShipping(event) { - this.cartEditor.setFreeShipping(this.cartId, event.currentTarget.value); - EventEmitter.on(eventMap.cartFreeShippingSet, (cartInfo) => { - this.shippingRenderer.render(cartInfo.shipping, cartInfo.products.length === 0); - }); - } - - /** - * @todo: for cart to order convertion - * Stores cart summary into "session" like variable - * - * @param {Object} cartInfo - * - * @private - */ - _persistCartInfoData(cartInfo) { - this.data.cartId = cartInfo.cart.id; - this.data.delivery_address_id = cartInfo.cart.id_address_delivery; - this.data.invoice_address_id = cartInfo.cart.id_address_invoice; - } - - /** - * @todo: for cart to order convertion - * Choses previous cart from which order will be created - * - * @param {Number} cartId - * - * @private - */ - _choosePreviousCart(cartId) { - $.ajax(this.$container.data('cart-summary-url'), { - method: 'POST', - data: { - id_cart: cartId, - id_customer: this.data.customerId, - }, - }).then((response) => { - this._persistCartInfoData(response); - this._renderCartInfo(response); - }); } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js index fe1cb0785ed64..eb7388b41ffb2 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/customer-manager.js @@ -23,8 +23,10 @@ * International Registered Trademark & Property of PrestaShop SA */ -import createOrderPageMap from './create-order-map'; +import createOrderMap from './create-order-map'; import CustomerRenderer from './customer-renderer'; +import {EventEmitter} from '../../../components/event-emitter'; +import eventMap from './event-map'; import Router from '../../../components/router'; const $ = window.$; @@ -37,31 +39,68 @@ export default class CustomerManager { this.customerId = null; this.router = new Router(); - this.$container = $(createOrderPageMap.customerSearchBlock); - this.$searchInput = $(createOrderPageMap.customerSearchInput); - this.$customerSearchResultBlock = $(createOrderPageMap.customerSearchResultsBlock); - this.renderer = new CustomerRenderer(); + this.$container = $(createOrderMap.customerSearchBlock); + this.$searchInput = $(createOrderMap.customerSearchInput); + this.$customerSearchResultBlock = $(createOrderMap.customerSearchResultsBlock); + this.customerRenderer = new CustomerRenderer(); + + this._initListeners(); return { - onCustomerSearch: () => { - this._search(); - }, - onCustomerSelect: event => this._selectCustomer(event), - onCustomerChange: () => { - this.renderer.showCustomerSearch(); - }, - loadCustomerCarts: (currentCartId) => { - this._loadCustomerCarts(currentCartId); - }, - loadCustomerOrders: () => { - this._loadCustomerOrders(); - }, + search: searchPhrase => this._search(searchPhrase), + selectCustomer: event => this._selectCustomer(event), + loadCustomerCarts: currentCartId => this._loadCustomerCarts(currentCartId), + loadCustomerOrders: () => this._loadCustomerOrders(), }; } /** - * Gets customer carts - * After Request is complete, emits event providing carts list + * Initializes event listeners + * + * @private + */ + _initListeners() { + this.$container.on('click', createOrderMap.changeCustomerBtn, () => this._changeCustomer()); + this._onCustomerSearch(); + this._onCustomerSelect(); + } + + /** + * Listens for customer search event + * + * @private + */ + _onCustomerSearch() { + EventEmitter.on(eventMap.customerSearched, (response) => { + this.customerRenderer.renderSearchResults(response.customers); + }); + } + + /** + * Listens for customer select event + * + * @private + */ + _onCustomerSelect() { + EventEmitter.on(eventMap.customerSelected, (event) => { + const $chooseBtn = $(event.currentTarget); + this.customerId = $chooseBtn.data('customer-id'); + + this.customerRenderer.displaySelectedCustomerBlock($chooseBtn); + }); + } + + /** + * Handles use case when customer is changed + * + * @private + */ + _changeCustomer() { + this.customerRenderer.showCustomerSearch(); + } + + /** + * Loads customer carts list * * @param currentCartId */ @@ -69,21 +108,20 @@ export default class CustomerManager { const customerId = this.customerId; $.get(this.router.generate('admin_customers_carts', {customerId})).then((response) => { - this.renderer.renderCarts(response.carts, currentCartId); + this.customerRenderer.renderCarts(response.carts, currentCartId); }).catch((e) => { showErrorMessage(e.responseJSON.message); }); } /** - * Gets customer carts - * After Request is complete, emits event providing orders list + * Loads customer orders list */ _loadCustomerOrders() { const customerId = this.customerId; $.get(this.router.generate('admin_customers_orders', {customerId})).then((response) => { - this.renderer.renderOrders(response.orders); + this.customerRenderer.renderOrders(response.orders); }).catch((e) => { showErrorMessage(e.responseJSON.message); }); @@ -95,22 +133,17 @@ export default class CustomerManager { * @return {Number} */ _selectCustomer(chooseCustomerEvent) { - const $chooseBtn = $(chooseCustomerEvent.currentTarget); - this.customerId = $chooseBtn.data('customer-id'); - - this.renderer.displaySelectedCustomerBlock($chooseBtn); + EventEmitter.emit(eventMap.customerSelected, chooseCustomerEvent); return this.customerId; } /** * Searches for customers - *@todo: fix showing not found customers and rerender after change customer + * @todo: fix showing not found customers and rerender after change customer * @private */ - _search() { - const searchPhrase = this.$searchInput.val(); - + _search(searchPhrase) { if (searchPhrase.length < 3) { return; } @@ -118,7 +151,9 @@ export default class CustomerManager { $.get(this.router.generate('admin_customers_search'), { customer_search: searchPhrase, }).then((response) => { - this.renderer.renderSearchResults(response.customers); + EventEmitter.emit(eventMap.customerSearched, response); + }).catch((response) => { + showErrorMessage(response.responseJSON.message); }); } } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js index b062b36d78e89..cf5afe9d009bc 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/event-map.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/event-map.js @@ -27,6 +27,10 @@ * Encapsulates js events used in create order page */ export default { + // when customer search action is done + customerSearched: 'customerSearched', + // when new customer is selected + customerSelected: 'customerSelected', // when new cart is loaded, no matter if its empty, selected from carts list or duplicated by order. cartLoaded: 'cartLoaded', // when cart addresses information has been changed @@ -35,12 +39,16 @@ export default { cartDeliveryOptionChanged: 'cartDeliveryOptionChanged', // when cart free shipping value has been changed cartFreeShippingSet: 'cartFreeShippingSet', + // when cart rules search action is done + cartRuleSearched: 'cartRuleSearched', // when cart rule is removed from cart cartRuleRemoved: 'cartRuleRemoved', // when cart rule is added to cart cartRuleAdded: 'cartRuleAdded', // when cart rule cannot be added to cart cartRuleFailedToAdd: 'cartRuleFailedToAdd', + // when product search action is done + productSearched: 'productSearched', // when product is added to cart productAddedToCart: 'productAddedToCart', // when product is removed from cart diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 3d9aa1297ddf1..3db5f89c791a6 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -41,59 +41,114 @@ export default class ProductManager { this.selectedProductId = null; this.selectedCombinationId = null; - this.renderer = new ProductRenderer(); + this.productRenderer = new ProductRenderer(); this.router = new Router(); this.cartEditor = new CartEditor(); - this._initEvents(); + this._initListeners(); return { - onAddProductToCart: (cartId) => { - this._addProductToCart(cartId); + search: (searchPhrase) => { + this._search(searchPhrase); }, - onRemoveProductFromCart: (cartId, productId) => { - this._removeProductFromCart(cartId, productId); + addProductToCart: (cartId) => { + this.cartEditor.addProduct(cartId, this._getProductData(cartId)); + }, + removeProductFromCart: (cartId, productId) => { + this.cartEditor.removeProductFromCart(cartId, productId); } }; } /** - * Initialize page's events. + * Initializes event listeners + * + * @private + */ + _initListeners() { + $(createOrderMap.productSelect).on('change', e => this._initProductSelect(e)); + $(createOrderMap.combinationsSelect).on('change', e => this._initCombinationSelect(e)); + + this._onProductSearch(); + this._onAddProductToCart(); + this._onRemoveProductFromCart(); + } + + /** + * Listens for product search event * * @private */ - _initEvents() { - $(createOrderMap.productSearch).on('input', event => this._search(event)); - $(createOrderMap.productSelect).on('change', (event) => { - const productId = Number($(event.currentTarget).find(':selected').val()); - this._selectProduct(productId); + _onProductSearch() { + EventEmitter.on(eventMap.productSearched, (response) => { + this.products = JSON.parse(response); + this.productRenderer.renderSearchResults(this.products); + this._selectFirstResult(); }); - $(createOrderMap.combinationsSelect).on('change', (event) => { - const combinationId = Number($(event.currentTarget).find(':selected').val()); - this._selectCombination(combinationId); + } + + /** + * Listens for add product to cart event + * + * @private + */ + _onAddProductToCart() { + EventEmitter.on(eventMap.productAddedToCart, (cartInfo) => { + EventEmitter.emit(eventMap.cartLoaded, cartInfo); }); } + /** + * Listens for remove product from cart event + * + * @private + */ + _onRemoveProductFromCart() { + EventEmitter.on(eventMap.productRemovedFromCart, (cartInfo) => { + EventEmitter.emit(eventMap.cartLoaded, cartInfo); + }); + } + + /** + * Initializes product select + * + * @param event + * + * @private + */ + _initProductSelect(event) { + const productId = Number($(event.currentTarget).find(':selected').val()); + this._selectProduct(productId); + } + + /** + * Initializes combination select + * + * @param event + * + * @private + */ + _initCombinationSelect(event) { + const combinationId = Number($(event.currentTarget).find(':selected').val()); + this._selectCombination(combinationId); + } + /** * Searches for product * * @private */ - _search(event) { + _search(searchPhrase) { const minSearchPhraseLength = 3; - const $productSearchInput = $(event.currentTarget); - const name = $productSearchInput.val(); - if (name.length < minSearchPhraseLength) { + if (searchPhrase.length < minSearchPhraseLength) { return; } $.get(this.router.generate('admin_products_search'), { - search_phrase: name, + search_phrase: searchPhrase, }).then((response) => { - this.products = JSON.parse(response); - this.renderer.renderSearchResults(this.products); - this._selectFirstResult(); + EventEmitter.emit(eventMap.productSearched, response); }).catch((response) => { if (typeof response.responseJSON !== 'undefined') { showErrorMessage(response.responseJSON.message); @@ -127,7 +182,7 @@ export default class ProductManager { this.selectedProductId = productId; const product = this.products[productId]; - this.renderer.renderProductMetadata(product); + this.productRenderer.renderProductMetadata(product); // if product has combinations select the first else leave it null if (product.combinations.length !== 0) { @@ -148,7 +203,7 @@ export default class ProductManager { const combination = this.products[this.selectedProductId].combinations[combinationId]; this.selectedCombinationId = combinationId; - this.renderer.renderStock(combination.stock); + this.productRenderer.renderStock(combination.stock); return combination; } @@ -171,33 +226,6 @@ export default class ProductManager { this.selectedProductId = null; } - /** - * Adds selected product to current cart - * - * @private - */ - _addProductToCart(cartId) { - this.cartEditor.addProduct(cartId, this._getProductData(cartId)); - EventEmitter.on(eventMap.productAddedToCart, (cartInfo) => { - EventEmitter.emit(eventMap.cartLoaded, cartInfo); - }); - } - - /** - * Removes product from cart - * - * @param {Number} cartId - * @param {Number} productId - * - * @private - */ - _removeProductFromCart(cartId, productId) { - this.cartEditor.removeProductFromCart(productId, productId); - EventEmitter.on(eventMap.productRemovedFromCart, (cartInfo) => { - EventEmitter.emit(eventMap.cartLoaded, cartInfo); - }); - } - /** * Retrieves product data from product search result block fields * diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js index 20f6261cbe63d..d5da2aead709f 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js @@ -78,7 +78,7 @@ export default class ProductRenderer { this._showNotFound(); this._hideTaxWarning(); - return null; + return; } this._renderFoundProducts(foundProducts); @@ -163,7 +163,7 @@ export default class ProductRenderer { ``, + ` ); } diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Customer/CustomerController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Customer/CustomerController.php index 4dda09441a7fc..e6921a3871635 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Customer/CustomerController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Customer/CustomerController.php @@ -384,7 +384,14 @@ public function searchAction(Request $request) $phrases = explode(' ', $query); $isRequestFromLegacyPage = !$request->query->has('sf2'); - $customers = $this->getQueryBus()->handle(new SearchCustomers($phrases)); + try { + $customers = $this->getQueryBus()->handle(new SearchCustomers($phrases)); + } catch (Exception $e) { + return $this->json( + ['message' => $this->getErrorMessageForException($e, $this->getErrorMessages($e))], + Response::HTTP_INTERNAL_SERVER_ERROR + ); + } // if call is made from legacy page // it will return response so legacy can understand it From 6302ca51e5fa12d2ff15e71437726ece8af7bd2c Mon Sep 17 00:00:00 2001 From: Julius Zukauskas Date: Fri, 25 Oct 2019 18:06:54 +0300 Subject: [PATCH 117/195] refactoring --- .../themes/new-theme/js/fos_js_routes.json | 2 +- .../js/pages/order/create/cart-editor.js | 17 +++-- .../pages/order/create/cart-rule-manager.js | 16 ++--- .../pages/order/create/create-order-page.js | 29 +++++--- .../pages/order/create/customer-renderer.js | 6 +- .../js/pages/order/create/product-manager.js | 19 ++---- .../js/pages/order/create/product-renderer.js | 6 +- .../AddCustomizationFieldsHandler.php | 21 +++--- .../RemoveProductFromCartHandler.php | 1 + .../QueryHandler/SearchProductsHandler.php | 67 ++++++++----------- .../Admin/Sell/Order/CartController.php | 36 +++++++--- .../routing/admin/sell/orders/carts.yml | 7 +- .../config/services/adapter/order.yml | 10 --- .../config/services/adapter/product.yml | 6 +- .../Order/Order/Blocks/Create/cart.html.twig | 6 +- .../Order/Blocks/Create/cart_rules.html.twig | 6 +- .../Order/Blocks/Create/customer.html.twig | 4 +- 17 files changed, 127 insertions(+), 132 deletions(-) diff --git a/admin-dev/themes/new-theme/js/fos_js_routes.json b/admin-dev/themes/new-theme/js/fos_js_routes.json index 2176f3780fc65..bf3a18a885da6 100644 --- a/admin-dev/themes/new-theme/js/fos_js_routes.json +++ b/admin-dev/themes/new-theme/js/fos_js_routes.json @@ -1 +1 @@ -{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_product":{"tokens":[["text","\/delete"],["variable","\/","\\d+","productId"],["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file +{"base_url":"","routes":{"admin_products_search":{"tokens":[["text","\/sell\/catalog\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_free_shipping":{"tokens":[["text","\/rules\/free-shipping"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_product":{"tokens":[["text","\/delete-product"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":[]} \ No newline at end of file diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js index 423d647c31ddb..1a1dc70a90dcd 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-editor.js @@ -58,7 +58,7 @@ export default class CartEditor { */ changeDeliveryOption(cartId, value) { $.post(this.router.generate('admin_carts_edit_carrier', {cartId}), { - carrier_id: value, + carrierId: value, }).then((cartInfo) => { EventEmitter.emit(eventMap.cartDeliveryOptionChanged, cartInfo); }); @@ -72,7 +72,7 @@ export default class CartEditor { */ setFreeShipping(cartId, value) { $.post(this.router.generate('admin_carts_set_free_shipping', {cartId}), { - free_shipping: value, + freeShipping: value, }).then((cartInfo) => { EventEmitter.emit(eventMap.cartFreeShippingSet, cartInfo); }); @@ -86,7 +86,7 @@ export default class CartEditor { */ addCartRuleToCart(cartRuleId, cartId) { $.post(this.router.generate('admin_carts_add_cart_rule', {cartId}), { - cart_rule_id: cartRuleId, + cartRuleId, }).then((cartInfo) => { EventEmitter.emit(eventMap.cartRuleAdded, cartInfo); }).catch((response) => { @@ -123,7 +123,6 @@ export default class CartEditor { data: product, processData: false, contentType: false, - cache: false, }).then((cartInfo) => { EventEmitter.emit(eventMap.productAddedToCart, cartInfo); }).catch((response) => { @@ -135,10 +134,14 @@ export default class CartEditor { * Removes product from cart * * @param {Number} cartId - * @param {Number} productId + * @param {Object} product */ - removeProductFromCart(cartId, productId) { - $.post(this.router.generate('admin_carts_delete_product', {cartId, productId})).then((cartInfo) => { + removeProductFromCart(cartId, product) { + $.post(this.router.generate('admin_carts_delete_product', {cartId}), { + productId: product.productId, + attributeId: product.attributeId, + customizationId: product.customizationId, + }).then((cartInfo) => { EventEmitter.emit(eventMap.productRemovedFromCart, cartInfo); }).catch((response) => { showErrorMessage(response.responseJSON.message); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js index d2b8e6b9d26b0..857711dccd2c3 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/cart-rule-manager.js @@ -45,18 +45,10 @@ export default class CartRuleManager { this._initListeners(); return { - search: () => { - this._search(); - }, - stopSearching: () => { - this.cartRulesRenderer.hideResultsDropdown(); - }, - addCartRuleToCart: (cartRuleId, cartId) => { - this.cartEditor.addCartRuleToCart(cartRuleId, cartId); - }, - removeCartRuleFromCart: (cartRuleId, cartId) => { - this.cartEditor.removeCartRuleFromCart(cartRuleId, cartId); - }, + search: () => this._search(), + stopSearching: () => this.cartRulesRenderer.hideResultsDropdown(), + addCartRuleToCart: (cartRuleId, cartId) => this.cartEditor.addCartRuleToCart(cartRuleId, cartId), + removeCartRuleFromCart: (cartRuleId, cartId) => this.cartEditor.removeCartRuleFromCart(cartRuleId, cartId), }; } diff --git a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js index 5e0d88c590f24..ebc52866ff120 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/create-order-page.js @@ -85,8 +85,6 @@ export default class CreateOrderPage { * @private */ _initCartEditing() { - this.$container.on('change', createOrderMap.addressSelect, () => this._changeCartAddresses()); - this.$container.on('change', createOrderMap.deliveryOptionSelect, e => this.cartEditor.changeDeliveryOption(this.cartId, e.currentTarget.value) ); @@ -99,10 +97,8 @@ export default class CreateOrderPage { this.productManager.addProductToCart(this.cartId) ); - this.$container.on('click', createOrderMap.productRemoveBtn, (e) => { - const productId = Number($(e.currentTarget).data('product-id')); - this.productManager.removeProductFromCart(this.cartId, productId); - }); + this.$container.on('change', createOrderMap.addressSelect, () => this._changeCartAddresses()); + this.$container.on('click', createOrderMap.productRemoveBtn, e => this._initProductRemoveFromCart(e)); this._addCartRuleToCart(); this._removeCartRuleFromCart(); @@ -254,6 +250,23 @@ export default class CreateOrderPage { this.productManager.search(searchPhrase); } + /** + * Inits product removing from cart + * + * @param event + * + * @private + */ + _initProductRemoveFromCart(event) { + const product = { + productId: $(event.currentTarget).data('product-id'), + attributeId: $(event.currentTarget).data('attribute-id'), + customizationId: $(event.currentTarget).data('customization-id'), + }; + + this.productManager.removeProductFromCart(this.cartId, product); + } + /** * Renders cart summary on the page * @@ -279,8 +292,8 @@ export default class CreateOrderPage { */ _changeCartAddresses() { const addresses = { - delivery_address_id: $(createOrderMap.deliveryAddressSelect).val(), - invoice_address_id: $(createOrderMap.invoiceAddressSelect).val(), + deliveryAddressId: $(createOrderMap.deliveryAddressSelect).val(), + invoiceAddressId: $(createOrderMap.invoiceAddressSelect).val(), }; this.cartEditor.changeCartAddresses(this.cartId, addresses); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js index 0c8d0a3a6fe59..e0f727fbcc972 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/customer-renderer.js @@ -54,8 +54,8 @@ export default class CustomerRenderer { const customerResult = foundCustomers[customerId]; const customer = { id: customerId, - first_name: customerResult.firstname, - last_name: customerResult.lastname, + firstName: customerResult.firstname, + lastName: customerResult.lastname, email: customerResult.email, birthday: customerResult.birthday !== '0000-00-00' ? customerResult.birthday : ' ', }; @@ -173,7 +173,7 @@ export default class CustomerRenderer { const $customerSearchResultTemplate = $($(createOrderMap.customerSearchResultTemplate).html()); const $template = $customerSearchResultTemplate.clone(); - $template.find(createOrderMap.customerSearchResultName).text(`${customer.first_name} ${customer.last_name}`); + $template.find(createOrderMap.customerSearchResultName).text(`${customer.firstName} ${customer.lastName}`); $template.find(createOrderMap.customerSearchResultEmail).text(customer.email); $template.find(createOrderMap.customerSearchResultId).text(customer.id); $template.find(createOrderMap.customerSearchResultBirthday).text(customer.birthday); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js index 3db5f89c791a6..df4c1ec1c6ffd 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-manager.js @@ -48,15 +48,9 @@ export default class ProductManager { this._initListeners(); return { - search: (searchPhrase) => { - this._search(searchPhrase); - }, - addProductToCart: (cartId) => { - this.cartEditor.addProduct(cartId, this._getProductData(cartId)); - }, - removeProductFromCart: (cartId, productId) => { - this.cartEditor.removeProductFromCart(cartId, productId); - } + search: searchPhrase => this._search(searchPhrase), + addProductToCart: cartId => this.cartEditor.addProduct(cartId, this._getProductData()), + removeProductFromCart: (cartId, product) => this.cartEditor.removeProductFromCart(cartId, product), }; } @@ -232,13 +226,12 @@ export default class ProductManager { * @returns {FormData} * @private */ - _getProductData(cartId) { + _getProductData() { const formData = new FormData(); - formData.append('cart_id', cartId); - formData.append('product_id', this.selectedProductId); + formData.append('productId', this.selectedProductId); formData.append('quantity', $(createOrderMap.quantityInput).val()); - formData.append('combination_id', this.selectedCombinationId); + formData.append('combinationId', this.selectedCombinationId); this._getCustomFieldsData(formData); diff --git a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js index d5da2aead709f..5b79d7c43f297 100644 --- a/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js +++ b/admin-dev/themes/new-theme/js/pages/order/create/product-renderer.js @@ -59,6 +59,8 @@ export default class ProductRenderer { $template.find(createOrderMap.productUnitPriceInput).text(product.unitPrice); $template.find(createOrderMap.productTotalPriceField).text(product.price); $template.find(createOrderMap.productRemoveBtn).data('product-id', product.productId); + $template.find(createOrderMap.productRemoveBtn).data('attribute-id', product.attributeId); + $template.find(createOrderMap.productRemoveBtn).data('customization-id', product.customizationId); this.$productsTable.find('tbody').append($template); } @@ -200,9 +202,9 @@ export default class ProductRenderer { const $template = templateTypeMap[customField.type].clone(); $template.find(createOrderMap.productCustomInput) - .attr('name', `customization[${customField.customization_field_id}]`); + .attr('name', `customizations[${customField.customization_field_id}]`); $template.find(createOrderMap.productCustomInputLabel) - .attr('for', `customization[${customField.customization_field_id}]`) + .attr('for', `customizations[${customField.customization_field_id}]`) .text(customField.name); $customFieldsContainer.append($template); diff --git a/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php b/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php index 09c49d19e3589..59db4f083eb3f 100644 --- a/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php +++ b/src/Adapter/Cart/CommandHandler/AddCustomizationFieldsHandler.php @@ -55,17 +55,18 @@ public function handle(AddCustomizationFieldsCommand $command) foreach ($customizationFields as $customizationField) { $customizationId = (int) $customizationField['id_customization_field']; //@todo validation - if (isset($customizations[$customizationId])) { - if ($customizationField['type'] == Product::CUSTOMIZE_TEXTFIELD) { - $cart->addTextFieldToProduct( - $productId, - $customizationId, - Product::CUSTOMIZE_TEXTFIELD, - $customizations[$customizationId] - ); - continue; - } + if (!isset($customizations[$customizationId])) { + continue; + } + if ($customizationField['type'] == Product::CUSTOMIZE_TEXTFIELD) { + $cart->addTextFieldToProduct( + $productId, + $customizationId, + Product::CUSTOMIZE_TEXTFIELD, + $customizations[$customizationId] + ); + } else { //@todo: file validation $cart->addPictureToProduct( $productId, diff --git a/src/Adapter/Cart/CommandHandler/RemoveProductFromCartHandler.php b/src/Adapter/Cart/CommandHandler/RemoveProductFromCartHandler.php index 6715806139171..af72dc7ef0f07 100644 --- a/src/Adapter/Cart/CommandHandler/RemoveProductFromCartHandler.php +++ b/src/Adapter/Cart/CommandHandler/RemoveProductFromCartHandler.php @@ -30,6 +30,7 @@ use PrestaShop\PrestaShop\Core\Domain\Cart\Command\RemoveProductFromCartCommand; use PrestaShop\PrestaShop\Core\Domain\Cart\CommandHandler\RemoveProductFromCartHandlerInterface; use PrestaShop\PrestaShop\Core\Domain\Cart\Exception\CartException; +use Product; /** * Handles removing product from context cart. diff --git a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php index 80bb8fea28fa1..61e23bee0e81a 100644 --- a/src/Adapter/Product/QueryHandler/SearchProductsHandler.php +++ b/src/Adapter/Product/QueryHandler/SearchProductsHandler.php @@ -26,16 +26,12 @@ namespace PrestaShop\PrestaShop\Adapter\Product\QueryHandler; -use PrestaShop\PrestaShop\Adapter\LegacyContext; use PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts; use PrestaShop\PrestaShop\Core\Domain\Product\QueryHandler\SearchProductsHandlerInterface; use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\FoundProduct; use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\ProductCombination; use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\ProductCustomizationField; -use PrestaShop\PrestaShop\Core\Localization\Exception\LocalizationException; -use PrestaShop\PrestaShop\Core\Localization\Locale; -use PrestaShop\PrestaShop\Core\Localization\Locale\RepositoryInterface; -use PrestaShopException; +use PrestaShop\PrestaShop\Core\Localization\LocaleInterface; use Product; /** @@ -46,45 +42,45 @@ final class SearchProductsHandler implements SearchProductsHandlerInterface /** * @var int */ - private $langId; + private $contextLangId; /** * @var string */ - private $currencyCode; + private $contextCurrencyCode; /** - * @var RepositoryInterface + * @var LocaleInterface */ - private $localeRepository; + private $contextLocale; /** - * @var string + * @param int $contextLangId + * @param string $contextCurrencyCode + * @param LocaleInterface $contextLocale */ - private $locale; - - /** - * @param LegacyContext $context - * @param RepositoryInterface $localeRepository - * @param string $locale - */ - public function __construct(LegacyContext $context, RepositoryInterface $localeRepository, string $locale) + public function __construct(int $contextLangId, string $contextCurrencyCode, LocaleInterface $contextLocale) { - $this->langId = $context->getLanguage()->getId(); - $this->currencyCode = $context->getContext()->currency->iso_code; - $this->localeRepository = $localeRepository; - $this->locale = $locale; + $this->contextLangId = $contextLangId; + $this->contextCurrencyCode = $contextCurrencyCode; + $this->contextLocale = $contextLocale; } /** * {@inheritdoc} * - * @throws LocalizationException - * @throws PrestaShopException + * @param SearchProducts $query + * + * @return array */ public function handle(SearchProducts $query): array { - $products = Product::searchByName($this->langId, $query->getPhrase(), null, $query->getResultsLimit()); + $products = Product::searchByName( + $this->contextLangId, + $query->getPhrase(), + null, + $query->getResultsLimit() + ); $foundProducts = []; @@ -102,21 +98,17 @@ public function handle(SearchProducts $query): array * @param Product $product * * @return FoundProduct - * - * @throws LocalizationException */ private function createFoundProductFromLegacy(Product $product): FoundProduct { //@todo: sort products alphabetically - /** @var Locale $locale */ - $locale = $this->localeRepository->getLocale($this->locale); $priceTaxExcluded = Product::getPriceStatic($product->id, false); $foundProduct = new FoundProduct( $product->id, - $product->name[$this->langId], - $locale->formatPrice($priceTaxExcluded, $this->currencyCode), + $product->name[$this->contextLangId], + $this->contextLocale->formatPrice($priceTaxExcluded, $this->contextCurrencyCode), Product::getQuantity($product->id), $this->getProductCombinations($product), $this->getProductCustomizationFields($product) @@ -139,10 +131,10 @@ private function getProductCustomizationFields(Product $product): array foreach ($fields as $typeId => $typeFields) { foreach ($typeFields as $field) { $customizationField = new ProductCustomizationField( - (int) $field[$this->langId]['id_customization_field'], + (int) $field[$this->contextLangId]['id_customization_field'], (int) $typeId, - $field[$this->langId]['name'], - (bool) $field[$this->langId]['required'] + $field[$this->contextLangId]['name'], + (bool) $field[$this->contextLangId]['required'] ); $customizationFields[$customizationField->getCustomizationFieldId()] = $customizationField; @@ -157,8 +149,6 @@ private function getProductCustomizationFields(Product $product): array * @param Product $product * * @return ProductCombination[] - * - * @throws LocalizationException */ private function getProductCombinations(Product $product): array { @@ -166,9 +156,6 @@ private function getProductCombinations(Product $product): array $combinations = $product->getAttributeCombinations(); if (false !== $combinations) { - /** @var Locale $locale */ - $locale = $this->localeRepository->getLocale($this->locale); - foreach ($combinations as $combination) { $productAttributeId = (int) $combination['id_product_attribute']; $attribute = $combination['attribute_name']; @@ -184,7 +171,7 @@ private function getProductCombinations(Product $product): array $productAttributeId, $attribute, $combination['quantity'], - $locale->formatPrice($priceTaxExcluded, $this->currencyCode) + $this->contextLocale->formatPrice($priceTaxExcluded, $this->contextCurrencyCode) ); $productCombinations[$productCombination->getAttributeCombinationId()] = $productCombination; diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php index 2b8419c09fb5f..8656c2df8c09e 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Order/CartController.php @@ -180,7 +180,7 @@ public function editAddressesAction(int $cartId, Request $request): JsonResponse public function editCarrierAction(Request $request, int $cartId): JsonResponse { try { - $carrierId = (int) $request->request->get('carrier_id'); + $carrierId = (int) $request->request->get('carrierId'); $this->getCommandBus()->handle(new UpdateCartCarrierCommand( $cartId, $carrierId @@ -208,7 +208,7 @@ public function setFreeShippingAction(Request $request, int $cartId) try { $this->getCommandBus()->handle(new SetFreeShippingToCartCommand( $cartId, - $request->request->getBoolean('free_shipping') + $request->request->getBoolean('freeShipping') )); return $this->json($this->getCartInfo($cartId)); @@ -221,6 +221,8 @@ public function setFreeShippingAction(Request $request, int $cartId) } /** + * Adds cart rule to cart + * * @AdminSecurity("is_granted('create', request.get('_legacy_controller'))") * * @param Request $request @@ -228,9 +230,9 @@ public function setFreeShippingAction(Request $request, int $cartId) * * @return JsonResponse */ - public function addCartRuleToCartAction(Request $request, int $cartId): JsonResponse + public function addCartRuleAction(Request $request, int $cartId): JsonResponse { - $cartRuleId = $request->request->getInt('cart_rule_id'); + $cartRuleId = $request->request->getInt('cartRuleId'); try { $this->getCommandBus()->handle(new AddCartRuleToCartCommand($cartId, $cartRuleId)); @@ -244,6 +246,8 @@ public function addCartRuleToCartAction(Request $request, int $cartId): JsonResp } /** + * Deletes cart rule from cart + * * @AdminSecurity("is_granted('delete', request.get('_legacy_controller'))") * * @param int $cartId @@ -251,7 +255,7 @@ public function addCartRuleToCartAction(Request $request, int $cartId): JsonResp * * @return JsonResponse */ - public function removeCartRuleFromCartAction(int $cartId, int $cartRuleId) + public function deleteCartRuleAction(int $cartId, int $cartRuleId) { try { $this->getCommandBus()->handle(new RemoveCartRuleFromCartCommand($cartId, $cartRuleId)); @@ -295,15 +299,24 @@ public function addProductAction(Request $request, int $cartId): JsonResponse * * @AdminSecurity("is_granted('delete', request.get('_legacy_controller'))") * + * @param Request $request * @param int $cartId - * @param int $productId * * @return JsonResponse */ - public function deleteProductAction(int $cartId, int $productId): JsonResponse + public function deleteProductAction(Request $request, int $cartId): JsonResponse { try { - $this->getCommandBus()->handle(new RemoveProductFromCartCommand($cartId, $productId)); + $productId = $request->request->getInt('productId'); + $attributeId = $request->request->getInt('attributeId'); + $customizationId = $request->request->getInt('customizationId'); + + $this->getCommandBus()->handle(new RemoveProductFromCartCommand( + $cartId, + $productId, + $attributeId, + $customizationId + )); return $this->json($this->getCartInfo($cartId)); } catch (Exception $e) { @@ -345,11 +358,12 @@ private function getErrorMessages(Exception $e) */ private function getAddProductToCartCommand(Request $request, int $cartId): UpdateProductQuantityInCartCommand { - $productId = $request->request->getInt('product_id'); + $productId = $request->request->getInt('productId'); $quantity = $request->request->getInt('quantity'); - $combinationId = $request->request->getInt('combination_id'); + $combinationId = $request->request->getInt('combinationId'); - if ($customizations = $request->request->get('customization')) { + if ($customizations = $request->request->get('customizations')) { + //@todo: return saved customization id and use it in proceeding command $this->getCommandBus()->handle(new AddCustomizationFieldsCommand($cartId, $productId, $customizations)); //@todo: Add updateCustomizationsCommand //check AdminCartsController::jaxProcessUpdateCustomizationFields diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml index c1961e32db998..2e83e9618c47c 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/orders/carts.yml @@ -72,7 +72,7 @@ admin_carts_add_cart_rule: path: /{cartId}/cart-rules methods: [POST] defaults: - _controller: PrestaShopBundle:Admin/Sell/Order/Cart:addCartRuleToCart + _controller: PrestaShopBundle:Admin/Sell/Order/Cart:addCartRule _legacy_controller: AdminCarts options: expose: true @@ -81,7 +81,7 @@ admin_carts_delete_cart_rule: path: /{cartId}/cart-rules/{cartRuleId}/delete methods: [POST] defaults: - _controller: PrestaShopBundle:Admin/Sell/Order/Cart:removeCartRuleFromCart + _controller: PrestaShopBundle:Admin/Sell/Order/Cart:deleteCartRule _legacy_controller: AdminCarts options: expose: true @@ -99,7 +99,7 @@ admin_carts_add_product: productId: \d+ admin_carts_delete_product: - path: /{cartId}/products/{productId}/delete + path: /{cartId}/delete-product methods: [POST] defaults: _controller: PrestaShopBundle:Admin/Sell/Order/Cart:deleteProduct @@ -108,4 +108,3 @@ admin_carts_delete_product: expose: true requirements: cartId: \d+ - productId: \d+ diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/order.yml b/src/PrestaShopBundle/Resources/config/services/adapter/order.yml index 9134fbc1a35a2..59e60a1a67f4c 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/order.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/order.yml @@ -133,13 +133,3 @@ services: tags: - name: tactician.handler command: 'PrestaShop\PrestaShop\Core\Domain\Order\Query\GetOrderPreview' - - prestashop.adapter.order.query_handler.search_products_for_order_creation: - class: PrestaShop\PrestaShop\Adapter\Order\QueryHandler\SearchProductsForOrderCreationHandler - arguments: - - '@prestashop.adapter.legacy.context' - - '@prestashop.core.localization.locale.repository' - - "@=service('prestashop.adapter.legacy.context').getContext().language.getLocale()" - tags: - - name: tactician.handler - command: PrestaShop\PrestaShop\Core\Domain\Order\Query\SearchProductsForOrderCreation diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/product.yml b/src/PrestaShopBundle/Resources/config/services/adapter/product.yml index 15b76b576b8c9..f59b73d149663 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/product.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/product.yml @@ -44,9 +44,9 @@ services: prestashop.adapter.product.query_handler.search_products: class: PrestaShop\PrestaShop\Adapter\Product\QueryHandler\SearchProductsHandler arguments: - - '@prestashop.adapter.legacy.context' - - '@prestashop.core.localization.locale.repository' - - "@=service('prestashop.adapter.legacy.context').getContext().language.getLocale()" + - '@=service("prestashop.adapter.legacy.context").getContext().language.id' + - '@=service("prestashop.adapter.legacy.context").getContext().currency.iso_code' + - '@prestashop.core.localization.locale.context_locale' tags: - name: tactician.handler command: PrestaShop\PrestaShop\Core\Domain\Product\Query\SearchProducts diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig index 50b8bb0152c9d..72ba1700958b3 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/cart.html.twig @@ -167,7 +167,7 @@
- - - - - diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/customer.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/customer.html.twig index fae606496a18c..0e38db761afba 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/customer.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Order/Order/Blocks/Create/customer.html.twig @@ -154,7 +154,7 @@
- -