Snippets Collections
/**
 * Implements hook_theme().
 */
function application_theme() {
  return [
    'application_info' => [
      'variables' => [
        'application_type_label' => NULL,
        'funding_amount' => NULL,
        'application_started' => NULL,
        'contact_person' => NULL,
        'project_leader' => [
          'first_name' => NULL,
          'last_name' => NULL,
          'phone' => NULL,
        ],
        'files' => [],
        'criteria_link' => NULL,
      ],
  ]
}
  $element = [
    '#theme' => 'application_info',
    '#application_type_label' => $application->getApplicationType()->label(),
    '#funding_amount' => $application->getFundingAmountPrinted(),
    '#project_leader' => [
      'first_name' => $advisor_address->given_name,
      'last_name' => $advisor_address->family_name,
      'phone' => $advisor->get('field_ezs_festnetz_telefon')->value,
    ],
    '#files' => [],
    '#criteria_link' => Url::fromRoute('application.application_files', [
      'application_type' => $application->bundle(),
      'file_type' => 'criteria',
    ])->toString(),
//      [
//      '#type' => 'link',
//      '#title' => t('Here'),
//      '#url' => Url::fromRoute('application.application_files', [
//        'application_type' => $application->bundle(),
//        'file_type' => 'criteria',
//      ])
//    ],
    '#cache' => [
      'context' => [
        'user.roles',
        'languages:language_interface',
      ]
    ]
  ];
<div class="application-info-block">
  <h3>{% trans %}Information{% endtrans %}</h3>
  <div><span class="field-label">{{ 'Application program'|trans }} {{ application_type_label }}</span></div>
  {% if funding_amount %}
    <div><span class="field-label">{{ 'Founding Amount'|trans }} {{ funding_amount }}</span></div>
  {% endif %}
  <div><span class="field-label">{{ 'EZS project manager'|trans }}</span> {{ _self.render_person(project_leader) }}</div>
  {% if contact_person %}
    <div><span class="field-label">{{ 'Contact'|trans }}</span> {{ _self.render_person(contact_person) }}</div>
  {% endif %}
  {% if files %}
    <label>{{ 'Files'|trans }}</label>
    <div>{{ files }}</div>
  {% endif %}
  <div>
    {# TODO: Replace for renderable element #}
    {{ '<a target="_blank" href="@url">Here</a> you will find the latest funding criteria'|trans({'@url': criteria_link }) }}
  </div>
</div>

{% macro render_person(person) %}
  {% if person.company %}
    <span>{{ person.company }}</span>
  {% endif %}
  {% if person.salutation %}
    <span>{{ person.salutation }}</span>
  {% endif %}

  <span>{{ person.first_name }} {{ person.last_name }}</span>
  {% if person.street %}
    <span>{{ person.street }}</span>
  {% endif %}
  <span>
    {% if person.postal_code %}
      {{ person.postal_code }}
    {% endif %}
    {% if person.locality %}
      {{ person.locality }}
    {% endif %}
    {% if person.country %}
      {{ person.country }}
    {% endif %}
  </span>
  {% if person.email %}
    <span>{{ person.email }}</span>
  {% endif %}
  <span>{{ person.phone }} </span>
  {% if person.availability %}
    <span>{{ person.availability }}</span>
  {% endif %}
{% endmacro %}
<?php


namespace Drupal\cfp_payment_request\Form;

use Drupal\cfp_payment_request\Entity\PaymentRequestItem;
use Drupal\cfp_payment_request\Entity\PaymentRequestItemType;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\inline_entity_form\Form\EntityInlineForm;

class PaymentRequestItemInlineForm extends EntityInlineForm {

  const DATA_SOURCE_CLASS = 'pr-data-source';
  const DATA_COPY_NAME = 'data-pr-set';
  const DATA_NAME_STARTS_WITH = 'data-pr-name-starts-with';

  /**
   * {@inheritdoc}
   */
  public function entityForm(array $entity_form, FormStateInterface $form_state) {
    $parent_entity = $form_state->getFormObject()->getEntity();
    if ($parent_entity->getEntityTypeId() === 'cfp_payment_request') {
      $entity_form['#entity']->setParent($parent_entity);
    }
    $entity = $entity_form['#entity'];
    $form_display = $this->getFormDisplay($entity, $entity_form['#form_mode']);

    $this->extraFields($entity_form, $entity, $form_display);

    $entity_form = parent::entityForm($entity_form, $form_state);

    $this->attachValuesCopyBehaviour($entity_form, $parent_entity);

    return $entity_form;
  }

  /**
   * {@inheritdoc}
   */
  public function isTableDragEnabled($element) {
    return FALSE;
  }

  protected function extraFields(&$form, PaymentRequestItem $entity, EntityFormDisplayInterface $form_display) {
    if ($form_display->getComponent('item_support_intensity') && $form_display->getComponent('cost_item_name')) {
      $form['#after_build'][] = '\Drupal\cfp_payment_request\Form\PaymentRequestItemInlineForm::statesToSupportIntensity';
      $form['item_support_intensity'] = [
        '#type' => 'container',
      ];
      $entity = $form['#entity'];
      if (($pr = $entity->getParent()) && ($cfp = $pr->getParent())) {
        $name = 'will_be_build_after';

        /** @var \Drupal\pos_entity_cost_item\AdditionalCostItem $items */
        $items = $cfp->getWrappedAllItems(FALSE, TRUE);

        foreach ($items as $item_id => $item) {
          $form['item_support_intensity'][$item_id] = [
            '#type' => 'container',
            'contents' => [
              '#markup' => '<label>Tételhez kapcsolódó támogatási intenzitás: </label><span class="price">' . $item->getSupportRatio() . '%</span>'
            ],
            '#states' => [
              'visible' => [
                $name => ['value' => $item_id],
              ],
            ],
            '#attributes' => [
              'class' => ['item-support-intensity']
            ],
          ];
        }
      }
    }
    if ($form_display->getComponent('participation_type')) {
      $payment_request = $entity->getParent();

      $cfp = $entity->getCfp();
      $cost_item = $cfp->getCostItemEntities();
      $cost_item = reset($cost_item);
      $own_participation = $cost_item->get('cost_item_participation');
      if ($own_participation_value = $own_participation->value) {
        $own_participation_definition = $cost_item->getFieldDefinition('cost_item_participation');
        $own_participation_label = $own_participation_definition->getSettings()['allowed_values'][$own_participation_value];
      }
      $form['participation_type'] = [
        '#type' => 'markup',
        '#markup' => '<div class="own-participation"><b>Vrsta sopstvenog učešća: </b>' . $own_participation_label . '</div>',
        '#weight' => 10,
      ];
    }

    $payment_request_item_type = PaymentRequestItemType::load($entity->bundle());

    if (!empty($payment_request_item_type->get('documents_helptext')) && $form_display->getComponent('documents_helptext')) {
      $helptext = $payment_request_item_type->get('documents_helptext');
      $form['documents_helptext'] = [
        '#type' => 'container',
        'helptext' => [
          '#type' => 'markup',
          '#markup' => '<div class="documents_helptext">' . $helptext . '</div>',
        ],
      ];
    }


  }

  public static function statesToSupportIntensity($form, FormStateInterface $form_state) {
    if (isset($form['cost_item_name']['widget']['#name']) && isset($form['item_support_intensity'])) {
      $name = $form['cost_item_name']['widget']['#name'];
      foreach ($form['item_support_intensity'] as $item_id => &$element) {
        if (isset($element['#states']['visible'])) {
          $element['#states']['visible'] = [
            '[name="'  . $name . '"]' => ['value' => $item_id],
          ];
        }

      }
    }
//    if (isset($form['cost_item_name']['widget']['#name'])) {
//      $entity = $form['#entity'];
//      if (($pr = $entity->getParent()) && ($cfp = $pr->getParent())) {
//        $name = $form['cost_item_name']['widget']['#name'];
//        /** @var \Drupal\pos_entity_cost_item\AdditionalCostItem $items */
//        $items = $cfp->getWrappedAllItems(FALSE, TRUE);
//
//        foreach ($items as $item_id => $item) {
//
//          $form['item_support_intensity'][$item_id] = [
//            '#type' => 'container',
//            'contents' => [
//              '#markup' => $item->getSupportRatio(),
//            ],
//            '#states' => [
//              'visible' => [
//                '[name="'  . $name . '"]' => ['value' => $item_id],
//              ],
//            ]
//          ];
//        }
//      }
//    }
    return $form;
  }

  /**
   * @param $entity_form
   *
   */
  protected function attachValuesCopyBehaviour(&$entity_form, $parent_entity) {
    if (isset($entity_form['cost_item_name']['widget'])) {
      $entity_form['cost_item_name']['widget']['#attributes']['class'][] = self::DATA_SOURCE_CLASS;
      $entity_form['cost_item_name']['widget']['#after_build'][] = '\Drupal\cfp_payment_request\Form\PaymentRequestItemInlineForm::constructNameStartsWithAfterBuild';
    }
    else {
      return;
    }

    /** @var \Drupal\pos_entity_cfp\Entity\Cfp $cfp */
    $cfp = $parent_entity->getParent();

    $wrapped_items = $cfp->getWrappedAllItems(FALSE, TRUE);
    /**
     * Second parameter is either method for wrapped item, or field name of cost
     *   item.
     *
     * @var $field_mapper
     */
    $field_mapper = [
      'cf_manufacturer' => 'cost_item_manufacturer',
      'cf_unique_item_id' => 'cost_item_identificator',
      'cf_distributor' => 'cost_item_distributor',
      'cf_distributor_vat_number' => 'cost_item_vat_number',
      'cf_distributor_bank_account_num' => 'cost_item_distributor_account',
      'item_support' => 'getSupportValue',
      'item_pay_off' => 'getEligibleCost',
      'item_net_unit' => 'cost_item_neto_price',
      'item_unit_vat' => 'cost_item_vat',
      'item_quantity' => 'cost_item_quantity',
    ];

    foreach ($field_mapper as $value_field_name => $method_or_field) {
      $values = [];
      if (!isset($entity_form[$value_field_name]['widget'][0]['value'])) {
        continue;
      }
      foreach ($wrapped_items as $id => $wrapped_item) {
        if (method_exists($wrapped_item, $method_or_field)) {
//          $wrapped_item->getEligibleCost();
          $values[$id] = $wrapped_item->$method_or_field();
        }
        elseif ($wrapped_item->getCostItem()?->hasField($method_or_field)) {
          $values[$id] = $wrapped_item->getCostItem()->get($method_or_field)->value;
        }
      }
      $entity_form[$value_field_name]['widget'][0]['value']['#attributes'][self::DATA_COPY_NAME] = Json::encode($values);
    }

    $entity_form['#attached']['library'][] = 'cfp_payment_request/pr_item_autopopulate.js';
  }

  /**
   * Constructs name starts with for inline entity form.
   */
  public static function constructNameStartsWithAfterBuild($element, FormStateInterface $form_state) {
    $name_starts_with = explode('['. $element['#field_name'] . ']', $element['#name'])[0];
    $element['#attributes'][self::DATA_NAME_STARTS_WITH] = $name_starts_with;
    return $element;
  }

}
(function (Drupal, once) {
  'use strict';

  const DATA_COPY_NAME = 'data-pr-set';
  const DATA_SOURCE_CLASS = 'pr-data-source';
  const DATA_NAME_STARTS_WITH = 'data-pr-name-starts-with';

  const handleCopy = (context, value, name_starts_with) => {
    context.querySelectorAll(`[name^="${name_starts_with}"][${DATA_COPY_NAME}]`).forEach((element) => {
      const values = JSON.parse(element.getAttribute(DATA_COPY_NAME));
      if (values.hasOwnProperty(value)) {
        setElementValue(element, values[value]);
      }
      else {
        setElementValue(element);
      }
    });
  }

  const setElementValue = (element, value) => {
    if (element.classList.contains('mask-money--apply')) {
      // element.value = value;
      Drupal.behaviors.autoCalculateFormulaBehaviour.setValue(element.getAttribute('name'), value === undefined ? 0 : parseFloat(value));
    }
    else {
      element.value = value === undefined ? '' : value;
      const event = new Event('change');
      element.dispatchEvent(event);
    }
  }

  Drupal.behaviors.copyItemValues = {
    attach: (context) => {
      once('copy-item-values', `.${DATA_SOURCE_CLASS}`,  context).forEach((element) =>  {
        element.addEventListener('change', (event) => {
          handleCopy(context, event.target.value, event.target.getAttribute(DATA_NAME_STARTS_WITH));
        });
      });
    }
  }

})(Drupal, once);
<?php

namespace Drupal\pos_field_address;

use Drupal\Core\Cache\Cache;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Messenger\MessengerInterface;

/**
 * Class PosAddressConfigBuilder.
 */
class PosAddressConfigBuilder {

  const COUNTRIES = 'countries.yml';

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

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

  /**
   * PosAddressConfigBuilder constructor.
   *
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   Cache.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   Messenger.
   */
  public function __construct(CacheBackendInterface $cache, MessengerInterface $messenger) {
    $this->cache = $cache;
    $this->messenger = $messenger;
  }

  /**
   * Parse file.
   *
   * @param string $file
   *   Filename.
   *
   * @return bool|mixed
   *   File content.
   */
  private function parseFile($file) {
    $cache_name = 'address__' . str_replace('.yml', '', $file);
    if ($cached = $this->cache->get($cache_name)) {
      return $cached->data;
    }
    $path = realpath(__DIR__) . '/../values/' . $file;
    if (!is_file($path)) {
      return FALSE;
    }
    $content = file_get_contents($path);
    if ($content === FALSE) {
      return $content;
    }
    $content = Yaml::decode($content);
    $this->cache->set($cache_name, $content, Cache::PERMANENT);

    return $content;
  }

  /**
   * Get country codes.
   *
   * @return array
   *   Country codes.
   */
  public function getCountryCodes() {
    return array_keys($this->getCountriesDetailed());
  }

  /**
   * Get country list.
   *
   * @param array $allowed_countries
   *   Allowed countries.
   *
   * @return array|bool|mixed
   *   Countries.
   */
  public function getCountries(array $allowed_countries = []) {
    $countries = $this->getCountriesDetailed();
    foreach ($countries as $country_code => $country) {
      if (empty($allowed_countries) || in_array($country_code, $allowed_countries)) {
        $countries[$country_code] = $country['country'];
      }
      else {
        unset($countries[$country_code]);
      }
    }
    return $countries;
  }

  /**
   * Detailed country data.
   *
   * @return array|bool|mixed
   *   Data.
   */
  public function getCountriesDetailed() {
    $countries_detailed = &drupal_static('address_countries_detailed');
    if (!isset($countries_detailed)) {
      $countries_detailed = $this->parseFile(self::COUNTRIES);
    }
    return $countries_detailed;
  }

  /**
   * Country schema.
   *
   * @param string $country_code
   *   Country code.
   *
   * @return bool|mixed
   *   Schema.
   */
  public function getCountrySchema($country_code, $force_input_schema = FALSE) {
    $countries = $this->getCountriesDetailed();
    if ($countries === FALSE || !array_key_exists($country_code, $countries)) {
      return FALSE;
    }
    if ($force_input_schema) {
      return ['input' => t('Input')];
    }
    return $countries[$country_code]['schema'];
  }

  public function isOriginallyInputCountrySchema($country_code) {
    $schema = $this->getCountrySchema($country_code);
    if ($schema === FALSE) {
      return TRUE;
    }
    return array_key_exists('input', $schema);
  }

  /**
   * Check if country schema exists.
   *
   * @param string $country_code
   *   Country code.
   *
   * @return bool
   *   Does schema exist?
   */
  public function countrySchemaExists($country_code) {
    $countries = $this->getCountriesDetailed();
    if ($countries === FALSE || !array_key_exists($country_code, $countries)) {
      return FALSE;
    }
    return TRUE;
  }

  /**
   * Returns tree for single country.
   */
  public function getTree($country_code) {
    $tree = &drupal_static('address_countries_schema_' . $country_code);
    if (!isset($tree)) {
      $tree = $this->parseFile($country_code . '.yml');
    }
    if ($tree === FALSE) {
      return [];
    }
    return $tree === FALSE ? [] : $tree;
  }

  /**
   * Returns data at depth.
   */
  public function getCurrentDepthData($country_code, $data = [], $filter = FALSE, $filter_cache_context = FALSE, $force_input_schema = FALSE) {
    $microtime = microtime(TRUE);
    $tree = $this->cache->get($filter_cache_context);
    if ($filter !== FALSE && $filter_cache_context !== FALSE && $tree) {
      $tree = $tree->data;
    }
    else {
      $tree = $this->getTree($country_code);
      if ($filter !== FALSE) {
        $this->filterTree($tree, $filter, $country_code);
        if ($filter_cache_context !== FALSE) {
          $this->cache->set($filter_cache_context, $tree);
        }
      }
    }
    $schema = $this->getCountrySchema($country_code, $force_input_schema);
    if ($schema === FALSE) {
      return FALSE;
    }

    $return_data = [];
    $current_depth_data = $tree;
    $return_data['country'] = $tree;
    if (empty($schema)) {
      return [];
    }
    elseif (key($schema) == 'input') {
      return $return_data['country'];
    }
    do {
      if (current($data) !== FALSE) {
        $current_code = current($data);
      }
      else {
        $current_code = key($current_depth_data[key($schema)]);
      }
      $current_depth_data = $current_depth_data[key($schema)];

      if (!array_key_exists($current_code, $current_depth_data)) {
        $current_code = key($current_depth_data);
      }

      $return_data[key($schema)] = $current_depth_data[$current_code];
      $return_data[key($schema)]['code'] = $current_code;
      $current_depth_data = $current_depth_data[$current_code];
      if ((microtime(TRUE) - $microtime) > 2) {
        $this->messenger->addMessage('Report this problem to administrator');
        break;
      }
      next($data);
    } while (next($schema) !== FALSE);

    return $return_data;
  }

  /**
   * Filters tree.
   */
  private function filterTree(&$tree, $filter, $country_code) {
    if (!is_array($filter)) {
      return $tree;
    }
    $schema = $this->getCountrySchema($country_code);

    foreach ($filter as $key => $value) {
      if ($value[0] !== $country_code) {
        unset($filter[$key]);
      }
    }

    foreach ($schema as $schema_key => $label) {
      $this->unsetUnexisting($tree, $filter, $schema, $schema_key);
    }
  }

  /**
   * Deletes locations not in filter.
   */
  private function unsetUnexisting(&$tree, $filter, $schema, $schema_key) {
    $tree_copy = $tree;
    $filter_key = 1;
    reset($schema);
    $this->goToCurrentDepth($tree[key($schema)], $filter, $schema, $filter_key, $schema_key);
    return $tree_copy;
  }

  /**
   * Go through the depth.
   *
   * @param array $tree
   *   Tree.
   * @param mixed $filter
   *   Filter.
   * @param array $schema
   *   Schema.
   * @param mixed $filter_key
   *   Filter key.
   * @param mixed $schema_key
   *   Schema key.
   */
  private function goToCurrentDepth(array &$tree, &$filter, array &$schema, &$filter_key, $schema_key) {
    if (key($schema) === $schema_key) {
      foreach ($tree as $key => $value) {
        $found = FALSE;
        foreach ($filter as $filter_code) {
          if ($filter_code[$filter_key] == $key) {
            $found = TRUE;
            break;
          }
        }
        if (!$found) {
          unset($tree[$key]);
        }
      }
    }
    else {
      next($schema);
      $current_key = key($schema);
      $filter_key++;
      $current_filter = $filter_key;
      foreach ($tree as $code_key => $code) {
        $this->goToCurrentDepth($tree[$code_key][$current_key], $filter, $schema, $filter_key, $schema_key);
        $filter_key = $current_filter;
        reset($schema);
        while (key($schema) !== $current_key) {
          next($schema);
        }
      }
    }
  }

  /**
   * Get select schema.
   *
   * @param string $country_code
   *   Country code.
   *
   * @return array
   *   Schema.
   */
  public function getSelectSchema($country_code) {
    $tree = $this->getTree($country_code);
    $schema = $this->getCountrySchema($country_code);
    $values = [];
    $codes = [];
    end($schema);
    $this->getSubnames($tree, key($schema), $values, $codes, TRUE, $country_code);
    return array_combine($codes, $values);
  }

  /**
   * Get subnames.
   *
   * @param array $current_tree
   *   Current tree.
   * @param mixed $last_schema_key
   *   Last schema key.
   * @param array $values
   *   Values.
   * @param array $codes
   *   Codes.
   * @param mixed $first
   *   First.
   * @param string $country_code
   *   Country code.
   *
   * @return array
   *   Subnames.
   */
  private function getSubnames(array $current_tree, $last_schema_key, array &$values, array &$codes, $first, $country_code = '') {
    $keys = array_keys($current_tree);
    if (!array_key_exists($country_code, $this->getCountriesDetailed())) {
      $country_name = '';
    }
    else {
      $country_name = $this->getCountriesDetailed()[$country_code]['country'];
    }

    $schema_key = FALSE;
    foreach ($keys as $key) {
      if ($key != 'name') {
        $schema_key = $key;
      }
    }
    $subnames = [];
    if (empty($current_tree)) {
      return [];
    }
    foreach ($current_tree[$schema_key] as $code => $tree) {
      if ($first) {
        $subnames = $this->getSubnames($tree, $last_schema_key, $values, $codes, FALSE);
        foreach ($subnames as $subname) {
          $values[] = $country_name . '->' . $tree['name'] . '->' . $subname['name'];
          $codes[] = $country_code . '{||}' . $code . '{||}' . $subname['code'];
        }
      }
      elseif ($last_schema_key === $schema_key) {
        $subnames[] = ['name' => $tree['name'], 'code' => $code];
      }
      else {
        $return_value = $this->getSubnames($tree, $last_schema_key, $values, $codes, FALSE);
        foreach ($return_value as $subname) {
          $subnames[] = [
            'name' => $tree['name'] . '->' . $subname['name'],
            'code' => $code . '{||}' . $subname['code'],
          ];
        }
      }
    }
    if (!$first) {
      return $subnames;
    }
  }

  /**
   * Get all options for country schema element.
   *
   * @param string $country_code
   *   Country code.
   * @param mixed $schema_key
   *   Schema key.
   *
   * @return array
   *   Options.
   */
  public function getAllOptionsForCountrySchemaElement($country_code, $schema_key) {
    $tree = $this->getTree($country_code);
    $country_schema = $this->getCountrySchema($country_code);
    $values = [];
    if (!empty($country_schema)) {
      $this->getAllValuesToGivenDepth($tree[key($country_schema)], $country_schema, $schema_key, $values);
    }
    return $values;
  }

  /**
   * Get all values to given depth.
   *
   * @param array $tree
   *   Tree.
   * @param array $country_schema
   *   Schema.
   * @param mixed $schema_key
   *   Schema key.
   * @param array $values
   *   Values.
   * @param array $current_depth_value
   *   Current value.
   */
  public function getAllValuesToGivenDepth(array $tree, array &$country_schema, $schema_key, array &$values, array $current_depth_value = []) {
    $current_key_schema = key($country_schema);
    next($country_schema);
    foreach ($tree as $value => $sub_tree) {
      $current_depth_value[$current_key_schema] = (string) $value;
      if ($schema_key === $current_key_schema) {
        $values[] = [
          'name' => $sub_tree['name'],
          'value' => $current_depth_value,
        ];
      }
      else {
        $this->getAllValuesToGivenDepth($sub_tree[key($country_schema)], $country_schema, $schema_key, $values, $current_depth_value);
      }
    }
    prev($country_schema);
  }

  /**
   * Validates if data is valid.
   */
  public function validateValue($country_code, $data) {
    $tree = $this->getTree($country_code);
    $schema = $this->getCountrySchema($country_code);
    if (key($schema) === 'input' && is_string($data)) {
      return TRUE;
    }
    if (count($data) == 0 && !empty($tree)) {
      return TRUE;
    }
    elseif (count($data) == 0 && empty($tree)) {
      return FALSE;
    }
    foreach ($schema as $schema_key => $label) {
      if (array_key_exists(current($data), $tree[$schema_key])) {
        $tree = $tree[$schema_key][current($data)];
      }
      else {
        return FALSE;
      }
      if (next($data) === FALSE) {
        break;
      }
    }
    return TRUE;
  }

  /**
   * Returns all schema keys that exists.
   */
  public function getAllSchemaKeys() {
    $detailed_countries = $this->getCountriesDetailed();
    $all_schema_keys = [];
    foreach ($detailed_countries as $detailed_country) {
      foreach ($detailed_country['schema'] as $key => $label) {
        if (!in_array($key, $all_schema_keys)) {
          $all_schema_keys[] = $key;
        }
      }
    }
    return $all_schema_keys;
  }

}
/**
 * @file
 * Remove button for the Address field.
 */

(function ($, Drupal) {
  'use strict';

  Drupal.behaviors.addressFilterBehaviour = {
    attach: function (context) {
      $('.address-filer-country').once('address-filter-behaviour').on('change', function () {
        let countryElement = $(this);
        $.get('/pos_field_address/country-schema?country=' + $(this).val(), function (data) {
          let element = $(countryElement.data('schema-selector'));
          element.empty();
          $.each(data, function (key,value) {
            element.append($('<option></option>')
              .attr('value', key).text(value));
          });
          element.trigger('change');
        }, 'json');
        $.get('/pos_field_address/schema-options?country=' + $(this).val() + '&schema=' + 'settlement', function (data) {
          let newElement = $(countryElement.data('value-selector'));
          newElement.empty();
          $.each(data, function (key, value) {
            newElement.append($('<option></option>')
              .attr('value', key).text(value));
          });
          newElement.trigger('change');
        })
      });
      $('.address-filter-schema').once('address-filter-behaviour').on('change', function () {
        let schemaElement = $(this);
        let countryCode = $(schemaElement.data('country-selector')).val();
        $.get('/pos_field_address/schema-options?country=' + countryCode + '&schema=' + schemaElement.val(), function (data) {
          let element = $(schemaElement.data('value-selector'));
          element.empty();
          $.each(data, function (key,value) {
            element.append($('<option></option>')
              .attr('value', key).text(value));
          });
        }, 'json');
      });
    }
  };

})(jQuery, Drupal);
<?php

namespace Drupal\pos_field_address\Plugin\views\filter;

use Drupal\Core\Database\Connection;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\pos_field_address\PosAddressConfigBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a custom filter for filtering by addresses.
 *
 * @ingroup views_filter_handlers
 * @ViewsFilter("pos_address_advance_filter")
 */
class PosAddressAdvanceFilter extends FilterPluginBase {

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

  /**
   * Config builder.
   *
   * @var \Drupal\pos_field_address\PosAddressConfigBuilder
   */
  protected $configBuilder;

  /**
   * {@inheritdoc}
   */
  public $operator = 'LIKE';

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $connection, PosAddressConfigBuilder $config_builder) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->connection = $connection;
    $this->configBuilder = $config_builder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('database'),
      $container->get('pos_field_address.config_builder')
    );
  }

  /**
   * {@inheritdoc}
   */
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['operator'] = ['default' => 'LIKE'];
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function buildExposedForm(&$form, FormStateInterface $form_state) {
    $identifier = $this->options['expose']['identifier'];
    $exposed_input = $this->view->getExposedInput();
    $form[$identifier] = [
      '#type' => 'fieldset',
      '#title' => $this->options['expose']['label'],
      '#attached' => [
        'library' => [
          'pos_field_address/view-exposed-filter',
        ],
      ],
    ];
    $element = &$form[$identifier];
    // Define classes.
    $country_class = str_replace('_', '-', $identifier) . '-country';
    $schema_class = str_replace('_', '-', $identifier) . '-schema';
    $value_class = str_replace('_', '-', $identifier) . '-value';

    // Country element.
    $countries = $this->configBuilder->getCountries();
    $country_code = isset($exposed_input[$identifier . '_country']) ? $exposed_input[$identifier . '_country'] : key($countries);
    $element[$identifier . '_country'] = [
      '#type' => 'select',
      '#title' => 'Entitet',
      '#options' => $this->configBuilder->getCountries(),
      '#attributes' => [
        'class' => ['address-filer-country', $country_class],
        'data-schema-selector' => '.' . $schema_class,
        'data-value-selector' => '.' . $value_class,
      ],
      '#default_value' => $country_code,
    ];
    // Country schema element.
    $country_schema = $this->configBuilder->getCountrySchema($country_code);
    $country_schema_key = isset($exposed_input[$identifier . '_country_schema']) ? $exposed_input[$identifier . '_country_schema'] : key($country_schema);
//    $element[$identifier . '_country_schema'] = [
//      '#type' => 'select',
//      '#title' => $this->t('Search by'),
//      '#options' => $country_schema,
//      '#attributes' => [
//        'class' => ['address-filter-schema', $schema_class],
//        'data-country-selector' => '.' . $country_class,
//        'data-value-selector' => '.' . $value_class,
//      ],
//      '#default_value' => $country_schema_key,
//    ];
    // Value element.
    $values = $this->configBuilder->getAllOptionsForCountrySchemaElement($country_code, $country_schema_key);
    $options = ['' => $this->t('- None -')];
    foreach ($values as $value) {
      $serialized = serialize($value['value']);
      $options[str_replace('}', '', substr($serialized, strpos($serialized, '{') + 1))] = $value['name'];
    }
    $default_value = isset($exposed_input[$identifier]) ? $exposed_input[$identifier] : NULL;
    $element[$identifier] = [
      '#title' => $this->options['expose']['label'],
      '#type' => 'select',
      '#attributes' => [
        'class' => [$value_class, 'address-filter-city'],
        'data-schema-selector' => '.' . $schema_class,
      ],
      '#options' => $options,
      '#default_value' => $default_value,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function query() {
    if (!empty($this->value) && !empty(reset($this->value))) {
      $this->ensureMyTable();
      $this->query->addWhere($this->options['group'], "$this->tableAlias.$this->realField", '%' . $this->connection->escapeLike(reset($this->value)) . '%', $this->operator);
    }
    else if (!empty($this->view->getExposedInput()) && $this->view->getExposedInput()['main_company_address_country'] === 'BD') {
      $no_settlement = 's:0:""';
      $this->query->addWhere($this->options['group'], "$this->tableAlias.$this->realField", '%' . $this->connection->escapeLike($no_settlement) . '%', $this->operator);
    }
  }

}
<?php

/**
 * @file
 * Contains cfp_payment_request.module.
 */

use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\cfp_payment_request\Entity\PaymentRequestType;
use Drupal\pos_entity_cfp\Entity\CfpType;
use Drupal\Core\Entity\EntityTypeInterface;

module_load_include('inc', 'cfp_payment_request', 'cfp_payment_request.extra_fields');
module_load_include('inc', 'cfp_payment_request', 'cfp_payment_request.tokens');
\Drupal::moduleHandler()->loadInclude('cfp_payment_request', 'inc', 'cfp_payment_request.validation');
require_once realpath(__DIR__) . '/cfp_payment_request.currency_conv.inc';

/**
 * Implements hook_help().
 */
function cfp_payment_request_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the cfp_payment_request module.
    case 'help.page.cfp_payment_request':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Payment Request for CFP') . '</p>';
      return $output;

    default:
  }
}

