commerce_xero-8.x-1.x-dev/src/Form/StrategyForm.php

src/Form/StrategyForm.php
<?php

namespace Drupal\commerce_xero\Form;

use Drupal\commerce_xero\CommerceXeroDataTypeManager;
use Drupal\commerce_xero\CommerceXeroProcessorManager;
use Drupal\commerce_xero\Entity\CommerceXeroStrategyInterface;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\Exception\MissingDataException;
use Drupal\xero\XeroQueryFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides base form for modifying Commerce Xero strategy entities.
 */
class StrategyForm extends EntityForm implements ContainerInjectionInterface {

  /**
   * The Xero Query Factory service.
   *
   * @var \Drupal\xero\XeroQueryFactory
   */
  protected $queryFactory;

  /**
   * Entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The commerce_xero cache bin.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cache;

  /**
   * The commerce_xero data type plugin manager.
   *
   * @var \Drupal\commerce_xero\CommerceXeroDataTypeManager
   */
  protected $dataTypeManager;

  /**
   * The commerce_xero processor plugin manager.
   *
   * @var \Drupal\commerce_xero\CommerceXeroProcessorManager
   */
  protected $processorManager;

  /**
   * The commerce_xero strategy configuration entity.
   *
   * @var \Drupal\commerce_xero\Entity\CommerceXeroStrategyInterface
   */
  protected $entity;

  /**
   * The cache expiration time for the commerce_xero account cache.
   *
   * This defaults to seven days.
   *
   * @var int
   */
  public static int $cacheExpiration = 604800;

