contacts_events-8.x-1.x-dev/src/Controller/EventFinanceController.php

src/Controller/EventFinanceController.php
<?php

namespace Drupal\contacts_events\Controller;

use CommerceGuys\Intl\Formatter\CurrencyFormatterInterface;
use Drupal\commerce_price\Price;
use Drupal\contacts_events\Entity\EventInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Returns responses for Contacts Events routes.
 *
 * @todo Handle multiple currencies?
 */
class EventFinanceController extends ControllerBase {

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

  /**
   * The store's currency code.
   *
   * @var string|null|false
   */
  protected $storeCurrency = FALSE;

  /**
   * The controller constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \CommerceGuys\Intl\Formatter\CurrencyFormatterInterface $currency_formatter
   *   The currency formatter.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, CurrencyFormatterInterface $currency_formatter, MessengerInterface $messenger, ConfigFactoryInterface $config_factory) {
    $this->entityTypeManager = $entity_type_manager;
    $this->currencyFormatter = $currency_formatter;
    $this->messenger = $messenger;
    $this->configFactory = $config_factory;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager'),
      $container->get('commerce_price.currency_formatter'),
      $container->get('messenger'),
      $container->get('config.factory')
    );
  }

  /**
   * Get the default currency for the store.
   *
   * @return string|null
   *   The default currency code or NULL if there isn't one.
   */
  protected function getCurrencyCode(): ?string {
    // Don't work it out more than once.
    if ($this->storeCurrency !== FALSE) {
      return $this->storeCurrency;
    }

    $this->storeCurrency = NULL;

    $store_id = $this->configFactory
      ->get('contacts_events.booking_settings')
      ->get('store_id');
    if (!$store_id) {
      $this->messenger->addError($this->t('The store is not configured for bookings.'));
      return NULL;
    }

    /** @var \Drupal\commerce_store\Entity\StoreInterface|null $store */
    $store = $this->entityTypeManager
      ->getStorage('commerce_store')
      ->load($store_id);
    if (!$store) {
      $this->messenger->addError($this->t('The store %id does not exist.', [
        '%id' => $store_id,
      ]));
      return NULL;
    }

    // Get the store currency and return.
    return $this->storeCurrency = $store->getDefaultCurrencyCode();
  }

  /**
   * Get the event specific page title.
   *
   * @param \Drupal\contacts_events\Entity\EventInterface $contacts_event
   *   The event entity.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
   *   The page title.
   */
  public function title(EventInterface $contacts_event) {
    return new TranslatableMarkup('Finance for @event', [
      '@event' => $contacts_event->label(),
    ]);
  }

  /**
   * Build the event finance page.
   *
   * @param \Drupal\contacts_events\Entity\EventInterface $contacts_event
   *   The event entity.
   *
   * @return array
   *   The page render array.
   */
  public function build(EventInterface $contacts_event) {
    // Ensure there is a currency code we can use.
    if (!$this->getCurrencyCode()) {
      return [];
    }

    // Ensure caches are cleared appropriately.
    $build['#cache']['tags'] = [
      'commerce_order_list',
      'commerce_order_item_list',
    ];

    // Get the totals for the event.
    $total = $this->getOrderTotal($contacts_event);
    if ($total === NULL) {
      $this->messenger->addWarning($this->t('There are no bookings for this event.'));
      return $build;
    }

    $build['confirmed'] = [
      '#type' => 'item',
      '#title' => $this->t('Confirmed booking value'),
      '#price' => $total,
      '#description' => $this->t('All confirmed bookings and items.'),
      '#description_display' => 'after',
    ];

    // Adjust for pending bookings.
    $build['pending'] = [
      '#type' => 'item',
      '#title' => $this->t('Pending booking value'),
      '#price' => $this->getOrderTotal($contacts_event, ['draft']),
      '#description' => $this->t('Un-confirmed bookings and items.'),
      '#description_display' => 'after',
    ];

    $build['total'] = [
      '#type' => 'item',
      '#title' => $this->t('Potential booking value'),
      '#price' => $total,
      '#description' => $this->t('Including both confirmed and un-confirmed bookings and items.'),
      '#description_display' => 'after',
    ];

    $build['confirmed']['#price'] = $build['total']['#price']->subtract($build['pending']['#price']);

    // Adjust for pending items.
    $adjustment = $this->getOrderItemTotal($contacts_event, ['pending']);
    $build['pending']['#price'] = $build['pending']['#price']->add($adjustment);
    $build['confirmed']['#price'] = $build['confirmed']['#price']->subtract($adjustment);

    // Separator before we move onto paid.
    $build[] = ['#markup' => '<hr/>'];

    // Get the total paid amount.
    $build['paid'] = [
      '#type' => 'item',
      '#title' => $this->t('Fees paid to date'),
      '#price' => $this->getOrderTotal($contacts_event, NULL, 'total_paid'),
      '#description' => $this->t('Total completed payments.'),
      '#description_display' => 'after',
    ];

    // Calculate the balance.
    $build['balance'] = [
      '#type' => 'item',
      '#title' => $this->t('Balance due'),
      '#price' => $build['confirmed']['#price']->subtract($build['paid']['#price']),
      '#description' => $this->t('Outstanding confirmed balance.'),
      '#description_display' => 'after',
    ];

    // Format the currency.
    foreach ($build as &$item) {
      if (isset($item['#price'])) {
        $item['#markup'] = $this->currencyFormatter->format($item['#price']->getNumber(), $item['#price']->getCurrencyCode());
      }
    }

    return $build;
  }