/**
 * Implements hook_mail().
 */
function cfp_payment_request_mail($key, &$message, $params) {
  switch ($key) {
    case 'pos_payment_request_mail':
      $mail_sender = \Drupal::config('system.site')->get('mail');
      $message['headers']['From'] = $mail_sender;
      $message['headers']['Sender'] = $mail_sender;
      $message['headers']['Return-Path'] = $mail_sender;
      $message['headers']['Content-Type'] = 'text/html';
      $message['from'] = $mail_sender;
      /** @var \Drupal\Core\Utility\Token $token */
      $token = \Drupal::service('token');
      $token_data = [
        'cfp_payment_request' => $params['entity'],
      ];
      $message['subject'] = $token->replace($params['title'], $token_data);
      $message['body'][] = $token->replace($params['message'], $token_data);

      $email_log = \Drupal::entityTypeManager()
        ->getStorage('pos_entity_email_logs')
        ->create([
          'title' => $message['subject'],
          'body' => isset($message['body'][0]) ? $message['body'][0] : '',
          'cfp' => $params['entity']->get('parent')->target_id,
        ]);
      $email_log->save();
      break;
  }
}

/**
 * Implements hook_theme().
 */
function cfp_payment_request_theme() {
  $theme = [];
  $theme['cfp_payment_request'] = [
    'render element' => 'elements',
    'file' => 'cfp_payment_request.page.inc',
    'template' => 'cfp_payment_request',
  ];
  $theme['cfp_payment_request_content_add_list'] = [
    'render element' => 'content',
    'variables' => ['content' => NULL],
    'file' => 'cfp_payment_request.page.inc',
  ];
  $theme['cfp_payment_request_item'] = [
    'render element' => 'elements',
    'file' => 'cfp_payment_request_item.page.inc',
    'template' => 'cfp_payment_request_item',
  ];
  $theme['cfp_payment_request_item_content_add_list'] = [
    'render element' => 'content',
    'variables' => ['content' => NULL],
    'file' => 'cfp_payment_request_item.page.inc',
  ];
  $theme['cfp_payment_request_checklist'] = [
    'render element' => 'elements',
    'file' => 'cfp_payment_request_checklist.page.inc',
  ];
  $theme['pr_invoice'] = [
    'render element' => 'elements',
  ];
  return $theme;
}

/**
 * Prepares variables for invoice templates.
 *
 * Default template: pr-invoice.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - elements: An associative array containing the invoice information and any
 *     fields attached to the entity.
 *   - attributes: HTML attributes for the containing element.
 */