  /**
   * Initialize method.
   *
   * @param \Drupal\xero\XeroQueryFactory $queryFactory
   *   The xero.query.factory service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity_type.manager service.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache.xero_query service.
   * @param \Drupal\commerce_xero\CommerceXeroDataTypeManager $dataTypeManager
   *   The commerce_xero_data_type.manager service.
   * @param \Drupal\commerce_xero\CommerceXeroProcessorManager $processorManager
   *   The commerce_xero_processor.manager service.
   */
  public function __construct(XeroQueryFactory $queryFactory, EntityTypeManagerInterface $entityTypeManager, CacheBackendInterface $cache, CommerceXeroDataTypeManager $dataTypeManager, CommerceXeroProcessorManager $processorManager) {
    $this->queryFactory = $queryFactory;
    $this->entityTypeManager = $entityTypeManager;
    $this->cache = $cache;
    $this->dataTypeManager = $dataTypeManager;
    $this->processorManager = $processorManager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('xero.query.factory'),
      $container->get('entity_type.manager'),
      $container->get('cache.xero_query'),
      $container->get('commerce_xero_data_type.manager'),
      $container->get('commerce_xero_processor.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\commerce_xero\Entity\CommerceXeroStrategy $strategy */
    $strategy = $this->getEntity();
    $bank_accounts = [];
    $accounts = $bank_accounts;
    $data_types = $bank_accounts;

    $form['name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#maxlength' => 255,
      '#default_value' => $strategy->label(),
      '#required' => TRUE,
    ];

    $form['id'] = [
      '#type' => 'machine_name',
      '#title' => $this->t('ID'),
      '#default_value' => $strategy->id(),
      '#machine_name' => [
        'exists' => [$this, 'exists'],
        'source' => ['name'],
        'label' => $this->t('ID'),
      ],
      '#disabled' => !$strategy->isNew(),
    ];

    try {
      // Gets the bank and revenue accounts.
      $bank_accounts = $this->getAccountOptions();
      $accounts = $this->getAccountOptions(FALSE);
      $processors = $this->processorManager->getDefinitions();

      foreach ($this->dataTypeManager->getDefinitions() as $plugin_id => $definition) {
        $data_types[$plugin_id] = $definition['label'];
      }
    }
    catch (PluginNotFoundException $e) {
      $this->messenger()->addError($this->t('An error occurred creating data from Xero.'));
    }
    catch (MissingDataException $e) {
      $this->messenger()->addError($this->t('An error occurred setting Xero account data.'));
    }

    // Sets the bank account and account options if an error occurred above.
    $bank_accounts = $bank_accounts ?? [];
    $accounts = $accounts ?? [];
    $processors = $processors ?? [];

    $bank_account = $strategy->get('bank_account');
    $revenue_account = $strategy->get('revenue_account');

    $form['bank_account'] = [
      '#type' => 'select',
      '#title' => $this->t('Bank Account'),
      '#options' => $bank_accounts,
      '#default_value' => !empty($bank_account) ? $bank_account['AccountID'] : '',
      '#required' => TRUE,
    ];

    $form['revenue_account'] = [
      '#type' => 'select',
      '#title' => $this->t('Revenue Account'),
      '#options' => $accounts,
      '#default_value' => !empty($revenue_account) ? $revenue_account['Code'] : '',
      '#required' => TRUE,
    ];

    $form['xero_type'] = [
      '#type' => 'select',
      '#title' => $this->t('Xero type'),
      '#description' => $this->t('Choose the Xero data type to use for this strategy such as Invoice, Bank Transaction, Order, Invoice-Payment.'),
      '#options' => $data_types,
      '#default_value' => $strategy->get('xero_type'),
      '#required' => TRUE,
    ];

    try {
      $gateways = $this->entityTypeManager
        ->getStorage('commerce_payment_gateway')
        ->loadMultiple();
    }
    catch (\Exception $e) {
      $this->messenger()->addError($this->t('An error occurred loading Commerce Payment gateways.'));
      $gateways = [];
    }
    $gateway_options = [];
    /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $gateway */
    foreach ($gateways as $id => $gateway) {
      $gateway_options[$gateway->id()] = $gateway->label();
    }

    $form['payment_gateway'] = [
      '#type' => 'select',
      '#title' => $this->t('Payment Gateway'),
      '#description' => $this->t('Choose the Commerce Payment Gateway to associate with this strategy.'),
      '#options' => $gateway_options,
      '#default_value' => $strategy->get('payment_gateway'),
      '#required' => TRUE,
    ];

    $form['status'] = [
      '#type' => 'radios',
      '#title' => $this->t('Status'),
      '#options' => [
        '0' => $this->t('Disabled'),
        '1' => $this->t('Enabled'),
      ],
      '#default_value' => $strategy->get('status'),
    ];

    // Processor status.
    $form['processors']['status'] = [
      '#type' => 'item',
      '#title' => $this->t('Processors'),
    ];

    $form['processor_settings'] = [
      '#type' => 'container',
      '#prefix' => '<div id="processor-settings-wrapper">',
      '#suffix' => '</div>',
      '#tree' => TRUE,
    ];

    // Processor order (tabledrag).
    $form['processor_settings']['order'] = [
      '#type' => 'table',
      '#attributes' => ['id' => 'processor-order'],
      '#title' => $this->t('Processor order'),
      '#tabledrag' => [
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => 'processor-order-weight',
        ],
      ],
      '#header' => [
        $this->t('Processor'),
        $this->t('Weight'),
      ],
      '#tree' => FALSE,
      '#input' => FALSE,
      '#theme_wrappers' => ['form_element'],
    ];

    // Processor settings.
    $form['processor_settings']['settings'] = [
      '#type' => 'vertical_tabs',
      '#title' => $this->t('Processor settings'),
    ];

    $this->setProcessors($form_state);

    // Assembles the list of available processor plugins.
    foreach ($processors as $name => $processor_definition) {
      $info = $this->entity->getEnabledPlugin($name);

      $form['processors']['status'][$name] = [
        '#type' => 'checkbox',
        '#title' => $processor_definition['label'],
        '#default_value' => $info || $processor_definition['required'],
        '#parents' => ['processors', $name, 'status'],
        '#disabled' => $processor_definition['required'],
        '#ajax' => [
          'callback' => [$this, 'onStatusChanged'],
          'event' => 'change',
          'effect' => 'fade',
          'speed' => 'fast',
          'wrapper' => 'processor-settings-wrapper',
        ],
      ];
    }

    // Assembles processor settings for enabled processors.
    $plugins = $this->entity->getEnabledPlugins();
    foreach ($plugins as $weight => $info) {
      $name = $info['name'];
      $configuration = [
        'settings' => $info['settings'] ?? [],
      ];

      /** @var \Drupal\commerce_xero\CommerceXeroProcessorPluginInterface $processor */
      $processor = $this->processorManager->createInstance($name, $configuration);
      $processor_definition = $processor->getPluginDefinition();

      $form['processor_settings']['order'][$name]['#attributes']['class'][] = 'draggable';
      $form['processor_settings']['order'][$name]['#weight'] = $weight;
      $form['processor_settings']['order'][$name]['processor'] = [
        '#markup' => $processor_definition['label'],
      ];
      $form['processor_settings']['order'][$name]['weight'] = [
        '#type' => 'weight',
        '#title' => $this->t('Weight for @title', ['@title' => $processor_definition['label']]),
        '#title_display' => 'invisible',
        '#delta' => 50,
        '#default_value' => $weight,
        '#parents' => ['processors', $name, 'weight'],
        '#attributes' => ['class' => ['processor-order-weight']],
      ];

      // Retrieve the settings form of the processor plugin.
      $settings_form = [
        '#parents' => ['processors', $name, 'settings'],
        '#tree' => TRUE,
      ];
      $settings_form = $processor->settingsForm($settings_form, $form_state);
      if (!empty($settings_form)) {
        $form['processor_settings']['settings'][$name] = [
          '#type' => 'details',
          '#title' => $processor_definition['label'],
          '#open' => TRUE,
          '#weight' => $weight,
          '#parents' => ['processors', $name, 'settings'],
          '#group' => 'processor_settings',
        ];
        $form['processor_settings']['settings'][$name] += $settings_form;
      }
    }

    $form = parent::buildForm($form, $form_state);

    $form['#entity_builders'][] = [$this, 'mapAccountSettings'];
    $form['#entity_builders'][] = [$this, 'mergePluginSettings'];

    return $form;
  }

