contacts_events-8.x-1.x-dev/src/Plugin/Commerce/CheckoutFlow/BookingFlow.php

src/Plugin/Commerce/CheckoutFlow/BookingFlow.php
<?php

namespace Drupal\contacts_events\Plugin\Commerce\CheckoutFlow;

use Drupal\commerce\Response\NeedsRedirectException;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\HasPaymentInstructionsInterface;
use Drupal\commerce_price\Price;
use Drupal\contacts_events\OrderStateTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;

/**
 * Provides the default multistep checkout flow.
 *
 * @CommerceCheckoutFlow(
 *   id = "booking_flow",
 *   label = "Booking Flow",
 * )
 */
class BookingFlow extends CheckoutFlowWithPanesBase implements BookingFlowInterface {

  use OrderStateTrait;

  const ROUTE_NAME = 'entity.commerce_order.booking_process';

  /**
   * {@inheritdoc}
   */
  public function setOrder(OrderInterface $order) {
    $this->order = $order;
    return $this;
  }

  /**
   * Gets the order.
   *
   * This is invoked by
   * Drupal\commerce_order\Form\OrderItemInlineForm::buildOrderItem.
   */
  public function getEntity() {
    return $this->order;
  }

  /**
   * {@inheritdoc}
   */
  public function getSteps() {
    return [
      'summary' => [
        'label' => $this->t('Dashboard'),
        'has_sidebar' => FALSE,
      ],
      'tickets' => [
        'label' => $this->t('Tickets'),
        'previous_label' => $this->t('Back to tickets'),
        'next_label' => $this->t('Continue to add tickets'),
        'has_sidebar' => FALSE,
      ],
      'accommodation' => [
        'label' => $this->t('Accommodation'),
        'previous_label' => $this->t('Back to accommodation'),
        'next_label' => $this->t('Continue to choose your accommodation'),
        'has_sidebar' => FALSE,
      ],
      'address' => [
        'label' => $this->t('Delivery & Billing Address'),
        'previous_label' => $this->t('Back to your details'),
        'next_label' => $this->t('Continue to your details'),
        'has_sidebar' => FALSE,
      ],
      'terms' => [
        'label' => $this->t('Terms and Conditions'),
        'next_label' => $this->t('Review and pay'),
        'has_sidebar' => FALSE,
        'hidden' => TRUE,
      ],
      'review' => [
        'display_label' => $this->t('Payment'),
        'label' => $this->t('Payment (Review)'),
        'next_label' => $this->t('Review and pay'),
        'has_sidebar' => FALSE,
      ],
      'payment' => [
        'display_label' => $this->t('Payment'),
        'label' => $this->t('Payment (Process)'),
        'next_label' => $this->t('Make payment'),
        'has_sidebar' => FALSE,
        'hidden' => TRUE,
      ],
      'complete' => [
        'label' => $this->t('Complete'),
        'has_sidebar' => FALSE,
        'hidden' => TRUE,
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getInitialStep(): string {
    $steps = array_keys($this->getVisibleSteps());
    $step = array_shift($steps);
    if ($step == 'summary') {
      $step = array_shift($steps);
    }
    return $step;
  }

  /**
   * {@inheritdoc}
   */
  public function getContinueUrl(): Url {
    $url = $this->order->toUrl('booking_process');
    if ($this->order->getState()->value == 'draft') {
      $url->setRouteParameter('step', $this->order->get('checkout_step')->value);
    }
    return $url;
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $step_id = NULL) {
    // If complete, redirect to the dashboard with a message.
    if ($step_id == 'complete') {
      $this->messenger()->addStatus($this->t('Thank you for confirming your booking. You will receive an email confirmation shortly with the details of your booking.'));
      $payment_order_updater = \Drupal::service('commerce_payment.order_updater');
      $payment_order_updater->requestUpdate($this->order);

      // If there are any outstanding payments, show any relevant payment
      // instructions. This is necessary as we don't show the normal completion
      // page.
      $this->showPaymentInstructions($this->order);

      $this->redirectToStep('summary');
    }
    return parent::buildForm($form, $form_state, $step_id);
  }

  /**
   * {@inheritdoc}
   */
  public function getNextStepId($step_id) {
    // Redirect to the summary if in doubt.
    return parent::getNextStepId($step_id) ?? 'summary';
  }

  /**
   * {@inheritdoc}
   */
  public function redirectToStep($step_id) {
    // Only update the order if we are progressing to a later state.
    $this->order->set('checkout_step', $step_id);
    $this->onStepChange($step_id);
    $this->order->save();

    // Use our route rather than the default commerce_checkout.form.
    throw new NeedsRedirectException(Url::fromRoute(self::ROUTE_NAME, [
      'commerce_order' => $this->order->id(),
      'step' => $step_id,
    ])->toString());
  }

  /**
   * {@inheritdoc}
   */
  protected function onStepChange($step_id) {
    // Prevent changing step to a previous step or storing the complete step.
    $current_step = $this->entityTypeManager
      ->getStorage('commerce_order')
      ->loadUnchanged($this->order->id())
      ->get('checkout_step')->value;

    // Get the order of the steps.
    $steps = array_keys($this->getSteps());

    $current_pos = array_search($current_step, $steps);
    $new_pos = array_search($step_id, $steps);
    $payment_pos = array_search('payment', $steps);
    if ($current_step == 'complete' || $new_pos < $current_pos) {
      // If the old position was earlier than the payment step, preserve it.
      // Otherwise set it to review.
      $this->order->set('checkout_step', $current_pos < $payment_pos ? $current_step : 'review');
    }

    // Lock the order while on the 'payment' checkout step. Unlock elsewhere.
    if ($step_id == 'payment') {
      $this->order->lock();
    }
    elseif ($step_id != 'payment') {
      $this->order->unlock();
    }
    // Place the order or move to confirmed paid in full.
    if ($step_id == 'complete') {
      $transition_id = 'place';
      $balance = $this->order->getBalance();

      // If balance is non-positive trigger both place and fully paid events.
      if (!$balance || $balance->isZero() || $balance->isNegative()) {
        $transition_id = 'confirmed_paid_in_full';
      }

      $state = $this->order->getState();
      $this->applyTransitionIfAllowed($state, $transition_id);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function needsPayment(bool $include_pending = FALSE) {
    $balance = $this->order->getBalance();

    if ($include_pending) {
      foreach ($this->getPendingAmounts() as $gateway_id => $amounts) {
        foreach ($amounts as $amount) {
          $balance->subtract($amount);
        }
      }
    }

    return $balance && !$balance->isZero();
  }

  /**
   * {@inheritdoc}
   */
  public function getPendingAmounts(): array {
    $query = $this->entityTypeManager
      ->getStorage('commerce_payment')
      ->getAggregateQuery();
    $query
      ->condition('order_id', $this->order->id())
      ->condition('state', 'pending');
    $query
      ->aggregate('amount.number', 'SUM')
      ->groupBy('amount.currency_code')
      ->groupBy('payment_gateway');
    $pending_amounts = $query->execute();
    $pending = [];
    foreach ($pending_amounts as $amount) {
      $pending[$amount['payment_gateway']][] = new Price($amount['amountnumber_sum'], $amount['amount__currency_code']);
    }
    return $pending;
  }

  /**
   * {@inheritdoc}
   */
  public function needsConfirmation() {
    $transitions = $this->order->getState()->getTransitions();
    return isset($transitions['place']);
  }

  /**
   * {@inheritdoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    // The summary has no actions.
    if ($form['#step_id'] == 'summary') {
      return [];
    }

    $steps = $this->getVisibleSteps();
    $next_step_id = $this->getNextStepId($form['#step_id']);
    $previous_step_id = $this->getPreviousStepId($form['#step_id']);
    $has_next_step = $next_step_id && isset($steps[$next_step_id]['next_label']);
    $has_previous_step = $previous_step_id && isset($steps[$previous_step_id]['previous_label']);

    $actions = [
      '#type' => 'actions',
      '#access' => $has_next_step,
    ];

    if ($has_next_step) {
      if ($next_step_id == 'payment' && !$this->needsPayment() && !$this->needsConfirmation()) {
        $parameters = [
          'commerce_order' => $this->order->id(),
          'step' => 'summary',
        ];
        $options = [
          'attributes' => ['class' => ['btn', 'btn-primary']],
        ];
        $actions['next'] = Link::createFromRoute($this->t('Go to summary'), self::ROUTE_NAME, $parameters, $options)->toRenderable();
      }
      else {
        $label = $steps[$next_step_id]['next_label'];
        if ($next_step_id == 'payment' && !$this->needsPayment()) {
          $label = $this->t('Confirm my booking');
        }
        $actions['next'] = [
          '#type' => 'submit',
          '#value' => $label,
          '#button_type' => 'primary',
          '#submit' => ['::submitForm'],
        ];
      }
    }

    if ($has_previous_step) {
      $label = $steps[$previous_step_id]['previous_label'];
      $parameters = [
        'commerce_order' => $this->order->id(),
        'step' => $previous_step_id,
      ];
      $options = [
        'attributes' => ['class' => ['btn', 'btn-light']],
      ];
      $actions['previous'] = Link::createFromRoute($label, self::ROUTE_NAME, $parameters, $options)->toRenderable();
      // Ensure some spacing.
      $actions['previous']['#prefix'] = ' ';
    }

    return $actions;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);
    $redirect = $form_state->getRedirect();
    if (!isset($redirect) || ($redirect instanceof Url && $redirect->getRouteName() == 'commerce_checkout.form')) {
      $form_state->setRedirect(self::ROUTE_NAME, [
        'commerce_order' => $this->order->id(),
        'step' => $this->getNextStepId($form['#step_id']),
      ]);
    }
  }

  /**
   * Show payment instructions for any pending payments.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   The order to show messages for.
   */
  protected function showPaymentInstructions(OrderInterface $order) {
    /** @var \Drupal\commerce_payment\Entity\PaymentInterface[] $payments */
    $payments = $this->entityTypeManager
      ->getStorage('commerce_payment')
      ->loadByProperties([
        'order_id' => $order->id(),
        'state' => 'pending',
      ]);
    /** @var \Drupal\Core\Render\RendererInterface $renderer */
    $renderer = \Drupal::service('renderer');
    foreach ($payments as $payment) {
      $payment_gateway_plugin = $payment->getPaymentGateway()->getPlugin();
      if ($payment_gateway_plugin instanceof HasPaymentInstructionsInterface) {
        $instructions = $payment_gateway_plugin->buildPaymentInstructions($payment);
        if ($instructions) {
          $message = $renderer->renderPlain($instructions);
          $this->messenger()->addStatus($message);
        }
      }
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc