Snippets Collections
<?php

/** @var \Drupal\halo_entity_cfp\Entity\Cfp[] $cfps */
  $cfps = \Drupal::entityTypeManager()->getStorage('cfp')->loadByProperties([
    'type' => 'cfp_2020_01',
    'state' => 'project_implementation',
  ]);
  $first_key = key($cfps);
  $writer = Box\Spout\Writer\Common\Creator\WriterEntityFactory::createXLSXWriter();
  $file_system = \Drupal::service('file_system');
  $temp_path = 'public://ced_NOVI_report.xlsx';
  $temp_path = $file_system->realpath($temp_path);
  $writer->openToFile($temp_path);
  foreach ($cfps as $key => $cfp) {
    $applicant = $cfp->getApplicantEntity();
    $row = [];
    $row['operational_number'] = $cfp->label();
    $row['company_name'] = $applicant->label();
    // Partner countries.
    for ($i = 0;$i < 3;$i++) {
        $index = $i + 1;
        $partner = $cfp->get('partners')[$i] ?? FALSE;
        if ($partner) {
            $partner_entity = $partner->entity;
            $project_implementation_place = $partner_entity->get('project_implementation_place');
            if ($project_implementation_place->first()) {
                $implementation_country = $project_implementation_place->first()->getCountryName();
            }
            $row["partner_$index"] = $partner_entity->get('short_name')->value;
            $row["partner_country_$index"] = $implementation_country ?? '';
        } else {
            $row["partner_$index"] = '';
            $row["partner_country_$index"] = '';
        }
    }
    $contact_person_items = $applicant->get('contact_person');
    for ($i = 0;$i < 3;$i++) {
      $index = $i + 1;
      /** @var \Drupal\halo_entity_person\Entity\PersonEntity $ontact_person */
      $contact_person = $contact_person_items->get($i)->entity ?? FALSE;
      if ($contact_person) {
        $row["contact_person_last_name_$index"] = $contact_person->getLastName();
        $row["contact_person_first_name_$index"] = $contact_person->getFirstName();
        $row["contact_person_phone_1_$index"] = $contact_person->getPhone1();
        $row["contact_person_phone_2_$index"] = $contact_person->getPhone2();
        $row["contact_person_email_$index"] = $contact_person->getEmail();
      } else {
        $row["contact_person_last_name_$index"] = '';
        $row["contact_person_first_name_$index"] = '';
        $row["contact_person_phone_1_$index"] = '';
        $row["contact_person_phone_2_$index"] = '';
        $row["contact_person_email_$index"] = '';
      }
    }
    if ($first_key === $key) {
      $writer->addRow(\Box\Spout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray(array_keys($row)));
    }
    $writer->addRow(\Box\Spout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray($row));
  }
  $writer->close();
function ced_giga_report() {
  /** @var \Drupal\halo_entity_cfp\Entity\Cfp[] $cfps */
  $cfps = \Drupal::entityTypeManager()->getStorage('cfp')->loadByProperties([
    'state' => ['under_contracting', 'reject', 'project_implementation'],
  ]);
  $first_key = key($cfps);
  $writer = Box\Spout\Writer\Common\Creator\WriterEntityFactory::createXLSXWriter();
  $file_system = \Drupal::service('file_system');
  $temp_path = 'public://ced_giga_report.xlsx';
  $temp_path = $file_system->realpath($temp_path);
  $writer->openToFile($temp_path);
  foreach ($cfps as $key => $cfp) {
    $applicant = $cfp->getApplicantEntity();
    $row = [];
    $row['operational_number'] = $cfp->label();
    $row['company_name'] = $applicant->label();
    $row['vat_number'] = $applicant->get('vat_number')->value;
    $row['company_registration_number'] = $applicant->get('company_registration_number')->value;
    $date = $applicant->get('company_establishment_date')->date;
    $row['company_establishment_date'] = $date ? $date->format('Y.m.d') : '';
    $row['entitled_to_vat_refund'] = ((bool) $applicant->get('entitled_to_vat_refund')->value) ? 'Igen' : 'Nem';
    $row['main_company_address'] = $applicant->get('main_company_address')
      ->isEmpty() ? '' : address_basic_view($applicant->get('main_company_address')
      ->first());
    // Bank account.
    $bank_account_item = $applicant->get('bank_account_details');
    for ($i = 0; $i < 2; $i++) {
      /** @var \Drupal\halo_entity_bank_account\Entity\BankAccountEntity $bank_account */
      $bank_account = $bank_account_item->get($i)->entity ?? FALSE;
      $index = $i + 1;
      if ($bank_account) {
        $row["bank_account_name_$index"] = $bank_account->getName();
        $row["bank_address_address_$index"] = $bank_account->getAddress();
        $row["bank_account_number_$index"] = $bank_account->getAccountNumber();
      }
      else {
        $row["bank_account_name_$index"] = '';
        $row["bank_address_address_$index"] = '';
        $row["bank_account_number_$index"] = '';
      }
    }
    $company_representative_item = $applicant->get('company_representative');
    for ($i = 0;$i < 4;$i++) {
      $index = $i + 1;
      /** @var \Drupal\halo_entity_person\Entity\PersonEntity $company_representative */
      $company_representative = $company_representative_item->get($i)->entity ?? FALSE;
      if ($company_representative) {
        $row["company_representative_last_name_$index"] = $company_representative->getLastName();
        $row["company_representative_first_name_$index"] = $company_representative->getFirstName();
        $row["company_representative_assignment_$index"] = $company_representative->getAssignment();
        $row["company_representative_representation_type_$index"] = $company_representative->getRepresentationTypeLabel();
        $row["company_representative_phone_1_$index"] = $company_representative->getPhone1();
        $row["company_representative_email_$index"] = $company_representative->getEmail();
      } else {
        $row["company_representative_last_name_$index"] = '';
        $row["company_representative_first_name_$index"] = '';
        $row["company_representative_assignment_$index"] = '';
        $row["company_representative_representation_type_$index"] = '';
        $row["company_representative_phone_1_$index"] = '';
        $row["company_representative_email_$index"] = '';
      }
    }
    $contact_person_items = $applicant->get('contact_person');
    for ($i = 0;$i < 3;$i++) {
      $index = $i + 1;
      /** @var \Drupal\halo_entity_person\Entity\PersonEntity $ontact_person */
      $contact_person = $contact_person_items->get($i)->entity ?? FALSE;
      if ($contact_person) {
        $row["contact_person_last_name_$index"] = $contact_person->getLastName();
        $row["contact_person_first_name_$index"] = $contact_person->getFirstName();
        $row["contact_person_assignment_$index"] = $contact_person->getAssignment();
        $row["contact_person_phone_1_$index"] = $contact_person->getPhone1();
        $row["contact_person_email_$index"] = $contact_person->getEmail();
      } else {
        $row["contact_person_last_name_$index"] = '';
        $row["contact_person_first_name_$index"] = '';
        $row["contact_person_assignment_$index"] = '';
        $row["contact_person_phone_1_$index"] = '';
        $row["contact_person_email_$index"] = '';
      }
    }
    if ($first_key === $key) {
      $writer->addRow(\Box\Spout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray(array_keys($row)));
    }
    $writer->addRow(\Box\Spout\Writer\Common\Creator\WriterEntityFactory::createRowFromArray($row));
  }
  $writer->close();
}
{#
/**
 * @file
 * Template for the commerce email.
 *
 * Available variables:
 * - order_entity: The order entity.
 * - body: The email body.
 * - totals: An array of order totals values with the following keys:
 *   - subtotal: The order subtotal price.
 *   - adjustments: An array of adjustment totals:
 *     - type: The adjustment type.
 *     - label: The adjustment label.
 *     - total: The adjustment total price.
 *     - weight: The adjustment weight, taken from the adjustment type.
 *
 * @ingroup themeable
 */
#}
<table style="margin: 15px auto 0 auto; max-width: 768px; font-family: arial,sans-serif">
  <tbody>
  <tr>
    <td>
      <table style="text-align: center; min-width: 450px; margin: 5px auto 0 auto; border: 1px solid #cccccc; border-radius: 5px; padding: 40px 30px 30px 30px;">
        <tbody>
        <tr>
          <td style="font-size: 30px; padding-bottom: 30px"><img src="/themes/custom/zadi_theme/belmil_wave_logo_email.png" style="height: 60px;" /></td>
        </tr>
        <tr>
          <td>
            {{ body|raw }}
          </td>

          <tr>
            <td style="font-weight: bold; padding-top:15px; padding-bottom: 15px; text-align: left; border-top: 1px solid #cccccc; border-bottom: 1px solid #cccccc">
              {{ 'Order #@number details:'|t({'@number': order_entity.getOrderNumber}) }}
            </td>
          </tr>
          <tr>
            <td>
              <table style="padding-top: 15px; padding-bottom:15px; width: 100%">
                <tbody style="text-align: left;">
                {% for order_item in order_entity.getItems %}
                  <tr>
                    <td>
                      {{ order_item.getQuantity|number_format }} x
                    </td>
                    <td>
                      <span>{{ order_item.label }}</span>
                      <span style="float: right;">{{ order_item.getTotalPrice|commerce_price_format }}</span>
                    </td>
                  </tr>
                {% endfor %}
                </tbody>
              </table>
            </td>
          </tr>
        <tr>
          <td>
            <p style="margin-bottom: 0;">
              {{ 'Subtotal: @subtotal'|t({'@subtotal': totals.subtotal|commerce_price_format}) }}
            </p>
          </td>
        </tr>
        {% for adjustment in totals.adjustments %}
          <tr>
            <td>
              <p style="margin-bottom: 0;">
                {{ adjustment.label }}: {{ adjustment.total|commerce_price_format }}
              </p>
            </td>
          </tr>
        {% endfor %}
        <tr>
          <td>
            <p style="font-size: 24px; padding-top: 15px; padding-bottom: 5px;">
              {{ 'Order Total: @total'|t({'@total': order_entity.getTotalPrice|commerce_price_format}) }}
            </p>
          </td>
        </tr>

          {% if store_info %}
          <tr>
            <td>
              {% block additional_information %}
                {{ store_info|raw }}
              {% endblock %}
            </td>
          </tr>
          {% endif %}
        </tr>
        </tbody>
      </table>
    </td>
  </tr>
  </tbody>
</table>
<?php

/**
 * @file
 * Provides a bb2b_order.
 */

use Drupal\block\Entity\Block;
use Drupal\commerce_order\Entity\Order;
use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\bb2b_order\Plugin\Commerce\CheckoutPane\OrderTypePane;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\views\Form\ViewsForm;
use Drupal\views\ViewExecutable;
use Drupal\Core\Entity\EntityInterface;

/**
 * Implements hook_cron().
 */
function bb2b_order_cron() {
  $request_time = \Drupal::time()->getRequestTime();
  $state_key = 'bb2b_order.last_queue_time';
  $last_queue_time = \Drupal::state()->get($state_key, 0);

  // Once daily.
  if ($request_time - $last_queue_time >= (24 * 60 * 60)) {
    \Drupal::service('bb2b_order.order_reservation_cron')->run();

    \Drupal::state()->set($state_key, $request_time);
  }
}

/**
 * Implements hook_form_alter().
 */
function bb2b_order_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Add to cart on product page. This page will be deactivated.
  if (strpos($form_id, 'commerce_order_item_add_to_cart_form') !== FALSE) {
    array_unshift($form['#validate'], 'validate_available_quantity');
  }

  // Cart page.
  if (strpos($form_id, 'views_form_commerce_cart_form_') !== FALSE) {
    array_unshift($form['#validate'], 'validate_min_quantity');
  }

  // Quick order page.
  if ($form_state->getFormObject() instanceof ViewsForm) {
    /** @var \Drupal\views\ViewExecutable $view */
    $view = reset($form_state->getBuildInfo()['args']);

    if ($view->storage->get('id') == 'quick_order' && !empty($view->result)) {
      $order_id = \Drupal::service('commerce_cart.cart_provider')->getCartIds(\Drupal::currentUser());
      $order_id = reset($order_id);
      $order = \Drupal::entityTypeManager()->getStorage('commerce_order')
        ->load($order_id);

      $form['ddp_package_list'] = [
        '#type' => 'details',
        '#title' => t('At DDP orders, every box must be filled with the MOQ / box quantity.'),
        '#weight' => -2,
        '#open' => FALSE,
      ];

      $form['ddp_package_list']['block'] = [
        '#type' => 'container',
        'block' => [
          '#markup' => bb2b_order_render_shipment_package_block(),
        ],
      ];

      $name = 'field_order_type';
      /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
      $form_display = \Drupal::entityTypeManager()->getStorage('entity_form_display')
        ->load('commerce_order.default.default');

      $widget = $form_display->getRenderer($name);

      $roles = \Drupal::currentUser()->getRoles();
      $is_admin = in_array('administrator', $roles) || in_array('site_administrator', $roles);

      if ($widget) {
        $items = $order->get($name);
        $items->filterEmptyItems();

        $store = \Drupal::service('commerce_store.current_store')->getStore();
        $cart_provider = \Drupal::service('commerce_cart.cart_provider');
        $cart = $cart_provider->getCart('default', $store);

        if (!$cart) {
          $cart = $cart_provider->createCart('default', $store);
        }

        $order_type = $cart->get('field_order_type')->value;

        $form[$name] = $widget->form($items, $form, $form_state);
        $form[$name]['#access'] = $items->access('edit');
        $form[$name]['#required'] = TRUE;
        $form[$name]['#weight'] = -1;
        $form[$name]['widget']['#default_value'] = $order_type ?? 'ddp';
        $form[$name]['widget']['#ajax'] = [
          'callback' => 'bb2b_order_update_min_values',
        ];

        if ($is_admin) {
          unset($form[$name]['widget']['#options']['ddp_reservation']);
        }
        else {
          $form[$name]['widget']['ddp_reservation'] = [
            '#description' => t('You have 5 days to pay the order, otherwise it will be canceled.'),
          ];
        }
      }

      $options = [];
      $companies = \Drupal::entityTypeManager()
        ->getStorage('company')
        ->loadMultiple();

      foreach ($companies as $company) {
        if ($employee = $company->getEmployeeByRole('company_manager')) {
          $options[$employee->id()] = $employee->getDisplayName() . ' (' . $company->getName() . ')';
        }
      }

      $form['client'] = [
        '#type' => 'select2',
        '#id' => 'update-client',
        '#options' => $options,
        '#title' => t('Client'),
        '#required' => FALSE,
        '#weight' => -2,
        '#access' => $is_admin,
        '#ajax' => [
          'callback' => 'bb2b_order_update_client',
        ],
      ];

      if ($client_id = $order->getData('client_id')) {
        $form['client']['#default_value'] = $client_id;
      }

      $form['actions']['checkout'] = [
        '#type' => 'submit',
        '#value' => t('Delivery data'),
        '#weight' => 5,
        '#access' => \Drupal::currentUser()->hasPermission('access checkout'),
        '#submit' => array_merge($form['#submit'], ['bb2b_order_checkout_views_form_submit']),
        '#order_id' => $order_id,
      ];

    }
  }

  if ($form_id == 'commerce_shipping_method_edit_form') {
    $form['plugin']['widget'][0]['target_plugin_configuration']['form']['rate_label']['#attributes'] = ['readonly' => 'readonly'];
    $form['plugin']['widget'][0]['target_plugin_configuration']['form']['rate_label']['#description'] = t('Shown to customers during checkout. This value cannot be changed because we need it to match the name in Linker.');
  }
}