function template_preprocess_pr_invoice(array &$variables) {
  foreach (Element::children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
}

/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function cfp_payment_request_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if ($entity_type->id() === 'cfp_payment_request') {
    $pr_type = \Drupal::entityTypeManager()->getStorage('cfp_payment_request_type')->load($bundle);
    if ($pr_type) {
      if (isset($fields['items'])) {
        $settings = $fields['items']->getSettings();
        $settings['handler_settings']['target_bundles'] = [$pr_type->get('payment_request_item_type') => $pr_type->get('payment_request_item_type')];
        $fields['items']->setSettings($settings);
      }
      if (isset($fields['invoices'])) {
        $settings = $fields['invoices']->getSettings();
        $settings['handler_settings']['target_bundles'] = [$pr_type->get('payment_request_invoice_type') => $pr_type->get('payment_request_invoice_type')];
        $fields['invoices']->setSettings($settings);
      }
    }
  }
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function cfp_payment_request_theme_suggestions_cfp_payment_request(array $variables) {
  $entity = $variables['elements']['#cfp_payment_request'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  return build_template_suggestions($entity, $sanitized_view_mode);
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function cfp_payment_request_theme_suggestions_cfp_payment_request_item(array $variables) {
  $entity = $variables['elements']['#cfp_payment_request_item'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  return build_template_suggestions($entity, $sanitized_view_mode);
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function cfp_payment_request_theme_suggestions_cfp_payment_request_checklist(array $variables) {
  $entity = $variables['elements']['#cfp_payment_request_checklist'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  return build_template_suggestions($entity, $sanitized_view_mode);
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function cfp_payment_request_theme_suggestions_pr_invoice(array $variables) {
  $entity = $variables['elements']['#pr_invoice'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  return build_template_suggestions($entity, $sanitized_view_mode);
}

/**
 * Adds template suggestions.
 */
function build_template_suggestions($entity, $sanitized_view_mode) {
  $suggestions = [];
  $suggestions[] = $entity->getEntityTypeId() . '__' . $sanitized_view_mode;
  $suggestions[] = $entity->getEntityTypeId() . '__' . $entity->bundle();
  $suggestions[] = $entity->getEntityTypeId() . '__' . $entity->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = $entity->getEntityTypeId() . '__' . $entity->id();
  $suggestions[] = $entity->getEntityTypeId() . '__' . $entity->id() . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_file_download().
 */
function cfp_payment_request_file_download($uri) {
  if (str_starts_with($uri, 'temporary://temporary_excel_exports/')) {
    return [
      'Content-disposition' => 'attachment; filename="' . basename($uri) . '"',
    ];
  }

}

/**
 * Implements hook_add_more_entities_to_field_access_alter().
 */
function cfp_payment_request_add_more_entities_to_field_access_alter(&$access, FieldItemListInterface $items, FieldDefinitionInterface $field_definition) {
  // @todo review this logic?
  $target_type = $field_definition->getSetting('target_type');
  if ($target_type === 'cfp_payment_request') {
    // TODO: Delete comment.
    /** @var \Drupal\pos_entity_cfp\Entity\Cfp $parent_entity */
//    $parent_entity = $items->getEntity();
//    if ($parent_entity->getState()->getId() != 'project_implementation') {
//      return FALSE;
//    }
//    $current_user = \Drupal::currentUser();
//    $access = $access ?: $current_user->id() == $parent_entity->getOwnerId();
//    if ($access) {
//      /** @var \Drupal\pos_entity_cfp\Entity\CfpType $cfp_type */
//      $cfp_type = CfpType::load($parent_entity->bundle());
//      /** @var \Drupal\cfp_payment_request\Entity\PaymentRequestType $payment_request_type */
//      $payment_request_type = PaymentRequestType::load($cfp_type->getPaymentRequestBundle());
//      if (!$payment_request_type) {
//        $access = FALSE;
//        return;
//      }
//      if ($current_user->hasPermission('administer payment request')) {
//        $entity_limit = $payment_request_type->get('entity_limit_for_admin');
//      } else {
//        $entity_limit = $payment_request_type->get('entity_limit_for_applicant');
//      }
//      if ($entity_limit !== -1  && $items->count() >= $entity_limit) {
//        $access = FALSE;
//      }
//    }
  }
}

/**
 * Implements hook_add_more_entity_alter().
 */
function cfp_payment_request_add_more_entity_alter(FieldableEntityInterface $entity, FieldableEntityInterface $parent, FieldDefinitionInterface $field_definition) {
  if ($entity->getEntityTypeId() === 'cfp_payment_request') {
    // @todo Probably move this logic to presave.
    $entity->set('parent', $parent);
    $entity->setOwnerId($parent->getOwnerId());
  }
}

/**
 * Implements hook_options_list_alter().
 */
function cfp_payment_request_options_list_alter(array &$options, array $context) {
  if ($context['fieldDefinition']->getName() === 'pr_type' && $context['fieldDefinition']->getTargetEntityTypeId() === 'cfp_payment_request') {
    /** @var \Drupal\cfp_payment_request\Entity\PaymentRequestType $pr_type */
    $pr_type = $context['entity']->get('type')->entity;
    $allowed_options = $pr_type->get('allowed_pr_types') + ['_none' => '_none'];
    if (!empty($allowed_options)) {
      foreach ($options as $option_key => $option) {
        if (!in_array($option_key, $allowed_options)) {
          unset($options[$option_key]);
        }
      }
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function cfp_payment_request_preprocess_field__entity_reference(&$variables) {
  if ($variables['field_name'] === 'payment_request' && $variables['entity_type'] === 'cfp') {
    foreach ($variables['items'] as $item) {
      $pr = $item['content']['#cfp_payment_request'];
      if ($pr->getState() === 'reopened') {
        $item['attributes']->setAttribute('style', 'border: 3px solid #ff0000');
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function cfp_payment_request_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if ($form['#id'] === 'views-exposed-form-payment-requests-page-1') {
    $workflow_manager = \Drupal::service('plugin.manager.workflow');
    $workflow = $workflow_manager->createInstance('payment_request');
    $states = $workflow->getStates();
    $states = array_map(function ($state) {
      return $state->getLabel();
    }, $states);

    $form['state'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $states,
    ];
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function cfp_payment_request_form_cfp_payment_request_pr_2022_01_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['cf_customs_documents']['#states'] = [
    'visible' => [
      ':input[name="pr_type"]' => ['value' => 'closing']
    ]
  ];
  $form['cf_eur1_form']['#states'] = [
    'visible' => [
      ':input[name="pr_type"]' => ['value' => 'closing']
    ]
  ];

  // @TODO: Ovo treba ispraviti. Ne verujem da ovo može tu da bude.
  // Payment request item conditional fields.
  $conditional_fields = [
    'cf_final_account',
    'cf_confirm_final_payment',
    'cf_purchase_contract',
    'cf_protocol_record',
    'cf_driving_license',
    'cf_pictures',
  ];
  foreach ($conditional_fields as $conditional_field) {
    if (isset($form[$conditional_field])) {
      $form[$conditional_field]['#states'] = [
        'visible' => [
          ':input[name="pr_type"]' => ['value' => 'closing']
        ]
      ];
    }
  }
}
<?php

/**
 * Implements hook_entity_validation_ENTITY_TYPE_alter().
 */
function cfp_payment_request_entity_validation_cfp_payment_request_alter($entity_typed_data, $validation_context) {
  $entity_typed_data->getDataDefinition()->addConstraint('GlobalPaymentRequestConstraint', []);
  if ($validation_context === 'cfp_payment_request.pr_2022_01') {
    $required_fields = [
      'cf_promissory_notes',
      'cf_no_debts_confirmation',
      'cf_technical_report_start',
      'cf_technical_report_end',
      'cf_project_description',
      'items',
    ];

    /** @var Drupal\Core\Entity\EntityInterface $entity */
    $entity = $entity_typed_data->getEntity();

    if ($entity->get('pr_type')->value === 'closing') {
      array_push($required_fields, 'cf_eur1_form', 'cf_customs_documents');
    }

    /** @var \Drupal\Core\Field\FieldDefinition[] $field_definitions */
    $field_definitions = $entity->getFieldDefinitions();
    foreach ($required_fields as $required_field) {
      $field_definitions[$required_field]->setRequired(TRUE);
    }
  }
}

/**
 * Implements hook_entity_validation_ENTITY_TYPE_rollback_alter().
 */
function cfp_payment_request_entity_validation_cfp_payment_request_item_rollback_alter($entity_typed_data, $validation_context, $rollback_info) {
  if ($validation_context === 'cfp_payment_request.pr_2022_01') {
    $required_fields = [
      'cost_item_name',
      'cf_manufacturer',
      'cf_distributor',
      'cf_unique_item_id',
      'cf_distributor_bank_account_num',
      'cf_distributor_vat_number',
      'item_quantity',
    ];

    $decimal_fields = [
      'item_net_unit',
      'item_unit_vat',
      'item_pay_off',
      'item_support',
    ];

    /** @var \Drupal\cfp_payment_request\Entity\PaymentRequestItem $entity */
    $entity = $entity_typed_data->getEntity();
    $payment_request = $entity->getParent();
    if ($payment_request->get('pr_type')->value === 'closing') {
      array_push($required_fields,
        'cf_final_account',
        'cf_confirm_final_payment',
        'cf_purchase_contract',
        'cf_protocol_record',
        'cf_driving_license',
        'cf_pictures',
      );
    }
    /** @var \Drupal\Core\Field\FieldDefinition[] $field_definitions */
    $field_definitions = $entity->getFieldDefinitions();
    foreach ($required_fields as $required_field) {
      $field_definitions[$required_field]->setRequired(TRUE);
    }
    foreach ($decimal_fields as $decimal_field) {
      if ($entity->get($decimal_field)->value === "0.00") {
        $entity->set($decimal_field, NULL);
      }
      $field_definitions[$decimal_field]->setRequired(TRUE);
    }
  }
}
<?php

namespace Drupal\cfp_payment_request\Entity;

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\pos_core\PosFieldVariables;
use Drupal\pos_entity_autocalculate\AutoCalculateFormula;
use Drupal\pos_entity_autocalculate\Entity\AutocalculateEntityTrait;
use Drupal\pos_entity_cost_item\AdditionalCostItem;
use Drupal\pos_entity_cost_item\Entity\CostItemAcceptableInterface;
use Drupal\pos_parent_entity\Entity\ChildEntityTrait;
use Drupal\user\UserInterface;

/**
 * Defines the Payment request item entity.
 *
 * @ingroup cfp_payment_request
 *
 * @ContentEntityType(
 *   id = "cfp_payment_request_item",
 *   label = @Translation("Payment request item"),
 *   bundle_label = @Translation("Payment request item type"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\cfp_payment_request\PaymentRequestItemListBuilder",
 *     "views_data" = "Drupal\cfp_payment_request\Entity\PaymentRequestItemViewsData",
 *
 *     "form" = {
 *       "default" = "Drupal\cfp_payment_request\Form\PaymentRequestItemForm",
 *       "add" = "Drupal\cfp_payment_request\Form\PaymentRequestItemForm",
 *       "edit" = "Drupal\cfp_payment_request\Form\PaymentRequestItemForm",
 *       "delete" = "Drupal\cfp_payment_request\Form\PaymentRequestItemDeleteForm",
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\cfp_payment_request\PaymentRequestItemHtmlRouteProvider",
 *     },
 *     "access" = "Drupal\cfp_payment_request\PaymentRequestItemAccessControlHandler",
 *     "inline_form" = "\Drupal\cfp_payment_request\Form\PaymentRequestItemInlineForm",
 *   },
 *   base_table = "cfp_payment_request_item",
 *   translatable = FALSE,
 *   admin_permission = "administer payment request item entities",
 *   entity_keys = {
 *     "id" = "id",
 *     "bundle" = "type",
 *     "uuid" = "uuid",
 *     "uid" = "user_id",
 *     "langcode" = "langcode",
 *     "published" = "status",
 *   },
 *   links = {
 *     "canonical" = "/admin/structure/cfp_payment_request_item/{cfp_payment_request_item}",
 *     "add-page" = "/admin/structure/cfp_payment_request_item/add",
 *     "add-form" = "/admin/structure/cfp_payment_request_item/add/{cfp_payment_request_item_type}",
 *     "edit-form" = "/admin/structure/cfp_payment_request_item/{cfp_payment_request_item}/edit",
 *     "delete-form" = "/admin/structure/cfp_payment_request_item/{cfp_payment_request_item}/delete",
 *     "collection" = "/admin/structure/cfp_payment_request_item",
 *   },
 *   bundle_entity_type = "cfp_payment_request_item_type",
 *   field_ui_base_route = "entity.cfp_payment_request_item_type.edit_form"
 * )
 */
class PaymentRequestItem extends ContentEntityBase implements PaymentRequestItemInterface {

  use EntityChangedTrait;
  use EntityPublishedTrait;
  use ChildEntityTrait;
  use AutocalculateEntityTrait {
    preSave as autoCalculatePreSave;
    addFormulas as autoCalculateAddFormulas;
  }

  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
    parent::preCreate($storage_controller, $values);
    $values += [
      'user_id' => \Drupal::currentUser()->id(),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);
    if ($this->getPRIType()->arePricesInCurrencies()) {
      if (($currency = $this->getCurrency()) && ($currency_date = $this->getCurrencyDate())) {
        if ($exchange_rate = self::getExchangeRate($currency, $currency_date)) {
          $this->populateValuesWithExchange($exchange_rate);
        }
      }
      $this->autoCalculatePreSave($storage);
    } else {
      $this->autoCalculatePreSave($storage);
    }
    if ($this->get('accepted')->value !== 'accepted_with_new_value') {
      $this->set('new_item_pay_off', NULL)
        ->set('new_item_support', NULL);
    }
  }

  /**
   * {@inheritDoc}
   * Some computed field directly depend on another field, and we need to access
   *   it right after.
   */
  public function onChange($name) {
    parent::onChange($name);
    $field_influence_connection = [
      'accepted' => ['item_pay_off_computed', 'item_support_computed'],
      'item_pay_off' => ['item_pay_off_computed'],
      'new_item_pay_off' => ['item_pay_off_computed'],
      'new_item_support' => ['item_support_computed'],
      'item_support' => ['item_support_computed'],
    ];
    foreach ($field_influence_connection[$name] ?? [] as $field_influence) {
      $this->get($field_influence)->forceRecomputeValue();
    }
  }

  /**
   * @param $exchange_value
   */
  public function populateValuesWithExchange($exchange_value) {
    $exchange_value = (float) $exchange_value;
    foreach ($this->getFieldDefinitions() as $field_definition) {
      if (strpos($field_definition->getName(), '_currency_value') !== FALSE) {
        $value = $this->get($field_definition->getName())->value;
        if ($value) {
          $forint_field_name = str_replace('_currency_value', '', $field_definition->getName());
          $this->set($forint_field_name, bcmul($value, $exchange_value));
        } elseif(is_numeric($value)) {
          $forint_field_name = str_replace('_currency_value', '', $field_definition->getName());
          $this->set($forint_field_name, 0);
        } else {
          $this->set($forint_field_name, NULL);
        }
      }
    }
  }

  public static function getExchangeRate($currency, $currency_date) {
    if ($currency === 'HUF') {
      return 1;
    }
    $exchange_rate = str_replace(',', '.', _get_mnb_currency_value($currency, $currency_date));
    return is_numeric($exchange_rate) ? $exchange_rate : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->get('name')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setName($name) {
    $this->set('name', $name);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() {
    return $this->get('created')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp) {
    $this->set('created', $timestamp);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwner() {
    return $this->get('user_id')->entity;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwnerId() {
    return $this->get('user_id')->target_id;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwnerId($uid) {
    $this->set('user_id', $uid);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwner(UserInterface $account) {
    $this->set('user_id', $account->id());
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function label() {
    return $this->getName();
  }

  /**
   * Payment request item type.
   *
   * @return \Drupal\cfp_payment_request\Entity\PaymentRequestItemType
   */
  public function getPRIType() {
    return PaymentRequestItemType::load($this->bundle());
  }

  /**
   * @return mixed
   */
  public function getCurrency() {
    $currency = $this->get('currency')->value;
    return $currency;
  }

  /**
   *
   */
  public function getCurrencyDate() {
    if (!$this->get('currency_date')->isEmpty()) {
      return $this->get('currency_date')->date->format(CURRENCY_DATE_FORMAT);
    }
  }

  /**
   * Cost item identifier.
   *
   * @return string
   *   Identifier of item.
   */
  public function getCostItemIdentifier() {
    return $this->get('cost_item_name')->value;
  }

  /**
   * Returns accepted state.
   *
   * @return string
   *   Accepted state.
   */
  public function getAcceptedState() {
    return $this->get('accepted')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function addFormulas() {
    $this->autoCalculateAddFormulas();
    if ($this->getPRIType()->arePricesInCurrencies()) {
      // Currency values.
      $this->addAutoCalculateFormula('item_net_amount_currency_value', AutoCalculateFormula::create()
        ->addFieldWithOperation('item_net_unit_currency_value')
        ->multiplyField('item_quantity'));

      $this->addAutoCalculateFormula('item_vat_currency_value', AutoCalculateFormula::create()
        ->addFieldWithOperation('item_unit_vat_currency_value')
        ->multiplyField('item_quantity')
      );

      $this->addAutoCalculateFormula('item_bruto_currency_value', AutoCalculateFormula::create()
        ->addFieldWithOperation('item_net_amount_currency_value')
        ->addField('item_vat_currency_value')
      );

    }

    // Forint values.
    $this->addAutoCalculateFormula('item_net_amount', AutoCalculateFormula::create()
      ->addFieldWithOperation('item_net_unit')
      ->multiplyField('item_quantity'));

    $this->addAutoCalculateFormula('item_vat', AutoCalculateFormula::create()
      ->addFieldWithOperation('item_unit_vat')
      ->multiplyField('item_quantity')
    );

    $this->addAutoCalculateFormula('item_bruto', AutoCalculateFormula::create()
      ->addFieldWithOperation('item_net_amount')
      ->addField('item_vat')
    );

    $this->addAutoCalculateFormula('item_support_percentage', AutoCalculateFormula::create()
      ->addField('item_support')
      ->divideField('item_pay_off')
      ->addNumberWithOperation(100, AutoCalculateFormula::MULTIPLY)
    );

    $this->addAutoCalculateFormula('item_own_source', AutoCalculateFormula::create()
      ->addFieldWithOperation('item_pay_off')
      ->subtractField('item_support'));

    $this->addAutoCalculateFormula('item_bruto_unit', AutoCalculateFormula::create()
      ->addFieldWithOperation('item_bruto')
      ->divideField('item_quantity')
    );
  }

  /**
   * {@inheritDoc}
   */
  public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
    $fields = [];
    if ($bundle) {
      /** @var \Drupal\cfp_payment_request\Entity\PaymentRequestItemType $pri_type */
      $pri_type = \Drupal::entityTypeManager()
        ->getStorage('cfp_payment_request_item_type')
        ->load($bundle);
      if ($pri_type) {
        $prices_in_currencies = $pri_type->arePricesInCurrencies();
        foreach ($base_field_definitions as $field_name => $base_field_definition) {
          if (strpos($field_name, '_currency_value') !== FALSE) {
            if (!$prices_in_currencies) {
              $fields[$field_name] = clone $base_field_definition;
              $fields[$field_name]->setDisplayConfigurable('form', FALSE)
                ->setDisplayConfigurable('view', FALSE);
            }
            else {
              $basic_field_name = str_replace('_currency_value', '', $field_name);
              $fields[$basic_field_name] = clone $base_field_definitions[$basic_field_name];
              $fields[$basic_field_name]->setDisplayConfigurable('form', FALSE);
            }
          }
        }
      }
    }

    return $fields;

  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = parent::baseFieldDefinitions($entity_type);

    // Add the published field.
    $fields += static::publishedBaseFieldDefinitions($entity_type);

    $fields['user_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Authored by'))
      ->setDescription(t('The user ID of author of the Payment request item entity.'))
      ->setRevisionable(TRUE)
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Tétel sorszáma'))
      ->setDescription(t('The name of the Payment request item entity.'))
      ->setSettings([
        'max_length' => 50,
        'text_processing' => 0,
      ])
      ->setDefaultValue('')
      ->setDisplayConfigurable('view', TRUE);

    $fields['cost_item_name'] = BaseFieldDefinition::create('list_string')
      ->setLabel('Stavka na koju se isplata odnosi')
      ->setSetting('allowed_values_function', '\Drupal\cfp_payment_request\Entity\PaymentRequestItem::itemNames')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
    // NUMERIC FIELDS.
    $numeric_fields = [
      'item_net_unit' => 'Neto jedinična cijena',
      'item_unit_vat' => 'PDV po neto jediničnoj cijeni',
      'item_bruto_unit' => 'Jedinična bruto cijena',
      'item_quantity' => 'Količina',
      'item_pay_off' => 'Prihvatljivi trošak nabavke artikla',
      'item_support' => 'Iznos bespovratnih sredstava koji tražite za artikal (EUR)',
      'item_net_amount' => 'Neto ukupni trošak nabavke artikla (EUR)',
      'item_vat' => 'Ukupan PDV',
      'item_bruto' => 'Bruto ukupni trošak nabavke artikla (EUR)',
      'item_own_source' => 'Iznos sopstvenog učešća (EUR)',
    ];
    $exception_numeric_field_names = [
      'item_quantity',
      'item_pay_off',
      'item_support',
      'item_own_source',
    ];
    foreach ($numeric_fields as $numeric_field_machine_name => $numeric_field) {
      $fields[$numeric_field_machine_name] = BaseFieldDefinition::create($numeric_field_machine_name === 'item_quantity' ? 'integer' : 'decimal')
        ->setLabel($numeric_field)
        ->setSettings([
          'precision' => 12,
          'scale' => 2,
          'suffix' => '€',
        ])
        ->setDisplayConfigurable('form', TRUE)
        ->setDisplayConfigurable('view', TRUE);
      if (!in_array($numeric_field_machine_name,  $exception_numeric_field_names)) {
        $fields[$numeric_field_machine_name .'_currency_value'] = BaseFieldDefinition::create('decimal')
          ->setLabel(str_replace(' (Ft)', '', $numeric_field))
          ->setSettings([
            'precision' => 12,
            'scale' => 2,
          ])
          ->setDisplayConfigurable('form', TRUE)
          ->setDisplayConfigurable('view', TRUE);
      }
    }

    $fields['new_item_pay_off'] = BaseFieldDefinition::create('decimal')
      ->setLabel('New payoff')
      ->setSettings([
        'precision' => 12,
        'scale' => 2,
        'suffix' => '€',
      ]);

    $fields['new_item_support'] = BaseFieldDefinition::create('decimal')
      ->setLabel('New Support')
      ->setSettings([
        'precision' => 12,
        'scale' => 2,
        'suffix' => '€',
      ]);

    $fields['currency'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Currency'))
      ->setSetting('allowed_values_function', '_get_mnb_currencies')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['currency_date'] = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Currency date'))
      ->setSettings([
        'datetime_type' => 'date',
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['item_support_percentage'] = BaseFieldDefinition::create('decimal')
      ->setLabel('Postotak bespovratnih sredstava koji tražite (%)')
      ->setSettings([
        'prefix' => '',
        'suffix' => '%',
      ])
      ->setSettings(PosFieldVariables::PERCENTAGE_DEFAULT_FIELD_SETTINGS)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['item_pay_off_computed'] = BaseFieldDefinition::create('decimal')
      ->setLabel('Ügyintéző által jóváhagyott elszámolható költség')
      ->setComputed(TRUE)
      ->setClass('\Drupal\cfp_payment_request\Plugin\Field\ItemPayOffComputedFieldItemList')
      ->setDisplayConfigurable('view', TRUE);

    $fields['item_support_computed'] = BaseFieldDefinition::create('decimal')
      ->setLabel('Ügyintéző által jóváhagyott támogatási összeg')
      ->setComputed(TRUE)
      ->setClass('\Drupal\cfp_payment_request\Plugin\Field\ItemSupportComputedFieldItemList')
      ->setDisplayConfigurable('view', TRUE);

    $fields['supplier'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Supplier'))
      ->setSetting('target_type', 'supplier')
      ->setSetting('handler', 'default:supplier')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['accepted'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Status'))
      ->setDefaultValue('accepted')
      ->setInitialValue('accepted')
      ->setSetting('allowed_values', PaymentRequestItem::getAllowedAcceptedValues())
      ->setDisplayConfigurable('view', TRUE);

    $fields['status']->setDescription(t('A boolean indicating whether the Payment request item is published.'));

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'));

    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));

    return $fields;
  }

  public static function getAllowedAcceptedValues() {
    return [
      'rejected' => t('Rejected'),
      'accepted' => t('Accepted'),
      'accepted_with_new_value' => t('Accepted with new value'),
    ];
  }

  /**
   * Get item names.
   *
   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition
   *   Field definition.
   * @param \Drupal\Core\Entity\ContentEntityInterface|null $entity
   *   Content entity.
   *
   * @return array
   *   Item names.
   */
  public static function itemNames(FieldStorageDefinitionInterface $definition = NULL, ContentEntityInterface $entity = NULL) {
    if ($parent = $entity->getParent()) {
      /** @var \Drupal\pos_payment_request\Entity\PaymentRequest $parent */
      if ($parent_cfp = $parent->get('parent')->entity) {
        /** @var \Drupal\pos_entity_cost_item\Entity\CostItemsParentEntityInterface $parent_cfp */
        $additional_options= array_filter(array_map(function (CostItemAcceptableInterface $cost_item) {
          if ($cost_item->isNewTotalSupportDefined() || $cost_item->isAccepted()) {
            return $cost_item->getName();
          }
        }, $parent_cfp->getCostItemEntities()));
        foreach ($parent_cfp->getAdditionalCostItems() as $key => $cost_item) {
          $additional_options["additional_cost_item_$key"] = $cost_item['name'];
        }
        return $additional_options;
      }
    }
    return [];
  }

  /**
   * @return array
   */
  public function getItemsCriteria() {
    if ($parent = $this->getParent()) {
      /** @var \Drupal\pos_payment_request\Entity\PaymentRequest $parent */
      if ($parent_cfp = $parent->get('parent')->entity) {
        /** @var \Drupal\pos_entity_cost_item\Entity\CostItemsParentEntityInterface $parent_cfp */
        $additional_options= array_filter(array_map(function (CostItemAcceptableInterface $cost_item) {
          if ($cost_item->isNewTotalSupportDefined() || $cost_item->isAccepted()) {
            return new AdditionalCostItem([
              'name' => $cost_item->getName(),
              'value' => $cost_item->getAcceptedTotalSupport(),
              'own_contribution_value' => $cost_item->getAcceptedOwnContribution(),
            ]);
          }
        }, $parent_cfp->getCostItemEntities()));
        foreach ($parent_cfp->getAdditionalCostItems() as $key => $cost_item) {
          $additional_options["additional_cost_item_$key"] = $cost_item;
        }
        return $additional_options;
      }
    }
  }

}
{#
/**
 * @file review.html.twig
 * Default theme implementation to present Review data.
 *
 * This template is used when viewing Review pages.
 *
 *
 * Available variables:
 * - content: A list of content items. Use 'content' to print all content, or
 * - attributes: HTML attributes for the container element.
 *
 * @see template_preprocess_review()
 *
 * @ingroup themeable
 */
#}
<link rel="stylesheet" type="text/css" href="/themes/custom/pos/css/print.css">

<script type="text/javascript">
  window.onload = () => {window.print();}
</script>

<div{{ attributes.addClass('review') }}>
  {% if content %}
    {{- content -}}
  {% endif %}
</div>
<div class="signatures">
  <div class="signature-date">
    <p>Mjesto i datum: ____________________________ 2022.</p>
  </div>
  <div class="signature"><p>____________________________________________________________</p>
    <p>Potpis</p>
  </div>
</div>
{#
/**
 * @file review.html.twig
 * Default theme implementation to present Review data.
 *
 * This template is used when viewing Review pages.
 *
 *
 * Available variables:
 * - content: A list of content items. Use 'content' to print all content, or
 * - attributes: HTML attributes for the container element.
 *
 * @see template_preprocess_review()
 *
 * @ingroup themeable
 */
#}
<link rel="stylesheet" type="text/css" href="/themes/custom/pos/css/print.css">

<script type="text/javascript">
  window.onload = () => {window.print();}
</script>

<div{{ attributes.addClass('review') }}>
  {% if content %}
    {{- content -}}
  {% endif %}
</div>
<div class="signatures">
  <div class="signature-date">
    <p>Mjesto i datum: ____________________________ 2022.</p>
  </div>
  <div class="signature"><p>____________________________________________________________</p>
    <p>Potpis</p>
  </div>
</div>
<?php

/**
 * @file
 * Contains pos_entity_review.module.
 */

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\pos_entity_cfp\Entity\Cfp;
use Drupal\pos_entity_form_part\Form\EntityPartForm;
use Drupal\pos_entity_review\Entity\ReviewType;
use Drupal\pos_entity_review\Entity\ReviewTypeInterface;

/**
 * Implements hook_help().
 */
function pos_entity_review_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the pos_entity_review module.
    case 'help.page.pos_entity_review':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Pos Review Base Entity Used by CFP in some states') . '</p>';
      return $output;

    default:
  }
}

/**
 * Returns all execution as options.
 *
 * @return array
 *   Array of all review types with labels.
 *
 * @throws \ReflectionException
 */
function _get_review_types_as_options() {
  $reflection_class = new ReflectionClass(ReviewTypeInterface::class);
  $states = [];
  foreach ($reflection_class->getConstants() as $constant_name => $constant) {
    if (strpos($constant_name, 'REVIEW_TYPE') !== FALSE) {
      $states[$constant] = ucfirst(strtolower(str_replace([
        'REVIEW_TYPE_',
        '_',
      ], [
        '',
        ' ',
      ], $constant_name)));
    }
  }
  return $states;
}

/**
 * Implements hook_theme().
 */
function pos_entity_review_theme() {
  $theme = [];
  $theme['review'] = [
    'render element' => 'elements',
    'file' => 'review.page.inc',
    'template' => 'review',
  ];
  $theme['review_content_add_list'] = [
    'render element' => 'content',
    'variables' => ['content' => NULL],
    'file' => 'review.page.inc',
  ];
  return $theme;
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function pos_entity_review_theme_suggestions_review(array $variables) {
  $suggestions = [];
  $entity = $variables['elements']['#review'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');

  $suggestions[] = 'review__' . $sanitized_view_mode;
  $suggestions[] = 'review__' . $entity->bundle();
  $suggestions[] = 'review__' . $entity->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = 'review__' . $entity->id();
  $suggestions[] = 'review__' . $entity->id() . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_entity_extra_field_info().
 */
function pos_entity_review_entity_extra_field_info() {
  $extra = [];
  foreach (ReviewType::loadMultiple() as $review_type) {
    $extra['review'][$review_type->id()]['display']['view_link'] = [
      'label' => t('View link'),
      'description' => 'Entity view link',
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['edit_link'] = [
      'label' => t('Edit link'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['entity_label'] = [
      'label' => t('Entity label'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    if ($review_type->questionCanBeClarified()) {
      $extra['review'][$review_type->id()]['display']['clarify_question'] = [
        'label' => t('Clarify question button'),
        'weight' => 0,
        'visible' => FALSE,
      ];
    }
    $extra['review'][$review_type->id()]['display']['correction_data'] = [
      'label' => t('Correction data(correction data for given)'),
      'description' => t('If review is for correction state, corrections will be shown'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['reviewer_additional_files'] = [
      'label' => t('Shows file upload for additional files'),
      'description' => t('Reviewer need to attach document during work in some cases'),
      'weight' => 0,
      'visible' => FALSE,
    ];
  }

  return $extra;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function pos_entity_review_review_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  /** @var \Drupal\pos_entity_review\Entity\Review $entity */

  if ($display->getComponent('view_link') && $entity->access('view') && !$entity->access('update')) {
    $view_link = $entity->toUrl();
    $build['view_link'] = [
      '#type' => 'link',
      '#title' => t('View'),
      '#url' => $view_link,
      '#attributes' => [
        'class' => ['button'],
      ],
    ];
  }
  if ($display->getComponent('edit_link') && $entity->access('update')) {
    $edit_link = $entity->toUrl('edit-form');
    $edit_link->setRouteParameter('destination', Url::fromRoute('<current>')->toString());
    $build['edit_link'] = [
      '#type' => 'link',
      '#title' => t('Edit'),
      '#url' => $edit_link,
      '#attributes' => [
        'class' => ['button', 'button--primary'],
      ],
    ];
  }
  if ($display->getComponent('entity_label')) {
    $build['entity_label'] = [
      '#type' => 'markup',
      '#markup' => '<h3>' . $entity->label() . '</h3>',
    ];
  }
  if ($display->getComponent('clarify_question') && $entity->access('update')) {
    $build['clarify_question'] = [
      '#title' => t('Clarify question'),
      '#type' => 'link',
      '#url' => $entity->toUrl('question-form')->setRouteParameter('destination', Url::fromRoute('<current>')->toString()),
      '#attributes' => [
        'class' => ['button'],
      ],
    ];
  }
  if ($display->getComponent('correction_data')) {
    /** @var \Drupal\pos_entity_cfp\Entity\Cfp $cfp */
    $cfp = $entity->getCfpEntity();
    if ($cfp && $state_for_review = $entity->get('state_for_review')->value) {
      $correction_plugin = $cfp->getStateCorrectionPlugin($state_for_review);
      if ($correction_plugin) {
        $build['correction_data']['#type'] = 'container';
        $build['correction_data']['questions'] = $correction_plugin->getCorrectionQuestionElement();
        $build['correction_data']['answers'] = $correction_plugin->getCorrectionAnswersElement();
      }
    }
  }
  if ($display->getComponent('reviewer_additional_files')) {
    if ((\Drupal::currentUser()->id() == $entity->getAssigneeId()) || (in_array('administrator', \Drupal::currentUser()->getRoles()))) {
      $field = 'reviewer_files';

      /** @var \Drupal\pos_entity_form_part\Form\EntityPartForm $form_part */
      $form_part = \Drupal::classResolver()
        ->getInstanceFromDefinition(EntityPartForm::class);
      $form_state = new FormState();
      $form_part->setFields([$field]);
      $form_part->setEntity($entity);
      $form_part->setRedirectionRoute('<current>');
      $form_part->setCurrentTabRedirect(TRUE);
      $form = \Drupal::formBuilder()->buildForm($form_part, $form_state);
      $form['reviewer_files']['widget']['#disable_dragging'] = TRUE;
      $build['reviewer_additional_files'] = $form;
    }
  }
}

/**
 * Implements hook_cron().
 */
function pos_entity_review_cron() {
  $query = \Drupal::database()->query('SELECT id FROM `review` WHERE cfp NOT IN (SELECT id FROM cfp)');
  $review_storage = \Drupal::entityTypeManager()->getStorage('review');
  while ($row = $query->fetch()) {
    $review = $review_storage->load($row->id);
    if ($review) {
      $review->delete();
    }
  }
}

/**
 * Implements hook_views_data_alter().
 */
function pos_entity_review_views_data_alter(array &$data) {
  $data['review']['review_project_implementation_filter'] = [
    'title' => t('Járás" - Okres - Project Implementation Place (Filter)'),
    'filter' => [
      'title' => t('Járás - Okres - Project Implementation Place (Filter)'),
      'field' => 'cfp',
      'id' => 'pos_review_address_filter',
    ],
  ];
}

/**
 * Implements hook_review_passed_alter().
 */
function pos_entity_review_review_passed_alter(&$passed, $entity) {
  /** @var \Drupal\pos_entity_review\Entity\ReviewType $review_type */
  $review_type = ReviewType::load($entity->bundle());
  if (($key_field = $review_type->getKeyField()) && !empty($accept_values = $review_type->getAcceptValues())) {
    if (in_array($entity->get($key_field)->value, $accept_values)) {
      if (pos_printer_is_printable_entity($entity->getEntityType(), $entity->bundle())) {
        if ($entity->isPrintConfirmed()) {
          $passed = TRUE;
        }
      }
      else {
        $passed = TRUE;
      }
    }
  }
}

/**
 * Implements hook_review_rejected_alter().
 */
function pos_entity_review_review_rejected_alter(&$rejected, $entity) {
  /** @var \Drupal\pos_entity_review\Entity\ReviewType $review_type */
  $review_type = ReviewType::load($entity->bundle());
  if (($key_field = $review_type->getKeyField()) && !empty($reject_values = $review_type->getRejectValues())) {
    if (in_array($entity->get($key_field)->value, $reject_values)) {
      if (pos_printer_is_printable_entity($entity->getEntityType(), $entity->bundle())) {
        if ($entity->isPrintConfirmed()) {
          $rejected = TRUE;
        }
      }
      else {
        $rejected = TRUE;
      }
    }
  }
}

/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function pos_entity_review_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if ($entity_type->id() === 'review' && !empty($bundle)) {
    $review_type = ReviewType::load($bundle);
    if ($key_field = $review_type->getKeyField()) {
      if (isset($fields[$key_field])) {
        $fields[$key_field]->addConstraint('reviewKeyFieldCondition');
      }
    }
  }
  if ($entity_type->id() === 'review' && $bundle === 'acceptance_2022_01') {
    if ($fields['cf_comment']) {
      $fields['cf_comment']->setRequired(TRUE);
      $fields['cf_comment']->addPropertyConstraints('value', ['NamedLength' => ['min' => 20, 'max' => 500]]);
    }
  }
}

/**
 * Implements hook_mail().
 */
function pos_entity_review_mail($key, &$message, $params) {
  switch ($key) {
    case 'reviewer_clarify_question':
      $message['headers']['From'] = $params['from'];
      $message['headers']['Sender'] = $params['from'];
      $message['subject'] = $params['subject'];
      $message['body'][] = $params['message'];
      $message['headers']['Content-Type'] = 'text/html';
      break;
  }
}

{#
/**
 * @file review.html.twig
 * Default theme implementation to present Review data.
 *
 * This template is used when viewing Review pages.
 *
 *
 * Available variables:
 * - content: A list of content items. Use 'content' to print all content, or
 * - attributes: HTML attributes for the container element.
 *
 * @see template_preprocess_review()
 *
 * @ingroup themeable
 */
#}
<link rel="stylesheet" type="text/css" href="/themes/custom/pos/css/print.css">

<script type="text/javascript">
  window.onload = () => {window.print();}
</script>

<div{{ attributes.addClass('review') }}>
  {% if content %}
    {{- content -}}
  {% endif %}
</div>
<div class="signatures">
  <div class="signature-date">
    <p>Mjesto i datum: ____________________________ 2022.</p>
  </div>
  <div class="signature"><p>____________________________________________________________</p>
    <p>Potpis</p>
  </div>
</div>
<?php

/**
 * @file
 * Contains pos_entity_review.module.
 */

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\pos_entity_cfp\Entity\Cfp;
use Drupal\pos_entity_form_part\Form\EntityPartForm;
use Drupal\pos_entity_review\Entity\ReviewType;
use Drupal\pos_entity_review\Entity\ReviewTypeInterface;

/**
 * Implements hook_help().
 */
function pos_entity_review_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the pos_entity_review module.
    case 'help.page.pos_entity_review':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Pos Review Base Entity Used by CFP in some states') . '</p>';
      return $output;

    default:
  }
}

/**
 * Returns all execution as options.
 *
 * @return array
 *   Array of all review types with labels.
 *
 * @throws \ReflectionException
 */
function _get_review_types_as_options() {
  $reflection_class = new ReflectionClass(ReviewTypeInterface::class);
  $states = [];
  foreach ($reflection_class->getConstants() as $constant_name => $constant) {
    if (strpos($constant_name, 'REVIEW_TYPE') !== FALSE) {
      $states[$constant] = ucfirst(strtolower(str_replace([
        'REVIEW_TYPE_',
        '_',
      ], [
        '',
        ' ',
      ], $constant_name)));
    }
  }
  return $states;
}

/**
 * Implements hook_theme().
 */
function pos_entity_review_theme() {
  $theme = [];
  $theme['review'] = [
    'render element' => 'elements',
    'file' => 'review.page.inc',
    'template' => 'review',
  ];
  $theme['review_content_add_list'] = [
    'render element' => 'content',
    'variables' => ['content' => NULL],
    'file' => 'review.page.inc',
  ];
  return $theme;
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function pos_entity_review_theme_suggestions_review(array $variables) {
  $suggestions = [];
  $entity = $variables['elements']['#review'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');

  $suggestions[] = 'review__' . $sanitized_view_mode;
  $suggestions[] = 'review__' . $entity->bundle();
  $suggestions[] = 'review__' . $entity->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = 'review__' . $entity->id();
  $suggestions[] = 'review__' . $entity->id() . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_entity_extra_field_info().
 */
function pos_entity_review_entity_extra_field_info() {
  $extra = [];
  foreach (ReviewType::loadMultiple() as $review_type) {
    $extra['review'][$review_type->id()]['display']['view_link'] = [
      'label' => t('View link'),
      'description' => 'Entity view link',
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['edit_link'] = [
      'label' => t('Edit link'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['entity_label'] = [
      'label' => t('Entity label'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    if ($review_type->questionCanBeClarified()) {
      $extra['review'][$review_type->id()]['display']['clarify_question'] = [
        'label' => t('Clarify question button'),
        'weight' => 0,
        'visible' => FALSE,
      ];
    }
    $extra['review'][$review_type->id()]['display']['correction_data'] = [
      'label' => t('Correction data(correction data for given)'),
      'description' => t('If review is for correction state, corrections will be shown'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['reviewer_additional_files'] = [
      'label' => t('Shows file upload for additional files'),
      'description' => t('Reviewer need to attach document during work in some cases'),
      'weight' => 0,
      'visible' => FALSE,
    ];
    $extra['review'][$review_type->id()]['display']['raw_signature'] = [
      'label' => t('Signature'),
      'visible' => FALSE,
    ];
  }

  return $extra;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function pos_entity_review_review_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  /** @var \Drupal\pos_entity_review\Entity\Review $entity */

  if ($display->getComponent('view_link') && $entity->access('view') && !$entity->access('update')) {
    $view_link = $entity->toUrl();
    $build['view_link'] = [
      '#type' => 'link',
      '#title' => t('View'),
      '#url' => $view_link,
      '#attributes' => [
        'class' => ['button'],
      ],
    ];
  }
  if ($display->getComponent('edit_link') && $entity->access('update')) {
    $edit_link = $entity->toUrl('edit-form');
    $edit_link->setRouteParameter('destination', Url::fromRoute('<current>')->toString());
    $build['edit_link'] = [
      '#type' => 'link',
      '#title' => t('Edit'),
      '#url' => $edit_link,
      '#attributes' => [
        'class' => ['button', 'button--primary'],
      ],
    ];
  }
  if ($display->getComponent('entity_label')) {
    $build['entity_label'] = [
      '#type' => 'markup',
      '#markup' => '<h3>' . $entity->label() . '</h3>',
    ];
  }
  if ($display->getComponent('clarify_question') && $entity->access('update')) {
    $build['clarify_question'] = [
      '#title' => t('Clarify question'),
      '#type' => 'link',
      '#url' => $entity->toUrl('question-form')->setRouteParameter('destination', Url::fromRoute('<current>')->toString()),
      '#attributes' => [
        'class' => ['button'],
      ],
    ];
  }
  if ($display->getComponent('correction_data')) {
    /** @var \Drupal\pos_entity_cfp\Entity\Cfp $cfp */
    $cfp = $entity->getCfpEntity();
    if ($cfp && $state_for_review = $entity->get('state_for_review')->value) {
      $correction_plugin = $cfp->getStateCorrectionPlugin($state_for_review);
      if ($correction_plugin) {
        $build['correction_data']['#type'] = 'container';
        $build['correction_data']['questions'] = $correction_plugin->getCorrectionQuestionElement();
        $build['correction_data']['answers'] = $correction_plugin->getCorrectionAnswersElement();
      }
    }
  }
  if ($display->getComponent('reviewer_additional_files')) {
    if ((\Drupal::currentUser()->id() == $entity->getAssigneeId()) || (in_array('administrator', \Drupal::currentUser()->getRoles()))) {
      $field = 'reviewer_files';

      /** @var \Drupal\pos_entity_form_part\Form\EntityPartForm $form_part */
      $form_part = \Drupal::classResolver()
        ->getInstanceFromDefinition(EntityPartForm::class);
      $form_state = new FormState();
      $form_part->setFields([$field]);
      $form_part->setEntity($entity);
      $form_part->setRedirectionRoute('<current>');
      $form_part->setCurrentTabRedirect(TRUE);
      $form = \Drupal::formBuilder()->buildForm($form_part, $form_state);
      $form['reviewer_files']['widget']['#disable_dragging'] = TRUE;
      $build['reviewer_additional_files'] = $form;
    }
  }
  if ($display->getComponent('raw_signature')) {
    /** @var \Drupal\pos_entity_cfp\Entity\Cfp $cfp */
    $cfp = $entity->getCfpEntity();
    $build['raw_signature'] = [
      '#type' => 'inline_template',
      '#template' => pos_entity_review_get_signature_part($cfp),
    ];
  }
}

/**
 * Get signature part.
 */
function pos_entity_review_get_signature_part(Cfp $cfp) {
  $representative = $cfp->getApplicantEntity()->getCompanyRepresentative();
  return '<div class="signatures">
        <div class="signature-date"><p>Mjesto i datum: ____________________________ 2022.</p></div>
        <div class="signature">
            <p>____________________________</p>
            <p>' . $cfp->getApplicantEntity()->get('applicant_name')->value . '</p>
            <p>' . $representative?->label() .'</p>
        </div>';
}

/**
 * Implements hook_cron().
 */
function pos_entity_review_cron() {
  $query = \Drupal::database()->query('SELECT id FROM `review` WHERE cfp NOT IN (SELECT id FROM cfp)');
  $review_storage = \Drupal::entityTypeManager()->getStorage('review');
  while ($row = $query->fetch()) {
    $review = $review_storage->load($row->id);
    if ($review) {
      $review->delete();
    }
  }
}

/**
 * Implements hook_views_data_alter().
 */
function pos_entity_review_views_data_alter(array &$data) {
  $data['review']['review_project_implementation_filter'] = [
    'title' => t('Járás" - Okres - Project Implementation Place (Filter)'),
    'filter' => [
      'title' => t('Járás - Okres - Project Implementation Place (Filter)'),
      'field' => 'cfp',
      'id' => 'pos_review_address_filter',
    ],
  ];
}

/**
 * Implements hook_review_passed_alter().
 */
function pos_entity_review_review_passed_alter(&$passed, $entity) {
  /** @var \Drupal\pos_entity_review\Entity\ReviewType $review_type */
  $review_type = ReviewType::load($entity->bundle());
  if (($key_field = $review_type->getKeyField()) && !empty($accept_values = $review_type->getAcceptValues())) {
    if (in_array($entity->get($key_field)->value, $accept_values)) {
      if (pos_printer_is_printable_entity($entity->getEntityType(), $entity->bundle())) {
        if ($entity->isPrintConfirmed()) {
          $passed = TRUE;
        }
      }
      else {
        $passed = TRUE;
      }
    }
  }
}

/**
 * Implements hook_review_rejected_alter().
 */
function pos_entity_review_review_rejected_alter(&$rejected, $entity) {
  /** @var \Drupal\pos_entity_review\Entity\ReviewType $review_type */
  $review_type = ReviewType::load($entity->bundle());
  if (($key_field = $review_type->getKeyField()) && !empty($reject_values = $review_type->getRejectValues())) {
    if (in_array($entity->get($key_field)->value, $reject_values)) {
      if (pos_printer_is_printable_entity($entity->getEntityType(), $entity->bundle())) {
        if ($entity->isPrintConfirmed()) {
          $rejected = TRUE;
        }
      }
      else {
        $rejected = TRUE;
      }
    }
  }
}

/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function pos_entity_review_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if ($entity_type->id() === 'review' && !empty($bundle)) {
    $review_type = ReviewType::load($bundle);
    if ($key_field = $review_type->getKeyField()) {
      if (isset($fields[$key_field])) {
        $fields[$key_field]->addConstraint('reviewKeyFieldCondition');
      }
    }
  }
  if ($entity_type->id() === 'review' && $bundle === 'acceptance_2022_01') {
    if ($fields['cf_comment']) {
      $fields['cf_comment']->setRequired(TRUE);
      $fields['cf_comment']->addPropertyConstraints('value', ['NamedLength' => ['min' => 20, 'max' => 500]]);
    }
  }
}

/**
 * Implements hook_mail().
 */
function pos_entity_review_mail($key, &$message, $params) {
  switch ($key) {
    case 'reviewer_clarify_question':
      $message['headers']['From'] = $params['from'];
      $message['headers']['Sender'] = $params['from'];
      $message['subject'] = $params['subject'];
      $message['body'][] = $params['message'];
      $message['headers']['Content-Type'] = 'text/html';
      break;
  }
}

<?php


namespace Drupal\pos_entity_review\Form;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\pos_entity_cfp\Entity\CfpType;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class ReviewQuestionClaficationForm
 *
 * @package Drupal\pos_entity_review\Form
 */
class ReviewQuestionClarificationForm extends ContentEntityForm {

  /**
   * Mail Manager.
   *
   * @var \Drupal\Core\Mail\MailManagerInterface
   */
  protected $mailManager;

  /**
   * Constructs a new ReviewQuestionClarificationForm.
   *
   * @param EntityRepositoryInterface $entity_repository
   * @param EntityTypeBundleInfoInterface $entity_type_bundle_info
   * @param TimeInterface $time
   * @param MailManagerInterface $mailManager
   */
  public function __construct(EntityRepositoryInterface $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info, TimeInterface $time, MailManagerInterface $mailManager) {
    parent::__construct($entity_repository, $entity_type_bundle_info, $time);
    $this->mailManager = $mailManager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    // Instantiates this form class.
    return new static(
      $container->get('entity.repository'),
      $container->get('entity_type.bundle.info'),
      $container->get('datetime.time'),
      $container->get('plugin.manager.mail'),
    );
  }

  /**
   * {@inheritDoc}
   */
  public function getFormDisplay(FormStateInterface $form_state) {
    $entity_form_display = EntityFormDisplay::create([
      'targetEntityType' => 'review',
      'third_party_settings' => [],
      'bundle' => $this->entity->bundle(),
      'content' => [
        'question' => [
          'type' => 'string_textarea',
          'region' => 'content',
          'weight' => 0,
          'settings' => [
            'rows' => 10,
            'placeholder' => '',
          ],
          'third_party_settings' => [],
        ]
      ],
    ]);
    return $entity_form_display;
  }

  /**
   * {@inheritDoc}
   */
  public function setFormDisplay(EntityFormDisplayInterface $form_display, FormStateInterface $form_state) {
    return parent::setFormDisplay($this->getFormDisplay($form_state), $form_state);
  }

  /**
   * {@inheritDoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    $actions = parent::actions($form, $form_state);
    unset($actions['delete']);
    return $actions;
  }

  /**
   * Route title callback.
   *
   * @return \Drupal\Core\StringTranslation\TranslatableMarkup
   */
  public function titleCallback() {
    return $this->t('Clarify question');
  }

  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);

    /** @var \Drupal\pos_entity_review\Entity\Review $review */
    $review = $this->entity;

    $cfp = $review->getCfpEntity();
    $cfp_type = CfpType::load($cfp->bundle());
    $reviewer = $review->getAssignee();

    // If admin is the one who is sending the mail, then it needs to be sent to reviewers email address.
    if ($this->currentUser()->hasPermission('admin view all call for proposal entities')) {
      $to = $reviewer->getEmail();
      $from = $cfp_type->get('mail_sender')->value ?? 'noreply@forsrpska.org';
    }
    else {
      $to = $cfp_type->get('notification_email')->value ?? 'info@forsrpska.org';
      $from = $reviewer->getEmail();
    }

    $this->mailManager->mail(
      'pos_entity_review',
      'reviewer_clarify_question',
      $to,
      \Drupal::languageManager()->getCurrentLanguage()->getId(),
      [
        'from' => $from,
        'subject' => t('Clarify question'),
        'message' => reset($form_state->getValue('question'))['value'],
      ],
      NULL,
      TRUE
    );

  }

}
<?php

namespace Drupal\cfp_payment_request\Entity;

use Drupal\cfp_payment_request\Event\PRTransitionEvent;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\halo_closable_entity\Entity\ClosableEntityTrait;
use Drupal\halo_entity_cfp\Entity\Cfp;
use Drupal\halo_entity_validator\Entity\HaloEntityValidationTrait;
use Drupal\halo_parent_entity\Entity\ChildEntityTrait;
use Drupal\halo_printer\Entity\HaloPrintableEntityTrait;
use Drupal\halo_printer\Entity\PrintTokenEntityInterface;
use Drupal\halo_transition_history_field\Plugin\Field\TransitionDateSearchItemList;
use Drupal\user\UserInterface;
use Drupal\halo_entity_cfp\Entity\CfpPaymentInterface;

/**
 * Defines the Payment request entity.
 *
 * @ingroup cfp_payment_request
 *
 * @ContentEntityType(
 *   id = "cfp_payment_request",
 *   label = @Translation("Payment request"),
 *   bundle_label = @Translation("Payment request type"),
 *   handlers = {
 *     "view_builder" = "Drupal\cfp_payment_request\PaymentRequestEntityViewBuilder",
 *     "list_builder" = "Drupal\cfp_payment_request\PaymentRequestListBuilder",
 *     "views_data" = "Drupal\cfp_payment_request\Entity\PaymentRequestViewsData",
 *
 *     "form" = {
 *       "default" = "Drupal\cfp_payment_request\Form\PaymentRequestForm",
 *       "add" = "Drupal\cfp_payment_request\Form\PaymentRequestForm",
 *       "edit" = "Drupal\cfp_payment_request\Form\PaymentRequestForm",
 *       "delete" = "Drupal\cfp_payment_request\Form\PaymentRequestDeleteForm",
 *       "suspended_documents" = "\Drupal\cfp_payment_request\Form\PaymentRequestSuspendedDocumentsForm",
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\cfp_payment_request\PaymentRequestHtmlRouteProvider",
 *     },
 *     "access" = "Drupal\cfp_payment_request\PaymentRequestAccessControlHandler",
 *   },
 *   base_table = "cfp_payment_request",
 *   translatable = FALSE,
 *   admin_permission = "administer payment request entities",
 *   entity_keys = {
 *     "id" = "id",
 *     "bundle" = "type",
 *     "label" = "name",
 *     "uuid" = "uuid",
 *     "uid" = "user_id",
 *     "langcode" = "langcode",
 *     "published" = "status",
 *   },
 *   links = {
 *     "canonical" = "/admin/structure/cfp_payment_request/{cfp_payment_request}",
 *     "add-page" = "/admin/structure/cfp_payment_request/add",
 *     "add-form" = "/admin/structure/cfp_payment_request/add/{cfp_payment_request_type}",
 *     "edit-form" = "/admin/structure/cfp_payment_request/{cfp_payment_request}/edit",
 *     "current-correction-form" = "/admin/structure/cfp_payment_request/{cfp_payment_request}/correction",
 *     "correction-submit-form" = "/admin/structure/cfp_payment_request/{cfp_payment_request}/correction_submit",
 *     "delete-form" = "/admin/structure/cfp_payment_request/{cfp_payment_request}/delete",
 *     "collection" = "/admin/structure/cfp_payment_request",
 *     "admin-view" = "/admin/structure/cfp_payment_request/{cfp_payment_request}/admin_view",
 *     "suspended-documents" = "/cfp_payment_request/{cfp_payment_request}/suspended_documents",
 *     "suspended-documents-submit" = "/cfp_payment_request/{cfp_payment_request}/suspended_documents_submit"
 *   },
 *   bundle_entity_type = "cfp_payment_request_type",
 *   field_ui_base_route = "entity.cfp_payment_request_type.edit_form"
 * )
 */
class PaymentRequest extends ContentEntityBase implements PaymentRequestInterface, CfpPaymentInterface, PrintTokenEntityInterface {

  use EntityChangedTrait;
  use EntityPublishedTrait;
  use HaloEntityValidationTrait;
  use ClosableEntityTrait;
  use HaloPrintableEntityTrait;
  use ChildEntityTrait;

  const ADD_UP_FIELDS = [
    'item_pay_off',
    'item_support',
    'item_pay_off_computed',
    'item_support_computed',
    'item_net_amount',
    'item_vat',
    'item_bruto',
    'item_own_source',
  ];

  /**
   * {@inheritdoc}
   */
  public static function preCreate(EntityStorageInterface $storage_controller, array &$values) {
    parent::preCreate($storage_controller, $values);
    $values += [
      'user_id' => \Drupal::currentUser()->id(),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getName() {
    return $this->get('label')->value;
  }

  /**
   * {@inheritdoc}
   * @todo Refactor this in more functions.
   */
  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);
    /** @var \Drupal\cfp_payment_request\Entity\PaymentRequestType $payment_request_type */
    $payment_request_type = $this->get('type')->entity;
    $clear_reopen_instructions = FALSE;
    if (!$this->isNew()) {
      // On opening payment request.
      if ($this->getState() === 'open_state' && $this->getState() !== $this->original->getState()) {
        $this->set('validation_passed', FALSE);
        $this->set('closed', FALSE);
        $this->set('print', NULL);
        $this->set('print_confirmed', FALSE);
      }
      // On reopening payment request.
      if ($this->getState() === 'reopened' && $this->getState() !== $this->original->getState()) {
        /** @var \Drupal\file\Entity\File $old_print_file */
        $old_print_file = $this->get('print')->entity;
        if ($old_print_file) {
          $this->get('old_prints')->appendItem([
            'target_id' => $old_print_file->id(),
            'description' => DrupalDateTime::createFromTimestamp($old_print_file->getCreatedTime())->format('Y.m.d.') . ' - print',
          ]);
        }
        $reopen_instructions = $this->get('reopen_instructions')->value ?? '-';
        $this->get('previous_reopen_instructions')->appendItem(['value' => $reopen_instructions]);
        $clear_reopen_instructions = TRUE;
        $this->invalidate()->open()->clearPrintFile()->unconfirmPrint();
      }
      if ($this->isPrintConfirmed() === TRUE && $this->original->isPrintConfirmed() === FALSE) {
        $this->appendChecklist($this->getState() === 'reopened');
        $this->setState('sent');
        if ($this->get('first_submission_date')->isEmpty()) {
          $this->set('first_submission_date', \Drupal::time()->getCurrentTime());
        }
      }
      if (!$this->original->isClosed() && $this->isClosed()) {
        $datetime = new DrupalDateTime();
        $datetime->setTimezone(new \DateTimeZone('UTC'));
        $this->set('closed_date', $datetime->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT));
      }
      if ($this->getState() === 'correction_submitted' &&
        $this->original->getState() === 'correction'
      ) {
        $current_correction = (int) $this->get('current_correction')->value;
        $this->appendChecklist(TRUE);
        $this->set('current_correction', ($current_correction + 1));
      }
      if ($this->getState() === 'deadline_expired' &&
        $this->original->getState() === 'reopened') {
        // This function needs to accept one more optional argument (after_deadline).
        $this->appendChecklist(FALSE, TRUE);
      }
      if (($this->getState() === 'approved' || $this->getState() === 'accepted_with_reduction') && $this->stateHasChangedFromOriginal()) {
        $this->set('date_of_completion', \Drupal::time()->getCurrentTime());
      }
      // Flag that cfp has closing payment request.
      if ($this->stateHasChangedFromOriginal() && $this->hasField('pr_type') && $this->get('pr_type')->value === 'closing') {
        if (in_array($this->getState(), Cfp::getPaidStates())) {
          if ($this->hasParent() && !$this->getParent()->isFlagged('has_closing_payment_request')) {
            $this->getParent()->flag('has_closing_payment_request')->save();
          }
        }
        if (in_array($this->getState(), Cfp::getCompletionStates())) {
          if ($this->hasParent() && !$this->getParent()->isFlagged('has_approved_closing_pr')) {
            $this->getParent()->flag('has_approved_closing_pr')->save();
          }
        }
      }
      if ($this->getState() !== $this->original->getState()) {
        $event = new PRTransitionEvent(
          $this,
          $this->original->getState(),
          $this->getState()
        );
        \Drupal::service('event_dispatcher')->dispatch(PRTransitionEvent::TRANSITION_EVENT, $event);
        $this->appendTransition($this->getState());
        $this->set('correction_deadline', NULL);
      }
      if ($this->stateHasChangedFromOriginal() && in_array($this->getState(), Cfp::getCompletionInvalidationStates())) {
        $this->setAmountDedicatedToAp(NULL);
        $this->set('date_of_completion', NULL);
      }
    }
    if ($this->get('label')->isEmpty() && $this->hasParent()) {
      $parent = $this->getParent();
      $order = $parent->get('payment_request')->count() + 1;
      $this->set('label', $parent->label() . '/' . $payment_request_type->get('order_prefix') . $order);
    }
    if ($this->hasParent()) {
      $this->setOwnerId($this->getParent()->getOwnerId());
    }
    if ($clear_reopen_instructions) {
      $this->set('reopen_instructions', NULL);
    }
    $items = $this->getItems(TRUE);
    $this->calculateTotals($items);
    $this->constructItemLabels($items);
    $this->setInvoiceOrdering();
  }

  /**
   * Calculates totals.
   */
  public function calculateTotals($items = NULL) {
    if (!isset($items)) {
      $items = $this->getItems(TRUE);
    }
    foreach (self::ADD_UP_FIELDS as $add_up_field) {
      $total = 0;
      foreach ($items as $item) {
        $total += $item->get($add_up_field)->value ?? 0;
      }
      $this->set("{$add_up_field}_total", $total);
    }

    if (($this->getState() === 'approved' || $this->getState() === 'accepted_with_reduction') && isset($this->original) && $this->original->getState() !== $this->getState()) {
      /** @var \Drupal\halo_entity_cfp\Entity\Cfp $cfp */
      $cfp = $this->getParent();
      $not_covered_ap = $cfp->getNotCoveredApAmount();
      if ($not_covered_ap > $this->getTotalSupport()) {
        $this->setAmountDedicatedToAp($this->getTotalSupport());
      }
      else {
        $this->setAmountDedicatedToAp($not_covered_ap);
      }
    }
  }

  /**
   * Has state changed since original.
   *
   * @return bool
   *   Has state changed.
   */
  protected function stateHasChangedFromOriginal() {
    if (!isset($this->original)) {
      return FALSE;
    }
    return $this->original->getState() !== $this->getState();
  }

  /**
   * Constructs labels based on order from parent payment request(this entity).
   *
   * @param \Drupal\cfp_payment_request\Entity\PaymentRequestItem[] $items
   *   Payment request items.
   */
  public function constructItemLabels($items) {
    foreach ($items as $delta => $item) {
      $label = $this->label() . '-' . str_pad(($delta + 1), 3, '0', STR_PAD_LEFT);
      if ($item->getName() != $label) {
        $item->setName($label);
        $item->save();
      }
    }
  }

  public function setInvoiceOrdering() {
    foreach ($this->getInvoices() as $order => $invoice) {
      if (((int) ($order + 1)) !== ((int) $invoice->get('invoice_order')->value)) {
        $invoice->set('invoice_order', $order + 1);
        $invoice->save();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function setName($name) {
    $this->set('label', $name);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime() {
    return $this->get('created')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp) {
    $this->set('created', $timestamp);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwner() {
    return $this->get('user_id')->entity;
  }

  /**
   * {@inheritdoc}
   */
  public function getOwnerId() {
    return $this->get('user_id')->target_id;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwnerId($uid) {
    $this->set('user_id', $uid);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setOwner(UserInterface $account) {
    $this->set('user_id', $account->id());
    return $this;
  }

  /**
   * Item type for this payment request.
   *
   * @return string
   *   Item type for this payment request.
   */
  public function getPaymentRequestItemType() {
    return \Drupal::entityTypeManager()
      ->getStorage('cfp_payment_request_type')
      ->load($this->bundle())
      ->get('payment_request_item_type');
  }

  /**
   * @return \Drupal\cfp_payment_request\Entity\PaymentRequestItem[]
   */
  public function getItems($with_invoice = FALSE) {
    $items = [];
    foreach ($this->get('items') as $field_item) {
      if ($item = $field_item->entity) {
        $items[] = $item;
      }
    }
    if ($with_invoice) {
      foreach ($this->getInvoices() as $invoice) {
        foreach ($invoice->getItems() as $item) {
          $items[] = $item;
        }
      }
    }
    return $items;
  }

  /**
   * @return \Drupal\cfp_payment_request\Entity\PrInvoice[]
   */
  public function getInvoices() {
    $invoices = [];
    foreach ($this->get('invoices') as $field_item) {
      if ($invoice = $field_item->entity) {
        $invoices[] = $invoice;
      }
    }
    return $invoices;
  }

  /**
   * Returns active state.
   *
   * @return string
   *   Active state.
   */
  public function getState() {
    return $this->get('state')->value ?: 'open_state';
  }

  /**
   * Returns active state.
   *
   * @return string
   *   Active state.
   */
  public function getStateId(): string {
    return $this->getState();
  }

  /**
   * {@inheritDoc}
   */
  public function getCompletionTimestamp(): int|string|null {
    return $this->get('date_of_completion')->value;
  }

  /**
   * Payment request state.
   *
   * @param string $state
   *   Payment request state.
   *
   * @return static
   *   Returns
   */
  public function setState($state) {
    $this->set('state', $state);
    return $this;
  }

  /**
   * Returns user friendly state value.
   *
   * @return string
   *   User friendly value.
   */
  public function getStateLabel() {
    return $this->get('state')->first()->getLabel();
  }

  /**
   * {@inheritdoc}
   */
  public function inheritsParentAccess() {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function label() {
    return $this->get('label')->value;
  }

  /**
   * Is item approvement closed.
   *
   * @return bool
   *
   */
  public function isItemApprovementClosed() {
    return (bool) $this->get('items_approvement_closed')->value;
  }

  /**
   * {@inheritDoc}
   */
  public function appendChecklist($during_correction = FALSE, $after_deadline = FALSE) {
    /** @var \Drupal\cfp_payment_request\Entity\PaymentRequestType $payment_request_type */
    $payment_request_type = $this->get('type')->entity;
    if (!$during_correction) {
      if ($after_deadline) {
        $payment_request_checklist = $this->generateDuplicateChecklist($during_correction, $after_deadline);
      }
      else {
        $payment_request_checklist = \Drupal::entityTypeManager()
          ->getStorage('cfp_payment_request_checklist')
          ->create([
            'bundle' => $payment_request_type->get('payment_request_checklist_type'),
            'created_during_correction' => $during_correction,
            'created_after_deadline' => $after_deadline,
          ]);
      }
    } else {
      $payment_request_checklist = $this->generateDuplicateChecklist($during_correction, $after_deadline);
    }
    $payment_request_checklist->save();
    $this->get('checklist')->appendItem($payment_request_checklist->id());
  }

  /**
   * Generates new checklist.
   *
   * @param $correction
   * @param $deadline
   *
   * @return \Drupal\cfp_payment_request\Entity\CfpPaymentRequestChecklist
   */
  private function generateDuplicateChecklist($correction, $deadline): CfpPaymentRequestChecklist {
    $last_checklist = $this->getLastChecklist();
    $payment_request_checklist = $last_checklist->createDuplicate();
    $payment_request_checklist->set('closed', FALSE);
    $payment_request_checklist->set('validation_passed', FALSE);
    $payment_request_checklist->set('created_during_correction', $correction);
    $payment_request_checklist->set('created_after_deadline', $deadline);

    return $payment_request_checklist;
  }

  /**
   * Returns last checklist.
   *
   * @return \Drupal\cfp_payment_request\Entity\CfpPaymentRequestChecklist
   */
  public function getLastChecklist() {
    if ($this->get('checklist')->isEmpty()) {
      return FALSE;
    }
    /** @var \Drupal\cfp_payment_request\Entity\CfpPaymentRequestChecklist $checklist */
    $checklist = $this->get('checklist')->get(($this->get('checklist')->count() - 1))->entity;
    return $checklist;
  }

  /**
   * @return \Drupal\cfp_payment_request\Entity\PrCorrectionData
   */
  public function getCurrentCorrectionData() {
    $current_correction = $this->get('current_correction')->value;
    if ($item = $this->get('correction_data')->get($current_correction)) {
      if ($correction_data = $item->entity) {
        return $correction_data;
      }
    }
    return FALSE;
  }

  /**
   * @param $to_state
   */
  public function appendTransition($to_state) {
    $this->get('transition_history')->appendItem([
      'to' => $to_state,
      'timestamp' => \Drupal::time()->getCurrentTime(),
      'user_id' => \Drupal::currentUser()->id(),
      'user_email' => \Drupal::currentUser()->getEmail(),
      'user_display_name' => \Drupal::currentUser()->getDisplayName(),
      'deadline' => $this->get('correction_deadline')->value,
    ]);
  }

  /**
   * Total support.
   *
   * @return int
   *   Total support.
   */
  public function getTotalSupport() {
    // TODO: Check every call.
    return (int) $this->get('item_support_computed_total')->value;
  }

  /**
   * Amount to pay.
   *
   * @return int
   *   Amount to pay.
   */
  public function getAmountToPay() {
    return (int) $this->get('amount_to_pay')->value;
  }

  /**
   * Amount dedicated to advance payment.
   *
   * @return int
   *   Amount dedicated to advance payment.
   */
  public function getAmountDedicatedToAp() {
    return (int) $this->get('computed_amount_dedicated_to_ap')->value;
  }

  /**
   * Returns pr type.
   *
   * @return string
   *   Returns value of config field pr type.
   */
  public function getPaymentRequestType() {
    return $this->hasField('pr_type') ? $this->get('pr_type')->value : NULL;
  }

  /**
   * @return string
   */
  public function getPaymentRequestTypeLabel() {
    if ($type = $this->getPaymentRequestType()) {
      $allowed_values = $this->get('pr_type')->getFieldDefinition()->getSetting('allowed_values');
      return $allowed_values[$type];
    }
  }

  /**
   * Set amount dedicated to advance payment coverage.
   *
   * @param $amount
   *   Amount dedicated to advance payment.
   */
  public function setAmountDedicatedToAp($amount) {
    $this->set('amount_dedicated_to_ap', $amount);
    return $this;
  }

  /**
   * @return string
   *   Last reopening instructions.
   */
  public function getLastReopeningInstructions() {
    if (!$this->get('previous_reopen_instructions')->isEmpty()) {
      return $this->get('previous_reopen_instructions')->get($this->get('previous_reopen_instructions')->count() - 1)->value;
    }
  }

  public function getSignatureToken() {
    /** @var \Drupal\halo_entity_cfp\Entity\Cfp $cfp */
    $cfp = $this->getParent();
    return $cfp->getApplicantEntity()->label();
  }

  public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
    $fields = [];
    $pr_type = \Drupal::entityTypeManager()->getStorage('cfp_payment_request_type')->load($bundle);
    if ($pr_type) {
      $settings = $base_field_definitions['items']->getSettings();
      $fields['items'] = clone $base_field_definitions['items'];
      $settings['handler_settings']['target_bundles'] = [$pr_type->get('payment_request_item_type') => $pr_type->get('payment_request_item_type')];
      $fields['items']->setSettings($settings);

      $settings = $base_field_definitions['invoices']->getSettings();
      $fields['invoices'] = clone $base_field_definitions['invoices'];
      $settings['handler_settings']['target_bundles'] = [$pr_type->get('payment_request_invoice_type') => $pr_type->get('payment_request_invoice_type')];
      $fields['invoices']->setSettings($settings);
    }
    return $fields;
  }

  /**
   * Returns checklist that was before given checklist.
   *
   * @param \Drupal\cfp_payment_request\Entity\CfpPaymentRequestChecklist $payment_request_checklist
   */
  public function getPreviousChecklist(CfpPaymentRequestChecklist $payment_request_checklist) {
    $current_entity = NULL;
    foreach ($this->get('checklist') as $item) {
      if ($looping_payment_request_checklist = $item->entity) {
        if ($looping_payment_request_checklist->id() == $payment_request_checklist->id()) {
          return $current_entity;
        }
        $current_entity = $looping_payment_request_checklist;
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = parent::baseFieldDefinitions($entity_type);

    // Add the published field.
    $fields += static::publishedBaseFieldDefinitions($entity_type);

    $fields['user_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Authored by'))
      ->setDescription(t('The user ID of author of the Payment request entity.'))
      ->setSetting('target_type', 'user')
      ->setSetting('handler', 'default')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['label'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Kifizetési Igénylés sorszáma'))
      ->setDescription(t('The name of the Payment request entity.'))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['item_pay_off_total'] = BaseFieldDefinition::create('integer')
      ->setLabel('Igényelt elszámolási összeg (Ft)')
      ->setSetting('size', 'big')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['item_support_total'] = BaseFieldDefinition::create('integer')
      ->setLabel('Igényelt támogatási összeg (Ft)')
      ->setSetting('size', 'big')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields_definitions = isolate_numeric_field_types('cfp_payment_request_item');
    foreach (static::ADD_UP_FIELDS as $add_up_field) {
      $field_name = $add_up_field . '_total';
      if (!isset($fields[$field_name])) {
        $fields[$field_name] = self::createAddUpFieldFromFieldDefinition($fields_definitions[$add_up_field]);
      }
    }

    $fields['item_pay_off_computed_total']->setInitialValueFromField('item_pay_off_total');
    $fields['item_support_computed_total']->setInitialValueFromField('item_support_total');

    $fields['computed_amount_dedicated_to_ap'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Amount dedicated to advance payment'))
      ->setClass('\Drupal\cfp_payment_request\Plugin\Field\AmountDedicatedToAP')
      ->setComputed(TRUE)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['amount_dedicated_to_ap'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Amount dedicated to advance payment'))
      ->setSetting('size', 'big')
      ->setReadOnly(TRUE);

    $fields['amount_to_pay'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Amount to pay'))
      ->setComputed(TRUE)
      ->setClass('\Drupal\cfp_payment_request\Plugin\Field\PrAmountToPayItemList')
      ->setSetting('size', 'big')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['items'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Items'))
      ->setSetting('target_type', 'cfp_payment_request_item')
      ->setSetting('handler', 'default')
      ->setSetting('handler_settings', [
        'target_bundles' => [],
      ])
      ->setClass('\Drupal\cfp_payment_request\Plugin\Field\FieldType\PRFieldItemList')
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['items_approvement_closed'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Items approvement closed'))
      ->setReadOnly(TRUE);

    $fields['invoices'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Invoices'))
      ->setSetting('target_type', 'pr_invoice')
      ->setSetting('handler', 'default')
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['checklist'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Checklist'))
      ->setSetting('target_type', 'cfp_payment_request_checklist')
      ->setSetting('handler', 'default')
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['state'] = BaseFieldDefinition::create('state')
      ->setLabel(t('State'))
      ->setSetting('max_length', 255)
      ->setDefaultValue('open_state')
      ->setSetting('workflow', 'payment_request')
      ->setDisplayConfigurable('view', TRUE);

    $fields['comment'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Comment'))
      ->setDisplayConfigurable('view', TRUE);

    $fields['justification'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Justification'))
      ->setDisplayConfigurable('view', TRUE);

    $fields['reopen_instructions'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Reopen instructions'))
      ->setDisplayConfigurable('view', TRUE);

    $fields['previous_reopen_instructions'] = BaseFieldDefinition::create('string_long')
      ->setLabel(t('Previous reopening instructions'))
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
      ->setDisplayConfigurable('view', TRUE);

    $fields['closed_date'] = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Closed date'))
      ->setDisplayConfigurable('view', TRUE);

    $fields['submission_date'] = BaseFieldDefinition::create('datetime')
      ->setLabel(t('Last submission date'))
      ->setComputed(TRUE)
      ->setSettings([
        'state' => 'sent',
        'search_for' => TransitionDateSearchItemList::SEARCH_LAST,
        'field_name' => 'transition_history',
      ])
      ->setClass('\Drupal\halo_transition_history_field\Plugin\Field\TransitionDateSearchItemList')
      ->setDisplayConfigurable('view', TRUE);

    $fields['first_submission_date'] = BaseFieldDefinition::create('timestamp')
      ->setLabel(t('First submission date'))
      ->setReadOnly(TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['current_correction'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Current correction'))
      ->setDefaultValue(0)
      ->setReadOnly(TRUE);

    $fields['correction_data'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel('Correction data')
      ->setSetting('target_type', 'correction_data')
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
      ->setDisplayConfigurable('view', TRUE);

    $fields['old_prints'] = BaseFieldDefinition::create('file')
      ->setLabel(t('Older prints'))
      ->setSettings([
        'file_directory' => 'payment_request/new/print_files',
        'max_filesize' => '100 MB',
        'file_extensions' => 'pdf',
        'description_field' => TRUE,
        'uri_scheme' => 'public',
      ])
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
      ->setDisplayConfigurable('view', TRUE);

    $fields['date_of_completion'] = BaseFieldDefinition::create('timestamp')
      ->setLabel(t('Date of completion'))
      ->setDisplayConfigurable('view', TRUE)
      ->setDisplayConfigurable('form', TRUE);

    $fields['statements_accepted'] = BaseFieldDefinition::create('boolean')
      ->setLabel(t('Statements'))
      ->setReadOnly(TRUE)
      ->setDefaultValue(FALSE);

    $fields['attached_documents'] = BaseFieldDefinition::create('file')
      ->setLabel(t('Documents attached by an administrator'))
      ->setSettings([
        'file_directory' => 'pr_attached_files',
        'max_filesize' => '100 MB',
        'file_extensions' => 'pdf png jpg doc docx xls xlsx',
        'description_field' => TRUE,
        'uri_scheme' => 'public',
      ])
      ->setDisplayConfigurable('view', TRUE);

    $fields['suspended_documents'] = BaseFieldDefinition::create('file')
      ->setLabel(t('Suspended documents'))
      ->setSettings([
        'file_directory' => 'payment_request/suspended_documents',
        'max_filesize' => '100 MB',
        'file_extensions' => 'doc docx jpeg jpg png pdf',
        'description_field' => TRUE,
        'uri_scheme' => 'public',
      ])
      ->setDescription(t('Documents attached during suspension'))
      ->setDisplayConfigurable('view', TRUE)
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);

    $fields['transition_history'] = BaseFieldDefinition::create('transition_history')
      ->setLabel(t('Transition History'))
      ->setSetting('state_field', 'state')
      ->setReadOnly(TRUE)
      ->setDisplayConfigurable('view', TRUE)
      ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED);

    $fields['status']->setDescription(t('A boolean indicating whether the Payment request is published.'));

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'));

    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));

    $fields['correction_deadline'] = BaseFieldDefinition::create('timestamp')
      ->setLabel(t('Correction deadline'))
      ->setDescription(t('Deadline for submitting PR correction'))
      ->setDisplayConfigurable('view', TRUE);

    return $fields;
  }

  /**
   * Create add up field form definition.
   */
  protected static function createAddUpFieldFromFieldDefinition(FieldDefinitionInterface $field_definition) {
    return BaseFieldDefinition::create($field_definition->getType())
      ->setLabel($field_definition->getLabel() . ' ' . t('total'))
      ->setSettings($field_definition->getSettings())
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);
  }

}
<?php

/**
 * @file
 * Contains halo_views_2.module.
 */

use Drupal\cfp_modification_request\Entity\CfpModificationRequest;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\user\Entity\User;

module_load_include('inc', 'halo_front_page', 'halo_front_page.views');

/**
 * Implements hook_toolbar().
 */
function halo_front_page_toolbar() {
  return [
    'admin_front_page_link' => [
      '#type' => 'toolbar_item',
      'tab' => [
        '#type' => 'link',
        '#title' => t('Admin front page'),
        '#url' => Url::fromRoute('halo_front_page.admin_front_page'),
      ],
      '#weight' => -5,
    ],
  ];
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function halo_front_page_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Cfp state.
  if (in_array($form['#id'], [
    'views-exposed-form-cfp-page-1',
    'views-exposed-form-cfp-page-2',
    'views-exposed-form-cfp-page-3',
    'views-exposed-form-ced-main-data-page-1',
    'views-exposed-form-cfp-modification-requests-page-1',
  ])) {
    $workflow_manager = \Drupal::service('plugin.manager.workflow');
    $workflow = $workflow_manager->createInstance('cfp');
    $states = $workflow->getStates();
    $states = array_map(function ($state) {
      return $state->getLabel();
    }, $states);
    $form['state'] = [
      '#type' => 'select',
      '#options' => [
        '' => t('- Any -'),
      ] + $states,
      '#default_value' => reset($states),
    ];

    $entity_field_manager = \Drupal::service('entity_field.manager');
    $fields = $entity_field_manager->getFieldStorageDefinitions('cfp');
    $sub_state_options = options_allowed_values($fields['sub_state']);
    unset($sub_state_options['void_sub_state']);
    $form['sub_state'] = [
      '#title' => t('Sub state'),
      '#type' => 'select',
      '#options' => [
        '' => t('- Any -'),
      ] + $sub_state_options,
    ];
  }

  if ($form['#id'] === 'views-exposed-form-payment-requests-page-1') {
    $workflow_manager = \Drupal::service('plugin.manager.workflow');
    $workflow = $workflow_manager->createInstance('payment_request');
    $states = $workflow->getStates();
    $states = array_map(function ($state) {
      return $state->getLabel();
    }, $states);

    $form['state'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $states,
    ];

    $form['state'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $states,
    ];
    $entity_field_manager = \Drupal::service('entity_field.manager');
    $fields = $entity_field_manager->getFieldStorageDefinitions('cfp');
    $form['location_check_state'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $fields['location_check_state']->getSetting('allowed_values'),
    ];
    $form['location_check_type'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $fields['location_check_type']->getSetting('allowed_values'),
    ];
  }

  if ($form['#id'] === 'views-exposed-form-locations-halo-page-1') {
    if (!isset($entity_field_manager)) {
      $entity_field_manager = \Drupal::service('entity_field.manager');
    }
    $fields = $entity_field_manager->getFieldStorageDefinitions('cfp');
    $form['location_check_state'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $fields['location_check_state']->getSetting('allowed_values'),
    ];
    $form['location_check_type'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $fields['location_check_type']->getSetting('allowed_values'),
    ];
  }

  if ($form['#id'] === 'views-exposed-form-advance-payment-page-1') {
    $workflow_manager = \Drupal::service('plugin.manager.workflow');
    $workflow = $workflow_manager->createInstance('advance_payment');
    $states = $workflow->getStates();
    $states = array_map(function ($state) {
      return $state->getLabel();
    }, $states);
    $form['state'] = [
      '#type' => 'select',
      '#options' => [
        '' => t('- Any -'),
      ] + $states,
    ];
  }
  if ($form['#id'] === 'views-exposed-form-cfp-modification-requests-page-1') {
    $form['request_type'] = [
      '#type' => 'select',
      '#options' => [
        '' => t('- Any -'),
      ] + _request_type_callback()
    ];
    $form['state_1'] = [
      '#type' => 'select',
      '#options' => [
        '' => t('- Any -'),
      ] + CfpModificationRequest::getStates(),
    ];
  }
  if ($form['#id'] === 'views-exposed-form-technical-report-page-1') {
    $workflow_manager = \Drupal::service('plugin.manager.workflow');
    $workflow = $workflow_manager->createInstance('technical_report');
    $states = $workflow->getStates();
    $states = array_map(function ($state) {
      return $state->getLabel();
    }, $states);
    $form['state']  = $form['transition_history_to'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + $states
    ];
  }

  // First submission date.
  if (in_array($form['#id'], ['views-exposed-form-payment-requests-page-1', 'views-exposed-form-technical-report-page-1', 'views-exposed-form-advance-payment-page-1'])) {
    $form['first_submission_date_wrapper']['first_submission_date']['min']['#type'] = 'date';
    $form['first_submission_date_wrapper']['first_submission_date']['min']['#title'] = '-tól';
    $form['first_submission_date_wrapper']['first_submission_date']['max']['#type'] = 'date';
    $form['first_submission_date_wrapper']['first_submission_date']['max']['#title'] = '-ig';
//    if (isset($form['correction_deadline_wrapper'])) {
//      $form['correction_deadline_wrapper']['correction_deadline']['min']['#type'] = 'date';
//      $form['correction_deadline_wrapper']['correction_deadline']['min']['#title'] = '-tól';
//      $form['correction_deadline_wrapper']['correction_deadline']['max']['#type'] = 'date';
//      $form['correction_deadline_wrapper']['correction_deadline']['max']['#title'] = '-ig';
//    }
    if (isset($form['deadline_expired_wrapper'])) {
      $form['deadline_expired_wrapper']['deadline_expired']['min']['#type'] = 'date';
      $form['deadline_expired_wrapper']['deadline_expired']['min']['#title'] = '-tól';
      $form['deadline_expired_wrapper']['deadline_expired']['max']['#type'] = 'date';
      $form['deadline_expired_wrapper']['deadline_expired']['max']['#title'] = '-ig';
    }
  }

  if ($form['#id'] === 'views-exposed-form-applicants-page-1') {
    $form['cfp_type_group_owner_value'] = [
      '#type' => 'select',
      '#options' => [
          '' => t('- Any -'),
        ] + _cfp_group_types(),
    ];
  }


  if ($form['#id'] === 'views-exposed-form-locations-page-1') {
    $form['closed']['#options'][1] = t('Closed');
    $form['closed']['#options'][0] = t('Open');
    $form['date']['#type'] = 'date';
    $form['date_1']['#type'] = 'date';
  }
}

/**
 * Implements hook_link_alter().
 */
function halo_front_page_link_alter(&$variables) {
  $url = $variables['url'];
  if ($url->isExternal() || !$url->isRouted()) {
    return;
  }

  $route = $url->getRouteName();
  if ($route === 'user.page') {
    $variables['text'] = \Drupal::currentUser()->getDisplayName();
  }
}

/**
 * Implements hook_user_format_name_alter().
 */
function halo_front_page_user_format_name_alter(&$name, AccountInterface $account) {
  $user = User::load($account->id());
  $name = $user->field_lastname->value . ' ' . $user->field_firstname->value;
}

/**
 * Implements hook_views_data_alter().
 */
function halo_front_page_views_data_alter(array &$data) {
  $data['review']['assignable_filter'] = [
    'title' => t('Assignable filter'),
    'filter' => [
      'title' => t('Assignable filter'),
      'help' => t('Filters by assignable entities.'),
      'field' => 'id',
      'id' => 'assignable_filter',
    ],
  ];
}
<?php

namespace Drupal\cfp_payment_request\Plugin\views\filter;

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

/**
 * Filter PR view by correction deadline date.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("pr_correction_deadline")
 */
class PaymentRequestCorrectionDeadlineFilter extends FilterPluginBase {

  use StringTranslationTrait;

  /**
   * {@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();

    $form[$identifier] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Correction deadline'),
    ];
    $form[$identifier]['start_date'] = [
      '#type' => 'date',
      '#title' => $this->t('Start date'),
      '#default_value' => $exposed_input['start_date'] ?? NULL,
    ];
    $form[$identifier]['end_date'] = [
      '#type' => 'date',
      '#title' => $this->t('End date'),
      '#default_value' => $exposed_input['end_date'] ?? NULL,
    ];

    return $form;
  }

  /**
   * {@inheritDoc}
   */
  public function query() {
    $this->ensureMyTable();

    /** @var \Drupal\views\Plugin\views\query\Sql $query */
    $query = $this->query;

    $query->addTable("cfp_payment_request__transition_history");
    $input = $this->view->getExposedInput();
    $start = isset($input['start_date']) ? strtotime($input['start_date']) : NULL;
    if ($start) {
      $query->addWhere($this->options['group'], "cfp_payment_request__transition_history.transition_history_deadline", $start, ">=");
    }
    $end = isset($input['end_date']) ? strtotime($input['end_date']) : NULL;
    if ($end) {
      $query->addWhere($this->options['group'], "cfp_payment_request__transition_history.transition_history_deadline", $end, "<=");
    }
  }

}
<?php

namespace Drupal\cfp_payment_request\Entity;

use Drupal\views\EntityViewsData;

/**
 * Provides Views data for Payment request entities.
 */
class PaymentRequestViewsData extends EntityViewsData {

  /**
   * {@inheritdoc}
   */
  public function getViewsData() {
    $data = parent::getViewsData();
    $data['cfp_payment_request']['correction_deadline']['filter']['id'] = 'pr_correction_deadline';
    return $data;
  }

}
<?php

namespace Drupal\pos_entity_cfp\Plugin;

use Drupal\Component\Plugin\PluginBase;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\file\Entity\File;
use Drupal\pos_entity_cfp\Form\CorrectionTextForm;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Base class for Cfp correction type plugins.
 */
abstract class CfpCorrectionTypeBase extends PluginBase implements CfpCorrectionTypeInterface, ContainerFactoryPluginInterface {

  use StringTranslationTrait;

  /**
   * Form builder.
   *
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * State to handle.
   *
   * @var string
   */
  protected $correctionState;

  /**
   * Correction order.
   *
   * @var int
   */
  protected $correctionOrder;

  /**
   * Cfp to handle.
   *
   * @var \Drupal\pos_entity_cfp\Entity\Cfp
   */
  protected $cfp;

  /**
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, FormBuilderInterface $form_builder, AccountProxyInterface $account_proxy, EntityTypeManagerInterface $entity_type_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->correctionState = $configuration['correctionState'];
    $this->cfp = $configuration['cfp'];
    $this->formBuilder = $form_builder;
    $this->currentUser = $account_proxy;
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('form_builder'),
      $container->get('current_user'),
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getCorrectionState() {
    return $this->correctionState;
  }

  /**
   * {@inheritdoc}
   */
  public function getCorrectionOrder() {
    return $this->correctionOrder;
  }

  /**
   * {@inheritdoc}
   */
  public function getCfp() {
    return $this->cfp;
  }

  /**
   * {@inheritdoc}
   */
  public function startCorrection() {
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function endCorrection() {
    return TRUE;
  }

  /**
   * Get correction config form.
   *
   * @return array
   *   Config form.
   */
  public function getCorrectionConfigForm() {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  public function isCorrectionActive() {
    return $this->getCfp()->getState()->getId() === $this->getCorrectionState();
  }

  /**
   * Check if multiple.
   *
   * @return bool
   *   Is multiple.
   */
  public function isMultiple() {
    $definition = $this->getPluginDefinition();
    return isset($definition['multiple']) ? $definition['multiple'] : FALSE;
  }

  /**
   * Get correction text.
   */
  abstract public function getCorrectionText();

  /**
   * Get correction question element.
   */
  public function getCorrectionQuestionElement() {
    return $this->formBuilder->getForm(CorrectionTextForm::class, $this->getCfp(), $this->getCorrectionState(), $this->getPluginId());
  }

  /**
   * Returns if user has access to start correction.
   *
   * @return bool
   *   Returns if user has access to start correction.
   */
  public function userHasAccessToStartCorrection() {
    if ($this->cfp->getState()->getId() !== $this->correctionState) {
      return FALSE;
    }
    if ($this->cfp->isInCorrection()) {
      return FALSE;
    }
    return $this->currentUser->hasPermission('start cfp correction');
  }

  /**
   * Get correction operations form.
   */
  public function getCorrectionOperationsForm() {
    $state_item = $this->getCfp()->getState();
    $current_url = Url::fromRoute('<current>');
    if ($this->getCfp()->getSubState() != 'correction_started') {
      if ($this->userHasAccessToStartCorrection()) {
        $correction_data = $this->getCfp()->getCorrectionData($state_item->getId());
        $element['start_correction'] = [
          '#type' => 'link',
          '#title' => $this->t('Start correction @order', ['@order' => number_to_roman_representation($correction_data['number_of_corrections'] + 1)]),
          '#url' => Url::fromRoute('pos_entity_cfp.confirm_start_correction_user_cfp_form', [
            'cfp' => $this->getCfp()->id(),
            'destination' => $current_url->toString(),
          ]),
          '#attributes' => [
            'class' => ['button'],
          ],
        ];
      }
    }
    else {
      $element['start_correction'] = [
        '#type' => 'markup',
        '#markup' => '<div class="correction-info-text">' . $this->t('Correction is in progress') . '</div>',
      ];
      if ($this->currentUser->hasPermission('access correction form')) {
        $plugin_definition = $this->getCfp()->getCurrentStateCorrectionPlugin()->getPluginDefinition();
        $element['help_correction'] = [
          '#type' => 'link',
          '#title' => $this->t('Help applicant with correction'),
          '#url' => Url::fromRoute($plugin_definition['correction_form_route'], [
            'cfp' => $this->getCfp()->id(),
            'destination' => $current_url->toString(),
          ]),
          '#attributes' => [
            'class' => ['button'],
          ],
        ];
        $element['close_correction'] = [
          '#type' => 'link',
          '#title' => $this->t('Close correction'),
          '#url' => Url::fromRoute('pos_entity_cfp.confirm_correction_user_cfp_form', [
            'cfp' => $this->getCfp()->id(),
            'destination' => $current_url->toString(),
          ]),
          '#attributes' => [
            'class' => ['button'],
          ],
        ];
      }
    }
    return $element ?? [];
  }

  /**
   * Correction answers.
   */
  public function getCorrectionAnswersElement() {
    $correction_data = $this->getCfp()->getCorrectionData($this->getCorrectionState());
    $element = [];
    foreach ($correction_data['info'] as $correction_order => $correction_datum) {
      if (!is_numeric($correction_order)) {
        continue;
      }
      if (isset($correction_datum['started'])) {
        $started_date = DrupalDateTime::createFromTimestamp($correction_datum['started']);
        $started_date->setTimezone(new \DateTimeZone('Europe/Budapest'));
        $element[$correction_order]['started'] = [
          '#type' => 'item',
          '#title' => $this->t('Correction started'),
          '#markup' => $started_date->format('d/m/Y - H:i'),
        ];
      }
      if (isset($correction_datum['ended'])) {
        $ended_date = DrupalDateTime::createFromTimestamp($correction_datum['ended']);
        $ended_date->setTimezone(new \DateTimeZone('Europe/Budapest'));
        $element[$correction_order]['ended'] = [
          '#type' => 'item',
          '#title' => $this->t('Correction ended'),
          '#markup' => $ended_date->format('d/m/Y - H:i'),
        ];
      }
        if (isset($correction_data['info'][$correction_order]['correction_text'])) {
          $element[$correction_order][] = [
            '#type' => 'item',
            // @codingStandardsIgnoreStart
            '#title' => $this->t(ucfirst('correction') . ' text @order', [
              '@order' => number_to_roman_representation($correction_order + 1),
            ]),
            // @codingStandardsIgnoreEnd
            'markup' => ['#markup' => check_markup($correction_data['info'][$correction_order]['correction_text'])],
          ];
        }
        elseif (isset($correction_data['info'][$correction_order]['correction_text'])) {
          $element[$correction_order][] = [
            '#type' => 'item',
            // @codingStandardsIgnoreStart
            '#title' => $this->t(ucfirst('correction') . ' text @order', [
              '@order' => number_to_roman_representation($correction_order + 1),
            ]),
            // @codingStandardsIgnoreEnd
            'markup' => ['#markup' => check_markup($correction_data['info'][$correction_order]['correction_text'])],
          ];
        }


      if (isset($correction_datum['correction_answer_text']) && !empty($correction_datum['correction_answer_text'])) {
        $element[$correction_order]['correction_text'] = [
          '#type' => 'item',
          '#title' => t('Correction answer @order', ['@order' => number_to_roman_representation($correction_order + 1)]),
          '#markup' => check_markup($correction_datum['correction_answer_text']),
        ];
      }
      if (isset($correction_datum['correction_files']) && !empty($correction_datum['correction_files'])) {
        foreach ($correction_datum['correction_files'] as $delta => $fid) {
          $file = File::load($fid);
          $element[$correction_order]['correction_files'][$delta] = [
            '#theme' => 'file_link',
            '#file' => $file,
            '#cache' => [
              'tags' => $file->getCacheTags(),
            ],
          ];
        }
      }
      if (isset($element[$correction_order])) {
        $element[$correction_order]['#type'] = 'fieldset';
        $element[$correction_order]['#title'] = $this->t('Correction @order', ['@order' => number_to_roman_representation($correction_order + 1)]);
      }

      if (isset($correction_datum['started']) && !isset($correction_datum['ended'])) {
        $started_date = DrupalDateTime::createFromTimestamp($correction_datum['started']);
        $started_date->setTimezone(new \DateTimeZone('Europe/Budapest'));
        $now = DrupalDateTime::createFromTimestamp(time());
        $now->setTimezone(new \DateTimeZone('Europe/Budapest'));
        $days_limit = $this->pluginDefinition['correction_limit_in_days'];
        if ($started_date->diff($now)->d > $days_limit) {
          $element[$correction_order]['warning'] = [
            '#type' => 'markup',
            '#markup' => '<p class="correction-warning">' . $this->t('@number_of_days days limit expired!', [
              '@number_of_days' => $days_limit,
            ]) . '</p>',
          ];
        }
      }
    }
    if (!empty($element)) {
      $element['#type'] = 'container';
    }
    return $element;
  }

}
<?php

use Drupal\Core\Entity\EntityTypeInterface;

/**
 * Implements hook_entity_bundle_field_info_alter().
 */
function pos_cfp_bundle_2022_01_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
  if ($entity_type->id() === 'review' && $bundle === 'acceptance_2022_01') {
    $fields['cf_acceptable_application']->addConstraint('ConditionalValues', [
      'conditions' => [
        [
          'required_choice' => 'no',
          'on_values' => [
            'cf_submit_ontime' => ['no'],
            'cf_submit_original_ontime' => ['no'],
            'cf_scanned_application' => ['no'],
            'cf_application_submitt' => ['no'],
            'cf_liquidation_applicant' => ['no'],
            'cf_rpg_registration' => ['no'],
            'cf_anti_labour_protection' => ['no'],
            'cf_disqualification_conditions' => ['no'],
          ],
          'conjunction' => 'OR',
          'error_message' => 'Ukoliko su polja iz prve dve sekcije "Ne", ovo polje mora biti Ne.',
        ],
        [
          'required_choice' => 'yes',
          'on_values' => [
            'cf_submit_ontime' => ['yes'],
            'cf_submit_original_ontime' => ['yes'],
            'cf_scanned_application' => ['yes'],
            'cf_application_submitt' => ['yes'],
            'cf_liquidation_applicant' => ['yes'],
            'cf_rpg_registration' => ['yes'],
            'cf_anti_labour_protection' => ['yes'],
            'cf_disqualification_conditions' => ['yes'],
          ],
          'conjunction' => 'AND',
          'error_message' => 'Ukoliko su sva polja iz prve dve sekcije "Da", ovo polje mora biti Da.',
        ]
      ],
    ]);
  }
}

/**
 * Implements hook_review_passed_alter().
 */
function pos_cfp_bundle_2022_01_review_passed_alter(&$passed, $entity) {
  if ($entity->bundle() === 'technical_review_2022_01') {
    $passed = $entity->isPrintConfirmed();
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function pos_entity_review_form_review_acceptance_2022_01_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if (isset($form['cf_application_identifier'])) {
    $form['cf_application_identifier']['#states'] = [
      'visible' => [
        ':input[name="cf_only_one_request"]' => ['value' => 'no']
      ],
      'required' => [
        ':input[name="cf_only_one_request"]' => ['value' => 'no']
      ]
    ];
  }
  $form['#validate'][] = 'custom_field_validation';
}

function custom_field_validation($form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $only_request_field = !empty($form_state->getValue('cf_only_one_request')) ? $form_state->getValue('cf_only_one_request')[0]['value'] : '';
  $cf_application_identifier = $form_state->getValue('cf_application_identifier');
  if ($only_request_field === 'no' && empty($cf_application_identifier[0]['value'])) {
    $form_state->setErrorByName('cf_application_identifier', t('Identifikator aplikacije je obavezan ako je podnosioc zahtjeva podnio više od jednog zahtjeva'));
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function pos_cfp_bundle_2022_01_form_review_legal_check_2022_01_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form_object = $form_state->getFormObject();
  $review = $form_object->getEntity();
  foreach ($review->getFields() as $machine_name => $field) {
    if ($field->getFieldDefinition()->getType() === 'options_correction') {
      if ($review->get($machine_name)->value === 'replacement') {
        $form[$machine_name]['#attributes'] = [
          'class' => ['red_field'],
        ];
      }
    }
  }
}
<?php

namespace Drupal\pos_entity_email_template\Plugin\Action;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Site\Settings;
use Drupal\file\Entity\File;

/**
 * Provides a a Send Applicant email action.
 *
 * @Action(
 *   id = "send_applicant_email",
 *   label = @Translation("Send Applicant email"),
 *   type = "user",
 *   category = @Translation("Custom"),
 *   confirm = TRUE
 * )
 *
 * @DCG
 * For a simple updating entity fields consider extending FieldUpdateActionBase.
 */
class SendApplicantEmailAction extends ConfigurableActionBase {

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'title' => '',
      'body' => '',
      'attachment_files' => [],
      ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    //@todo: Check which templates will be available for this kind of e-mails. Maybe create a new type of templates.

    $templates = \Drupal::entityTypeManager()
      ->getStorage('pos_entity_email_template')->loadMultiple();

    $options = ['_none' => $this->t('None')];
    foreach ($templates as $template) {
      $options[$template->id()] = $template->id() . ') ' . $template->label();
    }
    ksort($options, SORT_NUMERIC);

    $form['template'] = [
      '#type' => 'select',
      '#title' => $this->t('Template'),
      '#options' => $options,
      '#ajax' => [
        'callback' => '\Drupal\pos_entity_email_template\Plugin\Action\SendApplicantEmailAction::replaceElements'
      ]
    ];

    $form['title'] = [
      '#title' => $this->t('Title'),
      '#type' => 'textfield',
      '#wrapper_attributes' => [
        'class' => 'mrmot-title',
      ],
    ];

    $form['body'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Body'),
      '#format' => 'ckeditor_5',
      '#wrapper_attributes' => [
        'class' => 'mrmot-body',
      ],
    ];

    $form['attachment_files'] = [
      '#type' => 'managed_file',
      '#title' => $this->t('Attachments'),
      '#description' => t('Allowed types: pdf xls xlsx odt doc docx.'),
      '#upload_validators' => [
        'file_validate_extensions' => ['pdf xls xlsx odt doc docx'],
      ],
      '#upload_location' => 'public://email_attachments/',
      '#progress_indicator' => 'bar',
      '#multiple' => TRUE,
      '#wrapper_attributes' => [
        'class' => 'mrmot',
      ],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    $this->configuration['title'] = $form_state->getValue('title');
    $this->configuration['body'] = $form_state->getValue('body')['value'];
    $this->configuration['file_ids'] = $form_state->getValue('attachment_files');
    foreach ($form_state->getValue('attachment_files') as $fid) {
      $file = File::load($fid);
      $this->configuration['attachments'][] = [
        'filepath' => $file->getFileUri(),
        'filename' => $file->getFilename(),
        'filemime' => $file->getMimeType(),
      ];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function access($node, AccountInterface $account = NULL, $return_as_object = FALSE) {
    return $return_as_object ? AccessResult::allowedIfHasPermission($account, 'send emails from administration') : $account->hasPermission('send emails from administration');
  }

  /**
   * {@inheritdoc}
   */
  public function execute($user = NULL) {
    $params['headers'] = [
      'Content-Type' => 'text/html; charset=UTF-8;',
      'Content-Transfer-Encoding' => '8Bit',
    ];
    /** @var \Drupal\Core\Mail\MailManagerInterface $mail_manager */
    $mail_manager = \Drupal::service('plugin.manager.mail');
    $user_mail = $user->getEmail();
    $emails = [$user_mail];
    $params['title'] = $this->configuration['title'];
    $params['message'] = $this->configuration['body'];
    $params['file_ids'] = $this->configuration['file_ids'];
    $params['attachments'] = $this->configuration['attachments'];
    $params['entity'] = $user;
    $result = $mail_manager->mail('pos_entity_applicant', 'pos_entity_applicant_mail_send', implode(',', $emails), 'hu', $params, NULL, TRUE);
  }

  public static function replaceElements(array &$form, FormStateInterface $form_state) {
    // @todo: Check what kind of templates will be used.
    $triggering_element = $form_state->getTriggeringElement();
    $response = new AjaxResponse();
    /** @var \Drupal\pos_entity_email_template\Entity\EmailTemplateEntity $email_template */
    if (is_numeric($triggering_element['#value'])) {
      $email_template = \Drupal::entityTypeManager()
        ->getStorage('pos_entity_email_template')
        ->load($triggering_element['#value']);
      $form['title']['#value'] = $email_template->getTitle();
      $form['body']['value']['#value'] = $email_template->getBody();

      $form['attachment_files']['#value']['fids'] = array_unique(array_merge($form['attachment_files']['#value']['fids'], $email_template->getAttachmentsInElementFormat()));
      $form['attachment_files']['fids']['#value'] = array_unique(array_merge($form['attachment_files']['fids']['#value'], $email_template->getAttachmentsInElementFormat()));
      foreach ($form['attachment_files']['#value']['fids'] as $fid) {
        $form['attachment_files']['#files'][$fid] = File::load($fid);
      }
      $element = &$form['attachment_files'];
      foreach ($element['#files'] as $delta => $file) {
        $file_link = [
          '#theme' => 'file_link',
          '#file' => $file,
        ];
        if ($element['#multiple']) {
          $element['file_' . $delta]['selected'] = [
            '#type' => 'checkbox',
            '#title' => \Drupal::service('renderer')->renderPlain($file_link),
          ];
        }
        else {
          $element['file_' . $delta]['filename'] = $file_link + ['#weight' => -10];
        }
        // Anonymous users who have uploaded a temporary file need a
        // non-session-based token added so $this->valueCallback() can check
        // that they have permission to use this file on subsequent submissions
        // of the same form (for example, after an Ajax upload or form
        // validation error).
        if ($file->isTemporary() && \Drupal::currentUser()->isAnonymous()) {
          $element['file_' . $delta]['fid_token'] = [
            '#type' => 'hidden',
            '#value' => Crypt::hmacBase64('file-' . $delta, \Drupal::service('private_key')->get() . Settings::getHashSalt()),
          ];
        }
      }
      $form_state->setRebuild(TRUE);
    }
    else {
      $form['title']['#value'] = '';
      $form['body']['value']['#value'] = '';
    }
    $response->addCommand(new RemoveCommand('[id^=edit-body-format]'));
    $response->addCommand(new ReplaceCommand('.mrmot-body', $form['body']));
    $response->addCommand(new ReplaceCommand('.mrmot-title', $form['title']));
    $response->addCommand(new ReplaceCommand('.mrmot', $form['attachment_files']));
    return $response;
  }
}
<?php

/**
 * @file
 * Contains pos_entity_applicant.module.
 */

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\pos_entity_applicant\Entity\ApplicantType;
use Drupal\pos_entity_applicant\Event\UserCancelEvent;

module_load_include('inc', 'pos_entity_applicant', 'pos_entity_applicant.tokens');
module_load_include('inc','pos_entity_applicant', 'pos_entity_applicant.views');
module_load_include('inc', 'pos_entity_applicant', 'pos_entity_applicant.validation');

/**
 * Implements hook_help().
 */
function pos_entity_applicant_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the pos_entity_applicant module.
    case 'help.page.pos_entity_applicant':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('General Applicant Entity') . '</p>';
      return $output;

    default:
  }
}

/**
 * Implements hook_theme().
 */
function pos_entity_applicant_theme() {
  $theme = [];
  $theme['applicant'] = [
    'render element' => 'elements',
    'file' => 'applicant.page.inc',
    'template' => 'applicant',
  ];
  $theme['applicant_content_add_list'] = [
    'render element' => 'content',
    'variables' => ['content' => NULL],
    'file' => 'applicant.page.inc',
  ];
  return $theme;
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function pos_entity_applicant_theme_suggestions_applicant(array $variables) {
  $suggestions = [];
  $entity = $variables['elements']['#applicant'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');

  $suggestions[] = 'applicant__' . $sanitized_view_mode;
  $suggestions[] = 'applicant__' . $entity->bundle();
  $suggestions[] = 'applicant__' . $entity->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = 'applicant__' . $entity->id();
  $suggestions[] = 'applicant__' . $entity->id() . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_entity_extra_field_info().
 */
function pos_entity_applicant_entity_extra_field_info() {
  $extra = [];
  foreach (ApplicantType::loadMultiple() as $bundle) {
    $extra['applicant'][$bundle->id()]['display']['edit_link'] = [
      'label' => t('Edit link'),
      'description' => 'Entity edit link',
      'weight' => 20,
      'visible' => TRUE,
    ];

    $extra['applicant'][$bundle->id()]['display']['view_link'] = [
      'label' => t('View link'),
      'description' => 'Entity view link',
      'weight' => 20,
      'visible' => TRUE,
    ];

    $extra['applicant'][$bundle->id()]['display']['state_description'] = [
      'label' => t('State description'),
      'description' => 'Shows state description defined in applicant bundle',
      'weight' => 20,
      'visible' => TRUE,
    ];

    $extra['applicant'][$bundle->id()]['display']['locked_button'] = [
      'label' => t('Locked button'),
      'description' => 'Locked',
      'weight' => 20,
      'visible' => TRUE,
    ];

    $extra['applicant'][$bundle->id()]['display']['representative'] = [
      'label' => t('Ovlašteno lice za zastupanje'),
      'description' => t('Ime i prezime predstavnika'),
      'weight' => 20,
      'visible' => FALSE,
    ];
  }
  return $extra;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function pos_entity_applicant_applicant_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  if ($display->getComponent('edit_link') && $entity->access('update')) {
    $build['edit_link'] = [
      '#type' => 'link',
      '#title' => t('Edit'),
      '#url' => Url::fromRoute('entity.' . $entity->getEntityTypeId() . '.edit_form', [
        $entity->getEntityTypeId() => $entity->id(),
      ]),
      '#attributes' => [
        'class' => ['button'],
      ],
    ];
  }
  if ($display->getComponent('view_link') && $entity->access('view')) {
    $build['view_link'] = [
      '#type' => 'link',
      '#title' => t('View'),
      '#url' => Url::fromRoute('entity.' . $entity->getEntityTypeId() . '.canonical', [
        $entity->getEntityTypeId() => $entity->id(),
      ]),
      '#attributes' => [
        'class' => ['button'],
      ],
    ];
  }
  if ($display->getComponent('state_description')) {
    $applicant_type = ApplicantType::load($entity->bundle());
    if ($description = $applicant_type->getStateDescription($entity->getState())) {
      $build['state_description'] = [
        '#type' => 'inline_template',
        '#template' => '<div class="applicant__state-description">' . $description . '</div>',
      ];
    }
  }
  if ($display->getComponent('locked_button')) {
    if (!$entity->isLocked() && (bool) $entity->get('validation_passed')->value) {
      $build['locked_button'] = [
        '#type' => 'link',
        '#url' => new Url('pos_entity_applicant.applicant_lock_data_confirm', [
          'applicant' => $entity->id(),
        ]),
        '#title' => t('Lock'),
        '#attributes' => [
          'class' => [
            'button',
          ],
        ],
      ];
    }
  }
  if ($display->getComponent('representative') && $entity->get('company_representative')->target_id) {
    $representative = $entity->get('company_representative')->entity;
    $rep_name = $representative->get('field_firstname')->value ?? '';
    $rep_lastname = $representative->get('field_lastname')->value ?? '';
    if ($rep_name === '' && $rep_lastname === '') {

    }
    else {
      $build['representative'] = [
        '#type' => 'container',
        'zastupnik' => [
          '#type' => 'container',
          'label' => [
            '#type' => 'markup',
            '#markup' => t('Ovlašteno lice za zastupanje'),
          ],
          '#attributes' => [
            'class' => ['current_applicant_representative-label'],
          ]
        ],
        'representative' => [
          '#type' => 'markup',
          '#markup' => '<div class="current_applicant_representative-item">' . $rep_lastname . ' ' . $rep_name . '</div>',
        ]
      ];
    }
  }
}

/**
 * Implements hook_cron().
 */
function pos_entity_applicant_cron() {
  $query = \Drupal::database()->query('SELECT applicant.id FROM applicant WHERE applicant.id NOT IN (SELECT applicant FROM users_field_data) ');
  $storage = \Drupal::entityTypeManager()->getStorage('applicant');
  while ($row = $query->fetch()) {
    $company = $storage->load($row->id);
    $company->delete();
  }
}

/**
 * Get applicant bundles.
 *
 * @return array
 *   Applicant bundles.
 */
function get_applicant_bundles() {
  $applicant_types = ApplicantType::loadMultiple();
  $applicant_list = [];
  foreach ($applicant_types as $applicant_type) {
    $applicant_list[$applicant_type->id()] = $applicant_type->label();
  }
  return $applicant_list;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function pos_entity_applicant_form_applicant_company_edit_form_alter(&$form, FormStateInterface $form_state) {
  $form['vat_number_entitled_true']['#states'] = [
    'visible' => [
      ':input[name="entitled_to_vat_refund"]' => ['value' => 'yes']
    ],
  ];
  $form['annual_income']['widget']['#description'] = 'Molimo vas da unesete najmanje podatke za 2021.godinu, ukoliko je vaše pravno lice osnovano prije 31. decembra 2021.';
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function pos_entity_applicant_form_applicant_individual_edit_form_alter(&$form, FormStateInterface $form_state) {
  $form['vat_number_entitled_true']['#states'] = [
    'visible' => [
      ':input[name="entitled_to_vat_refund"]' => ['value' => 'yes']
    ],
  ];
  $form['annual_income']['widget']['#description'] = 'Molimo vas da unesete najmanje podatke za 2021.godinu, ukoliko je vaše gazdinstvo osnovano prije 31. decembra 2021.';
}

/**
 * Implements hook_inline_entity_form_entity_form_alter().
 */
function pos_entity_applicant_inline_entity_form_entity_form_alter(array &$entity_form, FormStateInterface &$form_state) {
  if ($entity_form['#entity']->getEntityTypeId() === 'basic' && $entity_form['#entity']->bundle() === 'owned_other_companies') {
    if (isset($entity_form['cf_vat_number_entitled_true'])) {
      $entity_form['cf_vat_number_entitled_true']['#states'] = [
        'visible' => [
          [
            ':input[name="cf_owners_companies[form][inline_entity_form][entities][0][form][cf_entitled_to_vat_refund]"]' => ['value' => 1],
          ],
          'or',
          [
            ':input[name="cf_owners_companies[form][0][cf_entitled_to_vat_refund]"]' => ['value' => 1],
          ],
        ],
      ];
    }
  }
}

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

  $data['views']['applicant_has_cfp'] = [
    'title' => t('Did the applicant start cfp - Custom filter'),
    'filter' => [
      'title' => t('Did the applicant start cfp - Custom filter'),
      'field' => 'id',
      'id' => 'applicant_has_cfp',
    ],
  ];
  $data['views']['applicant_cfp'] = [
    'title' => t('Did the applicant start cfp - NEW Custom filter'),
    'filter' => [
      'title' => t('Did the applicant start cfp - NEW Custom filter'),
      'field' => 'id',
      'id' => 'applicant_cfp'
    ],
  ];

  return $data;
}

/**
 * Implements hook_ENTITY_TYPE_predelete().
 */
function pos_entity_applicant_user_predelete(\Drupal\Core\Entity\EntityInterface $entity) {
  $event = new UserCancelEvent($entity);

  $event_dispatcher = \Drupal::service('event_dispatcher');
  $event_dispatcher->dispatch($event, UserCancelEvent::EVENT_NAME);
}

/**
 * Implements hook_mail().
 */
function pos_entity_applicant_mail($key, &$message, $params) {
  switch($key) {
    case 'pos_entity_applicant_mail_send':
      $mail_sender = \Drupal::config('system.site')->get('mail');
      $message['headers']['From'] = $mail_sender;
      $message['headers']['Sender'] = $mail_sender;
      $message['headers']['Return-Path'] = $mail_sender;
      $message['headers']['Content-Type'] = 'text/html';
      $message['from'] = $mail_sender;
      $message['subject'] = $params['title'];
      /** @var \Drupal\Core\Utility\Token $token */
      $token = \Drupal::service('token');
      /** @var \Drupal\pos_entity_applicant\Entity\Applicant $applicant */
      $applicant = $params['entity']->get('applicant')->entity ?? NULL;
      // Not using this tokens currently, but maybe we will some day in the future.
      $token_data = [
        'applicant' => $applicant,
        'user' => $params['entity'],
      ];
      $message['subject'] = $token->replace($params['title'], $token_data, ['clear' => TRUE]);
      $message['body'][] = $token->replace($params['message'], $token_data, ['clear' => TRUE]);

      $files = [];
      if (isset($params['attachments'])) {
        $message['params']['files'] = $params['attachments'];
        foreach ($params['attachments'] as $file) {
          $files[] = $file['filename'];
        }
      }
      $email_log = \Drupal::entityTypeManager()
        ->getStorage('pos_entity_email_logs')
        ->create([
          'title' => $message['subject'],
          'body' => isset($message['body'][0]) ? $message['body'][0] : '',
        ]);
      $email_log->save();
      break;
  }
}
<?php

namespace Drupal\maintenance_2020_01\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Plugin implementation of the 'Maintenance Documents' formatter.
 *
 * @FieldFormatter(
 *   id = "maintenance_documents",
 *   label = @Translation("Maintenance Documents"),
 *   field_types = {
 *     "file"
 *   }
 * )
 */
class MaintenanceDocumentsFormatter extends FormatterBase {

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings() {
    return parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function viewElements(FieldItemListInterface $items, $langcode) {
    $element = [];

    foreach ($items as $delta => $item) {
      $description = $item->getValue()['description'];
      $file = $item->entity;
      $created_timestamp = $file->get('created')->value;
      $element[$delta] = [
        '#theme' => 'file_link',
        '#file' => $file,
        '#description' => $description . ' (' . \Drupal::service('date.formatter')->format($created_timestamp, 'custom', 'Y.m.d') . ')',
        '#cache' => [
          'tags' => $file->getCacheTags(),
        ],
      ];
    }

    return $element;
  }

}
function pos_core_update_9014($sandbox) {
  $user_ids_to_delete = [32, 30, 29, 28, 27, 24, 22, 20, 16, 15, 14, 13];

  // Delete all cfps where the users are going to be deleted.
  \Drupal::database()->delete('cfp')
    ->condition('user_id', $user_ids_to_delete, 'IN')
    ->execute();

  // Delete all applicants associated to the user that is going to be deleted.
  foreach ($user_ids_to_delete as $user_id_to_delete) {
    $user = \Drupal::entityTypeManager()
      ->getStorage('user')
      ->load($user_id_to_delete);

    if ($applicant_id = $user->get('applicant')->target_id) {
      \Drupal::database()
        ->delete('applicant')
        ->condition('id', $applicant_id)
        ->execute();
    }
    // Delete users.
    $user->delete();
  }
}
// Ovo ide ispod prethodnog filtera (koji ću verovatno i obrisati).
$data['views']['applicant_cfp'] = [
    'title' => t('Did the applicant start cfp - NEW Custom filter'),
    'filter' => [
      'title' => t('Did the applicant start cfp - NEW Custom filter'),
      'field' => 'id',
      'id' => 'applicant_cfp'
    ],
  ];
<?php

namespace Drupal\pos_entity_applicant\Plugin\views\filter;

use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Symfony\Contracts\Translation\TranslatorTrait;

/**
 * Class ProgramCoordinatorFilter
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("applicant_cfp")
 */
class ApplicantCfpFilter extends FilterPluginBase {

  use TranslatorTrait;

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

  public function buildExposedForm(&$form, FormStateInterface $form_state) {
    if (empty($this->options['exposed'])) {
      return;
    }
    $identifier = $this->options['expose']['identifier'];
    $exposed_input = $this->view->getExposedInput();

    $form[$identifier] = [
      '#type' => 'select',
      '#title' => $this->t('Da li aplikant ima konkurs?'),
      '#options' => [
        NULL => $this->t('- Any -'),
        'yes' => $this->t('Da'),
        'no' => $this->t('Ne'),
      ],
      '#default_value' => $exposed_input,
      '#validated' => TRUE,
    ];
    return $form;
  }

  public function query() {
    $this->ensureMyTable();
    /** @var \Drupal\views\Plugin\views\query\Sql $query */
    $query = $this->query;
    $input = $this->view->getExposedInput();

    if (isset($input['applicant_cfp']) && !empty($input['applicant_cfp'])) {
      $input['applicant_cfp'] === 'yes' ?
        $query->addWhereExpression(0,"users_field_data.uid IN (SELECT DISTINCT user_id FROM users_field_data JOIN cfp ON cfp.user_id)")
      : $query->addWhereExpression(0,"users_field_data.uid NOT IN (SELECT DISTINCT user_id FROM users_field_data JOIN cfp ON cfp.user_id)");
    }
  }
}
services:
  hederavita_core.event_subscriber:
    class: Drupal\hederavita_core\EventSubscriber\OderPlacedSubscriber
    arguments: ['@messenger', '@marketing_tools.google_event_storage', '@path.current']
    tags:
      - { name: event_subscriber }
<?php

namespace Drupal\hederavita_core\EventSubscriber;

use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\marketing_tools\GoogleEventStorage;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Hederavita Core event subscriber.
 */
class OderPlacedSubscriber implements EventSubscriberInterface {

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

  /**
   * The marketing tool.
   *
   * @var \Drupal\marketing_tools\GoogleEventStorage
   */
  protected $marketing_tool;

  /**
   * The current path.
   *
   * @var \Drupal\Core\Path\CurrentPathStack
   */
  protected $current_path;

  /**
   * Constructs event subscriber.
   *
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger.
   * @param \Drupal\marketing_tools\GoogleEventStorage $marketing_tool
   *   The marketing tool.
   * @param \Drupal\Core\Path\CurrentPathStack $current_path
   */
  public function __construct(MessengerInterface $messenger, GoogleEventStorage $marketing_tool, CurrentPathStack $current_path) {
    $this->messenger = $messenger;
    $this->marketing_tool = $marketing_tool;
    $this->current_path = $current_path;
  }

  /**
   * Kernel request event handler.
   *
   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
   *   Response event.
   */
  public function onKernelRequest(GetResponseEvent $event) {
    $this->messenger->addStatus(__FUNCTION__);
  }

  /**
   * Kernel response event handler.
   *
   * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
   *   Response event.
   */
  public function onKernelResponse(FilterResponseEvent $event) {
    $this->messenger->addStatus(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events = [
      'commerce_order.place.pre_transition' => ['addEvent', 50],
    ];
    return $events;
  }

  /**
   * Create google event
   *
   * @return void
   */
  public function addEvent() {
    $path = $this->current_path->getPath();
    $path_parts = explode('/', $path);
    foreach ($path_parts as $path_part) {
      if (is_numeric($path_part)) {
        $order = \Drupal::entityTypeManager()
          ->getStorage('commerce_order')
          ->load($path_part);
      }
    }

    /** @var \Drupal\commerce_order\Entity\Order $order */
    $value = $order->getTotalPrice()->getNumber();
    $order_id = $order->id();

    $this->marketing_tool->addEvent([
      'event'  => 'conversion',
      'sendTo' => 'GTM-PZQ4TQZ',
      'value' => $value,
      'currency' => 'RSD',
      'transaction_id' => $order_id,
    ]);
  }

}
name: Baross core
type: module
description: Baross Core module
package: Custom
core: 8.x
core_version_requirement: ^8 || ^9
<?php

/**
 * Implements hook_install().
 */
function baross_core_install() {
  if (!\Drupal::moduleHandler()->moduleExists('path_alias')) {
    \Drupal::database()->delete('key_value')
      ->condition('name', 'path_alias')
      ->execute();
  }
}

/**
 * Implements hook_update_N().
 */
function baross_core_update_8002(&$sandbox) {
  \Drupal::database()->schema()->dropTable('menu_link_content_revision');
}
<?php

/**
 * @file
 * Contains belmil_product_rating.module.
 */

use Drupal\block\Entity\Block;
use Drupal\commerce_product\Entity\ProductInterface;
use Drupal\commerce_product\Entity\ProductType;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Routing\RouteMatchInterface;
use \Drupal\user\EntityOwnerInterface;
/**
 * Implements hook_help().
 */
function belmil_product_rating_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the belmil_product_rating module.
    case 'help.page.belmil_product_rating':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('This module provides custom rating functionalities for products.') . '</p>';
      return $output;

    default:
  }

  return '';
}

/**
 * Implements hook_theme().
 */
function belmil_product_rating_theme() {
  return [
    'belmil_product_rating' => [
      'render element' => 'children',
    ],
    'belmil_product_rating_statistics_widget' => [
      'render element' => 'children',
    ]
  ];
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function belmil_product_rating_comment_insert(EntityInterface $entity) {
  $userRoles = \Drupal::currentUser()->getRoles();
  if ($entity instanceof EntityOwnerInterface) {
      $ownerEmail = $entity->getOwner()->getEmail();
    if (!in_array('administrator', $userRoles) && $ownerEmail != 'trustami@studiopresent.com') {
      $link = '/comment/' . $entity->id() . '/edit';
      $body = t('There is new comment on your site') . '<br> <a target="_blank" href="' . $link . '">' . t('Show') . ' </a>';

      // Send mail.
      /** @var \Drupal\Core\Mail\MailManagerInterface $mailManager */
      $mailManager = \Drupal::service('plugin.manager.mail');

      $module = $key = 'belmil_product_rating';
      // Get email from config and remove whitespaces.
      $to = \Drupal::config('system.site')->get('mail');
      $params['subject'] = t('New comment on website');
      $params['body'] = $body;
      $params['format'] = 'text/html';
      $langcode = \Drupal::currentUser()->getPreferredLangcode();

      $result = $mailManager->mail($module, $key, $to, $langcode, $params, NULL, TRUE);

      // Log result.
      if ($result['result'] != TRUE) {
        $message = t('There was a problem sending your email notification to @email.', ['@email' => $to]);
        \Drupal::logger('mail')->error($message);
      }
      else {
        $message = t('An email notification for new comment has been sent to @email.', ['@email' => $to]);
        \Drupal::logger('mail')->notice($message);
      }
    }
  }
}

/**
 * Implements hook_mail().
 */
function belmil_product_rating_mail($key, &$message, $params) {
  switch ($key) {
    case 'belmil_product_rating':
      $message['subject'] = $params['subject'];
      $message['body'][] = Markup::create($params['body']);
      $message['headers']['Content-Type'] = $params['format'];
      break;
  }
}

/**
 * Implements hook_form_alter().
 */
function belmil_product_rating_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Open details for admin comment approval.
  if ($form_id == "comment_product_comment_form") {
    $form['#validate'][] = 'belmil_product_rating_settings_validate';
    if (isset($form['author'])) {
      $form['author']['#open'] = TRUE;
    }
  }
}

/**
 * Implements hook_form_submit().
 */
function belmil_product_rating_settings_validate(&$form, FormStateInterface $form_state) {
  $uid = \Drupal::currentUser()->id();
  if (empty($form_state->getValue('uid'))) {
    $form['author']['uid']['#value'] = $uid;
    $form['author']['name']['#value'] = 'Anonymous';
  }
}

/**
 * Implements hook_entity_extra_field_info().
 */
function belmil_product_rating_entity_extra_field_info() {
  $stars_field = [];

  foreach (ProductType::loadMultiple() as $bundle) {
    $stars_field['commerce_product'][$bundle->id()]['display']['trustamiproductreview'] = [
      'label' => t('Trustami product review'),
      'description' => t('A pseudo field to display the Trustami product review block'),
      'weight' => 0,
      'visible' => TRUE,
    ];

    // Define stars field for displaying rating by stars and link to comment section.
    $stars_field['commerce_product'][$bundle->id()]['display']['stars_field'] = [
      'label' => t('Product stars'),
      'description' => t('This is pseudo field for showing stars rating'),
      'visible' => FALSE,
    ];

    // Define statistic widget for rating.
    $stars_field['commerce_product'][$bundle->id()]['display']['stars_widget'] = [
      'label' => t('Stars widget'),
      'description' => t('Stars widget with statistic by marks'),
      'visible' => FALSE,
    ];
  }

  return $stars_field;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 *
 * Add stars field & widget to product view.
 */
function belmil_product_rating_commerce_product_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  if ($display->getComponent('trustamiproductreview')) {
    $block = Block::load('trustamiproductreview');
    if ($block) {
      $build['trustamiproductreview'] = $block->getPlugin()->build();
    }
  }

  if ($display->getComponent('stars_field') ||
    $display->getComponent('stars_widget')
  ) {
    /** @var \Drupal\belmil_product_rating\BelmilProductRatingManager $product_rating_manager */
    $product_rating_manager = \Drupal::service('belmil_product_rating.belmil_product_rating_manager');

    $productRating = 0;
    $numOfComments = 0;
    $productsWithSameTagWithComments = [];
    $ratings = [];

    // Get product with same tag which contain comments for default product type.
    if ($entity->bundle() === "default") {
      $tagId = (int) $entity->get('field_tag')->target_id;
      $productsWithSameTagWithComments = $product_rating_manager
        ->getProductsWithSameTagWithComments($tagId);
    }
    // Get comments of single product for universal product type.
    elseif ($entity->bundle() === "universal") {
      $productsWithSameTagWithComments = [$entity->id()];
    }

    // Call get rating function.
    $product_rating_manager->getRating($productsWithSameTagWithComments, $productRating, $numOfComments, $ratings);
    // Prepare stars data - num of stars is fixed to 5.
    $starsData = $product_rating_manager->prepareStarsData(5, $productRating);
  }

  // Render product stars.
  if ($display->getComponent('stars_field')) {
    $build['stars_field'] = [
      '#markup' => \Drupal::theme()->render('belmil_product_rating', [
        'rating' => $productRating,
        'stars' => 5,
        'vote_type' => "fivestar-widget-static-vote",
        'num_of_comments' => $numOfComments,
        'widget' => ['name' => 'fivestar-basic'],
        'stars_data' => $starsData,
        'product_id' => $entity->id(),
      ]),
    ];
  }

  // Render stars statistics widget.
  if ($display->getComponent('stars_widget')) {
    // Render only if there is any comment.
    if (!empty($ratings)) {
      $totalRating = ($productRating / 20 );
      $statistics = $product_rating_manager->getStatisticsRatingData($ratings);
      // Get percentage of each mark.
      foreach ($statistics as $key => $rating) {
        $statistics[$key] = $rating / count($ratings) * 100;
      }

      $build['stars_widget'] = [
        '#markup' => \Drupal::theme()->render('belmil_product_rating_statistics_widget', [
          'ratings' => $statistics,
          'total_rating' => $totalRating,
          'rating' => $productRating,
          'stars' => 5,
          'vote_type' => "fivestar-widget-static-vote",
          'num_of_comments' => $numOfComments,
          'widget' => ['name' => 'fivestar-basic'],
          'stars_data' => $starsData,
          'product_id' => $entity->id(),
        ]),
      ];
    }
  }
}

/**
 * Implements hook_page_attachments().
 */
function belmil_product_rating_page_attachments(array &$page) {
  // Get request and entity.
  $request = \Drupal::request();
  $path = $request->attributes->get('_route_object')->getPath();
  /** @var \Drupal\commerce_product\Entity\ProductInterface $entity */
  $entity = $request->attributes->get('_entity');

  // Check if we are on product view page.
  if (!empty($entity) && $path == "/product/{commerce_product}") {
    // Attach google schema style script.
    /** @var \Drupal\belmil_product_rating\BelmilProductRatingManager $product_rating_manager */
    $product_rating_manager = \Drupal::service('belmil_product_rating.belmil_product_rating_manager');

    $product_rating_manager->attachGoogleSchemaStyleScript($entity, $page);

    // Attach Trustami Comment Widget.
    $referrerMeta = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'referrer',
        'content' => 'strict-origin-when-cross-origin',
      ],
    ];
    $page['#attached']['html_head'][] = [$referrerMeta, 'referrerMeta'];
    // attachTrustamiCommentsWidgetScript($entity, $page);
  }
}

/**
 * Trustami product widget script.
 *
 * @param \Drupal\commerce_product\Entity\ProductInterface $entity
 *   The product entity.
 * @param array $page
 *   The page array.
 */
function attachTrustamiCommentsWidgetScript(ProductInterface $entity, array &$page) {
  $variations = $entity->getVariations();
  $variation = reset($variations);

  if ($variation) {
    $trustamiProductWidgetScript = [
      '#tag' => 'script',
      '#attributes' => [
        'type' => 'text/javascript',
        'id' => 'trustami-product-widget',
        'src' => 'https://cdn.trustami.com/widgetapi/productWidget/trustami-product-widget.js',
        'data-uid' => '601e2fffcc96c5152b8b46de',
        'data-gtin' => $variation->getSku(),
        'data-asin' => $variation->get('field_asin')->value,
        'data-modes' => "[4]",
      ],
      '#value' => '',
    ];

    // Add script to head.
    $page['#attached']['html_head'][] = [
      $trustamiProductWidgetScript,
      'trustamiProductWidgetScript'
    ];
  }
}
<?php

declare(strict_types = 1);

namespace Drupal\enmon_core\Plugin\search_api\processor;

use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;

/**
 * @SearchApiProcessor(
 *   id = "only_special_offer",
 *   label = @Translation("Only special offer"),
 *   description = @Translation("Calculates whether the only special offer is true on all variation"),
 *   stages = {
 *     "add_properties" = 0,
 *   },
 * )
 */
class OnlySpecialOffer extends ProcessorPluginBase {

  /**
   * {@inheritdoc}
   */
  public static function supportsIndex(IndexInterface $index) {
    $supported_entity_types = ['commerce_product'];
    foreach ($index->getDatasources() as $datasource) {
      if (in_array($datasource->getEntityTypeId(), $supported_entity_types)) {
        return TRUE;
      }
    }

    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
    $properties = [];

    if (!$datasource) {
      $definition = [
        'label' => $this->t('Only special offers'),
        'description' => $this->t('Whether the product all variation marked with only special offer'),
        'type' => 'boolean',
        'is_list' => TRUE,
        'processor_id' => $this->getPluginId(),
      ];
      $properties['only_special_offers'] = new ProcessorProperty($definition);
    }

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function addFieldValues(ItemInterface $item) {
    $fields = $item->getFields();

    $only_special_offer_fields = $this->getFieldsHelper()
      ->filterForPropertyPath($fields, NULL, 'only_special_offers');

    /** @var \Drupal\Core\Entity\Plugin\DataType\EntityAdapter $entity_adapter */
    $entity_adapter = $item->getOriginalObject();

    /** @var \Drupal\commerce_product\Entity\Product $product */
    $product = $entity_adapter->getEntity();

    /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $product_variation */
    foreach ($product->getVariations() as $product_variation) {
      $is_only_special_offer = $product_variation->get('field_only_special_offer')->value;

      foreach ($only_special_offer_fields as $only_special_offer_field) {
        $only_special_offer_field->addValue($is_only_special_offer);
      }
    }
  }

}
/**
 * Implements hook_views_pre_view().
 */
function enmon_core_views_pre_view(ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() === 'special_offers') {
    $format_values = \Drupal::entityTypeManager()
      ->getStorage('commerce_product_attribute_value')
      ->getQuery()
      ->condition('attribute', 'master_format')
      ->execute();
    $color_values = \Drupal::entityTypeManager()
      ->getStorage('commerce_product_attribute_value')
      ->getQuery()
      ->condition('attribute', 'master_color')
      ->execute();
    $filters = $view->display_handler->getOption('filters');
    if (array_key_exists('attribute_master_format_target_id_1', $filters)) {
      foreach ($format_values as $format_key => $format_value) {
        $filters['attribute_master_format_target_id_1']['value'][$format_key] = $format_value;
      }
    }
    if (array_key_exists('attribute_master_color_target_id_1', $filters)) {
      foreach ($color_values as $color_key => $color_value) {
        $filters['attribute_master_color_target_id_1']['value'][$color_key] = $color_value;
      }
    }
    $view->display_handler->overrideOption('filters', $filters);
  }
}
TRUNCATE cache_config;
TRUNCATE cache_container;
TRUNCATE cache_data;
TRUNCATE cache_default;
TRUNCATE cache_discovery;
TRUNCATE cache_dynamic_page_cache;
TRUNCATE cache_entity;
TRUNCATE cache_menu;
TRUNCATE cache_render;
TRUNCATE cache_toolbar;
(function (Drupal) {

  const handleExpand = (element) => {
    const expandElement = element.target.parentElement.querySelector('.to-expand');
    if (expandElement.style.display === "inline") {
      expandElement.style.display = "none";
      element.target.innerHTML = Drupal.t('Read more');
    } else {
      expandElement.style.display = "inline";
      element.target.innerHTML = Drupal.t('Collapse');
    }
  };

  document.querySelectorAll('.expand-link').forEach((element) => {
    element.addEventListener('click', handleExpand);
  });
}) (Drupal);
read_more:
  version: 1.x
  js:
    js/read_more.js: {}
  dependencies:
    - core/drupal
<?php

namespace Drupal\halo_widget_altering\Element;

use Drupal\Core\Render\Element\RenderElement;

/**
 * Provides a render element to display read more text.
 *
 * Properties:
 * - #text: Text to be printed.
 * - #limit_chars: Number of characters to limit.
 *
 * Usage Example:
 * @code
 * $build['read_more'] = [
 *   '#type' => 'correction_value_viewer',
 *   '#entity' => EntityInterface,
 * ];
 * @endcode
 * @RenderElement("read_more")
 */
class ReadMore extends RenderElement {

  /**
   * {@inheritDoc}
   */
  public function getInfo() {
    return [
      '#pre_render' => [
        [get_class($this), 'printText'],
      ],
      '#text' => NULL,
      '#limit_chars' => 250,
    ];
  }



  /**
   * @return
   */
  public static function printText(array $element) {
    $text = $element['#text'];
    $limit_characters = $element['#limit_chars'];

    if (empty($text)) {
      return [];
    }

    if (strlen($text) <= $limit_characters) {
      return [
        'text' => [
          '#type' => 'inline_template',
          '#template' => '{{ value|nl2br }}',
          '#context' => ['value' => $text],
        ],
      ];
    }
    $first_half_text = substr($text, 0, $limit_characters);
    $second_half_text = substr($text, $limit_characters);

    $element['text'] = [
      'text' => [
        '#type' => 'inline_template',
        '#template' => '{{ value1|nl2br }}<span class="to-expand" style="display: none">{{ value2|nl2br }}</span><a class="expand-link">{{ read_more_text }}</a>',
        '#context' => [
          'value1' => $first_half_text,
          'value2' => $second_half_text,
          'read_more_text' => t('Read more'),
        ],
      ],
    ];
    $element['#attached']['library'][] = 'halo_widget_altering/read_more';
    return $element;
  }

}
      $entity_row[] = [
        '#type' => 'read_more',
        '#text' => $entity->get('description')->value,
//        '#limit_chars' => 10,
      ];
    }
<?php
/**
* Implements hook_token_info().
*/
function mycustomtokenmodule_token_info() {
   $type = [
       'name' => t('Custom Token'),
       'description' => t('Tokens for custom things.'),
   ];
   $node['title'] = [
       'name' => t("Node Title"),
       'description' => t('The node\'s title'),
   ];
   $node['dateformat'] = [
       'name' => t("Custom Date Format"),
       'dynamic' => TRUE,
       'description' => t('Show a custom format for the current date'),
   ];
   return [
       'types' => ['customtoken' => $type],
       'tokens' => ['customtoken' => $node],
   ];
}
/**
* Implements hook_tokens().
*/
function mycustomtokenmodule_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
   $replacements = [];
   
   if ($type == 'customtoken' && !empty($data['node'])) {
       foreach ($tokens as $name => $original) {
           switch ($name) {
               case 'title':
                   $replacements[$original] = $data['node']->getTitle();
               break;
           }
       }
       if ($dateTokens = \Drupal::token()->findWithPrefix($tokens, 'dateformat')) {
           // var_dump($dateTokens)
           // retult: array(1) { ["Y-m-d"]=> string(30) "[customtoken:dateformat:Y-m-d]" }
           foreach ($dateTokens as $format => $original) {
               $replacements[$original] = date($format);
           }
       }
   }
   return $replacements;
}
function change_maintenance_date() {
  //Change the date so the Report date is 2024. 07. 15.
  $cfp = \Drupal::entityTypeManager()->getStorage('cfp')->load(190);
  $cfp->set('maintenance_period', '2021-07-15')->save(); // Ovaj datum je 
}
SELECT entity_id, COUNT(variations_target_id)
FROM `commerce_product__variations`
GROUP BY entity_id
HAVING COUNT(variations_target_id) > 1;
<?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;
    }
+------------+--------------------------------------------------+-----------+
| Collection | Config                                           | Operation |
+------------+--------------------------------------------------+-----------+
|            | field.storage.user.field_phone_number            | Create    |
|            | field.storage.node.field_media_contact           | Create    |
|            | field.field.node.news.field_media_contact        | Create    |
|            | field.field.user.user.field_phone_number         | Create    |
|            | user.role.media_team                             | Create    |
|            | system.action.user_remove_role_action.media_team | Create    |
|            | system.action.user_add_role_action.media_team    | Create    |
|            | core.entity_form_display.node.news.default       | Update    |
|            | core.entity_view_display.user.user.default       | Update    |
|            | core.entity_view_display.user.user.compact       | Update    |
|            | core.entity_view_display.node.news.teaser_card   | Update    |
|            | core.entity_view_display.node.news.teaser        | Update    |
|            | core.entity_view_display.node.news.image_card    | Update    |
|            | core.entity_view_display.node.news.default       | Update    |
|            | core.entity_form_display.user.user.register      | Update    |
|            | core.entity_form_display.user.user.default       | Update    |
|            | content_access.settings                          | Update    |
+------------+--------------------------------------------------+-----------+
/**
 * Implements hook_preprocess_HOOK().
 */
function carters_core_preprocess_facets_result_item(&$variables) {
  if ($variables['facet']->get('name') == 'Popust') {
    $variables['value'] = $variables['value'] . '%';
  }
}
$types = [];
$contentTypes = \Drupal::entityTypeManager()->getStorage('node_type')->loadMultiple();
foreach ($contentTypes as $contentType) {
  $types[] = $contentType->id();
}
<?php

/**
 * @file
 * Contains musin_core.module.
 */

use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;

/**
 * Implements hook_views_data_alter().
 */
function musin_core_views_data_alter(array &$data) {

  $data['private_message_threads']['members_list'] = [
    'title' => t('Member(s) name list'),
    'help' => t('The member(s) name of the private message thread'),
    'field' => [
      'id' => 'musin_core_private_message_members_list',
    ],
  ];

  $data['private_message_threads']['members_pictures'] = [
    'title' => t('Member(s) pictures'),
    'help' => t('The member(s) pictures of the private message thread'),
    'field' => [
      'id' => 'musin_core_private_message_members_pictures',
    ],
  ];

  $data['private_message_threads']['thread_last_message'] = [
    'title' => t('Last message'),
    'help' => t('The last message of the private message thread'),
    'field' => [
      'id' => 'musin_core_private_message_thread_last_message',
    ],
  ];

  $data['private_message_threads']['thread_is_unread'] = [
    'title' => t('Is unread or not'),
    'help' => t('The last message is unreaded of the private message thread'),
    'field' => [
      'id' => 'musin_core_private_message_thread_is_unread',
    ],
  ];

}

/**
 * Implements hook_entity_extra_field_info().
 */
function musin_core_entity_extra_field_info() {
  $extra_field = [];

  $extra_field['private_message']['private_message']['display']['author_image'] = [
    'label' => t('Author image'),
    'description' => t('Author image.'),
    'weight' => 100,
    'visible' => TRUE,
  ];

  $extra_field['private_message']['private_message']['display']['author_name'] = [
    'label' => t('Author name'),
    'description' => t('Author name.'),
    'weight' => 100,
    'visible' => TRUE,
  ];

  return $extra_field;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function musin_core_private_message_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {

  if ($display->getComponent('author_image')) {

    /** @var \Drupal\musin_core\MessagesService $messages_service */
    $messages_service = \Drupal::service('musin_core.messages.service');

    $build['author_image'] = $messages_service->getUserPictureElement($entity->getOwner());

  }
  if ($display->getComponent('author_name')) {

    /** @var \Drupal\private_message\Entity\PrivateMessage $entity*/
    //$author_name = $entity->getOwner()->get('field_first_name')->value . ' ' . $entity->getOwner()->get('field_last_name')->value;
    $author_name = $entity->getOwner()->get('name')->value;

    $build['author_name'] = [
      '#type' => 'markup',
      '#markup' => $author_name,
    ];

  }
}

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

  if ($view && $view['view']->id() == 'search') {
    $form['search_api_fulltext_1']['#prefix'] = '<div class="search-dropdown">';
    $form['field_genres']['#suffix'] = '</div>';
    $form['field_instruments']['#options']['All'] = 'Instruments';
    $form['field_genres']['#options']['All'] = 'Genres';
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 * Redirect user to profile page after saving the profile
 */

function musin_core_form_alter(&$form, $form_state, $form_id) {
  if ($form_id === 'user_form') {
    $form['#after_build'][] = '_musin_core_user_edit_change_password_label';
    $form['actions']['submit']['#submit'][] = 'musin_core_user_edit_form_submit';
  }

  if ($form_id === 'node_gig_edit_form') {
    $form['revision_information']['#access'] = FALSE;
  }
}

function _musin_core_user_edit_change_password_label($form, &$form_state) {
  //dsm($form['group_email_password_artist_name']['#groups']);
  $form['group_email_password_artist_name']['#groups']['group_email_password_artist_name'][0]['pass']['pass1']['#title'] = t('New password');
  $form['group_email_password_artist_name']['#groups']['group_email_password_artist_name'][0]['pass']['pass2']['#title'] = t('Confirm new password');

  return $form;
}


/**
 * Implements hook_field_widget_file_generic_form_alter().
 */
function musin_core_field_widget_file_generic_form_alter(&$element, FormStateInterface $form_state, $context) {
  if ($context['form']['#parents'][0] == "field_sample_tracks") {
    $element['#after_build'][] = '_set_description_field_required';
  }
}

function _set_description_field_required($element, FormStateInterface $form_state) {
  if (isset($element['description'])) {
    $element['description']['#required'] = TRUE;
  }
  return $element;
}

function musin_core_user_edit_form_submit($form, &$form_state) {
  $form_state->setRedirect('entity.user.canonical', ['user' => \Drupal::currentUser()->id()]);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function musin_core_form_user_login_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['#submit'][] = 'musin_core_user_login_form_submit';
}

/**
 * Custom submit handler for the login form.
 */
function musin_core_user_login_form_submit($form, FormStateInterface $form_state) {
  $url = Url::fromRoute('<front>');
  $form_state->setRedirectUrl($url);
}

/**
 * Implements hook_preprocess_HOOK().
 */
function musin_core_preprocess_region(&$variables) {
  if ($variables['region'] == 'primary_navigation') {
    $variables['#cache']['contexts'][] = 'user';
  }
}

/**
 * Implements hook_entity_presave().
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 * @param $term
 */
function musin_core_entity_presave(EntityInterface $entity) {
  if ($entity instanceof \Drupal\user\UserInterface) {
    $completeness = \Drupal\musin_core\Services\UserHelperService::getProfileCompleteness($entity);
    $entity->set('field_completeness', $completeness);
//    // Add typed in instrument to users profile view.
//    $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
//      'field_typed_in_instrument' => 1
//    ]);
//    $term = reset($terms);
//    if ($term !== false) {
//      $entity->set('field_typed_in_instrument', $term);
//    }
  }


  // Moving typed in instruments to their vocabulary.
  if ($entity instanceof \Drupal\taxonomy\Entity\Term) {
    $name = $entity->get('name')->value;
    if ($entity->get('vid')->getValue()[0]['target_id'] == 'instruments' && $entity->get('field_typed_in_instrument')->value === 1) {
      $entity->set('vid', 'extra_instruments');
    } else if ($entity->get('vid')->getValue()[0]['target_id'] == 'genres' && $entity->get('field_typed_in_genre')->value === 1) {
      $entity->set('vid', 'extra_genres');
    }
  }

}
<?php

/**
 * @file
 * Contains fontana_commerce.module.
 */

use Drupal\commerce\EntityHelper;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\commerce_product\Entity\ProductVariationInterface;

/**
 * Implements hook_views_data_alter().
 */
function fontana_commerce_views_data_alter(array &$data) {

  $data['views']['custom_category_filter'] = [
    'title' => t('Custom category filter'),
    'filter' => [
      'title' => t('Custom category filter'),
      'help' => t('Provides a custom filter logic.'),
      'field' => 'nid',
      'id' => 'category_views_filter',
    ],
  ];

  $data['views']['sku_filter'] = [
    'title' => t('SKU filter'),
    'filter' => [
      'title' => t('SKU filter'),
      'help' => t('Provides a custom filter for filtering orders by sku.'),
      'field' => 'id',
      'id' => 'sku_filter',
    ],
  ];

  $data['commerce_shipment']['shipping_profile'] = [
    'title' => t('Shipping Profile'),
    'help' => t('Reference to the shipping profile of a commerce shipment.'),
    'relationship' => [
      'group' => 'Shipment',
      // Views name of the table being joined to from commerce_shipment.
      'base' => 'profile',
      // Database field name in profile for the join.
      'base field' => 'profile_id',
      // Real database field name in commerce_shipment for the join, to override
      // 'unique_dummy_name'.
      'field' => 'shipping_profile__target_id',
      // ID of relationship handler plugin to use.
      'id' => 'standard',
      'label' => t('Shipping Profile'),
    ],
  ];

  $data['commerce_order']['quick_view'] = [
    'title' => t('Quick view'),
    'field' => [
      'id' => 'commerce_order_quick_view',
    ],
  ];

  $data['commerce_order']['update_state']['field'] = [
    'title' => t('Update state'),
    'help' => t('Add buttons for update the order state.'),
    'id' => 'commerce_order_update_state',
  ];

}

/**
 * Implements hook_entity_extra_field_info().
 */
function fontana_commerce_entity_extra_field_info() {
  $extra = [];

  $extra['commerce_product']['default']['display']['manufacturer_declaration'] = [
    'label' => t('Manufacturer(display: declaration)'),
    'visible' => FALSE,
  ];

  $extra['commerce_product']['default']['display']['variation_sizes'] = [
    'label' => t('Product variations size attribute'),
    'visible' => TRUE,
  ];

  $extra['commerce_product']['default']['display']['variation_colors'] = [
    'label' => t('Product variations color attribute'),
    'visible' => TRUE,
  ];

  return $extra;
}

/**
 * Implements hook_entity_view().
 */
function fontana_commerce_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  if ($entity->getEntityTypeId() === 'commerce_product' && $entity->bundle() === 'default' && $display->getComponent('manufacturer_declaration')) {
    if ($entity->hasField('field_manufacturer')) {
      /** @var \Drupal\taxonomy\Entity\Term $manufacturer */
      if ($manufacturer = $entity->get('field_manufacturer')->entity) {
        $view_builder = \Drupal::entityTypeManager()->getViewBuilder('taxonomy_term');
        $build['manufacturer_declaration'] = $view_builder->view($manufacturer, 'declaration');
      }
    }
  }

  if ($view_mode === 'teaser' && $entity->getEntityTypeId() === 'commerce_product' && $entity->bundle() === 'default') {
    $sizes = [];
    $colors = [];

    /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation */
    foreach ($entity->getVariations() as $variation) {
      if ($size = $variation->getAttributeValue('attribute_size')) {
        $sizes[] = $size->label();
      }

      if ($color = $variation->getAttributeValue('attribute_boja')) {
        $colors[] = $color->label();
      }
    }

    if ($display->getComponent('variation_sizes')) {
      $build['variation_sizes'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#value' => implode(', ', array_unique($sizes)),
        '#attributes' => [
          'class' => ['size-wrapper'],
        ],
      ];
    }

    if ($display->getComponent('variation_colors')) {
      $build['variation_colors'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#value' => implode(', ', array_unique($colors)),
        '#attributes' => [
          'class' => ['color-wrapper'],
        ],
      ];
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function fontana_commerce_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Get add to cart form from product page.
  if (
    str_contains($form_id, "commerce_order_item_dc_ajax_add_cart_form_commerce_product") ||
    str_contains($form_id, 'commerce_order_item_add_to_cart_form_commerce_product')
  ) {
    // Check if product contain variations.
    if (isset($form['purchased_entity']['widget'][0]['variation']['#options'])) {
      // Get variations.
      $variations = $form['purchased_entity']['widget'][0]['variation']['#options'];
      foreach ($variations as $key => $item) {
        // Load variation by key(id).
        $variation = \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load($key);
        // Set boja if exists.
        !empty($variation->get('attribute_boja')->target_id) ? $color = $variation->get('attribute_boja')->entity->getName() : $color = "";
        // Set size if exists.
        !empty($variation->get('attribute_size')->target_id) ? $size = $variation->get('attribute_size')->entity->getName() : $size = "";
        // If both fields are empty use default option.
        if (!empty($size) || !empty($color)) {
          $form['purchased_entity']['widget'][0]['variation']['#options'][$key] = $size . " " . $color;
        }
        $item_title = $variation->getTitle();
        $variation_images = $variation->get('field_image')->getValue();
        $images = [];
        foreach ($variation_images as $image) {
          if (empty($image['title'])) {
            $image['title'] = $item_title;
          }
          if (empty($image['alt'])) {
            $image['alt'] = $item_title;
          }
          $images[] = $image;
        }
        $variation->set('field_image', $images);
      }
    }
    if (isset($form['purchased_entity']['widget'][0]['variation'])) {
      if (isset($form['purchased_entity']['widget'][0]['variation']['#value'])) {
        $variation_id = $form['purchased_entity']['widget'][0]['variation']['#value'];
      }
      else {
        $variation_id = $form['purchased_entity']['widget'][0]['variation']['#default_value'];
      }
      $variation = \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load($variation_id);
      $square_meter_per_box = $variation->get('field_square_meters_per_box')->value;
      if (!empty($square_meter_per_box)) {
        $form['quantity']['widget'][0]['value']['#step'] = '0.01';
      }
    }
  }
  // Get checkout flow form and step order information.
  if ($form_id == 'commerce_checkout_flow_multistep_default' && $form['#step_id'] == 'order_information') {
    $form['actions']['captcha'] = [
      '#type' => 'captcha',
      '#captcha_type' => 'recaptcha/reCAPTCHA',
    ];
  }

  if ($form_id == 'commerce_order_default_edit_form') {
    $order_id = \Drupal::routeMatch()->getRawParameter('commerce_order');
    $order = \Drupal::entityTypeManager()->getStorage('commerce_order')->load($order_id);
    $payment_gateway = $order->payment_gateway->first()->entity;
    if (!empty($payment_gateway)) {
      $payment_label = $payment_gateway->label();
      $form['payment_gateway'] = [
        '#type' => 'details',
        '#group' => 'advanced',
        '#open' => TRUE,
        '#title' => t('Payment Method'),
        'payment_gateway' => [
          '#type' => 'item',
          '#markup' => $payment_label,
        ],
      ];
    }
  }

  $input = $form_state->getUserInput();
  $is_ajax = (bool) ($input['_drupal_ajax'] ?? FALSE);
  if (str_contains($form_id, 'state_machine_transition_form_commerce_order_state') && $is_ajax) {
    foreach ($form['actions'] as $name => $action) {
      if (strpos($name, '#') !== FALSE) {
        continue;
      }
      $form['actions'][$name]['#ajax'] = [
        'callback' => 'fontana_commerce_order_state_ajax',
      ];
    }
  }
}

/**
 * Ajax callback for the order state form.
 *
 * @param array $form
 *   The form.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 *
 * @return \Drupal\Core\Ajax\AjaxResponse
 *   The ajax response.
 */
function fontana_commerce_order_state_ajax(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  $destination = \Drupal::request()->query->get('destination');
  $response->addCommand(new RedirectCommand($destination));

  return $response;
}

/**
 * Implements hook_element_info_alter().
 */
function fontana_commerce_element_info_alter(array &$info) {
  if (isset($info['address'])) {
    $info['address']['#process'][] = 'fontana_commerce_make_address_line2_visible';
  }
}

/**
 * Process callback for address element.
 */
function fontana_commerce_make_address_line2_visible(array &$element, FormStateInterface $form_state, array &$complete_form) {
  $element['address_line2']['#title_display'] = 'before';
  return $element;
}

/**
 * Implements hook_preprocess_HOOK().
 */
function fontana_commerce_preprocess_commerce_product(&$variables) {
  /** @var \Drupal\commerce_product\Entity\ProductInterface $product_entity */
  $product_entity = $variables['product_entity'];
  $variation = $product_entity->getDefaultVariation();

  if (!$variation instanceof ProductVariationInterface) {
    $variables['calculator_visible'] = FALSE;
  }
  else {
    /** @var \Drupal\presentcommerce_box_calculator\Service\BoxCalculator $box_calculator */
    $box_calculator = \Drupal::service('presentcommerce_box_calculator.box_calculator');
    if ($box_calculator->isVisible($variation)) {
      $variables['calculator_visible'] = TRUE;
    }
    else {
      $variables['calculator_visible'] = FALSE;
    }
  }
}

/**
 * Implements hook_entity_presave().
 */
function fontana_commerce_entity_presave(EntityInterface $entity) {
  // Check if entity is product variation and if autogenerate title is selected.
  if ($entity->getEntityTypeId() == "commerce_product_variation" && $entity->get('field_auto_generate_title')->value == TRUE) {
    $title = generate_variation_title($entity);
    $entity->setTitle($title);
  }
  elseif ($entity->getEntityTypeId() == "commerce_product_variation") {
    $title_alt = generate_image_title_alt($entity);
    if ($title_alt !== "") {
      $entity->set('field_image', $title_alt);
    }
  }

  if ($entity->getEntityTypeId() == 'commerce_product') {
    $userId = \Drupal::currentUser()->id();
    $user = \Drupal::entityTypeManager()->getStorage('user')->load($userId);
    $entity->set('field_updated_by', $user);
  }
   if ($entity->getEntityTypeId() == 'commerce_order_item' && $entity->getPurchasedEntity()->get('field_square_meters_per_box')->value !== NULL) {
     $quantity = $entity->getQuantity();
     $product = \Drupal::entityTypeManager()
       ->getStorage('commerce_product_variation')
       ->load($entity->getPurchasedEntityId());
     $square_meters = $product->get('field_square_meters_per_box')->value;
     $number_of_boxes = (integer) round($quantity / $square_meters);
     if ($number_of_boxes < 1) {
       $number_of_boxes = 1;
     }
     $entity->set('field_number_of_boxes', $number_of_boxes);
   }
}

/**
 * Generate Variation title based on attributes.
 */
function generate_variation_title(EntityInterface $entity) {
  if (!$entity->getProductId()) {
    // Title generation is not possible before the parent product is known.
    return '';
  }

  $product_title = $entity->getProduct()->getTitle();
  if ($attribute_values = $entity->getAttributeValues()) {
    $attribute_labels = EntityHelper::extractLabels($attribute_values);
    $title = $product_title . ' - ' . implode(', ', $attribute_labels);
  }
  else {
    // When there are no attribute fields, there's only one variation.
    $title = $product_title;
  }

  return $title;
}

/**
 * Generate Variation title based on attributes.
 */
function generate_image_title_alt(EntityInterface $entity) {

  if (!$entity->getProductId()) {
    // Title generation is not possible before the parent product is known.
    return '';
  }

  $product_title = $entity->getProduct()->getTitle();

  $variation_images = $entity->get('field_image')->getValue();
  $images = reset($variation_images);
  foreach ($variation_images as $image) {
    if (empty($image['alt'])) {
      $image['alt'] = $product_title;
    }
    if (empty($image['title'])) {
      $image['title'] = $product_title;
    }
    $images[] = $image;
  }
  return $images;
}

/**
 * Implements hook_page_attachments().
 */
function fontana_commerce_page_attachments(array &$attachments) {
  $config = \Drupal::config('system.theme');
  $theme = \Drupal::theme()->getActiveTheme()->getName();
  if ($theme == $config->get('admin')) {
    $attachments['#attached']['library'][] = 'fontana_commerce/extra.admin';
  }
}

/**
 * Implements hook_field_widget_form_alter().
 */
function fontana_commerce_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
  // Fix problem with displaying description on shipping pane
  // for single shipping method.
  if ($context['items']->getFieldDefinition()->getName() === 'shipping_method' && $context['items']->getFieldDefinition()->getTargetEntityTypeId() === 'commerce_shipment') {
    foreach ($element['#options'] as $key => $option) {
      $title = $element[$key]["#rate"]->getService()->getLabel();
      $option = (new FormattableMarkup('@service @amount', [
        '@service' => $title,
        '@amount' => '',
      ]));
      $element['#options'][$key] = $option;
    }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function fontana_commerce_form_views_form_commerce_cart_form_default_alter(&$form, FormStateInterface $form_state) {
  $order_items = $form['output'][0]['#view']->result[0]->_entity->getItems();
  foreach ($order_items as $key => $order_item) {
    $purchased_entity = $order_item->getPurchasedEntity();
    if (!empty($purchased_entity->get('field_square_meters_per_box')->getValue())) {
      $form['edit_quantity'][$key]['#step'] = '0.01';
    }
  }
}
<?php

namespace Drupal\musin_core\Plugin\Field\FieldWidget;

use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Custom field widget for instruments.
 *
 * @FieldWidget(
 *   id = "instruments_widget",
 *   label = @Translation("Instruments widget"),
 *   description = @Translation("Instruments widget"),
 *   field_types = {
 *     "entity_reference",
 *     "string"
 *   }
 * )
 */
class InstrumentsWidget extends WidgetBase {

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

  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entityTypeManager) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    $this->entityTypeManager = $entityTypeManager;
  }

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $plugin_id,
      $plugin_definition,
      $configuration['field_definition'],
      $configuration['settings'],
      $configuration['third_party_settings'],
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    // TODO: Implement formElement() method.
    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';

    $rows = [];
    $taxonomies = $this->entityTypeManager
      ->getStorage('taxonomy_term')
      ->loadByProperties([
        'vid' => 'instruments',
      ]);
    foreach ($taxonomies as $term) {
      $rows[$term->getName()] = $term->getName();
    }

    $element['first'] = [
      '#type' => 'checkboxes',
      '#options' => $rows,
      '#attributes' => [
        'name' => 'base_taxonomy',
      ]
    ];

    $element['second'] = [
      '#type' => 'textfield',
      '#title' => 'Enter your instruments',
      '#states' => [
        'visible' => [
          ':input[name="base_taxonomy"]' => ['value' => 'Other']
        ]
      ]
    ];

    return $element;
  }

}

$variation = \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load(id)->delete();
<?php

namespace Drupal\csv_import\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\file\Entity\File;
use Drupal\node\Entity\Node;
use Drupal\paragraphs\Entity\Paragraph;
use Symfony\Component\DependencyInjection\ContainerInterface;

class CSVImportForm extends FormBase {

  public function getFormId() {
    return 'csv_import';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['csv_file'] = [
      '#title' => 'Upload your file',
      '#type' => 'managed_file',
      '#upload_location' => 'public://',
      '#upload_validators' => [
        'file_validate_extensions' => ['csv']
      ]
    ];

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
    ];

    return $form;
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $csv_file = $form_state->getValue('csv_file');
    $csv_file = reset($csv_file);
    $file = File::load($csv_file);
    $data = $this->csvtoarray($file->getFileUri(), ',');
    foreach ($data as $row) {
      $operations[] = ['\Drupal\csv_import\Form\CSVImportForm::addImportContentItem', [$row]];
    }

    $batch = [
      'title' => $this->t('Importing data...'),
      'operations' => $operations,
      'init_message' => $this->t('Import is starting.'),
      'finished' => '\Drupal\csv_import\Form\CSVImportForm::addImportContentItemCallback',
    ];
    batch_set($batch);

    $this->messenger()->addMessage($this->t('Your form was submitted!'));
  }

  public function csvtoarray($filename='', $delimiter) {
    if (!file_exists($filename) || !is_readable($filename)) return FALSE;
    $header = NULL;

    if(($handle = fopen($filename, 'r')) !== FALSE) {
      while(($row = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
        if(!$header) {
          $header = $row;
        } else {
          $data[] = array_combine($header, $row);
        }
      }
      fclose($handle);
    }
    return $data;
  }

  public static function addImportContentItem($item, &$context) {
    $message = 'Creating ' . $item['Title'];
    $results = [];
    self::create_node($item);
    $context['message'] = $message;
    $context['results'] = $item;
  }

  public function addImportContentItemCallback($success, $results, $operations) {
    $number = count($results);
    if ($success) {
      $message = new PluralTranslatableMarkup($number, 'One item processed.','@count items processed.');
    } else {
      $message = t('Finished with an error.');
    }
    \Drupal::messenger()->addMessage(t($message->render()));
  }

  public static function create_node($item)
  {
    $data = file_get_contents($item['Image']);
    $file = file_save_data($data, 'public://sample.png', \Drupal\Core\File\FileSystemInterface::EXISTS_RENAME);

    $nodes = [
      'type' => 'articles_about_programming',
      'title' => $item['Title'],
      'field_description' => $item['Description'],
      'field_myimage' => [
        'target_id' => $file->id(),
        'alt' => 'Sample',
        'title' => 'Sample file',
      ],
      'field_link_to_website' => [
        'uri' => 'https://www.' . $item['Link to website'],
        'title' => $item['Title'],
        'options' => ['target' => '_blank'],
      ],
    ];
    $node = Node::create($nodes);
    $node->setPublished(TRUE);
    $node->save();
  }
}
// If we want to use it as a dependency injection (we should),
// than this is the right way.

/**
 * Logger interface.
 *
 * @var \Psr\Log\LoggerInterface
 */
protected $logger;

// After that we add it to constructor method.

/**
* Controller_name constructor
 *
 * @param \Psr\Log\LoggerInterface $logger
 *  Logger interface.
 */
public function __construct(LoggerInterface $logger) {
  $this->logger = $logger;
}

public function someFunction(){
  if (something) {
    // Line of code...
  } else {
    $this->logger->error('Error message');
  }
}
stormtextil_core.edifact_orders:
  path: '/cron/synchronize-edifact-orders'
  defaults:
    _controller: '\Drupal\stormtextil_synchronization\Controller\SynchronizeEdifactOrders::parse'
    _title: 'Edifact Parser'
  requirements:
    _permission: 'access content'
<?php

namespace Drupal\stormtextil_config\Form;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Class StormTextil Config Form.
 */
class StormTextilConfigForm extends ConfigFormBase {

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

  /**
   * StormTextilConfigForm constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The Entity Type Manager.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
    $this->entityTypeManager = $entityTypeManager;
  }

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

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'stormtextil_config.config_form';
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return ['stormtextil_config.settings'];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $config = $this->config('stormtextil_config.settings');

    // Promotions e-mail.
    $form['promotions_email'] = [
      '#type' => 'details',
      '#title' => $this->t('Promotions in Order e-mail'),
      '#description' => $this->t('Add custom promotional text into order confirmation e-mail'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $promotions_email = $config->get('promotions_email');

    $form['promotions_email']['enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable promotions'),
      '#default_value' => isset($promotions_email['enabled']) ? $promotions_email['enabled'] : '',
    ];

    $form['promotions_email']['EN'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - en'),
      '#format' => 'full_html',
      '#default_value' => isset($promotions_email['EN']['value']) ? $promotions_email['EN']['value'] : '',
    ];

    $form['promotions_email']['DA'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - da'),
      '#format' => 'full_html',
      '#default_value' => isset($promotions_email['DA']['value']) ? $promotions_email['DA']['value'] : '',
    ];

    $form['promotions_email']['DE'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - de'),
      '#format' => 'full_html',
      '#default_value' => isset($promotions_email['DE']['value']) ? $promotions_email['DE']['value'] : '',
    ];

    // Stock notify e-mail.
    $form['stock_notify_email'] = [
      '#type' => 'details',
      '#title' => $this->t('Stock notify e-mail'),
      '#description' => $this->t('Add custom text into stock notify e-mail'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $stock_notify_email = $config->get('stock_notify_email');

    $form['stock_notify_email']['en']['subject'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Subject - en'),
      '#default_value' => isset($stock_notify_email['en']['subject']) ? $stock_notify_email['en']['subject'] : '',
      '#size' => 50,
    ];

    $form['stock_notify_email']['en']['message'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - en'),
      '#format' => 'full_html',
      '#default_value' => isset($stock_notify_email['en']['message']['value']) ? $stock_notify_email['en']['message']['value'] : '',
    ];

    $form['stock_notify_email']['da']['subject'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Subject - da'),
      '#default_value' => isset($stock_notify_email['da']['subject']) ? $stock_notify_email['da']['subject'] : '',
      '#size' => 50,
    ];

    $form['stock_notify_email']['da']['message'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - da'),
      '#format' => 'full_html',
      '#default_value' => isset($stock_notify_email['da']['message']['value']) ? $stock_notify_email['da']['message']['value'] : '',
    ];

    $form['stock_notify_email']['de']['subject'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Subject - de'),
      '#default_value' => isset($stock_notify_email['de']['subject']) ? $stock_notify_email['de']['subject'] : '',
      '#size' => 50,
    ];

    $form['stock_notify_email']['de']['message'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - de'),
      '#format' => 'full_html',
      '#default_value' => isset($stock_notify_email['de']['message']['value']) ? $stock_notify_email['de']['message']['value'] : '',
    ];

    // Exchange rates.
    $form['exchange_rates'] = [
      '#type' => 'details',
      '#title' => $this->t('Exchange rates'),
      '#description' => $this->t('Defines the exchange rate for 1 unit of the currency in DKK.'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $exchange_rates = $config->get('exchange_rates');

    $form['exchange_rates']['EUR'] = [
      '#type' => 'textfield',
      '#title' => $this->t('EUR to DKK'),
      '#default_value' => isset($exchange_rates['EUR']) ? $exchange_rates['EUR'] : '',
      '#size' => 10,
    ];

    $form['exchange_rates']['SEK'] = [
      '#type' => 'textfield',
      '#title' => $this->t('SEK to DKK'),
      '#default_value' => isset($exchange_rates['SEK']) ? $exchange_rates['SEK'] : '',
      '#size' => 10,
    ];

    // Order fees.
    $form['order_fees'] = [
      '#type' => 'details',
      '#title' => $this->t('Order fees'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $form['order_fees']['administration_fee'] = [
      '#type' => 'details',
      '#title' => $this->t('Administration fees'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $administration_fee = $config->get('administration_fee');

    $form['order_fees']['administration_fee']['DKK']['range'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Invoice total (DKK)'),
      '#default_value' => isset($administration_fee['DKK']['range']) ? $administration_fee['DKK']['range'] : '',
      '#size' => 10,
    ];

    $form['order_fees']['administration_fee']['DKK']['fee'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Fee (DKK)'),
      '#default_value' => isset($administration_fee['DKK']['fee']) ? $administration_fee['DKK']['fee'] : '',
      '#size' => 10,
    ];

    $form['order_fees']['administration_fee']['EUR']['range'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Invoice total (EUR)'),
      '#default_value' => isset($administration_fee['EUR']['range']) ? $administration_fee['EUR']['range'] : '',
      '#size' => 10,
    ];

    $form['order_fees']['administration_fee']['EUR']['fee'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Fee (EUR)'),
      '#default_value' => isset($administration_fee['EUR']['fee']) ? $administration_fee['EUR']['fee'] : '',
      '#size' => 10,
    ];

    $form['order_fees']['administration_fee']['SEK']['range'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Invoice total (SEK)'),
      '#default_value' => isset($administration_fee['SEK']['range']) ? $administration_fee['SEK']['range'] : '',
      '#size' => 10,
    ];

    $form['order_fees']['administration_fee']['SEK']['fee'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Fee (SEK)'),
      '#default_value' => isset($administration_fee['SEK']['fee']) ? $administration_fee['SEK']['fee'] : '',
      '#size' => 10,
    ];

    // Added costs.
    $form['order_fees']['added_cost'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Added cost price (DKK)'),
      '#default_value' => $config->get('added_cost'),
      '#size' => 10,
    ];

    // VAT.
    $form['order_fees']['vat'] = [
      '#type' => 'textfield',
      '#title' => $this->t('VAT (%)'),
      '#default_value' => $config->get('vat'),
      '#size' => 10,
    ];

    // Balance is overdue notice.
    $form['balance_is_overdue'] = [
      '#type' => 'details',
      '#title' => $this->t('Balance is overdue notification'),
      '#description' => $this->t('Add custom notification text for user with overdue balance'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $balance_is_overdue = $config->get('balance_is_overdue');

    $form['balance_is_overdue']['enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable notifications'),
      '#default_value' => isset($balance_is_overdue['enabled']) ? $balance_is_overdue['enabled'] : '',
    ];

    $form['balance_is_overdue']['EN'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - en'),
      '#format' => 'full_html',
      '#default_value' => isset($balance_is_overdue['EN']['value']) ? $balance_is_overdue['EN']['value'] : '',
    ];

    $form['balance_is_overdue']['DA'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - da'),
      '#format' => 'full_html',
      '#default_value' => isset($balance_is_overdue['DA']['value']) ? $balance_is_overdue['DA']['value'] : '',
    ];

    $form['balance_is_overdue']['DE'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Text - de'),
      '#format' => 'full_html',
      '#default_value' => isset($balance_is_overdue['DE']['value']) ? $balance_is_overdue['DE']['value'] : '',
    ];
    // Edifact orders ftp connection.
    $form['edifact'] = [
      '#type' => 'details',
      '#title' => $this->t('Ftp connection'),
      '#description' => $this->t('Configure ftp connection to remote server'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $ftp_connection = $config->get('edifact');

    $form['edifact']['server'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Ftp server'),
      '#default_value' => isset($ftp_connection['server']) ? $ftp_connection['server'] : '',
      '#size' => 30,
    ];
    $form['edifact']['username'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Username'),
      '#default_value' => isset($ftp_connection['username']) ? $ftp_connection['username'] : '',
      '#size' => 30,
    ];
    $form['edifact']['password'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Password'),
      '#default_value' => isset($ftp_connection['password']) ? $ftp_connection['password'] : '',
      '#size' => 30,
    ];
    // Samba connection.
    $form['samba'] = [
      '#type' => 'details',
      '#title' => $this->t('Samba connection'),
      '#description' => $this->t('Configure connection to remote computer'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $samba = $config->get('samba');

    $form['samba']['username'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Username'),
      '#default_value' => isset($samba['username']) ? $samba['username'] : '',
      '#size' => 30,
    ];

    $form['samba']['password'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Password'),
      '#default_value' => isset($samba['password']) ? $samba['password'] : '',
      '#size' => 30,
    ];

    $form['samba']['workgroup'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Workgroup'),
      '#default_value' => isset($samba['workgroup']) ? $samba['workgroup'] : '',
      '#size' => 30,
    ];

    $form['samba']['pdf'] = [
      '#type' => 'details',
      '#title' => $this->t('Order PDF files'),
      '#description' => $this->t('Configure connection to remote computer to synchronize PDF files'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $form['samba']['pdf']['host'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Host (IP address of remote computer)'),
      '#default_value' => isset($samba['pdf']['host']) ? $samba['pdf']['host'] : '',
      '#size' => 30,
    ];

    $form['samba']['pdf']['share'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Shared folder'),
      '#default_value' => isset($samba['pdf']['share']) ? $samba['pdf']['share'] : '',
      '#size' => 30,
    ];

    $form['samba']['invoice'] = [
      '#type' => 'details',
      '#title' => $this->t('Order invoices'),
      '#description' => $this->t('Configure connection to remote computer to synchronize invoices'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $form['samba']['invoice']['host'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Host (IP address of remote computer)'),
      '#default_value' => isset($samba['invoice']['host']) ? $samba['invoice']['host'] : '',
      '#size' => 30,
    ];

    $form['samba']['invoice']['share'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Shared folder'),
      '#default_value' => isset($samba['invoice']['share']) ? $samba['invoice']['share'] : '',
      '#size' => 30,
    ];

    // Samba connection.
    $form['theme'] = [
      '#type' => 'details',
      '#title' => $this->t('Theme settings'),
      '#description' => $this->t('Configure theme specific variables'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $theme = $config->get('theme');

    $form['theme']['theme_color'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Theme color'),
      '#default_value' => isset($theme['theme_color']) ? $theme['theme_color'] : '',
      '#description' => $this->t('Insert color HEX code'),
      '#size' => 10,
    ];

    /** @var \Drupal\taxonomy\TermStorageInterface $termStorage */
    $termStorage = $this->entityTypeManager->getStorage('taxonomy_term');
    $categoriesTree = $termStorage->loadTree('categories', 0, NULL, TRUE);
    $options = [];

    foreach ($categoriesTree as $category) {
      $options[$category->id()] = $category->label();
    }

    $form['designer_exclude_categories'] = [
      '#type' => 'details',
      '#title' => $this->t('Designer Exclude Categories'),
      '#description' => $this->t('Designer Exclude Categories'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $form['designer_exclude_categories']['exclude'] = [
      '#type' => 'select',
      '#title' => $this->t('Excluded'),
      '#default_value' => $config->get('designer_exclude_categories'),
      '#options' => $options,
      '#multiple' => TRUE,
      '#size' => 120,
      '#attributes' => [
        'style' => 'height: 240px',
      ],
    ];

    $form['designer_query_maps_id'] = [
      '#type' => 'details',
      '#title' => $this->t('Designer Query Maps'),
      '#description' => $this->t('Set the active query maps.'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $form['designer_query_maps_id']['id'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Designer Query Maps'),
      '#description' => Link::createFromRoute(
        $this->t('See more'),
        'graphql.query_maps'
      ),
      '#default_value' => $config->get('designer_query_maps_id'),
    ];

    // Checkout text.
    $form['checkout'] = [
      '#type' => 'details',
      '#title' => $this->t('Checkout'),
      '#open' => FALSE,
      '#tree' => TRUE,
    ];

    $checkout = $config->get('checkout');

    $form['checkout']['enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable checkout text'),
      '#default_value' => isset($checkout['enabled']) ? $checkout['enabled'] : '',
    ];

    $form['checkout']['EN'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Checkout text - en'),
      '#format' => 'full_html',
      '#default_value' => isset($checkout['EN']['value']) ? $checkout['EN']['value'] : '',
    ];

    $form['checkout']['DA'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Checkout text - da'),
      '#format' => 'full_html',
      '#default_value' => isset($checkout['DA']['value']) ? $checkout['DA']['value'] : '',
    ];

    $form['checkout']['DE'] = [
      '#type' => 'text_format',
      '#title' => $this->t('Checkout text - de'),
      '#format' => 'full_html',
      '#default_value' => isset($checkout['DE']['value']) ? $checkout['DE']['value'] : '',
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $form_values = $form_state->getValues();

    if (!empty($form_values['exchange_rates'])) {
      $error_msg = $this->t('Exchange rate values must be numeric and decimal points should be used. Commas are not allowed.');

      foreach ($form_values['exchange_rates'] as $key => $value) {
        if (!is_numeric($value)) {
          $form_state->setError($form['exchange_rates'][$key], $error_msg);
        }
      }
    }

    if (!empty($form_values['order_fees']['added_cost'])) {
      $error_msg = $this->t('Added cost price must be numeric and decimal points should be used. Commas are not allowed.');

      if (!is_numeric($form_values['order_fees']['added_cost'])) {
        $form_state->setError($form['order_fees']['added_cost'], $error_msg);
      }
    }

    if (!empty($form_values['order_fees']['administration_fee'])) {
      $error_msg = $this->t('Administration fee values must be numeric and decimal points should be used. Commas are not allowed.');

      foreach ($form_values['order_fees']['administration_fee'] as $currency => $item) {
        foreach ($form_values['order_fees']['administration_fee'][$currency] as $key => $value) {
          if (!is_numeric($value)) {
            $form_state->setError($form['order_fees']['administration_fee'][$currency][$key], $error_msg);
          }
        }
      }
    }

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $config = $this->config('stormtextil_config.settings');
    $form_values = $form_state->getValues();

    $config->set('promotions_email', $form_values['promotions_email'])
      ->set('stock_notify_email', $form_values['stock_notify_email'])
      ->set('exchange_rates', $form_values['exchange_rates'])
      ->set('balance_is_overdue', $form_values['balance_is_overdue'])
      ->set('administration_fee', $form_values['order_fees']['administration_fee'])
      ->set('edifact', $form_values['edifact'])
      ->set('samba', $form_values['samba'])
      ->set('theme', $form_values['theme'])
      ->set('added_cost', $form_values['order_fees']['added_cost'])
      ->set('vat', $form_values['order_fees']['vat'])
      ->set('designer_exclude_categories', array_values($form_values['designer_exclude_categories']['exclude']))
      ->set('designer_query_maps_id', $form_values['designer_query_maps_id']['id'])
      ->set('checkout', $form_values['checkout'])
      ->save();

    Cache::invalidateTags(['designer_exclude_categories']);

    parent::submitForm($form, $form_state);
  }

}
<?php

namespace Drupal\stormtextil_core\Controller;

use Drupal\commerce_shipping\Entity\Shipment;
use Drupal\commerce_shipping\Entity\ShippingMethod;
use Drupal\commerce_shipping\ShipmentItem;
use Drupal\Core\Controller\ControllerBase;
use Drupal\physical\Weight;
use Drupal\physical\WeightUnit;
use Drupal\profile\Entity\Profile;
use EDI\Parser;
use Drupal\commerce_price\Price;
use Drupal\commerce_order\Entity\OrderItem;
use Drupal\commerce_order\Entity\Order;

class EdifactController extends ControllerBase {

  public function parse() {
    $parser = new Parser();
    $ediFile = file_get_contents('./sites/default/files/1509018E1-000523-1611970.EDI');
    $parsedEdiFile = $parser->loadString($ediFile);

    $lineItems = [];
    $orderItems = [];
    $currency = $parsedEdiFile[18][1][1];

    foreach ($parsedEdiFile as $ediElement) {
      switch($ediElement[0]) {
        case 'NAD':
          if ($ediElement[1] == 'DP') {
            $organization = $ediElement[3][0];
            $address_line1 = $ediElement[3][1];
            $postal_local = explode(" ", $ediElement[3][2]);
            $postal_code = $postal_local[0];
            $locality = $postal_local[1];
            $country_code = $ediElement[3][3];
          } else if ($ediElement[1] == 'BY') {
            $billing_address = $ediElement[5];
            $billing_city = $ediElement[6];
            $billing_company = $ediElement[4];
            $billing_zip_code = $ediElement[8];
          };
          break;
        // Name that we are going to use in profile.
        case 'CTA':
          $name = explode(" ", $ediElement[2][1]);
          $given_name = $name[0];
          $family_name = $name[1];
          break;
        case 'TAX':
          if (!isset($tax)) {
            $tax = $ediElement[5][3] / 100;
            break;
          }
        // Name of shipping method.
        case 'ALC':
          $shipping_method_name = $ediElement[2];
          break;
        case 'MOA':
          if($ediElement[1][0] == '8') {
            $shipping_price = $ediElement[1][1];
          }
        // Line item variation.
        case 'PIA':
          if ($ediElement[1] == '5') {
            $sku = $ediElement[2][0];
            $rawProductVariation = $this->entityTypeManager()
              ->getStorage('commerce_product_variation')
              ->loadByProperties([
                'sku' => $sku,
              ]);
            $productVariation = reset($rawProductVariation);
            $lineItems[$productVariation->getTitle()]['variation'] = $productVariation;
          }
          break;
        // Line item quantity.
        case 'QTY':
          $quantity = $ediElement[1][1];
          $lineItems[$productVariation->getTitle()]['quantity'] = $quantity;
          break;
        // Line item price.
        case 'PRI':
          if ($ediElement[1][0] == 'AAA') {
            $price = $ediElement[1][1];
            $lineItems[$productVariation->getTitle()]['price'] = $price;
          }
          break;
        // Number of line items.
        case 'CNT':
          $numberOfLineItems = $ediElement[1][1];
          break;
      }
    }

    // Creating order item for each line item.
    foreach ($lineItems as $lineItem) {
      $orderItem = OrderItem::create([
        'type' => 'default',
        'purchased_entity' => $lineItem['variation'],
        'quantity' => $lineItem['quantity'],
        'unit_price' => new Price($lineItem['price'], $currency),
        'overridden_unit_price' => TRUE,
      ]);
      $orderItem->save();
      $orderItems[] = $orderItem;
    }

    // Creating order.
    $order = Order::create([
      'type' => 'default',
      'mail' => $this->currentUser()->getEmail(),
      'uid' => $this->currentUser()->id(),
      'store_id' => 1,
      'order_items' => [$orderItems[0], $orderItems[1]],
      'placed' => \Drupal::time()->getCurrentTime(),
    ]);
    $order->save();

    // Creating shipment.
    if ($order->get('shipments')->count() == 0) {
      $first_shipment = Shipment::create([
        'type' => 'default',
        'order_id' => $order->id(),
        'title' => 'Shipment',
        'state' => 'ready'
      ]);
      $first_shipment->save();
    }

    foreach ($orderItems as $order_item) {
      $quantity = $order_item->getQuantity();
      $purchased_entity = $order_item->getPurchasedEntity();

      if ($purchased_entity->get('weight')->isEmpty()) {
        $weight = new Weight(1, WeightUnit::GRAM);
      } else {
        $weight_item = $purchased_entity->get('weight')->first();
        $weight = $weight_item->toMeasurement();
      }

      // Creating shipment item.