  /**
   * Checks for existing strategy by entity ID.
   *
   * @param string $entity_id
   *   The entity ID.
   * @param array $element
   *   The form array element that requests validation.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state array.
   *
   * @return bool
   *   TRUE if the entity exists by ID.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function exists($entity_id, array $element, FormStateInterface $form_state) {
    $entity = $this->entityTypeManager
      ->getStorage('commerce_xero_strategy')
      ->load($entity_id);
    return $entity !== NULL;
  }

  /**
   * Ajax callback when processor status changes.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   *
   * @return array
   *   Return the form element that should be modified.
   */
  public function onStatusChanged(array $form, FormStateInterface $formState) {
    $this->setProcessors($formState);
    return $form['processor_settings'];
  }

  /**
   * Sets the enabled processors and settings on the entity.
   *
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   */
  protected function setProcessors(FormStateInterface $formState) {
    $processors = $formState->getValue('processors');
    $plugins = [];

    if (!empty($processors)) {
      foreach ($processors as $name => $info) {
        if ($info['status']) {
          $plugins[] = [
            'name' => $name,
            'settings' => $info['settings'] ?? [],
          ];
        }
      }
      $this->entity->set('plugins', $plugins);
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    $actions = parent::actions($form, $form_state);
    $actions['submit']['#value'] = $this->t('Save');
    return $actions;
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $strategy = $this->getEntity();

    try {
      $status = $strategy->save();

      $message = '';
      $message_args = [
        '%name' => $strategy->label(),
      ];
      if ($status === SAVED_UPDATED) {
        $message = $this->t('Succesfully updated strategy %name', $message_args);
      }
      else {
        $message = $this->t('Succesfully created strategy %name', $message_args);
      }
      $this->messenger()->addStatus($message);

      $form_state->setRedirect('entity.commerce_xero_strategy.list');

      return $status;
    }
    catch (\Exception $e) {
      $this->logger('commerce_xero')->error($e->getMessage());
      $this->messenger()->addError($this->t('An error occurred while saving the strategy.'));

      return -1;
    }
  }

  /**
   * Get bank or revenue accounts from Xero.
   *
   * @param bool $bank
   *   Include or exclude bank accounts.
   *
   * @return array
   *   A list of xero accounts keyed by account code.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
   * @throws \Throwable
   */
  protected function getAccountOptions($bank = TRUE) {
    $account_options = [];
    $operator = $bank ? '==' : '!=';
    $code_key = $bank ? 'AccountID' : 'Code';
    $cache_key = $bank ? 'commerce_xero_bank_accounts' : 'commerce_xero_accounts';

    if ($cached = $this->cache->get($cache_key)) {
      // Data is cached.
      $accounts = $cached->data;
    }
    else {
      // Get bank account options from Xero API.
      $query = $this->queryFactory->get();

      $query
        ->setType('xero_account')
        ->addCondition('Type', 'BANK', $operator)
        ->setMethod('get')
        ->throwErrors(FALSE);

      $accounts = $query->execute();

      if ($accounts) {
        $this->cache->set($cache_key, $accounts, self::$cacheExpiration, ['xero_account']);
      }
      else {
        $this->messenger()->addError('Failed to get accounts from Xero.');
      }
    }

    if ($accounts) {
      /** @var \Drupal\xero\Plugin\DataType\Account $account */
      foreach ($accounts as $key => $account) {
        $code = $account->get($code_key)->getValue();
        $name = $account->get('Name')->getValue();
        $isRevenueAccount = $account->get('EnablePaymentsToAccount')->getValue();
        if (($bank && $account->isBankAccount()) || (!$bank && $isRevenueAccount)) {
          $account_options[$code] = $name;
        }
      }
    }
    return $account_options;
  }

  /**
   * Merge bank account and revenue account settings with the entity.
   *
   * @param string $entity_type
   *   The entity type.
   * @param \Drupal\commerce_xero\Entity\CommerceXeroStrategyInterface $strategy
   *   The strategy entity.
   * @param array &$form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   */
  public function mapAccountSettings($entity_type, CommerceXeroStrategyInterface $strategy, array &$form, FormStateInterface $formState) {
    $bankAccount = [];
    $revenueAccount = [];

    $accountId = $formState->getValue('bank_account', '');
    if (!empty($accountId)) {
      $bankAccount['AccountID'] = $accountId;
      $bankAccount['Name'] = $form['bank_account']['#options'][$accountId];
    }
    $strategy->set('bank_account', $bankAccount);

    $accountCode = $formState->getValue('revenue_account', '');
    if (!empty($accountCode)) {
      $revenueAccount['Code'] = $accountCode;
      $revenueAccount['Name'] = $form['revenue_account']['#options'][$accountCode];
    }
    $strategy->set('revenue_account', $revenueAccount);
  }

  /**
   * Merge plugin settings with the entity.
   *
   * @param string $entity_type
   *   The entity type.
   * @param \Drupal\commerce_xero\Entity\CommerceXeroStrategyInterface $strategy
   *   The strategy entity.
   * @param array &$form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   */
  public function mergePluginSettings($entity_type, CommerceXeroStrategyInterface $strategy, array &$form, FormStateInterface $formState) {
    $processors = $formState->getValue('processors', []);
    $plugins = [];

    if (!empty($processors)) {
      foreach ($processors as $name => $values) {
        if ($values['status']) {
          $plugins[] = [
            'name' => $name,
            'settings' => $values['settings'] ?? [],
          ];
        }
      }

      // Sort the plugins by the weight.
      usort($plugins, function ($a, $b) use ($formState) {
        $aWeight = $formState->getValue(['processors', $a['name'], 'weight'], 0);
        $bWeight = $formState->getValue(['processors', $b['name'], 'weight'], 0);

        if ($aWeight === $bWeight) {
          return 0;
        }
        return $aWeight < $bWeight ? -1 : 1;
      });
    }

    $strategy->set('plugins', $plugins);
  }

}

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

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