/**
 * Implements hook_views_pre_render().
 */
function bb2b_order_views_pre_render(ViewExecutable $view) {
  if ($view->id() == 'commerce_cart_form') {
    $order_id = $view->args[0];
    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = Order::load($order_id);

    /** @var \Drupal\bb2b_order\OrderServiceInterface $order_service */
    $order_service = \Drupal::service('bb2b_order.order_service');

    if (!$order_service->packageMinQuantityIsValid($order)) {
      $view->build_info['substitutions']['{{ admin_shipment_packages_block }}'] = bb2b_order_render_shipment_package_block();
    }
  }
  if ($view->id() === 'quick_order') {
    //@todo: This will go to history when we create stock base field on product.
    //@todo: Query will be in the StockStatus filter.
    $input = $view->getExposedInput();
    if (isset($input['stock_status']) && $input['stock_status'] === 'only_available') {
      // Get products from the view.
      $products = [];
      foreach ($view->result as $key => $product) {
        $products[$key] = $product->_entity;
      }
      // Check if those products are available.
      $order_service = \Drupal::service('bb2b_order.order_service');
      foreach ($products as $key => $product) {
        if (!$order_service->checkAccessForProduct($product))
        unset($view->result[$key]);
      }

    }
  }
}

/**
 * Gets the cart order for the given store and user.
 *
 * @param \Drupal\Core\Session\AccountInterface $account
 *   The user. If empty, the current user is assumed.
 *
 * @return \Drupal\commerce_order\Entity\OrderInterface|null
 *   The cart order, or NULL if none found.
 */
function bb2b_order_get_cart(AccountInterface $account = NULL) {
  $store = \Drupal::service('commerce_store.current_store')->getStore();
  $cart_provider = \Drupal::service('commerce_cart.cart_provider');
  $cart = $cart_provider->getCart('default', $store, $account);

  if (!$cart) {
    $cart = $cart_provider->createCart('default', $store, $account);
  }

  return $cart;
}

/**
 * Ajax handler used to remove min values validation for drop shipping.
 */
function bb2b_order_update_client($form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  $triggering_element = $form_state->getTriggeringElement();
  $trigger_name = array_shift($triggering_element['#parents']);

  if ($trigger_name === 'client') {
    $client_id = $triggering_element['#value'];
    $cart = bb2b_order_get_cart();

    $user = \Drupal::entityTypeManager()
      ->getStorage('user')
      ->load($client_id);

    $email = $user ? $user->getEmail() : NULL;

    $cart->setData('client_id', $client_id);
    $cart->setData('client_email', $email);
    $cart->save();

    $content = '<div class="ajax-progress-throbber"></div>';
    $response->addCommand(new AppendCommand('body', $content));
    $response->addCommand(new RedirectCommand(Url::fromUri('internal:/orders/quick-order')->toString()));
  }

  return $response;
}

/**
 * Ajax handler used to remove min values validation for drop shipping.
 */
function bb2b_order_update_min_values($form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  $triggering_element = $form_state->getTriggeringElement();
  $trigger_name = array_shift($triggering_element['#parents']);

  if ($trigger_name === 'field_order_type') {
    $order_type = $triggering_element['#value'];
    $rows = $form['output'][0]['#rows'];

    /** @var \Drupal\bb2b_order\OrderServiceInterface $order_service */
    $order_service = \Drupal::service('bb2b_order.order_service');

    $cart = bb2b_order_get_cart();

    /*if ($order_type !== 'drop_shipping') {
    $cart->setData('client_id', NULL);
    $cart->setData('client_email', NULL);
    }*/

    $cart->set('field_order_type', $order_type);
    $cart->save();

    foreach ($rows as $key => $row) {
      $field_stock = $form['warehouse_stock'][$key];
      $field_quantity = $form['edit_quantity'][$key];
      $field_chk = $form['chk_add_to_cart'][$key];
      $field_price = $form['price_group_price'][$key];
      $add_to_cart_button = $form['add_to_cart_button'][$key];

      /** @var \Drupal\commerce_product\Entity\ProductInterface $product */
      $product = $row->_entity;

      $access = $order_service->checkAccessForProduct($product);
      $available = $order_service->getAvailableStock($product);

      if ($order_type === 'drop_shipping') {
        $class = $field_quantity['#attributes']['class'] ?? [];
        $key = array_search('visually-hidden', $class);
        if ($key !== FALSE && $available > 0) {
          unset($field_quantity['#attributes']['class'][$key]);
        }

        $class = $field_chk['#attributes']['class'] ?? [];
        $key = array_search('visually-hidden', $class);
        if ($key !== FALSE && $available > 0) {
          unset($field_chk['#attributes']['class'][$key]);
        }

        $class = $add_to_cart_button['#attributes']['class'] ?? [];
        $key = array_search('visually-hidden', $class);
        if ($key !== FALSE && $available > 0) {
          unset($add_to_cart_button['#attributes']['class'][$key]);
        }
      }
      else {
        if (!$access || $available == 0) {
          if (!isset($field_quantity['#attributes']['class']['visually-hidden'])) {
            $field_quantity['#attributes']['class'][] = 'visually-hidden';
          }

          if (!isset($field_chk['#attributes']['class']['visually-hidden'])) {
            $field_chk['#attributes']['class'][] = 'visually-hidden';
          }

          $field_chk['#value'] = 0;
          if (!isset($add_to_cart_button['#attributes']['class']['visually-hidden'])) {
            $add_to_cart_button['#attributes']['class'][] = 'visually-hidden';
          }
        }
      }

      $method = $order_type === 'drop_shipping' ? 'addClass' : 'removeClass';
      $response->addCommand(new InvokeCommand('.min-qty', $method, ['visually-hidden']));

      $selector = $field_quantity['#attributes']['data-drupal-selector'];
      $quantity_selector = 'div[class*="' . preg_replace('/edit/', 'form-item', $selector, 1) . '"]';
      $response->addCommand(new ReplaceCommand($quantity_selector, $field_quantity));

      $selector = $field_chk['#attributes']['data-drupal-selector'];
      $chk_selector = 'div[class*="' . preg_replace('/edit/', 'form-item', $selector, 1) . '"]';
      $response->addCommand(new ReplaceCommand($chk_selector, $field_chk));

      $selector = $field_stock['#attributes']['data-drupal-selector'];
      $stock_selector = 'div[id="' . preg_replace('/edit/', 'form-item', $selector, 1) . '"]';
      $response->addCommand(new ReplaceCommand($stock_selector, $field_stock));

      $company = $order_service->getCompanyForClient($cart);
      $price = $order_service->getProductPriceForClient($product->getDefaultVariation(), $cart, $company);
      $field_price['list_price']['#value'] = $price['list_price'];
      $field_price['price']['#value'] = $price['price'];

      $selector = $field_price['#attributes']['data-drupal-selector'];
      $price_selector = 'div[id="' . preg_replace('/edit/', 'form-item', $selector, 1) . '"]';
      $response->addCommand(new ReplaceCommand($price_selector, $field_price));

      $button_selector = 'button[name="' . $add_to_cart_button['#name'] . '"]';
      $response->addCommand(new ReplaceCommand($button_selector, $add_to_cart_button));

      // Cart block.
      /** @var \Drupal\Core\Block\BlockPluginInterface $cart_block */
      $cart_block = \Drupal::service('plugin.manager.block')->createInstance('commerce_cart', []);
      if ($cart_block) {
        $response->addCommand(new ReplaceCommand('.cart--cart-block', $cart_block->build()));
      }
    }
  }

  return $response;
}

/**
 * Submit handler used to redirect to the checkout page.
 */
function bb2b_order_checkout_views_form_submit($form, FormStateInterface $form_state) {
  $order_id = $form_state->getTriggeringElement()['#order_id'];
  $form_state->setRedirect('commerce_checkout.form', ['commerce_order' => $order_id]);
}

/**
 * Form validation handler for commerce_order_item_add_to_cart_form*().
 *
 * @param array $form
 *   A build form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 */
function validate_available_quantity(array $form, FormStateInterface $form_state) {
  $product = $form_state->get('product');
  $quantity = (int) $form_state->getValue('quantity')[0]['value'];

  /** @var \Drupal\bb2b_order\OrderServiceInterface $order_service */
  $order_service = \Drupal::service('bb2b_order.order_service');

  $available = $order_service->getAvailableStock($product);
  if ($available < $quantity) {
    $form_state->setErrorByName('quantity][0][value', t('There are not enough quantity on the stock. Available: @available', [
      '@available' => $available,
    ]));
  }

}

/**
 * Form validation handler for views_form_commerce_cart_form_*().
 *
 * @param array $form
 *   A build form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 */
