contacts_events-8.x-1.x-dev/src/Form/TransferForm.php

src/Form/TransferForm.php
<?php

namespace Drupal\contacts_events\Form;

use CommerceGuys\Intl\Formatter\CurrencyFormatterInterface;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_payment\Entity\PaymentGateway;
use Drupal\commerce_price\Price;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\contacts_events\Entity\EventClass;
use Drupal\contacts_events\Entity\TicketInterface;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Entity form variant for transferring a ticket.
 */
class TransferForm extends ContentEntityForm {

  /**
   * The ticket to be transferred.
   *
   * @var \Drupal\contacts_events\Entity\TicketInterface
   */
  protected $entity;

  /**
   * The booking to transfer a ticket to.
   *
   * @var \Drupal\commerce_order\Entity\OrderInterface
   */
  protected $targetBooking;

  /**
   * The storage for Events Classes.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $classStorage;

  /**
   * The storage for Commerce Bookings.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $orderStorage;

  /**
   * The storage for Commerce Payments.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $paymentStorage;

  /**
   * The currency formatter service.
   *
   * @var \CommerceGuys\Intl\Formatter\CurrencyFormatterInterface
   */
  protected $currencyFormatter;

  /**
   * TransferForm constructor.
   *
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   The entity repository service.
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface|null $entity_type_bundle_info
   *   The entity type bundle service.
   * @param \Drupal\Component\Datetime\TimeInterface|null $time
   *   The time service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface|null $entity_type_manager
   *   The entity type manager service.
   * @param \CommerceGuys\Intl\Formatter\CurrencyFormatterInterface|null $currency_formatter
   *   The currency formatter service.
   */
  public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, EntityTypeManagerInterface $entity_type_manager = NULL, CurrencyFormatterInterface $currency_formatter = NULL) {
    parent::__construct($entity_repository, $entity_type_bundle_info, $time);

    $entity_type_manager = $entity_type_manager ?: \Drupal::service('entity_type.manager');
    $this->classStorage = $entity_type_manager->getStorage('contacts_events_class');
    $this->orderStorage = $entity_type_manager->getStorage('commerce_order');
    $this->paymentStorage = $entity_type_manager->getStorage('commerce_payment');

    $this->currencyFormatter = $currency_formatter ?: \Drupal::service('commerce_price.currency_formatter');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity.repository'),
      $container->get('entity_type.bundle.info'),
      $container->get('datetime.time'),
      $container->get('entity_type.manager'),
      $container->get('commerce_price.currency_formatter')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    // Show the booking step if we don't have a target booking.
    if (!$this->targetBooking) {
      $form = $this->bookingForm($form, $form_state);
    }
    // Otherwise show the confirmation step.
    else {
      $form = $this->confirmationForm($form, $form_state);
    }

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);

    // Only validate the booking ID if step one is being submitted.
    if (!$this->targetBooking) {
      $event_code = $this->entity->getEvent()->get('code')->value;

      $booking_id = $form_state->getValue('booking');
      // Remove the 0-padding to get the Order ID.
      $booking_id = ltrim($booking_id, '0');
      $booking = $this->orderStorage->load($booking_id);

      // Validate that the order exists.
      if (!$booking) {
        $form_state->setError($form['booking'], $this->t('Booking %booking does not exist.', ['%booking' => $event_code . '-' . $booking_id]));
      }
      // And is a booking.
      elseif ($booking->bundle() != 'contacts_booking') {
        $form_state->setError($form['booking'], $this->t('Order %booking is not a Booking.', ['%booking' => $booking_id]));
      }
      // And that the booking is for the same event.
      elseif ($booking->get('event')->target_id != $this->entity->getOrderItem()->getOrder()->get('event')->target_id) {
        $form_state->setError($form['booking'], $this->t('Booking %booking is for a different event to this booking.', ['%booking' => $booking->get('event')->entity->get('code')->value . '-' . $booking->id()]));
      }
      // And that the booking is not the source booking.
      elseif ($booking->id() == $this->entity->getOrderItem()->getOrderId()) {
        $form_state->setError($form['booking'], $this->t('You cannot transfer tickets to the same booking.'));
      }

      // Check whether we can calculate the amount paid on an Order Item.
      if ($form_state->getValue('transfer_payment') && is_null($this->getPaidForItem($this->entity->getOrderItem()))) {
        $form_state->setError($form['transfer_payment'], $this->t('Payments cannot be automatically transferred for this Ticket.'));
      }

      // If there are no errors set the target booking and rebuild the form.
      if (empty($form_state->getErrors())) {
        $this->targetBooking = $booking;
        $form_state->setRebuild(TRUE);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Get the amount paid before any changes are made.
    $paid = $this->getPaidForItem($this->entity->getOrderItem());

    // Move order item to target booking.
    $order_item = $this->entity->getOrderItem();
    $this->targetBooking->addItem($order_item);

    // Remove order item from original booking.
    $original_booking = $order_item->getOrder();
    $original_booking->removeItem($order_item);

    // Update the Order ID on the Order Item.
    $order_item->set('order_id', $this->targetBooking->id());
    $order_item->save();

    // Append a note to booking notes on both orders.
    $replacements = [
      '%time' => $this->time->getRequestTime(),
      '%ticket_holder' => $this->entity->getName(),
      '%original_booking' => $original_booking->label(),
      '%target_booking' => $this->targetBooking->label(),
      '%user' => $this->currentUser()->getDisplayName(),
      '%uid' => $this->currentUser()->id(),
    ];
    $transfer_message = $this->t("<br/>%time: Ticket for %ticket_holder transferred from %original_booking to %target_booking by %user [%uid].", $replacements);

    $target_notes = $this->targetBooking->get('back_end_notes')->value;
    $target_notes .= $transfer_message;
    $this->targetBooking->set('back_end_notes', $target_notes);
    $this->targetBooking->save();

    $original_notes = $original_booking->get('back_end_notes')->value;
    $original_notes .= $transfer_message;
    $original_booking->set('back_end_notes', $original_notes);
    $original_booking->save();

    // Show user a confirmation message.
    $replacements = [
      '%ticket_holder' => $this->entity->getName(),
      '%old_booking' => $original_booking->label(),
      '%new_booking' => $this->targetBooking->label(),
    ];
    $this->messenger()->addMessage($this->t('Ticket for %ticket_holder transferred from %old_booking to %new_booking', $replacements));

    // If transfer payments is selected, create payments to transfer the amount
    // paid on the order item.
    if ($form_state->getValue('transfer_payment')) {
      // Only transfer payments if we can calculate the amount paid for the
      // Order Item and the transfer payment gateway exists.
      $transfer_payment_gateway = PaymentGateway::load('booking_transfer');
      if ($paid && $paid->isPositive() && $transfer_payment_gateway) {
        // Add a Booking Transfer payment to target booking for paid value.
        /** @var \Drupal\commerce_payment\Entity\PaymentInterface $target_booking_payment */
        $target_booking_payment = $this->paymentStorage->create([
          'state' => 'completed',
          'amount' => $paid,
          'payment_gateway' => 'booking_transfer',
          'order_id' => $this->targetBooking->id(),
          'completed' => $this->time->getRequestTime(),
          'order_item_tracking' => [
            $order_item->id() => $paid->toArray() + ['target_id' => $order_item->id()],
          ],
        ]);
        $target_booking_payment->save();

        // Add Booking Transfer payment to original booking to negate the
        // amount that has been paid on that order.
        $paid = $paid->multiply('-1');
        /** @var \Drupal\commerce_payment\Entity\PaymentInterface $source_booking_payment */
        $source_booking_payment = $this->paymentStorage->create([
          'state' => 'completed',
          'amount' => $paid,
          'payment_gateway' => 'booking_transfer',
          'order_id' => $original_booking,
          'completed' => $this->time->getRequestTime(),
          'order_item_tracking' => [
            $order_item->id() => $paid->toArray() + ['target_id' => $order_item->id()],
          ],

        ]);
        $source_booking_payment->save();

        // Show user a message about the transferred payment after un-negating.
        $paid = $paid->multiply('-1');
        $replacements = [
          '%payment_value' => $this->currencyFormatter->format($paid->getNumber(), $paid->getCurrencyCode()),
          '%old_booking' => $original_booking->label(),
          '%new_booking' => $this->targetBooking->label(),
        ];
        $this->messenger()->addMessage($this->t('Payment of for %payment_value transferred from %old_booking to %new_booking', $replacements));
      }
    }

    // Redirect to the target booking to see the newly transferred ticket.
    $form_state->setRedirect('entity.commerce_order.booking_admin_tickets', ['commerce_order' => $this->targetBooking->id()]);
  }

  /**
   * {@inheritdoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    $actions['continue'] = [
      '#type' => 'submit',
      '#value' => $this->targetBooking ? $this->t('Transfer booking') : $this->t('Continue'),
      '#submit' => ['::submitForm'],
    ];
    return $actions;
  }

  /**
   * Create the booking step form for transferring a ticket.
   *
   * @param array $form
   *   The current form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @return array
   *   A Form API form for booking step of the ticket transfer.
   */
  protected function bookingForm(array $form, FormStateInterface $form_state) {
    $form['ticket'] = $this->ticketDetails();

    $form['booking'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Booking to transfer ticket to:'),
      'booking' => [
        '#type' => 'textfield',
        '#title' => $this->t('Booking to transfer to'),
        '#field_prefix' => $this->entity->getEvent()->get('code')->value . '-',
      ],
    ];

    return $form;
  }

  /**
   * Create the confirmation step form for transferring a ticket.
   *
   * @param array $form
   *   The current form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current form state.
   *
   * @return array
   *   A Form API form for the confirmation step of the ticket transfer.
   */
  protected function confirmationForm(array $form, FormStateInterface $form_state) {
    $form['ticket'] = $this->ticketDetails();
    $form['booking'] = $this->bookingDetails();

    $paid = $this->getPaidForItem($this->entity->getOrderItem());
    $transfer_payment_gateway = PaymentGateway::load('booking_transfer');

    // Only show the option to transfer payments if we can calculate the amount
    // paid on the Order Item and if the transfer payment gateway exists.
    if ($paid && !$paid->isZero() && $transfer_payment_gateway) {
      $form['transfer_payment'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Transfer payment with ticket'),
        '#description' => $this->t('If you do not transfer existing payments then you will need to manually fix any discrepancies between order totals and payments.'),
      ];
    }

    return $form;
  }

  /**
   * Create a render array of ticket details.
   *
   * @return array
   *   A renderable array of ticket details.
   */
  protected function ticketDetails() {
    $details = [
      '#type' => 'fieldset',
      '#title' => $this->t('Ticket to be transferred:'),
    ];

    $details['name'] = $this->renderWithLabel($this->t('Name'), $this->entity->getName());
    $details['class'] = $this->renderWithLabel($this->t('Ticket class'), $this->getClassLabel($this->entity));

    if ($price = $this->entity->getPrice()) {
      $details['price'] = $this->renderWithLabel($this->t('Price'), $this->currencyFormatter->format($price->getNumber(), $price->getCurrencyCode()));
    }

    if ($paid = $this->getPaidForItem($this->entity->getOrderItem())) {
      $details['paid'] = $this->renderWithLabel($this->t('Paid'), $this->currencyFormatter->format($paid->getNumber(), $paid->getCurrencyCode()));
    }

    if ($balance = $this->getBalanceForItem($this->entity->getOrderItem())) {
      $details['balance'] = $this->renderWithLabel($this->t('Balance'), $this->currencyFormatter->format($balance->getNumber(), $balance->getCurrencyCode()));
    }

    return $details;
  }

  /**
   * Create a render array of booking details.
   *
   * @return array
   *   A renderable array of booking details.
   */
  protected function bookingDetails() {
    $details = [
      '#type' => 'fieldset',
      '#title' => $this->t('Booking to transfer ticket to:'),
    ];

    $details['reference'] = $this->renderWithLabel($this->t('Booking reference'), $this->targetBooking->label());

    $customer_link = $this->targetBooking->getCustomer()->toLink(NULL, 'canonical', ['attributes' => ['target' => '_blank']])->toString();
    $details['manager'] = $this->renderWithLabel($this->t('Booking manager'), $customer_link);

    return $details;
  }

  /**
   * Get the event class label for a ticket.
   *
   * @param \Drupal\contacts_events\Entity\TicketInterface $ticket
   *   The ticket to retrieve for.
   *
   * @return string
   *   The class label.
   */
  protected function getClassLabel(TicketInterface $ticket) {
    if (!isset($this->classLabels)) {
      $this->classLabels = array_map(function (EventClass $class) {
        return $class->label();
      }, $this->classStorage->loadMultiple());
    }
    $class = $ticket->getMappedPrice()['class'];
    return $this->classLabels[$class] ?? '-';
  }

  /**
   * Render a value with a label.
   *
   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $label
   *   The translatable label.
   * @param string $value
   *   The value to be displayed.
   *
   * @return array
   *   A renderable array of label and value.
   */
  protected function renderWithLabel(TranslatableMarkup $label, $value) {
    return [
      '#markup' => '<div>' . $label . ': ' . $value . '</div>',
    ];
  }

  /**
   * Get a Price for the amount paid on an Order Item.
   *
   * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
   *   The Order Item to get the paid amount for.
   *
   * @return \Drupal\commerce_price\Price|null
   *   A Price with the paid value. NULL if not supported.
   */
  protected function getPaidForItem(OrderItemInterface $order_item) {
    if ($order_item->hasField('paid') && !$order_item->get('paid')->isEmpty()) {
      $paid_value = $order_item->get('paid')->first()->getValue();
      return new Price($paid_value['number'], $paid_value['currency_code']);
    }
    return NULL;
  }

  /**
   * Get a Price for the balance of an Order Item.
   *
   * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
   *   The Order Item to get the balance for.
   *
   * @return \Drupal\commerce_price\Price|null
   *   A Price with the balance value. NULL if not supported.
   */
  protected function getBalanceForItem(OrderItemInterface $order_item) {
    if ($order_item->hasField('balance') && !$order_item->get('balance')->isEmpty()) {
      $balance_value = $order_item->get('balance')->first()->getValue();
      return new Price($balance_value['number'], $balance_value['currency_code']);
    }

    return NULL;
  }

}

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

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