billwerk_subscriptions-1.x-dev/modules/billwerk_subscriptions_manage/src/Form/ChangeFormBase.php

modules/billwerk_subscriptions_manage/src/Form/ChangeFormBase.php
<?php

declare(strict_types=1);

namespace Drupal\billwerk_subscriptions_manage\Form;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\billwerk_subscriptions\Api;
use Drupal\billwerk_subscriptions\Environment;
use Drupal\billwerk_subscriptions\Subscriber;
use Drupal\billwerk_subscriptions_entities\BillwerkEntitiesHelper;
use Drupal\billwerk_subscriptions_entities\SubscriberBillwerkEntitiesHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a Billwerk Subscriptions Manage form.
 */
abstract class ChangeFormBase extends FormBase {

  /**
   * The subscriber billwerk entities helper.
   *
   * @var \Drupal\billwerk_subscriptions_entities\SubscriberBillwerkEntitiesHelper
   */
  protected SubscriberBillwerkEntitiesHelper $subscriberBillwerkEntitiesHelper;

  /**
   * The subscriber.
   *
   * @var \Drupal\billwerk_subscriptions\Subscriber
   */
  protected Subscriber $subscriber;
  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected LanguageManagerInterface $languageManager;

  /**
   * Constructs a new ChangeFormBase object.
   *
   * @param \Drupal\billwerk_subscriptions_entities\BillwerkEntitiesHelper $billwerkEntitiesHelper
   *   The billwerk entities helper.
   * @param \Drupal\billwerk_subscriptions\Environment $environment
   *   The environment.
   * @param \Drupal\billwerk_subscriptions\Api $api
   *   The API.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   */
  public function __construct(
    // @improve:
    // These should be "readonly", but that's currently not possible on
    // multi-page forms due to a dependency injection bug leading to an error:
    // Error: Cannot initialize readonly property Drupal\billwerk_subscriptions_manage\Form\BillwerkSubscriptionjsFormAbstract::$account from scope Drupal\Core\Form\FormBase in Drupal\Core\Form\FormBase->__wakeup() (line 89 of core/lib/Drupal/Core/DependencyInjection/DependencySerializationTrait.php).
    protected BillwerkEntitiesHelper $billwerkEntitiesHelper,
    protected Environment $environment,
    protected Api $api,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected LanguageManagerInterface $language_manager,
  ) {
    $currentUserId = (int) $this->currentUser()->id();
    $this->subscriberBillwerkEntitiesHelper = SubscriberBillwerkEntitiesHelper::create(Subscriber::loadByDrupalUid($currentUserId), $billwerkEntitiesHelper, $environment);
    $this->subscriber = Subscriber::loadByDrupalUid($currentUserId);
    $this->languageManager = $language_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('billwerk_subscriptions_entities.billwerk_entities_helper'),
      $container->get('billwerk_subscriptions.environment'),
      $container->get('billwerk_subscriptions.api'),
      $container->get('entity_type.manager'),
      $container->get('language_manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['current_user'] = [
      '#type' => 'item',
      '#title' => '<strong>' . $this->t('Your username:') . '</strong>',
      '#markup' => $this->currentUser()->getDisplayName(),
      '#wrapper_attributes' => ['class' => ['container-inline']],
    ];

    $activeBillwerkPlanVariant = $this->subscriberBillwerkEntitiesHelper->getActiveBillwerkPlanVariant();
    if (empty($activeBillwerkPlanVariant)) {
      $form['no_active_subscription'] = [
        '#type' => 'item',
        '#title' => '<strong>' . $this->t('Your current plan:') . '</strong>',
        '#markup' => $this->t('No active subscription'),
        '#attributes' => [
          'class' => 'nobillwerk-subscriptions--no-active-subscription',
        ],
        '#wrapper_attributes' => ['class' => ['container-inline']],
      ];
      return $form;
    }
    $form['current_plan_variant'] = [
      '#type' => 'item',
      '#title' => '<strong>' . $this->t('Your current plan:') . '</strong>',
      '#markup' => $activeBillwerkPlanVariant->label(),
      // Render as card:
      // 'content' => $this->entityTypeManager->getViewBuilder('billwerk_plan_variant')->view($activeBillwerkPlanVariant, 'card'),. // @codingStandardsIgnoreLine
      '#wrapper_attributes' => ['class' => ['container-inline']],
    ];

    switch ($form_state->get('step')) {
      case 'confirm':
        $form = $this->buildConfirmForm($form, $form_state);
        break;

      default:
        $form = $this->buildSelectionForm($form, $form_state);
        break;
    }

    // Add this library to prevent duplicate form submits:
    $form['#attached']['library'][] = 'core/drupal.form';

    // Add a class to the form to identify it:
    $form['#attributes']['class'][] = 'billwerk-subscriptions-change-form';

    return $form;
  }