function validate_min_quantity(array $form, FormStateInterface $form_state) {
  $quantity = $form_state->getValue('edit_quantity');
  $rows = $form['output'][0]['#rows'];

  $triggering_element = $form_state->getTriggeringElement();
  $trigger_name = array_shift($triggering_element['#parents']);

  if ($trigger_name !== 'remove_button') {
    foreach ($rows as $key => $row) {
      /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $product_variation */
      $product_variation = $row->_relationship_entities['commerce_product_variation'];

      /** @var \Drupal\commerce_product\Entity\ProductInterface $product */
      $product = $product_variation->getProduct();

      /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
      $order = $row->_entity;

      /** @var \Drupal\bb2b_order\OrderServiceInterface $order_service */
      $order_service = \Drupal::service('bb2b_order.order_service');

      $available = $order_service->getAvailableStock($product);

      foreach ($order->getItems() as $order_item) {
        if ($order_item->getPurchasedEntityId() == $product_variation->id()) {
          $order_quantity = (int) $order_item->getQuantity();
          $diff_quantity = $quantity[$key] - $order_quantity;

          if ($available < $diff_quantity || ($diff_quantity == 0 && $available < $order_quantity)) {
            $form_state->setErrorByName('edit_quantity][' . $key, t('There are not enough quantity on the stock for product: @product_title. Available: @available', [
              '@product_title' => $product->getTitle(),
              '@available' => $diff_quantity == 0 ? $available : $available + $order_quantity,
            ]));
          }
        }
      }

    }
  }

}

/**
 * Implements hook_commerce_inline_form_PLUGIN_ID_alter().
 */
function bb2b_order_commerce_inline_form_customer_profile_alter(array &$inline_form, FormStateInterface $form_state, array &$complete_form) {
  if ($inline_form['#profile_scope'] === 'checkout' && !isset($inline_form['rendered'])) {
    $inline_form['address']['widget'][0]['address']['#after_build'] = [
      'change_country_code_ajax_callback',
      ['\Drupal\address\Element\Address', 'clearValues'],
    ];
  }
}

/**
 * Form API callback: Change ajax callback on country select.
 *
 * @param array $element
 *   The element array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state object.
 *
 * @return array
 *   The element array.
 */
function change_country_code_ajax_callback(array $element, FormStateInterface &$form_state) {
  $element['country_code']['country_code']['#ajax']['callback'] = [
    OrderTypePane::class,
    'ajaxRefresh',
  ];

  if ($form_state->getTriggeringElement()) {
    if ($form_state->getTriggeringElement()['#name'] === $element['country_code']['country_code']['#name']) {
      $form_state->setTriggeringElement($element['country_code']['country_code']);
    }
  }

  return $element;
}

/**
 * Implements hook_mail_alter().
 */
