contacts_events-8.x-1.x-dev/src/Entity/OrderItemBookkeepingHooks.php

src/Entity/OrderItemBookkeepingHooks.php
<?php

namespace Drupal\contacts_events\Entity;

use Drupal\bookkeeping\Plugin\Field\FieldType\BookkeepingEntryItem;
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\contacts_events\Event\OrderItemTransactionEvent;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Commerce Order Item Bookkeeping hooks.
 */
class OrderItemBookkeepingHooks {

  /**
   * The bookkeeping transaction entity storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $storage;

  /**
   * The commerce order type entity storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $orderItemTypeStorage;

  /**
   * The bookkeeping commerce settings.
   *
   * @var \Drupal\Core\Config\Config|\Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcher
   */
  protected $eventDispatcher;

  /**
   * Constructs a commercepaymenthooks object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Config\ConfigFactory $config_factory
   *   The config factory.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   The event dispatcher.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactory $config_factory, EventDispatcherInterface $event_dispatcher) {
    $this->storage = $entity_type_manager->getStorage('bookkeeping_transaction');
    $this->orderItemTypeStorage = $entity_type_manager->getStorage('commerce_order_item_type');
    $this->config = $config_factory->get('bookkeeping.commerce');
    $this->eventDispatcher = $event_dispatcher;
  }

  /**
   * Post save (insert/update) for order items.
   *
   * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
   *   The order.
   * @param \Drupal\commerce_order\Entity\OrderItemInterface|null $original
   *   The original, if any.
   */
  public function postSave(OrderItemInterface $order_item, OrderItemInterface $original = NULL): void {
    // We only want to process stateful order items.
    if (!$order_item->hasField('state')) {
      return;
    }

    // Ensure the item belongs to an order.
    $order = $order_item->getOrder();
    if (!$order) {
      return;
    }

    // Only deal with bookings.
    if ($order->bundle() != 'contacts_booking') {
      return;
    }

    // Check for a disabled store.
    $store_id = $order->getStoreId();
    if ($this->config->get("stores.{$store_id}.disabled")) {
      return;
    }

    // Check whether it is/was payable.
    $is_payable = $this->isPayable($order_item);
    $was_payable = $original && $this->isPayable($original);

    // If it was not and is not payable, there's nothing to do.
    if (!$is_payable && !$was_payable) {
      return;
    }

    // Get the amount to post. Start with the total price.
    $post_amount = $order_item->getTotalPrice();

    // If it was and is payable, we need to check for a difference.
    if ($is_payable && $was_payable) {
      $original_amount = $original->getTotalPrice();
      $generator = 'commerce_order_item:changed';
      if ($original_amount) {
        $post_amount = $post_amount->subtract($original_amount);
      }
    }
    // Otherwise if it is no longer payable, we need the negative of the
    // original amount.
    elseif ($was_payable) {
      $post_amount = $original->getTotalPrice();
      $generator = 'commerce_order_item:unpayable';
      if ($post_amount) {
        $post_amount = $post_amount->multiply('-1');
      }
    }
    // Otherwise we post the total price.
    else {
      $generator = 'commerce_order_item:payable';
    }

    // See where we should track income for this store.
    $accounts_receivable = $this->config->get("stores.{$store_id}.accounts_receivable_account");
    $income_account = $this->config->get("stores.{$store_id}.income_account");

    // If the order is a booking, see if there is an event specific income
    // account override.
    if ($order->bundle() == 'contacts_booking') {
      /** @var \Drupal\contacts_events\Entity\EventInterface $event */
      $event = $order->get('event')->entity;
      if ($event->hasField('bookkeeping_income_account')) {
        $event_income_account = $event->get('bookkeeping_income_account')->target_id;
        if ($event_income_account) {
          $income_account = $event_income_account;
        }
      }
    }

    // Dispatch our event.
    $event = new OrderItemTransactionEvent(
      $generator,
      $post_amount,
      $income_account,
      $accounts_receivable,
      $order,
      $order_item,
      $original
    );
    $this->eventDispatcher->dispatch(OrderItemTransactionEvent::EVENT, $event);

    // If a subscriber asked us not to post, stop now.
    if ($event->isPrevented()) {
      return;
    }

    // Get our potentially modified post amount.
    $post_amount = $event->getValue();

    // If the amount is zero, there's nothing more to do.
    if ($post_amount->isZero()) {
      return;
    }

    // Create our transaction.
    /** @var \Drupal\bookkeeping\Entity\TransactionInterface $transaction */
    $transaction = $this->storage->create([
      'generator' => $generator,
    ]);

    // Add the entries.
    $transaction
      ->addEntry($event->getFrom(), $post_amount, BookkeepingEntryItem::TYPE_CREDIT)
      ->addEntry($event->getTo(), $post_amount, BookkeepingEntryItem::TYPE_DEBIT);

    // Add the related entities.
    foreach ($event->getRelated() as $related) {
      $transaction->addRelated($related);
    }

    // Save the transaction.
    $transaction->save();
  }

  /**
   * Check whether an order is payable (therefore should be tracked as income).
   *
   * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
   *   The order to check.
   *
   * @return bool
   *   Whether it is payable.
   *
   * @todo Consider making this something publically accessible for other uses.
   */
  protected function isPayable(OrderItemInterface $order_item) {
    /** @var \Drupal\state_machine\Plugin\Field\FieldType\StateItemInterface $state */
    $state = $order_item->get('state')->first();

    if ($state === NULL) {
      return FALSE;
    }

    /** @var \Drupal\commerce_order\Entity\OrderItemTypeInterface $order_item_type */
    $order_item_type = $this->orderItemTypeStorage->load($order_item->bundle());
    $payable_states = $order_item_type->getThirdPartySetting('contacts_events', 'payable_states');

    // Default to everything but pending.
    if (empty($payable_states)) {
      $payable_states = array_keys($state->getWorkflow()->getStates());
      $payable_states = array_diff($payable_states, ['pending']);
    }

    return in_array($state->value, $payable_states);
  }

}

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

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