  /**
   * Builds the selection form.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  abstract protected function buildSelectionForm(array $form, FormStateInterface $form_state): array;

  /**
   * Builds a plan variant change form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildPlanVariantChangeFormElement(array &$form, FormStateInterface $form_state) {
    $activePlanVariant = $this->subscriberBillwerkEntitiesHelper->getActiveBillwerkPlanVariant();
    $activePlanVariantMachineName = $activePlanVariant->getMachineName();
    $planVariantOptions = $this->billwerkEntitiesHelper->getPlanVariantsAllSelectOptions('card');
    if (!empty($planVariantOptions)) {
      $planVariantGet = $this->getRequest()->query->get('planvariant');
      $preselectedPlanVariant = !empty($planVariantGet) && in_array($planVariantGet, $planVariantOptions) ? $planVariantGet : $activePlanVariantMachineName;
      $form['planvariant'] = [
        '#type' => 'radios',
        '#title' => $this->t('Change subscription plan:'),
        '#options' => self::optionsArrayRender($planVariantOptions),
        '#default_value' => $form_state->getValue('planvariant', $preselectedPlanVariant),
        '#required' => TRUE,
        // Disable the current plan:
        $activePlanVariantMachineName => ['#disabled' => TRUE],
      ];
      // Mark the currently active plan variant:
      $form['planvariant'][$activePlanVariantMachineName]['#wrapper_attributes']['class'][] = 'form-item--active';
      $form['planvariant'][$activePlanVariantMachineName]['#attributes']['class'][] = 'active';
    }
  }

  /**
   * Build the components subscribe form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildComponentsSubscribeFormElement(array &$form, FormStateInterface $form_state) {
    // Components:
    $activeComponents = $this->subscriberBillwerkEntitiesHelper->getActiveBillwerkComponents();

    $componentSubscribeOptions = $this->billwerkEntitiesHelper->getComponentsSubscribeSelectOptions(array_keys($activeComponents), 'card');
    if (!empty($componentSubscribeOptions)) {
      $componentGet = $this->getRequest()->query->get('component');
      // We're using the machine names as keys to not expose the Billwerk IDs:
      $form['components_subscribe'] = [
        '#type' => 'checkboxes',
        '#title' => $this->t('Extend your subscription with add-ons'),
        '#options' => self::optionsArrayRender($componentSubscribeOptions),
        '#default_value' => $form_state->getValue('components_subscribe', in_array($componentGet, array_keys($componentSubscribeOptions)) ? [$componentGet] : []),
        '#multiple' => TRUE,
        '#description' => $this->t('Add these add-ons to your subscription'),
      ];
    }

  }

  /**
   * Build the components unsubscribe form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildComponentsUnsubscribeFormElement(array &$form, FormStateInterface $form_state) {
    $activeComponents = $this->subscriberBillwerkEntitiesHelper->getActiveBillwerkComponents(TRUE);
    $componentUnsubscribeOptions = $this->billwerkEntitiesHelper->getComponentsUnsubscribeSelectOptions(array_keys($activeComponents), 'card');
    if (!empty($componentUnsubscribeOptions)) {
      $componentGet = $this->getRequest()->query->get('component');
      // We're using the machine names as keys to not expose the Billwerk IDs:
      $form['components_unsubscribe'] = [
        '#type' => 'checkboxes',
        '#title' => $this->t('Cancel add-ons'),
        '#options' => self::optionsArrayRender($componentUnsubscribeOptions),
        '#default_value' => $form_state->getValue('components_unsubscribe', in_array($componentGet, array_keys($componentUnsubscribeOptions)) ? [$componentGet] : []),
        '#multiple' => TRUE,
        '#description' => $this->t('The selected add-ons will be canceled from your subscription at the end of the contract period'),
      ];
    }
  }

  /**
   * Build the coupon form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildCouponFormElement(array &$form, FormStateInterface $form_state) {
    $form['coupon'] = [
      '#type' => 'details',
      '#title' => $this->t('Coupon'),
      '#open' => FALSE,
    ];

    $form['coupon']['couponcode'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Coupon code'),
      '#description' => $this->t('If you have a coupon code, enter it here'),
      '#default_value' => $form_state->getValue('couponcode', ''),
      '#required' => FALSE,
    ];
  }

  /**
   * Build the order table form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \stdClass $billwerkOrderObj
   *   The Billwerk order object.
   * @param array $displayOptions
   *   The display options to pass.
   */
  protected function buildOrderTableFormElement(array &$form, FormStateInterface $form_state, \stdClass $billwerkOrderObj, array $displayOptions = []) {
    if (!empty($billwerkOrderObj)) {
      $form['order_table'] = self::billwerkOrderDtoToRenderArray($billwerkOrderObj, $displayOptions);
    }
  }