function bb2b_order_mail_alter(&$message) {
  if ($message['key'] == 'order_receipt' && !empty($message['params']['order'])) {
    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $message['params']['order'];
    $message['from'] = 'noreply@bescool.net';

    $subject_order_number = $order->getOrderNumber();
    if ($order->get('field_your_order_id')->isEmpty() === FALSE) {
      $client_order_number = $order->get('field_your_order_id')->value;
      $subject_order_number .= ' (' . $client_order_number . ')';
    }

    $message['subject'] = t('Order confirmation ID: @number', [
      '@number' => $subject_order_number,
    ]);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function bb2b_order_form_views_exposed_form_alter(&$form, FormStateInterface $form_state) {
  $view = $form_state->get('view');

  if ($view && in_array($view->id(), ['ongoing_orders', 'orders'])) {
    $form['search']['#placeholder'] = t('By email, order IDs, recipient name');
  }
}

/**
 * Implements hook_views_data_alter().
 */
function bb2b_order_views_data_alter(array &$data) {
  $data['commerce_order']['own_company_orders'] = [
    'title' => t('Own company orders'),
    'help' => t('Filter orders by own company for company managers.'),
    'filter' => [
      'group' => 'Order',
      'field' => 'uid',
      'id' => 'own_company_orders_filter',
      'entity_type' => 'commerce_order',
    ],
  ];
}

/**
 * Implements hook_views_data().
 */
function bb2b_order_views_data() {
  $data = [];

  //@todo: Maybe we could put ["products"] instead of ["views"].
  $data['views']['stock_status'] = [
    'title' => t('Stock status - Custom Filter'),
    'filter' => [
      'title' => t('Stock status - Custom Filter'),
      'field' => 'id',
      'id' => 'stock_status',
    ]
  ];

  return $data;
}

/**
 * Implements hook_ENTITY_TYPE_presave().
 */
function bb2b_order_commerce_order_presave(EntityInterface $entity) {
  if ($entity->get('field_created_invoice')->isEmpty()) {
    $entity->set('field_created_invoice', 'no');
  }
  // In case the your_order_id is edited after the order has been created,
  // and before the order is being shipped.
  if (!empty($entity->get('placed')->value)) {
    $order_service = \Drupal::service('bb2b_order.order_service');
    $order_service->resolveShipping($entity);
  }
}

/**
 * Implements hook_entity_access().
 */
function bb2b_order_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
  $allowed_entities = [
    'commerce_order',
    'commerce_order_item',
    'commerce_shipment',
  ];

  if (in_array($entity->getEntityTypeId(), $allowed_entities)) {
    if ($account->hasPermission('update default commerce_order')) {
      return AccessResult::allowed();
    }
  }

  return AccessResult::neutral();
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function bb2b_order_form_commerce_shipment_default_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (!\Drupal::currentUser()->hasPermission('access commerce administration pages')) {
    $form['title']['#access'] = FALSE;
    $form['shipping_method']['#access'] = FALSE;
    $form['tracking_code']['#access'] = FALSE;
    $form['shipment_items']['#access'] = FALSE;
    $form['recalculate_shipping']['#access'] = FALSE;
    $form['actions']['delete']['#access'] = FALSE;

    $request = \Drupal::requestStack()->getCurrentRequest();
    $shipment = $request->get('commerce_shipment');
    $form_state->set('shipment', $shipment);
    $shipment->setData('shipping_force_resolve', TRUE);

    if (isset($form['shipping_profile']['widget'][0]['profile']['edit_button'])) {
      $form['shipping_profile']['widget'][0]['profile']['edit_button']['#attributes']['class'][] = 'button-secondary';
    }

    // Redirect user to order view.
    $form['actions']['submit']['#submit'][] = 'bb2b_order_shipment_edit_form_submit';
  }
}

/**
 * Custom submit handler for the shipment edit form.
 */
function bb2b_order_shipment_edit_form_submit($form, FormStateInterface $form_state) {
  $shipment = $form_state->get('shipment');
  $account = \Drupal::currentUser();
  if ($shipment) {
    $admin_permission = $account
      ->hasPermission($shipment->getEntityType()->getAdminPermission());
    $order = $shipment->getOrder();

    if ($order && !$admin_permission) {
      /*$form_state->setRedirect('entity.commerce_order.user_view', [
      'commerce_order' => $order->id(),
      'user' => $order->getCustomerId(),
      ]);*/

      $form_state->setRedirect('entity.commerce_order.edit_form', [
        'commerce_order' => $order->id(),
      ]);
    }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function bb2b_order_form_commerce_order_default_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $user = \Drupal::currentUser();
  if (!$user->hasPermission('access commerce administration pages')) {
    $form['field_credit_note']['#access'] = FALSE;
    $form['field_internal_note']['#access'] = FALSE;
    $form['field_order_notes']['#access'] = FALSE;
    $form['field_order_type']['#access'] = FALSE;
    $form['field_vat_verified']['#access'] = FALSE;
    $form['field_tracking_code_date']['#access'] = FALSE;
    $form['coupons']['#access'] = FALSE;
    $form['billing_profile']['widget'][0]['profile']['select_address']['#access'] = FALSE;
    $form['billing_profile']['widget'][0]['profile']['edit_button']['#access'] = FALSE;
    $form['billing_profile']['widget'][0]['profile']['copy_fields']['enable']['#access'] = FALSE;
    $form['shipments']['#access'] = FALSE;

    $form['order_items']['widget']['actions']['ief_add']['#attributes']['class'][] = 'button-secondary';

    if (isset($form['order_items']['widget']['entities'])) {
      foreach ($form['order_items']['widget']['entities'] as $key => &$entity) {
        if (is_numeric($key)) {
          $entity['actions']['ief_entity_edit']['#attributes']['class'][] = 'button-secondary';
          $entity['actions']['ief_entity_remove']['#attributes']['class'][] = 'delete-order-item';
        }
      }
    }

    $form['ddp_package_list'] = [
      '#type' => 'details',
      '#title' => t('At DDP orders, every box must be filled with the MOQ / box quantity.'),
      '#weight' => -2,
      '#open' => FALSE,
    ];

    $form['ddp_package_list']['block'] = [
      '#type' => 'container',
      'block' => [
        '#markup' => bb2b_order_render_shipment_package_block(),
      ],
    ];

    $header = [t('Title'), t('Shipping method'), t('Price'), t('Actions')];
    $rows = [];
    $request = \Drupal::requestStack()->getCurrentRequest();

    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $request->get('commerce_order');
    if ($order && $user->hasPermission('update default commerce_order')) {
      $form_state->set('order', $order);
      /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface[] $shipments */
      $shipments = $order->get('shipments')->referencedEntities();
      foreach ($shipments as $shipment) {
        $row = $links = [];
        $links['edit'] = [
          'title' => t('Edit'),
          'url' => $shipment->toUrl('edit-form'),
          'attributes' => [
            'role' => 'button',
            'class' => ['button button-secondary'],
            // 'target' => '_blank',
          ],
        ];

        $row[] = [
          'data' => [
            '#type' => 'markup',
            '#markup' => $shipment->label(),
          ],
        ];

        $row[] = [
          'data' => [
            '#type' => 'markup',
            '#markup' => $shipment->getShippingMethod()->label(),
          ],
        ];

        $row[] = [
          'data' => [
            '#type' => 'markup',
            '#markup' => $shipment->getAmount(),
          ],
        ];

        $row[] = [
          'data' => [
            '#type' => 'operations',
            '#links' => $links,
          ],
        ];

        $rows[] = $row;
      }
    }

    $form['shipments_list'] = [
      '#type' => 'container',
      '#weight' => $form['shipments']['#weight'] ?? 0,
      'shipments' => [
        '#type' => 'fieldset',
        '#title' => t('Shipments'),
        'entities' => [
          '#theme' => 'table',
          '#rows' => $rows,
          '#header' => $header,
        ],
      ],
    ];

    array_unshift($form['#validate'], 'validate_first_package_is_full');

    // Redirect user to order view.
    $form['actions']['submit']['#submit'][] = 'bb2b_order_order_edit_form_submit';
  }
}

/**
 * Custom submit handler for the order edit form.
 */
function bb2b_order_order_edit_form_submit($form, FormStateInterface $form_state) {
  $order = $form_state->get('order');
  if ($order) {
    $form_state->setRedirect('entity.commerce_order.user_view', [
      'commerce_order' => $order->id(),
      'user' => $order->getCustomerId(),
    ]);
  }
}

/**
 * Render shipment packages block content.
 *
 * @return mixed
 *   The rendered content.
 */
function bb2b_order_render_shipment_package_block() {
  $shipment_package_view = views_embed_view('shipment_package_list', 'block_1');

  return \Drupal::service('renderer')
    ->render($shipment_package_view);
}

/**
 * Form validation handler for commerce_order_default_edit_form().
 *
 * @param array $form
 *   A build form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 */
function validate_first_package_is_full(array $form, FormStateInterface $form_state) {
  $triggering_element = $form_state->getTriggeringElement();

  $parents = $triggering_element['#array_parents'];
  $triggering_element_name = array_pop($parents);

  if ($triggering_element_name === 'submit') {
    $storage = $form_state->getStorage();
    $order_items_array = $storage['inline_entity_form']['order_items-form']['entities'] ?? [];

    /** @var \Drupal\bb2b_order\OrderServiceInterface $order_service */
    $order_service = \Drupal::service('bb2b_order.order_service');

    $order_type = NULL;
    $order_items = [];
    foreach ($order_items_array as $order_item_array) {
      if (isset($order_item_array['entity'])) {
        $order_items[] = $order_item_array['entity'];
        if (!$order_type) {
          $order = $order_item_array['entity']->getOrder();
          $order_type = $order ? $order->get('field_order_type')->value : NULL;
        }
      }
    }

    if ($order_items && $order_type !== 'drop_shipping') {
      $quantities = $order_service->getOrderedQuantitiesByTag($order_items);
      if ($order_service->minQuantityIsNotValid($quantities)) {
        $form_state->setErrorByName('full_package', t('Your order can not be completed because the conditions for minimum order quantity is not met.'));
      }
    }
  }

}

/**
 * Implements hook_inline_entity_form_entity_form_alter().
 */
function bb2b_order_inline_entity_form_entity_form_alter(&$entity_form, &$form_state) {
  $access_commerce_admin_pages = \Drupal::currentUser()
    ->hasPermission('access commerce administration pages');
  if (!$access_commerce_admin_pages) {
    if ($entity_form['#entity_type'] === 'commerce_shipment') {
      $entity_form['title']['#access'] = FALSE;
      $entity_form['shipping_method']['#access'] = FALSE;
      $entity_form['tracking_code']['#access'] = FALSE;
    }
    elseif ($entity_form['#entity_type'] === 'commerce_order_item') {
      $entity_form['unit_price']['#access'] = FALSE;
      $default_quantity = (int) ($entity_form['quantity']['widget'][0]['value']['#default_value'] ?? 0);
      $entity_form['quantity']['widget'][0]['value']['#default_value'] = $default_quantity;
      $entity_form['purchased_entity']['widget'][0]['target_id']['#attributes']['class'][] = 'form-order-item-autocomplete';
    }
  }
}

/**
 * Implements hook_entity_base_field_info_alter().
 */
function bb2b_order_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
  if ($entity_type->id() === 'commerce_order_item' && isset($fields['quantity'])) {
    $fields['quantity']->addConstraint('CheckAvailable');
  }
}

/**
 * Implements hook_ENTITY_TYPE_presave().
 */
function bb2b_order_commerce_shipment_presave(ShipmentInterface $shipment) {
  $order = $shipment->getOrder();
  $pending_order = $order && $order->getState()->getId() === 'pending';
  $shipping_force_resolve = $shipment->getData('shipping_force_resolve', FALSE);

  if ($shipment->original && $pending_order && $shipping_force_resolve) {
    /** @var \Drupal\bb2b_order\OrderServiceInterface $order_service */
    $order_service = \Drupal::service('bb2b_order.order_service');
    $order = $shipment->getOrder();

    $shipping_method = $order_service
      ->getShippingMethodForOrder($order, $shipment->getShippingProfile());

    if ($shipping_method) {
      $adjustments = $order->getAdjustments();
      $order_service->setShippingMethodForShipment($shipment, $shipping_method, 0, $adjustments);
      $order->set('adjustments', $adjustments);
    }
    $shipment->setData('shipping_force_resolve', FALSE);
  }
}

/**
 * Implements hook_theme().
 */
function bb2b_order_theme($existing, $type, $theme, $path) {
  return [
    'commerce_checkout_form__with_sidebar' => [
      'base hook' => 'commerce_checkout_form',
      'variables' => [
        'ordered_products' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_preprocess_HOOK().
 */
function bb2b_order_preprocess_commerce_checkout_form__with_sidebar(&$variables) {
  $block = Block::load('views_block__ordered_products_block_2');
  $step = $variables['form']['#step_id'] ?? NULL;

  if ($block && $step === 'complete') {
    $variables['ordered_products'] = $block->getPlugin()->build();
    $variables['ordered_products']['#attributes']['class'][] = 'ordered-products-block';
  }
}

/**
 * Implements hook_tokens_alter().
 */
function bb2b_order_tokens_alter(array &$replacements, array $context, BubbleableMetadata $bubbleable_metadata) {
  // Clear empty token.
  if (isset($context['data']['entity_type']) &&
    $context['data']['entity_type'] === 'commerce_order'
  ) {
    foreach ($context['tokens'] as $token) {
      if (!isset($replacements[$token])) {
        $replacements[$token] = '';
      }
    }
  }
}

/**
 * Implements hook_entity_view_alter().
 */
function bb2b_order_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
  if ($build['#view_mode'] === 'user' &&
    $build['#entity_type'] === 'commerce_order'
  ) {
    /** @var \Drupal\commerce_order\Entity\OrderInterface $entity */
    $entity_state = $entity->getState()->getId();

    if ($entity_state === 'pending') {
      $order = $build['#commerce_order'];
      $vat_verified = $order->get('field_vat_verified')->value;

      if (($vat_verified === '1') || ($vat_verified === '0')) {
        $ship_uri = '/orders/' . $entity->id() . '/send';
        $ship_url = Url::fromUri('internal:' . $ship_uri);

        $build['ship_the_order_link'] = [
          '#type' => 'link',
          '#title' => t('Ship the order'),
          '#url' => $ship_url,
          '#weight' => -11,
          '#attributes' => [
            'class' => ['button button-secondary'],
          ],
        ];
      }

      $user = $entity->getCustomer();
      if ($user->hasPermission('update default commerce_order')) {
        $build['edit_link'] = [
          '#type' => 'link',
          '#title' => t('Edit order'),
          '#url' => $entity->toUrl('edit-form'),
          '#weight' => -10,
          '#attributes' => [
            'class' => ['button button-secondary'],
          ],
        ];
      }
    }
  }
}
<?php

namespace Drupal\bb2b_order;

use Drupal\bb2b_company\CompanyInterface;
use Drupal\commerce_order\Adjustment;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_price\Price;
use Drupal\commerce_product\Entity\ProductInterface;
use Drupal\commerce_product\Entity\ProductVariationInterface;
use Drupal\commerce_shipping\Entity\ShipmentInterface;
use Drupal\commerce_shipping\Entity\ShippingMethod;
use Drupal\commerce_shipping\Entity\ShippingMethodInterface;
use Drupal\commerce_shipping\ShippingOrderManagerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\profile\Entity\ProfileInterface;

/**
 * Class Order Service.
 */
class OrderService implements OrderServiceInterface {

  use StringTranslationTrait;

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

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

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

  /**
   * The shipping order manager.
   *
   * @var \Drupal\commerce_shipping\ShippingOrderManagerInterface
   */
  protected $shippingOrderManager;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The private temp store.
   *
   * @var \Drupal\Core\TempStore\PrivateTempStore
   */
  protected $tempStore;

  /**
   * The Base Linker config.
   *
   * @var \Drupal\Core\Config\Config
   */
  protected $baseLinkerConfig;

  /**
   * Constructs a new OrderService object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache.
   * @param \Drupal\commerce_shipping\ShippingOrderManagerInterface $shipping_order_manager
   *   The shipping order manager.
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection.
   * @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
   *   The tempstore factory.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The configuration factory.
   */
  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    MessengerInterface $messenger,
    CacheBackendInterface $cache,
    ShippingOrderManagerInterface $shipping_order_manager,
    Connection $database,
    PrivateTempStoreFactory $temp_store_factory,
    ConfigFactoryInterface $config_factory
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->messenger = $messenger;
    $this->cache = $cache;
    $this->shippingOrderManager = $shipping_order_manager;
    $this->database = $database;
    $this->tempStore = $temp_store_factory->get('bb2b_order');
    $this->baseLinkerConfig = $config_factory->get('bb2b_baselinker.settings');
  }

  /**
   * {@inheritdoc}
   */
  public function getApplicableWarehouse(ProductInterface $product) {
    $cid = 'get_applicable_warehouse_' . $product->id();
    $cache_data = $this->cache->get($cid);

    if (isset($cache_data->data)) {
      return $cache_data->data;
    }
    else {
      // @todo Logic to check which warehouse will be used in reality.
      $warehouse = $this->entityTypeManager->getStorage('taxonomy_term')->loadByProperties([
        'vid' => 'warehouse',
        'field_warehouse_code' => 'PL',
      ]);

      $warehouse = reset($warehouse);

      $product_warehouses = $product->get('field_warehouse')->referencedEntities();

      $applicable_warehouse = NULL;
      foreach ($product_warehouses as $product_warehouse) {
        $warehouse_target_id = $product_warehouse->get('field_warehouse')->target_id;

        if ($warehouse_target_id == $warehouse->id()) {
          $applicable_warehouse = $product_warehouse;
        }
      }

      if (!empty($applicable_warehouse)) {
        $cache_tags = [
          'commerce_product:' . $product->id(),
          'paragraph:' . $applicable_warehouse->id(),
        ];
        $this->cache->set($cid, $applicable_warehouse, CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
      }

      return $applicable_warehouse;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function setStockOnWarehouse(ParagraphInterface $warehouse, int $stock) {
    if ($stock <= 0) {
      $warehouse->setUnpublished();
    }
    elseif (!$warehouse->isPublished() && $stock > 0) {
      $warehouse->setPublished();
    }

    $warehouse->set('field_available', $stock);
    $warehouse->set('field_last_linker_stock_sync', time());
    $warehouse->save();
  }

  /**
   * {@inheritdoc}
   */
  public function getAvailableStock(ProductInterface $product, ?ParagraphInterface $product_warehouse = NULL) {
    $cid = 'get_available_stock_' . $product->id();
    $cache_data = $this->cache->get($cid);

    if (isset($cache_data->data)) {
      return $cache_data->data;
    }
    else {
      // For regular non-bundle products.
      if ($product->get('field_bundle_product_skus')->isEmpty()) {
        $available_stock = $this->doGetAvailableStock($product, $product_warehouse);
      }
      // For bundle products we need to check all products that are part of the
      // bundle.
      else {
        /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation_item */
        $available_stock_for_product = [];

        foreach ($product->get('field_bundle_product_skus')->referencedEntities() as $variation_item) {
          $product_item = $variation_item->getProduct();
          $available_stock_for_product[] = $this->doGetAvailableStock($product_item, $product_warehouse);
        }

        $available_stock = min($available_stock_for_product);
      }

      $cache_tags = [
        'commerce_product:' . $product->id(),
      ];

      if ($product_warehouse !== NULL) {
        $cache_tags[] = 'paragraph:' . $product_warehouse->id();
      }

      $this->cache->set($cid, $available_stock, CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
      return $available_stock;
    }
  }

  /**
   * Gets the available stock value.
   *
   * @param \Drupal\commerce_product\Entity\ProductInterface $product
   *   The product.
   * @param \Drupal\paragraphs\ParagraphInterface|null $product_warehouse
   *   The warehouse.
   *
   * @return int
   *   The available stock.
   */
  protected function doGetAvailableStock(ProductInterface $product, ?ParagraphInterface $product_warehouse = NULL) {
    if (!$product_warehouse) {
      /** @var \Drupal\paragraphs\ParagraphInterface $product_warehouse */
      $product_warehouse = $this->getApplicableWarehouse($product);

      if (!$product_warehouse) {
        return 0;
      }
    }

    $local_stock = (int) $product_warehouse->get('field_available')->value;

    $stock_reserved_belmil_de = (int) $product->get('field_stock_reserved_belmil_de')->value;
    if ($stock_reserved_belmil_de > 0) {
      $local_stock -= $stock_reserved_belmil_de;
    }

    $stock_reserved = (int) $product_warehouse->get('field_reserved')->value;
    if ($stock_reserved > 0) {
      $local_stock -= $stock_reserved;
    }

    return $local_stock > 0 ? $local_stock : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function checkAccessForProduct(ProductInterface $product) {
    $cid = 'access_for_product_' . $product->id();
    $cache_data = $this->cache->get($cid);

    if (isset($cache_data->data)) {
      return $cache_data->data;
    }
    else {
      /** @var \Drupal\paragraphs\ParagraphInterface $warehouse */
      $warehouse = $this->getApplicableWarehouse($product);
      $available = $this->getAvailableStock($product, $warehouse);
      $result = $warehouse && $available > 0;

      $cache_tags = [
        'commerce_product:' . $product->id(),
      ];

      if ($warehouse !== NULL) {
        $cache_tags[] = 'paragraph:' . $warehouse->id();
      }

      $this->cache->set($cid, $result, CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
      return $result;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getCompanyForClient(OrderInterface $order) {
    $customer_id = $order->getData('client_id', $order->getCustomerId());
    if (!$customer_id) {
      return NULL;
    }

    return $this->getCompanyForCustomer($customer_id);
  }

  /**
   * {@inheritdoc}
   */
  public function getCompanyForCustomer($customer_id) {
    $company_storage = $this->entityTypeManager->getStorage('company');
    $company = $company_storage->loadByProperties([
      'employees' => $customer_id,
    ]);
    $company = reset($company);

    if (!$company) {
      return NULL;
    }

    return $company;
  }

  /**
   * {@inheritdoc}
   */
  public function getProductPriceForClient(ProductVariationInterface $variation, OrderInterface $order, CompanyInterface $company = NULL) {
    $price = $variation->getPrice();
    $list_price = $variation->getListPrice();
    $calculated_price = [
      'price' => $price,
      'list_price' => $list_price,
    ];

    if (!$company) {
      return $calculated_price;
    }

    $order_type = $order->get('field_order_type')->value;

    $client_price_groups = $company->get('price_group')->referencedEntities() ?? NULL;
    $with_return = $company->get('auto_generate_return_label')->value;

    if (!$client_price_groups) {
      return $calculated_price;
    }

    $client_price_group_ids = array_map(function ($client_price_group) {
      return $client_price_group->id();
    }, $client_price_groups);

    $product = $variation->getProduct();
    $product_price_groups = $product->get('field_price_group')->referencedEntities();

    $price_groups = array_filter($product_price_groups, function ($product_price_group) use ($client_price_group_ids) {
      $price_group_terms = $product_price_group->get('field_price_group')->referencedEntities();

      /** @var \Drupal\taxonomy\Entity\Term $price_group_term */
      $price_group_term = reset($price_group_terms);

      return $price_group_term && in_array($price_group_term->id(), $client_price_group_ids);
    });

    if (!$price_groups) {
      return $calculated_price;
    }

    $price_group_price = 0;
    foreach ($price_groups as $price_group) {
      if ($order_type === 'ddp' || $order_type === 'ddp_reservation') {
        $price_group_price = $price_group->get('field_ddp')->value;
      }
      elseif ($order_type === 'drop_shipping' && !$with_return) {
        $price_group_price = $price_group->get('field_dsnr')->value;
      }
      elseif ($order_type === 'drop_shipping' && $with_return) {
        $price_group_price = $price_group->get('field_dswr')->value;
      }

      if ($price_group_price > 0) {
        break;
      }
    }

    if ($price_group_price == 0) {
      return $calculated_price;
    }

    $currency = $price->getCurrencyCode();

    $calculated_price['price'] = new Price((string) $price_group_price, $currency);
    if ($list_price) {
      $calculated_price['list_price'] = new Price((string) $price_group_price, $currency);
    }

    return $calculated_price;
  }

  /**
   * {@inheritdoc}
   */
  public function packageMinQuantityIsValid(OrderInterface $order): bool {
    return $this->getNotFullPackageCount($order) === 0;
  }

  /**
   * {@inheritdoc}
   */
  public function getNotFullPackageCount(OrderInterface $order): int {
    $order_type = $order->get('field_order_type')->value;
    $not_valid_packages = 0;

    if ($order_type === 'drop_shipping') {
      return 0;
    }

    // Sum order item quantities by Tag.
    $quantities = $this->getOrderedQuantitiesByTag($order->getItems());

    if (!$quantities) {
      return 1;
    }

    $shipment_packages = $this->entityTypeManager
      ->getStorage('shipment_package')
      ->loadMultiple();

    // Check min quantities.
    foreach ($shipment_packages as $shipment_package) {
      $ordered_quantity = 0;
      $min_order_quantity = (int) $shipment_package->get('max_item_per_package')->value;
      $articles = $shipment_package->get('article_no')->referencedEntities();
      foreach ($articles as $article) {
        if (isset($quantities[$article->id()])) {
          $ordered_quantity += $quantities[$article->id()];
        }
      }

      if ($ordered_quantity > 0 &&
        (!$min_order_quantity
        || $ordered_quantity < $min_order_quantity
        || $ordered_quantity % $min_order_quantity != 0)
      ) {
        $not_valid_packages++;
      }
    }

    return $not_valid_packages;
  }

  /**
   * {@inheritdoc}
   */
  public function firstPackageIsNotFull(OrderInterface $order): bool {
    $order_type = $order->get('field_order_type')->value;
    if ($order_type === 'drop_shipping') {
      return FALSE;
    }

    // Sum order item quantities by Tag.
    $quantities = $this->getOrderedQuantitiesByTag($order->getItems());

    return $this->minQuantityIsNotValid($quantities);
  }

  /**
   * Check ordered quantity.
   *
   * @param array $quantities
   *   The ordered quantities.
   *
   * @return bool
   *   Return TRUE if minimum ordered quantity is not valid.
   */
  public function minQuantityIsNotValid(array $quantities): bool {
    $shipment_packages = $this->entityTypeManager
      ->getStorage('shipment_package')
      ->loadMultiple();

    // Check min quantities.
    foreach ($shipment_packages as $shipment_package) {
      $ordered_quantity = 0;
      $min_order_quantity = (int) $shipment_package->get('max_item_per_package')->value;
      $articles = $shipment_package->get('article_no')->referencedEntities();
      foreach ($articles as $article) {
        if (isset($quantities[$article->id()])) {
          $ordered_quantity += $quantities[$article->id()];
        }
      }

      if ($ordered_quantity > 0 && $ordered_quantity < $min_order_quantity) {
        return TRUE;
      }
    }

    return FALSE;
  }

  /**
   * Gets the order item quantities by tag.
   *
   * @param \Drupal\commerce_order\Entity\OrderItemInterface[] $order_items
   *   The order items.
   */
  public function getOrderedQuantitiesByTag(array $order_items): array {
    $quantities = [];

    foreach ($order_items as $order_item) {
      /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $purchased_entity */
      $purchased_entity = $order_item->getPurchasedEntity();
      if (!$purchased_entity || $purchased_entity->getEntityTypeId() != 'commerce_product_variation') {
        continue;
      }

      /** @var \Drupal\commerce_product\Entity\ProductInterface $product */
      $product = $purchased_entity->getProduct();

      $tag = $product->get('field_tag')->referencedEntities();
      $tag = reset($tag);

      if (!$tag) {
        return [];
      }

      $tag_id = $tag->id();

      if (isset($quantities[$tag_id])) {
        $quantities[$tag_id] += (int) $order_item->getQuantity();
      }
      else {
        $quantities[$tag_id] = (int) $order_item->getQuantity();
      }
    }

    return $quantities;
  }

  /**
   * {@inheritdoc}
   */
  public function getMinOrderQuantity(ProductInterface $product) {
    $min_order_quantity = 1;

    $tag = $product->get('field_tag')->referencedEntities();
    $tag = reset($tag);

    if ($tag) {
      $shipment_packages = $this->entityTypeManager
        ->getStorage('shipment_package')
        ->loadMultiple();

      $package = array_filter($shipment_packages, function ($shipment_package) use ($tag) {
        $terms = $shipment_package->get('article_no')->referencedEntities();

        $term_ids = array_map(function ($term) {
          return $term->id();
        }, $terms);

        return in_array($tag->id(), $term_ids);
      });

      $package = reset($package);

      $min_order_quantity = $package ? $package->get('max_item_per_package')->value : 1;
    }

    return $min_order_quantity;
  }

  /**
   * Resolve shipping for order.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   The order.
   * @param \Drupal\profile\Entity\ProfileInterface|null $billing_profile
   *   The billing profile.
   * @param \Drupal\profile\Entity\ProfileInterface|null $shipping_profile
   *   The shipping profile.
   * @param string|null $shipping_country
   *   The shipping country.
   */
  public function resolveShipping(
    OrderInterface $order,
    ?ProfileInterface $billing_profile = NULL,
    ?ProfileInterface $shipping_profile = NULL,
    ?string $shipping_country = NULL
  ) {

    if (!$billing_profile) {
      $billing_profile = $order->getBillingProfile();
    }

    if (!$shipping_profile) {
      $shipping_profile = $order->collectProfiles()['shipping'] ?? NULL;
    }

    if (!$shipping_profile && $billing_profile) {
      $shipping_profile = $billing_profile->createDuplicate();
      $shipping_profile->unsetData('copy_to_address_book');
    }

    if (!$billing_profile && $shipping_profile) {
      $billing_profile = $shipping_profile->createDuplicate();
      $billing_profile->unsetData('copy_to_address_book');
    }

    if (!$billing_profile || !$shipping_profile) {
      return;
    }

    $shipments = $this->shippingOrderManager->pack($order, $shipping_profile);

    $store_key = 'shipping_force_resolve:' . $order->id();
    $shipping_force_resolve = $this->tempStore->get($store_key);
    $this->tempStore->delete($store_key);

    $shipping_country = $shipping_country ?: $shipping_profile
      ->get('address')->country_code;
    $shipping_method = $this->getShippingMethodForOrder($order, $billing_profile, $shipping_country);

    if ($shipping_method) {
      $shipping_adjustments = $order->getAdjustments(['shipping']);
      foreach ($shipping_adjustments as $adjustment) {
        $order->get('adjustments')->removeAdjustment($adjustment);
      }

      $adjustments = $order->getAdjustments();

      /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment */
      foreach ($shipments as $key => $shipment) {
        $this->setShippingMethodForShipment($order, $shipment, $shipping_method, $key, $adjustments);
        if ($shipping_force_resolve) {
          $shipment->save();
        }
      }

      $order->set('adjustments', $adjustments);
    }

    $order->set('shipments', $shipments);
  }

  /**
   * Get shipping method for order.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   The order.
   * @param \Drupal\profile\Entity\ProfileInterface|null $profile
   *   The profile.
   * @param string|null $shipping_country
   *   The shipping country.
   *
   * @return \Drupal\commerce_shipping\Entity\ShippingMethodInterface|null
   *   The shipping method.
   */
  public function getShippingMethodForOrder(OrderInterface $order, ProfileInterface $profile, ?string $shipping_country = NULL) {
    $shipping_method = NULL;

    /** @var \Drupal\commerce_shipping\Entity\ShippingMethodInterface[] $shipping_methods */
    $shipping_methods = ShippingMethod::loadMultiple();
    $shipping_methods = array_filter($shipping_methods, function ($item) {
      return $item->getPlugin()->getPluginId() == 'belmil_shipping' && $item->isEnabled();
    });

    usort($shipping_methods, function ($a, $b) {
      return $a->getWeight() <=> $b->getWeight();
    });

    $time_now = DrupalDateTime::createFromTimestamp(time());
    $time_now->setTimezone(new \DateTimeZone('Europe/Belgrade'));
    // Saturday is also working day. Only sunday is weekend.
    $is_weekend = $time_now->format('N') == 7;

    $order_type = $order->get('field_order_type')->value;
    $order_source = $order->get('field_source')->value;
    $belmil_de_shipping_method = NULL;
    if ($order_source === 'belmilde') {
      $belmil_de_shipping_method = $order->getData('belmil_de_shipping_method', 'standard');
    }

    $amazon_shipping_method = NULL;
    if ($order_source === 'amazon') {
      $amazon_shipping_method = $order->getData('amazon_shipping_method', 'Std DE Dom_3');
    }

    $packstation_address = $profile->hasField('field_packstation_address') &&
      $profile->get('field_packstation_address')->value;

    $packstation_postnumber = $profile->hasField('field_packstation_postnumber') ?
      $profile->get('field_packstation_postnumber')->value : NULL;

    $shipping_profile_country = $shipping_country ?: $profile->get('address')->country_code;

    $shipping_with_packstation = $shipping_profile_country === 'DE' &&
      $packstation_address && $packstation_postnumber;

    foreach ($shipping_methods as $shipping_method_entity) {
      $method_configuration = $shipping_method_entity->getPlugin()
        ->getConfiguration();
      $method_countries = $method_configuration['belmil'] ?? [];

      // Set amazon related shipping method.
      if ($order_source == 'amazon') {
        // DE Standard or Express.
        if ($shipping_method_entity->label() == 'DEDHL' &&
          in_array($amazon_shipping_method, ['Std DE Dom_3', 'Exp DE Dom_1'])
        ) {
          $shipping_method = $shipping_method_entity;
          break;
        }
        // International EU.
        elseif ($shipping_method_entity->label() == 'DHL DE INTERNATIONAL' &&
          $amazon_shipping_method == 'Std DE Intl_2'
        ) {
          $shipping_method = $shipping_method_entity;
          break;
        }
      }

      // DHL Packstation address.
      if ($shipping_with_packstation &&
        isset($method_configuration['address']) &&
        $method_configuration['address']['packstation']
      ) {
        foreach ($method_countries as $country_code => $country_config) {
          if ($shipping_profile_country == $country_code &&
            !empty($country_config['enabled'])
          ) {
            $shipping_method = $shipping_method_entity;
            break 2;
          }
        }
      }

      // Check store condition.
      if (!in_array($order->getStoreId(), $shipping_method_entity->getStoreIds())) {
        continue;
      }

      // Order total conditions check.
      $conditions = $shipping_method_entity->getConditions();
      if ($conditions) {
        /** @var \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface[] $order_total_conditions */
        $order_total_conditions = array_filter($conditions, function ($condition) {
          /** @var \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface $condition */
          return $condition->getEntityTypeId() == 'commerce_order' &&
            $condition->getPluginId() === 'order_total_price';
        });
        $order_total_condition = reset($order_total_conditions);
        if ($order_total_condition && !$order_total_condition->evaluate($order)) {
          continue;
        }
      }

      if (
        isset($method_configuration['belmil']) &&
        isset($method_configuration['order_types']) &&
        isset($method_configuration['time']) &&
        !$shipping_with_packstation
      ) {
        foreach ($method_countries as $country_code => $country_config) {
          if (
            $shipping_profile_country == $country_code &&
            !empty($country_config['enabled']) &&
            !empty($method_configuration['order_types'][$order_type])
          ) {
            // Check time conditions for weekend.
            if ($is_weekend === TRUE) {
              if (!empty($method_configuration['time']['use_on_weekends']) && !empty($method_configuration['time']['use_on_weekends_type'])) {
                $weekends_time_until = new DrupalDateTime($method_configuration['time']['weekends_time_until']);
                $weekends_time_after = new DrupalDateTime($method_configuration['time']['weekends_time_after']);

                // For DDP orders we don't have to check time conditions.
                $ignore_time_conditions_for_ddp = !empty($method_configuration['time']['ignore_for_ddp']);
                if ($ignore_time_conditions_for_ddp === TRUE &&
                  in_array($order_type, ['ddp', 'ddp_reservation'])
                ) {
                  $shipping_method = $shipping_method_entity;
                  break 2;
                }

                // Check if the current time is in range for non-ddp orders.
                if ($method_configuration['time']['use_on_weekends_type'] == 'included') {
                  if ($time_now >= $weekends_time_until && $time_now <= $weekends_time_after) {
                    $shipping_method = $shipping_method_entity;
                    break 2;
                  }
                }
                elseif ($method_configuration['time']['use_on_weekends_type'] == 'excluded') {
                  if ($time_now < $weekends_time_until || $time_now > $weekends_time_after) {
                    $shipping_method = $shipping_method_entity;
                    break 2;
                  }
                }
              }
            }
            // Check time conditions for weekdays.
            else {
              if (!empty($method_configuration['time']['use_on_workdays']) && !empty($method_configuration['time']['use_on_workdays_type'])) {
                $workdays_time_until = new DrupalDateTime($method_configuration['time']['workdays_time_until']);
                $workdays_time_after = new DrupalDateTime($method_configuration['time']['workdays_time_after']);

                // For DDP orders we don't have to check time conditions.
                $ignore_time_conditions_for_ddp = !empty($method_configuration['time']['ignore_for_ddp']);
                if ($ignore_time_conditions_for_ddp === TRUE &&
                  in_array($order_type, ['ddp', 'ddp_reservation'])
                ) {
                  $shipping_method = $shipping_method_entity;
                  break 2;
                }

                // Check if the current time is in range for non-ddp orders.
                if ($method_configuration['time']['use_on_workdays_type'] == 'included') {
                  if ($time_now >= $workdays_time_until && $time_now <= $workdays_time_after) {
                    $shipping_method = $shipping_method_entity;
                    break 2;
                  }
                }
                elseif ($method_configuration['time']['use_on_workdays_type'] == 'excluded') {
                  $excluded = $time_now < $workdays_time_until ||
                    $time_now > $workdays_time_after;
                  if ($excluded ||
                    (!$excluded && $belmil_de_shipping_method === 'standard')
                  ) {
                    $shipping_method = $shipping_method_entity;
                    break 2;
                  }
                }
              }
            }
          }
        }
      }
    }

    return $shipping_method;
  }

  /**
   * Set shipping method for shipment.
   *
   * @param \Drupal\commerce_shipping\Entity\ShipmentInterface $shipment
   *   The shipment.
   * @param \Drupal\commerce_shipping\Entity\ShippingMethodInterface $shipping_method
   *   The shipping method.
   * @param int|null $key
   *   The shipment key.
   * @param \Drupal\commerce_order\Adjustment[] $adjustments
   *   The order adjustments.
   */
  public function setShippingMethodForShipment(OrderInterface $order, ShipmentInterface $shipment, ShippingMethodInterface $shipping_method, ?int $key = 0, array &$adjustments = []) {
    $shipment->setShippingMethod($shipping_method);

    $order_type = $order->get('field_order_type')->value;
    $order_source = $order->get('field_source')->value;
    $your_order_id = $order->get('field_your_order_id')->value;

    $shipping_method_plugin = $shipping_method->getPlugin();
    $shipping_rates = $shipping_method_plugin->calculateRates($shipment);
    $shipping_rate = reset($shipping_rates);
    $shipping_label = $shipping_rate->getService()->getLabel();
    /** @var \Drupal\commerce_price\Price $amount */
    $amount = $shipping_rate->getAmount();

    // Belmil.de express price is 10 EUR.
    if ($order_source === 'belmilde' &&
      $shipping_label === 'DEDHL Prime'
    ) {
      $amount = new Price('10', $amount->getCurrencyCode());
    }

    // DDP is FREE.
    if (in_array($order_type, ['ddp', 'ddp_reservation'])) {
      $amount = new Price('0', $amount->getCurrencyCode());
    }

    // Amazon.
    if ($order_source === 'amazon') {
      $amount_number = 0;
      $amazon_shipping_method = $order->getData('amazon_shipping_method', 'Std DE Dom_3');

      // DE Standard.
      if ($amazon_shipping_method === 'Std DE Dom_3') {
        $amount_number = $this->baseLinkerConfig->get('amazon_standard_de_price');
      }
      // DE Express.
      elseif ($amazon_shipping_method === 'Exp DE Dom_1') {
        $amount_number = $this->baseLinkerConfig->get('amazon_express_de_price');
      }
      // International EU.
      elseif ($amazon_shipping_method === 'Std DE Intl_2') {
        $amount_number = $this->baseLinkerConfig->get('amazon_eu_price');
      }

      $items = count($shipment->getItems());

      $amount = new Price($items * $amount_number, $amount->getCurrencyCode());
    }

    $shipment->setAmount($amount);

    $shipping_service = $shipping_rate->getService()->getId();
    $shipment->setShippingService($shipping_service);

    $shipment_number = str_pad(($key + 1), 2, '0', STR_PAD_LEFT);
    $shipment_number = $your_order_id ?
      $shipment_number . ' #' . $your_order_id : $shipment_number;

    $shipment_title = $this->t('Shipping for package @order_id', [
      '@order_id' => $shipment_number,
    ]);

    $shipment->setTitle($shipment_title);
    // In case there are some changes on placed order that are
    // affecting shipment information.
    if (!empty($order->get('placed')->value)) {
      $shipment->save();
    }

    $cloned_adjustment = NULL;
    foreach ($adjustments as $key => $adjustment) {
      // Edit existing shipment amount.
      if ($adjustment->getSourceId() == $shipment->id()) {
        $cloned_adjustment = $adjustment->toArray();
        $cloned_adjustment['amount'] = $amount;
        unset($adjustments[$key]);
        $order->get('adjustments')->removeAdjustment($adjustment);
        $adjustments[] = new Adjustment($cloned_adjustment);
      }
    }

    if (!$cloned_adjustment) {
      $adjustments[] = new Adjustment([
        'type' => 'shipping',
        'label' => $shipment_title,
        'amount' => $amount,
        'source_id' => $shipment->id(),
      ]);
    }
  }

  /**
   * Set surcharges on order.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   The order.
   */
  public function setOrderSurcharges(OrderInterface $order) {
    $adjustments = $order->getAdjustments(['custom']);
    foreach ($adjustments as $adjustment) {
      $order->get('adjustments')->removeAdjustment($adjustment);
    }

    $currency = $order->getTotalPrice()->getCurrencyCode();
    $not_full_package_cnt = $this->getNotFullPackageCount($order);

    // Convert back to while if we need more surcharge.
    if ($not_full_package_cnt > 0) {
      $order->addAdjustment(new Adjustment([
        'type' => 'custom',
        'label' => $this->t('Surcharge'),
        'amount' => new Price('4.00', $currency),
        'source_id' => $order->id() . '|' . $not_full_package_cnt,
      ]));

      // $not_full_package_cnt--;
    }
  }

  /**
   * Get bundle products variation ids.
   *
   * @return mixed
   *   The ids.
   */
  public function getBundleProductIds() {
    $query = $this->database->query("SELECT DISTINCT p.field_bundle_product_skus_target_id AS field_bundle_product_skus_target_id
      FROM {commerce_product__field_bundle_product_skus} p
      INNER JOIN {commerce_product_variation_field_data} pv ON pv.variation_id = p.field_bundle_product_skus_target_id
      LEFT OUTER JOIN {commerce_product__field_main_and_bundle_product} mb ON mb.entity_id = pv.product_id
      WHERE (p.deleted = 0) AND ((mb.field_main_and_bundle_product_value IS NULL) OR (mb.field_main_and_bundle_product_value <> 1))");

    return $query->fetchCol();
  }

}
<?php

namespace Drupal\bb2b_order\Plugin\views\filter;

use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;

/**
 * Filter products by stock status.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("stock_status")
 */
class StockStatus extends FilterPluginBase {

  /**
   * {@inheritDoc}
   */
  public function acceptExposedInput($input) {
    return TRUE;
  }

  /**
   * {@inheritDoc}
   */
  public function buildExposedForm(&$form, FormStateInterface $form_state) {
    if (empty($this->options['exposed'])) {
      return;
    }
    $identifier = $this->options['expose']['identifier'];
    $exposed_input = $this->view->getExposedInput();
    $options = [
      NULL => t('Show all products'),
      'only_available' => t('Show only available products'),
    ];

    $form[$identifier] = [
      '#type' => 'select',
      '#title' => t('Stock status'),
      '#options' => $options,
      '#default_value' => $exposed_input,
    ];
    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function query() {
    /** @var \Drupal\views\Plugin\views\query\Sql $query */
    $query = $this->query;
    $stock_status = $this->view->getExposedInput();
    if (isset($stock_status['stock_status']) && $stock_status['stock_status'] === 'only_available') {
      $query->addWhereExpression($this->options['group'], "commerce_product_field_data.stock > 0");
    }
  }

}
<?php

/**
 * @file
 * BaseLinker module functions.
 */

/**
 * Implements hook_cron().
 */
function bb2b_baselinker_cron() {
  /** @var \Drupal\bb2b_baselinker\SyncService $sync_service */
  $sync_service = \Drupal::service('bb2b_baselinker.sync_service');
  $sync_service->getOrders();
  $sync_service->addProduct();
}

/**
 * Implements hook_theme().
 */
function bb2b_baselinker_theme($existing, $type, $theme, $path) {
  return [
    'belmilb2b_baselinker_pdf' => [
      'variables' => [
        'title' => '',
        'order' => [],
        'order_total_paid' => '',
        'currency' => '',
      ],
    ],
  ];
}
<?php

namespace Drupal\bb2b_baselinker\Plugin\QueueWorker;

use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Drupal\Core\Queue\RequeueException;
use Drupal\Tests\Core\Logger\LoggerChannelFactoryTest;
use GuzzleHttp\Client;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines 'bb2b_baselinker_addproduct' queue worker.
 *
 * @QueueWorker(
 *   id = "bb2b_baselinker_addproduct",
 *   title = @Translation("AddProduct"),
 *   cron = {"time" = 60}
 * )
 */
class AddProductQueue extends QueueWorkerBase {

  /**
   * The http client.
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * The logger.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected $logger;

  public function __construct(
    array $configuration, $plugin_id,
    $plugin_definition,
    Client $httpClient,
    LoggerChannelFactoryInterface $logger_factory
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->httpClient = $httpClient;
    $this->logger = $logger_factory->get('bb2b_baselinker');
  }

  public static function create (
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition
  ) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('http_client'),
      $container->get('logger.factory')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function processItem($data) {
    // @todo Process data here.
    $params = [
      'storage_id' => '4721',
      'product_id' => $data['product']->id(),
      'ean' => $data['product']->get('ean')->value,
    ];

    try {
      $response = $this->httpClient->request('post', $data['connected_domain'], [
        'headers' => [
          'Content-Type' => 'application/x-www-form-urlencoded',
          'X-BLToken' => $data['token'],
        ],
        'form_params' => [
          'method' => 'addProduct',
          'parameters' => $params,
        ]
      ]);
    }
    catch (RequeueException $e) {
      $this->logger->debug('BaseLinker request failed with the message: @message', [
        '@message' => $e->getMessage(),
      ]);
    }

    $response = json_decode($response->getStatusCode());
    return $response;

  }

}
<?php

namespace Drupal\bb2b_baselinker;

use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\Core\Asset\AssetCollectionRendererInterface;
use Drupal\Core\Asset\AssetResolverInterface;
use Drupal\Core\Asset\AttachedAssets;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ExtensionPathResolver;
use Drupal\Core\Extension\InfoParserInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\RequeueException;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Theme\ThemeInitializationInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use GuzzleHttp\RequestOptions;
use Mpdf\Mpdf;
use Mpdf\Output\Destination;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * BaseLinker Sync Service.
 */
class SyncService {

  use StringTranslationTrait;

  /**
   * Get orders method name.
   *
   * @var string
   */
  const GET_ORDERS_METHOD = 'getOrders';

  /**
   * Last order confirmed time key.
   *
   * @var string
   */
  const LAST_ORDER_CONFIRMED_TIME = 'bb2b_baselinker.last_order_confirmed_time';

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

  /**
   * The config.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $config;

  /**
   * The config for athenapdf.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $athenapdfConfig;

  /**
   * The logger.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  protected $logger;

  /**
   * The http client.
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * The order queue.
   *
   * @var \Drupal\Core\Queue\QueueFactory
   */
  protected $getOrderQueue;

  /**
   * The state.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The renderer.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * The theme handler.
   *
   * @var \Drupal\Core\Extension\ThemeHandlerInterface
   */
  protected $themeHandler;

  /**
   * The extension path resolver.
   *
   * @var \Drupal\Core\Extension\ExtensionPathResolver
   */
  protected $extensionPathResolver;

  /**
   * The info parser.
   *
   * @var \Drupal\Core\Extension\InfoParserInterface
   */
  protected $infoParser;

  /**
   * The theme initialization.
   *
   * @var \Drupal\Core\Theme\ThemeInitializationInterface
   */
  protected $themeInitialization;

  /**
   * The asset resolver.
   *
   * @var \Drupal\Core\Asset\AssetResolverInterface
   */
  protected $assetResolver;

  /**
   * The CSS collection renderer.
   *
   * @var \Drupal\Core\Asset\AssetCollectionRendererInterface
   */
  protected $cssCollectionRenderer;

  /**
   * The current request.
   *
   * @var \Symfony\Component\HttpFoundation\Request
   */
  protected $request;

  /**
   * File system.
   *
   * @var \Drupal\Core\File\FileSystemInterface
   */
  protected $fileSystem;

  /**
   * The file URL generator.
   *
   * @var \Drupal\Core\File\FileUrlGeneratorInterface
   */
  protected $fileUrlGenerator;

  /**
   * Constructs a new SyncService object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The configuration factory.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory_athenapdf
   *   The configuration factory for athenapdf api.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger.
   * @param \GuzzleHttp\Client $http_client
   *   The http client.
   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
   *   The queue factory.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state.
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer.
   * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
   *   The theme handler.
   * @param \Drupal\Core\Extension\ExtensionPathResolver $extension_path_resolver
   *   The extension path resolver.
   * @param \Drupal\Core\Extension\InfoParserInterface $info_parser
   *   The info parser.
   * @param \Drupal\Core\Theme\ThemeInitializationInterface $theme_initialization
   *   The theme initialization.
   * @param \Drupal\Core\Asset\AssetResolverInterface $asset_resolver
   *   The asset resolver.
   * @param \Drupal\Core\Asset\AssetCollectionRendererInterface $css_collection_renderer
   *   The CSS collection renderer.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   * @param \Drupal\Core\File\FileSystemInterface $fileSystem
   *   File System.
   * @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
   *   The file URL generator.
   */
  public function __construct(
    EntityTypeManagerInterface $entity_type_manager,
    ConfigFactoryInterface $config_factory,
    ConfigFactoryInterface $config_factory_athenapdf,
    LoggerChannelFactoryInterface $logger_factory,
    Client $http_client,
    QueueFactory $queue_factory,
    StateInterface $state,
    Connection $database,
    RendererInterface $renderer,
    ThemeHandlerInterface $theme_handler,
    ExtensionPathResolver $extension_path_resolver,
    InfoParserInterface $info_parser,
    ThemeInitializationInterface $theme_initialization,
    AssetResolverInterface $asset_resolver,
    AssetCollectionRendererInterface $css_collection_renderer,
    RequestStack $request_stack,
    FileSystemInterface $fileSystem,
    FileUrlGeneratorInterface $file_url_generator
  ) {
    $this->entityTypeManager = $entity_type_manager;
    $this->config = $config_factory->get('bb2b_baselinker.settings');
    $this->athenapdfConfig = $config_factory_athenapdf->get('athenapdf_api.settings');
    $this->logger = $logger_factory->get('bb2b_baselinker');
    $this->httpClient = $http_client;
    $this->getOrderQueue = $queue_factory->get('get_orders_from_baselinker');
    $this->addProductQueue = $queue_factory->get('bb2b_baselinker_addproduct');
    $this->state = $state;
    $this->database = $database;
    $this->renderer = $renderer;
    $this->themeHandler = $theme_handler;
    $this->extensionPathResolver = $extension_path_resolver;
    $this->infoParser = $info_parser;
    $this->themeInitialization = $theme_initialization;
    $this->assetResolver = $asset_resolver;
    $this->cssCollectionRenderer = $css_collection_renderer;
    $this->request = $request_stack->getCurrentRequest();
    $this->fileSystem = $fileSystem;
    $this->fileUrlGenerator = $file_url_generator;
  }

  /**
   * Get orders from BaseLinker API.
   *
   * A maximum of 100 orders are returned at a time.
   */
  public function getOrders() {
    // The buyer's data are deleted after 30 days of shipment.
    $date_confirmed_from = $this->state
      ->get(self::LAST_ORDER_CONFIRMED_TIME, strtotime('-30 days'));

    $parameters = [
      'date_confirmed_from' => $date_confirmed_from,
    ];
    $items = $this->getItems(self::GET_ORDERS_METHOD, $parameters);

    $api_orders = $items['orders'] ?? [];
    if ($api_orders && $items['status'] === 'SUCCESS') {
      foreach ($api_orders as $order) {
        if ($order['delivery_fullname'] !== '-') {
          $query = $this->database->select('queue', 'q');
          $query->condition('name', 'get_orders_from_baselinker');
          $query->condition('data', serialize($order));
          $query->fields('q', ['item_id']);

          // Skip same queue items.
          if (empty($query->execute()->fetchAll())) {
            $this->getOrderQueue->createItem($order);
          }
        }
      }
    }

  }

  /**
   * Sync products to BaseLinker API.
   */
  public function addProduct() {
    // BaseLinker API credentials.
    $data = [];
    $data['token'] = $this->config->get('token');
    $data['connected_domain'] = $this->config->get('connected_domain');

    // Get products that we want to integrate in BaseLinker.
    $products = $this->entityTypeManager
      ->getStorage('commerce_product')
      ->loadMultiple();

    // Send request to BaseLinker.
    foreach ($products as $product) {
      $data['product'] = $product;

      $params = [
        'storage_id' => '4721',
        'product_id' => $data['product']->id(),
      ];

      try {
        $response = $this->httpClient->request('post', $data['connected_domain'], [
          'headers' => [
            'Content-Type' => 'application/x-www-form-urlencoded',
            'X-BLToken' => $data['token'],
          ],
          'form_params' => [
            'method' => 'addProduct',
            'parameters' => $params,
          ]
        ]);
      }
      catch (RequeueException $e) {
        $this->logger->debug('BaseLinker request failed with the message: @message', [
          '@message' => $e->getMessage(),
        ]);
      }

      $response = json_decode($response->getStatusCode());
    }
  }

  /**
   * Request to the connected domain.
   *
   * @param string $method
   *   Which method is used for request.
   * @param array $parameters
   *   Order parameters for request.
   *
   * @return array
   *   Return items.
   */
  public function getItems(string $method, array $parameters = []): array {
    $token = $this->config->get('token');
    $connected_domain = $this->config->get('connected_domain');

    $base_parameters = [];
    if ($method === self::GET_ORDERS_METHOD) {
      $base_parameters = [
        'get_unconfirmed_orders' => FALSE,
      ];
    }
    $parameters = array_merge($base_parameters, $parameters);

    try {
      $response = $this->httpClient->request('post', $connected_domain, [
        'headers' => [
          'Content-Type' => 'application/x-www-form-urlencoded',
          'X-BLToken' => $token,
        ],
        'form_params' => [
          'method' => $method,
          'parameters' => json_encode($parameters),
        ],
        'verify' => FALSE,
        'http_errors' => FALSE,
      ]);
    }
    catch (RequestException $e) {
      $this->logger->debug('BaseLinker request failed with the message: @message', [
        '@message' => $e->getMessage(),
      ]);
    }

    $this->logger->debug('BaseLinker API response: <pre>@body</pre>', [
      '@body' => $response ? $response->getBody()->getContents() : '',
    ]);

    $items = json_decode($response->getBody(), TRUE);

    return $items ?: [];
  }

  /**
   * Generate PDF file.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   Order Interface.
   * @param bool $is_html
   *   Html.
   * @param bool $return_with_file
   *   Return with file.
   *
   * @return string|\Symfony\Component\HttpFoundation\Response|null
   *   Retrun response.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Theme\MissingThemeDependencyException
   * @throws \GuzzleHttp\Exception\GuzzleException
   */
  public function generatePdf(OrderInterface $order, bool $is_html = FALSE, bool $return_with_file = FALSE) {
    $renderer = $this->renderer;
    $theme = $this->themeHandler->getDefault();

    $theme_path = $this->extensionPathResolver->getPath('theme', $theme);
    $theme_info = $this->infoParser->parse("$theme_path/$theme.info.yml");
    $pdf_title = $this->t('Baselinker');

    $applying_guidelines = NULL;

    $order_items = [];

    foreach ($order->getItems() as $order_item) {
      $title = $order_item->get('title')->value;
      $sku = substr($title, strpos($title, ":") + 1);
      $shipping = $this->entityTypeManager->getStorage('commerce_shipment')
        ->load($order->get('shipments')[0]->target_id);

      // Get asin from product variation.
      $product = $this->entityTypeManager->getStorage('commerce_product')->load($order_item->getPurchasedEntityId());

      if ($product == NULL) {
        continue;
      }

      $order_items[] = [
        'quantity' => $order_item->get('quantity')->value,
        'title' => $title,
        'price' => $order_item->get('total_price')->number,
        'sku' => $sku,
        'shipping' => $shipping->get('amount')[0]->number,
      ];
    }

    $save_order = [
      'delivery_fullname' => $order->getBillingProfile()->get('address')[0]->given_name . ' ' . $order->getBillingProfile()->get('address')->family_name,
      'delivery_address' => $order->getBillingProfile()->get('address')->address_line1,
      'delivery_postal_code' => $order->getBillingProfile()->get('address')->postal_code,
      'delivery_city' => $order->getBillingProfile()->get('address')[0]->locality,
      'delivery_country' => $order->getBillingProfile()->get('address')[0]->country_code,
      'order_number' => $order->getOrderNumber(),
      'date_add' => date('D., d.M.Y', $order->get('created')->value),
      'order_items' => $order_items,
    ];

    $currency = $this->entityTypeManager->getStorage('commerce_currency')->load($order->getTotalPrice()->getCurrencyCode());

    $build = [
      '#theme' => 'belmilb2b_baselinker_pdf',
      '#order_total_paid' => $order->getTotalPrice()->getNumber(),
      '#pdf_title' => $pdf_title,
      '#title' => 'test',
      '#applying_guidelines' => $applying_guidelines,
      '#order' => $save_order,
      '#attached' => [
        'library' => $theme_info['libraries'],
      ],
      '#currency' => $currency->getSymbol(),
    ];

    $logo = $this->themeInitialization->getActiveThemeByName($theme)
      ->getLogo();
    if (strpos($logo, '.svg')) {
      $logo = str_replace('.svg', '.png', $logo);
    }

    $build['#site_logo'] = $logo;

    $context = new RenderContext();

    $css_assets = $this->assetResolver->getCssAssets(AttachedAssets::createFromRenderArray($build), TRUE);
    $rendered_css = $this->cssCollectionRenderer->render($css_assets);

    /** @var \Drupal\Core\Cache\CacheableDependencyInterface $rendered_css_build */
    $rendered_css_build = $this->renderer->executeInRenderContext($context, function () use ($rendered_css, $renderer) {
      return $renderer->render($rendered_css);
    });

    if (!$context->isEmpty()) {
      $bubbleable_metadata = $context->pop();
      BubbleableMetadata::createFromObject($rendered_css_build)
        ->merge($bubbleable_metadata);
    }

    $build['#rendered_css'] = $rendered_css_build;

    /** @var \Drupal\Core\Cache\CacheableDependencyInterface $html */
    $html = $this->renderer->executeInRenderContext($context, function () use ($build, $renderer) {
      return $renderer->render($build);
    });

    if (!$context->isEmpty()) {
      $bubbleable_metadata = $context->pop();
      BubbleableMetadata::createFromObject($html)
        ->merge($bubbleable_metadata);
    }
    $host = $this->request->getSchemeAndHttpHost();

    if (!$is_html && strpos($host, 'docker') !== FALSE) {
      $host = 'http://nginx';
    }

    $media_string = '<link rel="stylesheet" media="all" href=""';
    $css_string = '<link rel="stylesheet" type="text/css" href=""';

    $html = str_replace('<img src=""', '<img src=""' . $host, $html);
    $html = str_replace($media_string, $media_string . $host, $html);
    $html = str_replace($css_string, $css_string . $host, $html);

    if (!$is_html) {
      $destination_dir = 'public://baselinker_order_pdf';
      $destination = $destination_dir . '/baselinker-pdf- ' . $save_order['order_number'] . '.pdf';
      $this->fileSystem->prepareDirectory($destination_dir, FileSystemInterface::CREATE_DIRECTORY || FileSystemInterface::MODIFY_PERMISSIONS);

      $pdf_data = $this->httpClient
        ->request('POST', $this->athenapdfConfig->get('endpoint') . '?auth=' . $this->athenapdfConfig->get('auth_key') . '&ext=html', [
          RequestOptions::MULTIPART => [
            [
              'name' => 'file',
              'contents' => $html,
              'filename' => 'input.html',
              'headers' => [
                'Content-Type' => 'text/html; charset=utf-8',
              ],
            ],

          ],

          RequestOptions::SINK => $destination,
        ])->getBody()->getContents();

      $pdf_file = $this->fileSystem
        ->saveData($pdf_data, $destination, FileSystemInterface::EXISTS_REPLACE);

      $order->field_documents->target_id = $pdf_file;
      $order->save();

      if ($return_with_file) {
        return $pdf_file ? $this->fileUrlGenerator->generateAbsoluteString($pdf_file) : NULL;
      }
      else {
        $this->setPDFTitle($destination, $pdf_title);

        $response = new Response();
        $response->headers->set('Content-Type', 'application/pdf');
        $response->headers->set('Content-Disposition', 'inline; filename="' . $pdf_title . '.pdf"');
        $response->setContent($pdf_data);

        return $response;
      }
    }
    else {
      return new Response($html);
    }

    throw new NotFoundHttpException();

  }

  /**
   * Set PDF title.
   *
   * @param string $outputFilePath
   *   Output file path.
   * @param string $title
   *   Title.
   */
  protected function setPdfTitle(string $outputFilePath, string $title) {
    try {
      $mpdf = new Mpdf(['tempDir' => $this->fileSystem->getTempDirectory()]);
      $page_count = $mpdf->setSourceFile($outputFilePath);
      for ($i = 1; $i <= $page_count; $i++) {
        $import_page = $mpdf->ImportPage($i);
        $mpdf->UseTemplate($import_page);

        if ($i < $page_count) {
          $mpdf->AddPage();
        }
      }

      $mpdf->setTitle($title);
      $mpdf->Output($outputFilePath, Destination::FILE);
    }
    catch (\Exception $e) {
      // Do nothing.
    }
  }

}
protected function sendPassMail($order) {
    // Gathering values for mail.
    $langcode = $order->getCustomer()->getPreferredLangcode();
    $client = $order->getCustomer()->name->value;
    $shipping_information = \Drupal::service('commerce_shipping.order_shipment_summary')->build($order);

    $body = [
      '#theme' => 'linker_order_pass',
      '#order' => $order,
      '#shipping_information' => $shipping_information,
    ];

    $params = [
      'headers' => [
        'Content-Type' => 'text/html; charset=UTF-8;',
        'Content-Transfer-Encoding' => '8Bit',
      ],
      'id' => 'linker_order_pass',
      'langcode' => $langcode,
      'subject' => $client . ' issued the order ' . $order->id(),
      'body' => $body,
    ];

    $this->mailManager
      ->mail('bb2b_linker', $params['id'], 'bestellung@bescool.net', $params['langcode'], $params);
  }

// DI za mailManager-a.

/**
   * The mail manager.
   *
   * @var \Drupal\Core\Mail\MailManagerInterface
   */
  protected $mailManager;

/**
 * Implements hook_mail().
 */
function bb2b_linker_mail($key, &$message, $params) {
  switch ($key) {
    case 'commerce_tracking_code':
    case 'linker_order_rejected':
    case 'linker_order_returned':
    case 'linker_order_not_pass':
    case 'linker_order_pass':
      /** @var \Drupal\Core\Render\RendererInterface $renderer */
      $renderer = \Drupal::service('renderer');

      if (isset($params['headers'])) {
        $message['headers'] = array_merge($message['headers'], $params['headers']);
      }
      if (!empty($params['from'])) {
        $message['from'] = $params['from'];
      }
      $message['subject'] = $params['subject'];
      $message['body'][] = $renderer->render($params['body']);
      break;
  }
}
if ($view->id() === 'more_like_this' &&
    $view->current_display === 'rest_similar_by_popularity'
  ) {
    $product_id = $view->args[0];
    $product = \Drupal::entityTypeManager()
      ->getStorage('commerce_product')
      ->load($product_id);
    $term = $product->get('app_group')->referencedEntities()[0];
    $top_term = get_top_level_parent($term);
    if ($top_term) {
      switch ($top_term->get('name')->value) {
        case 'Kültéri lámpák' || 'Beltéri lámpák':
          // Recommended manufacturer.
          $manufacturer = \Drupal::entityTypeManager()
            ->getStorage('taxonomy_term')
            ->loadByProperties([
              'vid' => 'manufacturer',
              'name' => 'AZzardo',
            ]);
          $manufacturer = reset($manufacturer);
          // Get all products belonging to the same term (group)
          // and recommended manufacturer.
          $products = \Drupal::entityTypeManager()
            ->getStorage('commerce_product')
            ->loadByProperties([
              'app_group' => $term->id(),
              'app_manufacturer' => $manufacturer->id(),
            ]);
          foreach ($products as $key => $product) {
            $onDiscount = checkIfOnDiscountAndOnStock($product);
            if (!$onDiscount) {
              unset($products[$key]);
            }
          }
          $product_ids = [];
          array_walk($products, function ($product) use (&$product_ids) {
            $product_ids[] = $product->id();
          });
          $query->addWhere(0, 'product_id', $product_ids, 'IN');
          break;
      }
    }
  }
  

function checkIfOnDiscountAndOnStock($product): bool {
  $variations = $product->getVariations();
  foreach ($variations as $variation) {
    if (!empty($variation->get('list_price')) && $variation->get('app_purchaseitem')->value == 1) {
      if ($variation->get('list_price')->number > $variation->get('price')->number) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

function get_top_level_parent($term) {
  if ($term->hasField('parent') && $term->get('parent')->target_id !== '0') {
    $term = $term->get('parent')->referencedEntities()[0];
    return get_top_level_parent($term);
  }
  else {
    return $term;
  }
}
/**
 * Implements hook_views_data().
 */
function app_core_views_data() {
  $data  = [];

  $data['views']['recommended_products'] = [
    'title' => t('Recommended products - Custom Filter' ),
    'filter' => [
      'title' => t('Recommended products - Custom Filter' ),
      'field' => 'id',
      'id' => 'recommended_products'
    ]
  ];

  return $data;
}
<?php

namespace Drupal\app_core\Plugin\views\filter;

use Drupal\views\Plugin\views\filter\FilterPluginBase;

/**
 * Filter for recommended products.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("recommended_products")
 */
class RecommendedProductsFilter extends FilterPluginBase {

  public function query() {
    $this->ensureMyTable();
    $product_id = $this->view->args[0];
    $product = \Drupal::entityTypeManager()
      ->getStorage('commerce_product')
      ->load($product_id);
    $term = $product->get('app_group')->referencedEntities()[0];
    // Recommended manufacturer.
    $manufacturer = \Drupal::entityTypeManager()
      ->getStorage('taxonomy_term')
      ->loadByProperties([
        'vid' => 'manufacturer',
        'name' => 'AZzardo',
      ]);
    $manufacturer = reset($manufacturer);
    // Get all products belonging to the same term (group)
    // and recommended manufacturer.
    $products = \Drupal::entityTypeManager()
      ->getStorage('commerce_product')
      ->loadByProperties([
        'app_group' => $term->id(),
        'app_manufacturer' => $manufacturer->id(),
      ]);
    foreach ($products as $key => $product) {
      $onDiscount = $this->checkIfOnDiscountAndOnStock($product);
      if (!$onDiscount) {
        unset($products[$key]);
      }
    }
    $product_ids = [];
    array_walk($products, function($product) use (&$product_ids) {
      $product_ids[] = $product->id();
    });
    $this->query->addWhere(0, 'product_id', $product_ids, 'IN');
    $mile = 'mile';
  }

  public function checkIfOnDiscountAndOnStock($product){
    $variations = $product->getVariations();
    foreach ($variations as $variation) {
      if (!empty($variation->get('list_price')) && $variation->get('app_purchaseitem')->value == 1) {
        if ($variation->get('list_price')->number > $variation->get('price')->number) {
          return TRUE;
        }
      }
    }
    return FALSE;
  }

}
<?php

namespace Drupal\app_core\Plugin\views\filter;

use Drupal\views\Plugin\views\filter\FilterPluginBase;

/**
 * Filter for recommended products.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("recommended_products")
 */
class RecommendedProductsFilter extends FilterPluginBase {

  public function query() {
    $this->ensureMyTable();
    $product_id = $this->view->args[0];
    $product = \Drupal::entityTypeManager()
      ->getStorage('commerce_product')
      ->load($product_id);
    $term = $product->get('app_group')->referencedEntities()[0];
    // Get all products belonging to the same term (group).
    $products = \Drupal::entityTypeManager()
      ->getStorage('commerce_product')
      ->loadByProperties([
        'app_group' => $term->id(),
      ]);
    $product_ids = [];
    array_walk($products, function($product) use (&$product_ids) {
      $product_ids[] = $product->id();
    });
    $this->query->addWhere(0, 'product_id', $product_ids, 'IN');
//    $product_top_group = $this->findProductsTopGroup($term);
//    $child_terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('group', $product_top_group->id());
//    $acceptable_terms = [];
//    foreach ($child_terms as $child_term) {
//      $acceptable_terms[] = $child_term->name;
//    }
//    $products = \Drupal::entityTypeManager()->getStorage('commerce_product')->loadMultiple();
    $mile = 'mile';
  }

//  public function findProductsTopGroup($term) {
//    if ($term->hasField('parent') && $term->get('parent')->target_id !== '0') {
//      $term = $term->get('parent')->referencedEntities()[0];
//      return $this->findProductsTopGroup($term);
//    }
//    else {
//      return $term;
//    }
//
//  }

}
if ($display->getComponent('item_approvement_table')) {
      $form = \Drupal::classResolver()->getInstanceFromDefinition(PaymentRequestItemsApprovementForm::class);
      $form->setPaymentRequest($entity);
      $form_state = new FormState();
      $form_array = \Drupal::formBuilder()->buildForm($form, $form_state);
      $form_array['#current_tab_redirect'] = TRUE;
      $build['item_approvement_table'] = $form_array;
    }
star

Wed May 25 2022 13:10:09 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Wed May 25 2022 13:08:21 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Mon May 23 2022 08:34:24 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Sat May 21 2022 15:06:48 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Sat May 21 2022 15:06:05 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Thu May 19 2022 20:45:31 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Wed May 18 2022 13:49:18 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Wed May 18 2022 13:48:53 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Wed May 18 2022 13:48:20 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Fri May 13 2022 06:35:46 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Fri May 13 2022 06:32:09 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Tue May 10 2022 20:18:41 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Tue May 10 2022 19:26:37 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Tue May 10 2022 19:25:28 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render
star

Tue May 10 2022 19:23:51 GMT+0000 (Coordinated Universal Time)

#drupal #pseudo-field #form-render

Save snippets that work with our extensions

Available in the Chrome Web Store Get Firefox Add-on Get VS Code extension