  /**
   * Get a total amount for orders.
   *
   * @param \Drupal\contacts_events\Entity\EventInterface $event
   *   The event.
   * @param array|null $state
   *   Optionally filter by state.
   * @param string $field
   *   The field to aggregate on, either 'total_price' or 'total_paid'.
   *
   * @return \Drupal\commerce_price\Price
   *   A price item, of zero value if there are no results.
   */
  protected function getOrderTotal(EventInterface $event, array $state = NULL, string $field = 'total_price'): Price {
    if (!$this->getCurrencyCode()) {
      throw new \Exception('Missing currency code.');
    }

    // Build our query.
    $query = $this->entityTypeManager
      ->getStorage('commerce_order')
      ->getAggregateQuery();
    $query->accessCheck(FALSE);

    $query->addTag('contacts_events_finance');
    $query->addMetaData('contacts_events_finance', compact('event', 'state', 'field'));
    $query->condition('type', 'contacts_booking');
    $query->condition('event', $event->id());
    $query->condition($field . '.currency_code', $this->getCurrencyCode());

    // Sum the field number and group on the field currency code.
    $query->aggregate($field . '.number', 'SUM');

    // Add the optional state filter.
    if ($state) {
      $query->condition('state', $state, 'IN');
    }

    // Extract our result.
    $result = $query->execute();
    return new Price($result[0][$field . 'number_sum'] ?? '0', $this->getCurrencyCode());
  }

  /**
   * Get a total amount for orders.
   *
   * @param \Drupal\contacts_events\Entity\EventInterface $event
   *   The event.
   * @param array|null $state
   *   Optionally filter by state.
   *
   * @return \Drupal\commerce_price\Price|null
   *   A price item, or NULL if there are no results and no default currency.
   */
  protected function getOrderItemTotal(EventInterface $event, array $state = NULL) {
    if (!$this->getCurrencyCode()) {
      throw new \Exception('Missing currency code.');
    }

    // Build our query.
    $query = $this->entityTypeManager
      ->getStorage('commerce_order_item')
      ->getAggregateQuery();
    $query->accessCheck(FALSE);
    $query->addTag('contacts_events_finance');
    $query->addMetaData('contacts_events_finance', compact('event', 'state'));
    $query->condition('order_id.entity.type', 'contacts_booking');
    $query->condition('order_id.entity.event', $event->id());
    $query->condition('order_id.entity.state', 'draft', '!=');
    $query->condition('state', $state);
    $query->condition('total_price.currency_code', $this->getCurrencyCode());

    // Sum the total price number and group on the total price currency code.
    $query->aggregate('total_price.number', 'SUM');

    // Add the optional state filter.
    if ($state) {
      $query->condition('state', $state, 'IN');
    }

    // Extract our result.
    $result = $query->execute();
    return new Price($result[0]['total_pricenumber_sum'] ?? '0', $this->getCurrencyCode());
  }

}

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

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