  /**
   * Build the order address form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \stdClass $billwerkOrderObj
   *   The Billwerk order object.
   */
  protected function buildOrderAddressFormElement(array &$form, FormStateInterface $form_state, \stdClass $billwerkOrderObj) {
    $form['order_address'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Billing address'),
      '#description' => $this->t("The values entered here will be used for this and future orders.") . ' ' . $this->t('You can edit your address and payment information in your <a href="@user_profile_url" target="_blank">user profile</a> at any time.', ['@user_profile_url' => Url::fromRoute('billwerk_subscriptions_manage.user_subscription_selfservice', ['user' => $this->currentUser()->id()])->toString()]),
    ];
    $form['order_address']['CompanyName'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Company'),
      '#required' => TRUE,
      '#size' => 30,
      '#default_value' => $form_state->getValue('CompanyName', $billwerkOrderObj->Customer->CompanyName ?? NULL),
    ];
    $form['order_address']['FirstName'] = [
      '#type' => 'textfield',
      '#title' => $this->t('First name'),
      // Billwerk returns an error if not filled even though we wouldn't need
      // this value:
      '#required' => TRUE,
      '#size' => 30,
      '#default_value' => $form_state->getValue('FirstName', $billwerkOrderObj->Customer->FirstName ?? NULL),
    ];
    $form['order_address']['LastName'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Last name'),
      // Billwerk returns an error if not filled even though we wouldn't need
      // this value:
      '#required' => TRUE,
      '#size' => 30,
      '#default_value' => $form_state->getValue('LastName', $billwerkOrderObj->Customer->LastName ?? NULL),
    ];

    // @codingStandardsIgnoreStart
    // $form['order_address']['AddressLine1'] = [
    //   '#type' => 'textfield',
    //   '#title' => $this->t('Address line 1'),
    //   '#required' => TRUE,
    //   '#size' => 30,
    //   '#default_value' => $billwerkOrderObj->Customer->AddressLine1 ?? NULL,
    // ];
    // $form['order_address']['AddressLine2'] = [
    //   '#type' => 'textfield',
    //   '#title' => $this->t('Address line 2'),
    //   '#size' => 30,
    //   '#default_value' => $billwerkOrderObj->Customer->AddressLine2 ?? NULL,
    // ];
    // @codingStandardsIgnoreEnd
    $form['order_address']['Street'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Street'),
      '#required' => TRUE,
      '#size' => 30,
      '#default_value' => $form_state->getValue('Street', $billwerkOrderObj->Customer->Address->Street ?? NULL),
    ];
    $form['order_address']['HouseNumber'] = [
      '#type' => 'textfield',
      '#title' => $this->t('House number'),
      '#required' => TRUE,
      '#size' => 5,
      '#default_value' => $form_state->getValue('HouseNumber', $billwerkOrderObj->Customer->Address->HouseNumber ?? NULL),
    ];
    $form['order_address']['PostalCode'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Postal code'),
      '#size' => 5,
      '#required' => TRUE,
      '#default_value' => $form_state->getValue('PostalCode', $billwerkOrderObj->Customer->Address->PostalCode ?? NULL),
    ];
    $form['order_address']['City'] = [
      '#type' => 'textfield',
      '#title' => $this->t('City'),
      '#required' => TRUE,
      '#size' => 30,
      '#default_value' => $form_state->getValue('City', $billwerkOrderObj->Customer->Address->City ?? NULL),
    ];
    $form['order_address']['Country'] = [
      '#type' => 'address_country',
      // '#available_countries' => ['DE', 'FR'],
      '#title' => $this->t('Country'),
      '#required' => TRUE,
      '#default_value' => $form_state->getValue('Country', strtoupper($billwerkOrderObj->Customer->Address->Country ?? '') ?: 'DE'),
    ];
    $form['order_address']['VatId'] = [
      '#type' => 'textfield',
      '#title' => $this->t('VAT ID'),
      '#size' => 15,
      '#default_value' => $form_state->getValue('VatId', $billwerkOrderObj->Customer->VatId ?? NULL),
    ];
  }

