diff --git a/modules/order/src/Plugin/views/area/OrderTotal.php b/modules/order/src/Plugin/views/area/OrderTotal.php index 0fbf2ae1..e63909a5 100644 --- a/modules/order/src/Plugin/views/area/OrderTotal.php +++ b/modules/order/src/Plugin/views/area/OrderTotal.php @@ -79,12 +79,17 @@ class OrderTotal extends AreaPluginBase { if (!in_array($argument->getField(), ['commerce_order.order_id', 'commerce_order_item.order_id'])) { continue; } + /** @var \Drupal\commerce_order\Entity\OrderInterface $order */ if ($order = $this->orderStorage->load($argument->getValue())) { - return $order->get('total_price')->view(['label' => 'hidden', 'type' => 'commerce_order_total_summary']); + $order_total = $order->get('total_price')->view([ + 'label' => 'hidden', + 'type' => 'commerce_order_total_summary', + 'weight' => $this->position, + ]); + return $order_total; } } } - return []; } diff --git a/modules/order/templates/commerce-order-total-summary.html.twig b/modules/order/templates/commerce-order-total-summary.html.twig index 2be3907a..9a8b8f82 100644 --- a/modules/order/templates/commerce-order-total-summary.html.twig +++ b/modules/order/templates/commerce-order-total-summary.html.twig @@ -18,7 +18,7 @@ */ #} {{ attach_library('commerce_order/total_summary') }} - +
{{ 'Subtotal'|t }} {{ totals.subtotal|commerce_price_format }}
diff --git a/modules/promotion/commerce_promotion.libraries.yml b/modules/promotion/commerce_promotion.libraries.yml index 3ff5b7d6..872850aa 100644 --- a/modules/promotion/commerce_promotion.libraries.yml +++ b/modules/promotion/commerce_promotion.libraries.yml @@ -3,3 +3,9 @@ form: css: theme: css/promotion.form.css: {} + +cart_redemption_form: + version: VERSION + css: + theme: + css/promotion.cart_redemption_form.css: {} diff --git a/modules/promotion/commerce_promotion.views.inc b/modules/promotion/commerce_promotion.views.inc new file mode 100644 index 00000000..07998824 --- /dev/null +++ b/modules/promotion/commerce_promotion.views.inc @@ -0,0 +1,20 @@ + t('Coupon redemption'), + 'help' => t('Displays a coupon redemption pane, requires an Order ID argument.'), + 'area' => [ + 'id' => 'commerce_coupon_redemption', + ], + ]; + return $data; +} diff --git a/modules/promotion/config/schema/commerce_promotion.schema.yml b/modules/promotion/config/schema/commerce_promotion.schema.yml index d8565a59..3f451785 100644 --- a/modules/promotion/config/schema/commerce_promotion.schema.yml +++ b/modules/promotion/config/schema/commerce_promotion.schema.yml @@ -4,3 +4,11 @@ commerce_checkout.commerce_checkout_pane.coupon_redemption: allow_multiple: type: boolean label: 'Allow multiple coupons' + +views.area.commerce_coupon_redemption: + type: views_area + label: 'Coupon redemption' + mapping: + allow_multiple: + type: boolean + label: 'Allow multiple coupons to be redeemed' diff --git a/modules/promotion/css/promotion.cart_redemption_form.css b/modules/promotion/css/promotion.cart_redemption_form.css new file mode 100644 index 00000000..c1b817d0 --- /dev/null +++ b/modules/promotion/css/promotion.cart_redemption_form.css @@ -0,0 +1,11 @@ +#coupon_redemption-ajax-wrapper { + float: right; + width: 350px; +} +#coupon_redemption-ajax-wrapper .form-item-coupon-redemption-code { + display: inline-block; + width: 200px; +} +.field--name-total-price { + clear: both; +} diff --git a/modules/promotion/src/Plugin/views/area/CouponRedemption.php b/modules/promotion/src/Plugin/views/area/CouponRedemption.php new file mode 100644 index 00000000..711426e3 --- /dev/null +++ b/modules/promotion/src/Plugin/views/area/CouponRedemption.php @@ -0,0 +1,149 @@ +orderStorage = $entity_type_manager->getStorage('commerce_order'); + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager')); + } + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + $options['allow_multiple'] = ['default' => FALSE]; + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['empty']['#description'] = $this->t("Even if selected, this area handler will never render if a valid order cannot be found in the View's arguments."); + + $form['allow_multiple'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Allow multiple coupons to be redeemed'), + '#default_value' => $this->options['allow_multiple'], + ]; + } + + /** + * {@inheritdoc} + */ + public function render($empty = FALSE) { + } + + /** + * Form constructor for the views form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function viewsForm(array &$form, FormStateInterface $form_state) { + foreach ($this->view->argument as $name => $argument) { + // First look for an order_id argument. + if (!$argument instanceof NumericArgument) { + continue; + } + if ($argument->getField() !== 'commerce_order.order_id') { + continue; + } + if ($order = $this->orderStorage->load($argument->getValue())) { + $form['coupon_redemption'] = [ + '#attached' => [ + 'library' => ['commerce_promotion/cart_redemption_form'], + ], + '#type' => 'commerce_coupon_redemption_form', + '#order_id' => $order->id(), + '#cardinality' => $this->options['allow_multiple'] ? NULL : 1, + '#element_ajax' => [ + [get_class($this), 'ajaxRefreshSummary'], + ], + '#weight' => $this->position, + ]; + } + } + } + + /** + * Method for the condition when view is empty. + * + * @param bool $empty + * Variable for view content exists or not. + * + * @return bool + * Returns boolean based on $empty. + */ + public function viewsFormEmpty($empty = FALSE) { + if (!$empty || !empty($this->options['empty'])) { + return $this->options['empty']; + } + return $empty; + } + + /** + * Ajax callback for refreshing the order summary. + */ + public static function ajaxRefreshSummary(array $form, FormStateInterface $form_state) { + $order_id = $form['output'][0]['#rows'][0]->order_id; + $order = $form['output'][0]['#rows'][0]->_entity->load($order_id); + $order_total = $order->get('total_price')->view(['label' => 'hidden', 'type' => 'commerce_order_total_summary']); + if (isset($order_total)) { + return new InsertCommand('[data-drupal-selector="order-total-summary"]', $order_total); + } + return NULL; + } + +} diff --git a/modules/promotion/tests/modules/commerce_promotion_test/commerce_promotion_test.module b/modules/promotion/tests/modules/commerce_promotion_test/commerce_promotion_test.module index 6eea21be..07b349c2 100644 --- a/modules/promotion/tests/modules/commerce_promotion_test/commerce_promotion_test.module +++ b/modules/promotion/tests/modules/commerce_promotion_test/commerce_promotion_test.module @@ -11,13 +11,5 @@ use Drupal\Core\Form\FormStateInterface; * Implements hook_form_BASE_FORM_ID_alter(). */ function commerce_promotion_test_form_views_form_commerce_cart_form_default_alter(&$form, FormStateInterface $form_state, $form_id) { - // We know that view forms are build on the base ID plus arguments. - $order_id = substr($form_id, strlen('views_form_commerce_cart_form_default_')); - - $form['coupons'] = [ - '#type' => 'commerce_coupon_redemption_form', - '#description' => 'Enter your coupon code to redeem a promotion.', - '#order_id' => $order_id, - '#cardinality' => Drupal::request()->query->get('coupon_cardinality'), - ]; + // After #2872455 this is no longer used to add the coupon redemption form. } diff --git a/modules/promotion/tests/src/FunctionalJavascript/CouponRedemptionElementTest.php b/modules/promotion/tests/src/FunctionalJavascript/CartCouponRedemptionElementTest.php similarity index 80% rename from modules/promotion/tests/src/FunctionalJavascript/CouponRedemptionElementTest.php rename to modules/promotion/tests/src/FunctionalJavascript/CartCouponRedemptionElementTest.php index 6b58f264..c510e9d0 100644 --- a/modules/promotion/tests/src/FunctionalJavascript/CouponRedemptionElementTest.php +++ b/modules/promotion/tests/src/FunctionalJavascript/CartCouponRedemptionElementTest.php @@ -8,13 +8,14 @@ use Drupal\commerce_price\Price; use Drupal\Core\Url; use Drupal\Tests\commerce\Functional\CommerceBrowserTestBase; use Drupal\Tests\commerce\FunctionalJavascript\JavascriptTestTrait; +use Drupal\views\Entity\View; /** * Tests the coupon redemption form element. * * @group commerce */ -class CouponRedemptionElementTest extends CommerceBrowserTestBase { +class CartCouponRedemptionElementTest extends CommerceBrowserTestBase { use JavascriptTestTrait; @@ -105,15 +106,29 @@ class CouponRedemptionElementTest extends CommerceBrowserTestBase { /** * Tests redeeming a single coupon. - * - * @see commerce_promotion_test_form_views_form_commerce_cart_form_default_alter */ public function testSingleCouponRedemption() { + // Update the default cart form view to use the commerce_coupon_redemption + // area plugin for coupon redemption. + /** @var \Drupal\views\Entity\View $view */ + $view = View::load('commerce_cart_form'); + $display = &$view->getDisplay('default'); + $display['display_options']['footer']['commerce_coupon_redemption'] = [ + 'id' => 'commerce_coupon_redemption', + 'table' => 'views', + 'field' => 'commerce_coupon_redemption', + 'relationship' => 'none', + 'group_type' => 'group', + 'admin_label' => '', + 'empty' => FALSE, + 'plugin_id' => 'commerce_coupon_redemption', + ]; + $view->save(); + $coupons = $this->promotion->getCoupons(); $coupon = reset($coupons); - $this->drupalGet(Url::fromRoute('commerce_cart.page', [], ['query' => ['coupon_cardinality' => 1]])); - $this->assertSession()->pageTextContains('Enter your coupon code to redeem a promotion.'); + $this->drupalGet(Url::fromRoute('commerce_cart.page')); // Empty coupon. $this->getSession()->getPage()->pressButton('Apply coupon'); $this->waitForAjaxToFinish(); @@ -143,10 +158,26 @@ class CouponRedemptionElementTest extends CommerceBrowserTestBase { /** * Tests redeeming coupon on the cart form, with multiple coupons allowed. - * - * @see commerce_promotion_test_form_views_form_commerce_cart_form_default_alter */ public function testMultipleCouponRedemption() { + // Update the default cart form view to use the commerce_coupon_redemption + // area plugin for coupon redemption. + /** @var \Drupal\views\Entity\View $view */ + $view = View::load('commerce_cart_form'); + $display = &$view->getDisplay('default'); + $display['display_options']['footer']['commerce_coupon_redemption'] = [ + 'id' => 'commerce_coupon_redemption', + 'table' => 'views', + 'field' => 'commerce_coupon_redemption', + 'relationship' => 'none', + 'group_type' => 'group', + 'admin_label' => '', + 'empty' => FALSE, + 'plugin_id' => 'commerce_coupon_redemption', + 'allow_multiple' => TRUE, + ]; + $view->save(); + $coupons = $this->promotion->getCoupons(); $first_coupon = reset($coupons); $second_coupon = end($coupons);