  /**
   * Build the order payment form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \stdClass $billwerkOrderObj
   *   The Billwerk order object.
   */
  protected function buildOrderPaymentFormElement(array &$form, FormStateInterface $form_state, \stdClass $billwerkOrderObj) {
    $paymentFormWrapperId = 'subscriptionjs-wrapper';
    $subscriberContractDetails = $this->subscriber->billwerkGetContractDetails();

    $form['payment'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Payment'),
      '#description' => $this->t('You can edit your address and payment information in your <a href="@user_profile_url" target="_blank">user profile</a> at any time.', ['@user_profile_url' => Url::fromRoute('billwerk_subscriptions_manage.user_subscription_selfservice', ['user' => $this->currentUser()->id()])->toString()]),
    ];

    $form['payment']['subscriptionjs_payment_wrapper'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      '#value' => $this->t('Your configured payment method will be used.'),
      '#attributes' => [
        'id' => $paymentFormWrapperId,
      ],
    ];
    $form['#attached']['library'][] = 'billwerk_subscriptions_manage/billwerk_subscriptions_manage';
    $form['#attached']['drupalSettings']['billwerk_context'] = 'form';
    $form['#attached']['drupalSettings']['billwerk']['selfserviceToken'] = $this->subscriber->billwerkGetSelfserviceToken();
    $form['#attached']['drupalSettings']['billwerk']['selfservicePublicApiKey'] = $this->environment->getSelfservicePublicApiKey();
    $form['#attached']['drupalSettings']['billwerk']['userLocale'] = $this->languageManager->getCurrentLanguage()->getId();
    $form['#attached']['drupalSettings']['billwerk']['providerReturnUrl'] = $this->getProviderReturnUrl();
    $form['#attached']['drupalSettings']['billwerk']['currentContractPaymentProvider'] = $subscriberContractDetails['PaymentProvider'];
    $form['#attached']['drupalSettings']['billwerk']['paymentFormWrapperId'] = $paymentFormWrapperId;
    $form['#attached']['drupalSettings']['billwerk']['order'] = [
      // @see: https://billwerk.readme.io/reference/subscriptionjsportal
      // The order object to be paid for. This object should have been created
      // by one of the following methods: SubscriptionJS.Signup.createOrder(), a
      // backend API call, or through the Billwerk+ Premium & Enterprise user
      // interface. It is expected to be structured as follows:
      // {"OrderId" : "order_id_value", "GrossTotal" : numeric_value,
      // "Currency" : "currency_code" }.
      'OrderId' => $billwerkOrderObj->Id,
      'GrossTotal' => $billwerkOrderObj->TotalGross,
      'Currency' => $billwerkOrderObj->Currency,
    ];
  }

  /**
   * Returns the provider return URL after the subscription has been changed.
   *
   * @return string
   *   The absolute URL.
   */
  protected function getProviderReturnUrl(): string {
    // Improve: If this is ONLY called for CHANGES and not even for cancel, we
    // can remove this method and put the callback URL directly into the values
    // above, where called, perhaps.
    return Url::fromRoute('billwerk_subscriptions_manage.current_user_subscription_finished_change')->setAbsolute(TRUE)->toString();
  }

  /**
   * Build the general order terms form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildGeneralOrderTermsFormElement(array &$form, FormStateInterface $form_state) {
    $orderTerms = $this->config('billwerk_subscriptions_manage.settings')->get('order_terms');
    if (!empty($orderTerms)) {
      $form['order_terms'] = [
        '#type' => 'processed_text',
        '#text' => $orderTerms['value'],
        '#format' => $orderTerms['format'],
      ];
    }
  }

  /**
   * Build the upgrade order terms form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildUpgradeOrderTermsFormElement(array &$form, FormStateInterface $form_state) {
    $orderTerms = $this->config('billwerk_subscriptions_manage.settings')->get('order_terms_upgrade');
    if (!empty($orderTerms)) {
      $form['order_terms_upgrade'] = [
        '#type' => 'processed_text',
        '#text' => $orderTerms['value'],
        '#format' => $orderTerms['format'],
      ];
    }
  }

  /**
   * Build the downgrade order terms form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function buildDowngradeOrderTermsFormElement(array &$form, FormStateInterface $form_state) {
    $orderTerms = $this->config('billwerk_subscriptions_manage.settings')->get('order_terms_downgrade');
    if (!empty($orderTerms)) {
      $form['order_terms_downgrade'] = [
        '#type' => 'processed_text',
        '#text' => $orderTerms['value'],
        '#format' => $orderTerms['format'],
      ];
    }
  }

  /**
   * Build the order actions form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $submitLabel
   *   The submit label.
   */
  protected function buildOrderActionsFormElement(array &$form, FormStateInterface $form_state, TranslatableMarkup $submitLabel) {
    $form['actions']['#type'] = 'actions';
    $form['actions']['back'] = [
      '#type' => 'link',
      '#title' => $this->t('Back'),
      // Back to the first page (current route):
      '#url' => Url::fromRoute($this->getRouteMatch()->getRouteName()),
      '#attributes' => [
        'class' => [
          'button',
          'button--back',
        ],
      ],
    ];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      // '#submit' => ['::submitConfirmForm'],
      // '#validate' => ['::validateConfirmForm'],
      '#value' => $submitLabel,
      '#button_type' => 'primary',
    ];
  }

  /**
   * Build the order actions fake form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $submitLabel
   *   The submit label.
   */
  protected function buildOrderActionsFakeFormElement(array &$form, FormStateInterface $form_state, TranslatableMarkup $submitLabel) {
    $form['actions']['#type'] = 'actions';
    $form['actions']['back'] = [
      '#type' => 'link',
      '#title' => $this->t('Back'),
      // Back to the first page (current route):
      '#url' => Url::fromRoute($this->getRouteMatch()->getRouteName()),
      '#attributes' => [
        'class' => [
          'button',
          'button--back',
        ],
      ],
    ];

    // For the JS payment we may not use a real submit button, but a fake submit
    // form element. The submit steps are all client-side:
    $form['actions']['submit'] = [
      '#type' => 'submit',
      // '#submit' => ['::submitConfirmForm'],
      // '#validate' => ['::validateConfirmForm'],
      '#value' => $submitLabel,
      '#id' => 'buy',
      '#attributes' => [
        'onclick' => 'event.preventDefault();',
      ],
      '#button_type' => 'primary',
    ];
  }

  /**
   * Build the confirm form element.
   *
   * @param array $form
   *   The form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  abstract protected function buildConfirmForm(array $form, FormStateInterface $form_state);

  /**
   * Get the new plan variant id.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function valueGetPlanvariant(FormStateInterface $form_state): string {
    $selectedPlanVariantMachineName = $form_state->getValue('planvariant');
    if (!empty($selectedPlanVariantMachineName)) {
      $newPlanVariantEntity = $this->billwerkEntitiesHelper->getBillwerkPlanVariantByMachineName($selectedPlanVariantMachineName);
      $newPlanVariantId = $newPlanVariantEntity->getBillwerkId($this->environment->getEnvironmentName());
    }
    else {
      // No changed plan variant given. Keep the old one.
      $newPlanVariantId = $this->subscriberBillwerkEntitiesHelper->getActiveBillwerkPlanVariant()->getBillwerkId($this->environment->getEnvironmentName());
    }
    return $newPlanVariantId;
  }

  /**
   * Get the new component subscription ids array.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function valueGetComponentsSubscribe(FormStateInterface $form_state): array {
    $newComponentSubscriptionIds = [];
    $selectedBookComponentsMachineNames = $form_state->getValue('components_subscribe');
    if (!empty($selectedBookComponentsMachineNames)) {
      foreach ($selectedBookComponentsMachineNames as $selectedComponentsMachineName => $value) {
        $componentEntity = $this->billwerkEntitiesHelper->getBillwerkComponentByMachineName($selectedComponentsMachineName);
        $componentBillwerkId = $componentEntity->getBillwerkId($this->environment->getEnvironmentName());
        // Drupal FAPI sets the array value to "0" for unchecked checkboxes:
        if (!empty($value)) {
          // Add the component which the user selected and wasn't subscribed to
          // yet:
          $newComponentSubscriptionIds[$componentBillwerkId] = $componentBillwerkId;
        }
      }
    }
    return $newComponentSubscriptionIds;
  }

  /**
   * Get the new component unsubscribe ids array.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function valueGetComponentsUnsubscribe(FormStateInterface $form_state): array {
    $endComponentSubscriptionIds = [];
    $selectedEndComponentsMachineNames = $form_state->getValue('components_unsubscribe');
    if (!empty($selectedEndComponentsMachineNames)) {
      foreach ($selectedEndComponentsMachineNames as $selectedComponentsMachineName => $value) {
        $componentEntity = $this->billwerkEntitiesHelper->getBillwerkComponentByMachineName($selectedComponentsMachineName);
        $componentBillwerkId = $componentEntity->getBillwerkId($this->environment->getEnvironmentName());
        // Drupal FAPI sets the array value to "0" for unchecked checkboxes:
        if (!empty($value)) {
          // The user is currently subscribed, so this may be ended:
          $endComponentSubscriptionIds[$componentBillwerkId] = $componentBillwerkId;
        }
      }
    }
    return $endComponentSubscriptionIds;
  }

  /**
   * Get the coupon code.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  protected function valueGetCouponcode(FormStateInterface $form_state) {
    return (string) $form_state->getValue('couponcode');
  }

  /**
   * Converts the billwerk Order DTO object to a render array.
   *
   * @param \stdClass $billwerkOrderObj
   *   The Billwerk order object.
   * @param array $displayOptions
   *   Optional array of display options to pass to the template.
   */
  protected static function billwerkOrderDtoToRenderArray(\stdClass $billwerkOrderObj, $displayOptions = []): array {
    $userLocale = \Drupal::languageManager()->getCurrentLanguage()->getId();
    return [
      '#theme' => 'billwerk_order',
      '#id' => $billwerkOrderObj->Id,
      '#planVariantId' => $billwerkOrderObj->PlanVariantId ?? NULL,
      '#planId' => $billwerkOrderObj->PlanId ?? NULL,
      '#allowWithoutPaymentData' => $billwerkOrderObj->AllowWithoutPaymentData ?? NULL,
      '#lineitems' => $billwerkOrderObj->LineItems ?? [],
      '#coupon' => $billwerkOrderObj->Coupon ?? [],
      '#currency' => $billwerkOrderObj->Currency ?? NULL,
      '#total' => $billwerkOrderObj->Total ?? NULL,
      '#totalVat' => $billwerkOrderObj->TotalVat ?? NULL,
      '#totalGross' => $billwerkOrderObj->TotalGross ?? NULL,
      '#nextTotalGross' => $billwerkOrderObj->NextTotalGross ?? NULL,
      '#nextTotalGrossDate' => $billwerkOrderObj->NextTotalGrossDate ?? NULL,
      '#isTrial' => $billwerkOrderObj->IsTrial ?? NULL,
      '#planName' => $billwerkOrderObj->PlanName ?? NULL,
      '#planVariantName' => $billwerkOrderObj->PlanVariantName ?? NULL,
      '#orderType' => $billwerkOrderObj->OrderType ?? NULL,
      '#contractCustomFields' => [],
      '#userLocale' => $userLocale,
      '#customer' => [
        '#theme' => 'billwerk_customer',
        '#companyName' => $billwerkOrderObj->Customer->CompanyName ?? NULL,
        '#firstName' => $billwerkOrderObj->Customer->FirstName ?? NULL,
        '#lastName' => $billwerkOrderObj->Customer->LastName ?? NULL,
        '#addressLine1' => $billwerkOrderObj->Customer->AddressLine1 ?? NULL,
        '#addressLine2' => $billwerkOrderObj->Customer->AddressLine2 ?? NULL,
        '#street' => $billwerkOrderObj->Customer->Street ?? NULL,
        '#houseNumber' => $billwerkOrderObj->Customer->HouseNumber ?? NULL,
        '#country' => $billwerkOrderObj->Customer->Country ?? NULL,
        '#postalCode' => $billwerkOrderObj->Customer->PostalCode ?? NULL,
        '#city' => $billwerkOrderObj->Customer->City ?? NULL,
        '#state' => $billwerkOrderObj->Customer->State ?? NULL,
        '#formattedStreetAddressLine' => $billwerkOrderObj->Customer->FormattedStreetAddressLine ?? NULL,
        '#formattedCityAddressLine' => $billwerkOrderObj->Customer->FormattedCityAddressLine ?? NULL,
        '#vatId' => $billwerkOrderObj->Customer->VatId ?? NULL,
        '#emailAddress' => $billwerkOrderObj->Customer->EmailAddress ?? NULL,
        '#attributes' => [],
      ],
      '#attributes' => [],
      '#displayOptions' => $displayOptions ?? [],
    ];
  }

  /**
   * Proceed to the confirm form.
   */
  public static function proceedToConfirm(array &$form, FormStateInterface $form_state) {
    $form_state->set('step', 'confirm')->setRebuild(TRUE);
  }

  /**
   * Validate the confirm form.
   */
  public static function validateConfirmForm(array &$form, FormStateInterface $form_state) {
    // @todo Implement this:
    // dsm('validateConfirmForm()');
    throw new \Exception('This was not expected to be called!');
  }

  /**
   * Submit the confirm form.
   */
  public static function submitConfirmForm(array &$form, FormStateInterface $form_state) {
    // @todo What needs to be done here?
    // $form_state->set('step', NULL);
    // $form_state->setUserInput([]);
    // $form_state->setRebuild();
    // dsm('submitConfirmForm()');
    throw new \Exception('This was not expected to be called!');
  }

  /**
   * Render the options array.
   */
  protected static function optionsArrayRender(array $optionsArray): array {
    array_walk($optionsArray, function (&$value, $key) {
      if (is_array($value)) {
        $value = \Drupal::service('renderer')->renderRoot($value);
      }
    });
    return $optionsArray;
  }

}

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

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