Snippets Collections
As an aspiring startup, you might be eager to turn your P2P Crypto Exchange Solutions into reality. Meanwhile, you may look for an ideal solution that sustains your business endeavors efficiently. To be honest, White label Paxful Clone Script is the solution you are looking for. Maticz’s Paxful Clone Script permits you quicker market entry with an enormous return on investment. To get more information contact us via,

Email: sales@maticz.com
Whatsapp: +91 93845 87998
Telegram: @maticzofficial 
Skype: live:.cid.817b888c8d30b212
Website: https://maticz.com/paxful-clone-script
I wonder why people choose on-demand applications over traditional business models. With the fact that the market value of the On Demand Services app is predicted to expand by $335 Billion by 2025. It is secure enough to say that the economy of on-demand is drastically enlarging at a rapid pace. 

Here let’s see the popular industries that leverage the On-demand applications.

Food Delivery
E-commerce & Retail
Transportation

When it comes to developing an on-demand application for your business, Maticz provides you the cutting-edge solutions with their On-demand app development. Their team of proficient developers and designers is equipped with the knowledge and talents that are required to develop an innovative and user-friendly app that will assist you in reaching your desired goals. By hiring on-demand app developers in India you can develop an application that provides convenience, saves your precious time, is cost-effective, offers more opportunities, is eco-friendly, and is user-friendly. 
Crypto Arbitrage Bot are software programs that automate the method of profiting from price discrepancies across crypto exchanges. As the Crypto market get evolves, these trading bots are becoming an increasingly more popular tool for investors seeking to capitalize on short-term inefficiencies in the crypto arena. 

In the economical markets, Arbitrage refers to buy and sell an asset simultaneously in various markets to profit from a price difference. Crypto Arbitrage Bots are programs that execute arbitrage trades automatically across crypto exchanges when profit opportunities arise.

To get detailed version then read the curated guide via >> https://maticz.com/how-to-build-a-crypto-arbitrage-bot
Welcome to a cutting-edge crypto exchange platform, where innovation meets seamless trading experiences, similar to paxful. Here, users can confidently buy and sell a diverse range of cryptocurrencies within a highly secure and user-friendly environment. The platform empowers investors with a plethora of payment options to cater to their unique requirements. Whether seasoned or a newcomer to the crypto space, users benefit from an intuitive interface and robust security measures, that assures a hassle-free and trustworthy trading experience. To more information check here >> https://maticz.com/paxful-clone-script
Supply Chain Management Software involves a high level of coordination, planning and execution. Supply Chain software is designed to eradicate pressure on the people included and permit each stage of the chain to flow without interruption. This Software can transform the way businesses engage in the supply chain by rendering real-time inventory visibility, that facilitate warehouse operations, ship optimization and more.,

As technology steadily grows, it is becoming more crucial to embrace digital solutions and adopt supply chain management software as a staple in modernized business management. Doing so is one of the best ways for businesses to remain competitive in the globalized market.
Here are five compelling reasons why you should consider investing in crypto trading bot development.

24/7 Trading Capabilities

A key benefit of crypto trading bots is their 24/7 trading capability without the requirements for breaks or sleep. Unlike human traders, bots can monitor and execute trades consistently, seizing market opportunities even during off-hours.

Emotion-Free Trading

Trading bots eliminate emotional bias by operating on predefined algorithms, preventing impulsive decisions driven by greed or fear.

Swift Execution in Volatile Markets

In cryptocurrency's fast-changing landscape, trading bots ensure rapid order execution, preventing missed opportunities and reducing latency.

Efficient Portfolio Diversification

Trading bots handle multiple assets and strategies simultaneously, simplifying portfolio management and spreading risk for potential profits in diverse market conditions.

Backtesting for Strategy Optimization

Crypto trading bots offer backtesting, allowing you to refine strategies on historical data, ensuring optimal performance and risk management before live deployment.
Crypto Trading Bots have the potential for substantial profits, But can crypto trading bots turn you and make you into a crypto millionaire? These trading bots automate trading, execute 24/7 eradicate emotional biases, and analyze data effectively, rendering risk management and diversification. While they provide benefits, attaining crypto millionaire status relies on market understanding, a solid technique, and adaptation. For those who are interested in developing a custom crypto trading bot, a professional Crypto trading bot development company are available to assist in pursuing trading ambitions.
According to MarketsandMarkets, the globalized crypto market size is expected to reach $1.40 Billion by 2024, at a CAGR of 6.18% during the forecast period. The crypto trading bot market is highly competitive, with many providers that render a wide range of trading bots to choose from. Some of the most popularized trading bot platforms such as Cryptohopper, HaasOnline, and 3commas and more.,

Before crypto trading bot development, you must decide whether you want to utilize a pre-developed trading bot platform or create a customized crypto trading bot. While pre-established trading bots are easy to use and need minimal programming skills, they provide limited customization and may not suit your particular crypto bot trading strategies. On the other hand, custom crypto trading bots provide more flexibility and can be tailored to your unique trading necessities. 
Entrepreneurs should examine using a white label cryptocurrency exchange software if they want to embark their own crypto exchange platform instantly, and cost-effectively. It permits them to concentrates on their core business process, such as user acquisition, marketing and customer support, without having to worry about the technical difficulties of executing a crypto exchange.

One of the company that develops white label cryptocurrency exchange software - Maticz. Maticz is leading Blockchain and IT software development company that offers white label solutions. Their platform comes with highly-customizable features, such as UI/UX, payment gateway integration, security and can be deployed instantly to assure a fast time-to-market. Their team of experienced developers and advisors can aid entrepreneurs launch successful exchange platforms and achieve their business goals.
/**
 * Add activity ratio.
 */
function add_activity_ratio() {
  /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $schema_repository */
  $schema_repository = \Drupal::service('entity.last_installed_schema.repository');
  /** @var Drupal\Core\Entity\EntityTypeBundleInfo $entity_bundle_info */
  $entity_bundle_info = \Drupal::service('entity_type.bundle.info');
  /** @var Drupal\Core\Entity\EntityFieldManager $entity_field_manager */
  $entity_field_manager = \Drupal::service('entity_field.manager');
  $definitions = \Drupal::entityTypeManager()->getDefinitions();
  $entities = [];
  foreach ($definitions as $entity_type_id => $definition) {
    if (!$definition instanceof ConfigEntityType) {
      $entities[] = $entity_type_id;
    }
  }
  $spec = [
    'type' => 'numeric',
    'unsigned' => TRUE,
    'precision' => 5,
    'scale' => 2,
  ];
  $database = \Drupal::database();
  $schema = $database->schema();
  foreach ($entities as $entity_type_id) {
    $bundles = $entity_bundle_info->getBundleInfo($entity_type_id);
    foreach ($bundles as $bundle_id => $bundle) {
      $field_definitions = $entity_field_manager->getFieldDefinitions($entity_type_id, $bundle_id);
      foreach ($field_definitions as $field_definition) {
        if ($field_definition->getType() === 'pos_nace_field_type') {
          /** @var Drupal\field\Entity\FieldStorageConfig $storage */
          $storage = $field_definition->getFieldStorageDefinition();
          $storage->setSetting('activity_ratio', FALSE);
          $key_value = \Drupal::keyValue('entity.storage_schema.sql');
          $key_name = $entity_type_id . '.field_schema_data.' . $field_definition->getName();
          $storage_schema = $key_value->get($key_name);
          if ($storage instanceof FieldStorageConfig || $storage->isMultiple()) {
            foreach ([
              $entity_type_id . '__' . $field_definition->getName(),
              $entity_type_id . '_revision' . '__' . $field_definition->getName(),
            ] as $table) {
              $field_name = $field_definition->getName();
              if ($schema->tableExists($table) && !$schema->fieldExists($table, $field_name . '_activity_ratio')) {
                $schema->addField($table, $field_name . '_activity_ratio', $spec);
              }
            }
          }
          else {
            $table = $entity_type_id . '_field_data';
            $field_name = $field_definition->getName();
            if ($schema->tableExists($table) && !$schema->fieldExists($table, $field_name . '__activity_ratio')) {
              $schema->changeField($table, $field_name, $field_name . '__value', [
                'type' => 'varchar',
                'length' => 255,
                'binary' => FALSE,
              ]);
              $schema->addField($table, $field_name . '__activity_ratio', $spec);
              $storage_schema[$table]['fields'][$field_name . '__value'] = [
                'type' => 'varchar',
                'length' => 255,
                'binary' => FALSE,
              ];
              if (isset($storage_schema[$table]['fields'][$field_name])) {
                unset($storage_schema[$table]['fields'][$field_name]);
              }
              $storage_schema[$table]['fields'][$field_name . '__activity_ratio'] = $spec;
            }
          }
          $schema_repository->setLastInstalledFieldStorageDefinition($storage);
          $key_value->set($key_name, $storage_schema);
        }
      }
    }
  }
}
$database = \Drupal::database();
  $entity_type_id = 'basic';
  $fields = [
    'employees',
    'employees_to_employ',
    'average_statistical_number_of_em',
  ];

  foreach ($fields as $field_name) {
    $old_rows = NULL;
    $table = $entity_type_id . '__' . $field_name;
    $new_fields_list = [];
    $field_storage = FieldStorageConfig::loadByName($entity_type_id, $field_name);

    if (!$field_storage) {
      continue;
    }

    // Get all current data from DB.
    if ($database->schema()->tableExists($table)) {
      // The table data to restore after the update is completed.
      $old_rows = $database->select($table, 'n')
        ->fields('n')
        ->execute()
        ->fetchAll();
    }

    // Use existing field config for new field.
    foreach ($field_storage->getBundles() as $bundle => $label) {
      if ($bundle !== 'partner_data') {
        continue;
      }
      $field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name);
      $new_field = $field->toArray();
      $new_field['field_type'] = 'decimal';
      $new_fields_list[] = $new_field;
    }

    // Deleting field storage which will also delete bundles(fields).
    $new_field_storage = $field_storage->toArray();
    $new_field_storage['type'] = 'decimal';

    $field_storage->delete();

    // Purge field data now to allow new field and field_storage with same name
    // to be created.
    field_purge_batch(40);

    // Create new field storage.
    $new_field_storage = FieldStorageConfig::create($new_field_storage);
    $new_field_storage->save();

    // Create new fields.
    foreach ($new_fields_list as $new_field) {
      $new_field_config = FieldConfig::create($new_field);
      $new_field_config->save();
    }

    // Restore existing data in new table.
    if ($old_rows) {
      foreach ($old_rows as $row) {
        $database->insert($table)
          ->fields((array) $row)
          ->execute();
      }
    }
  }
$pr = \Drupal::entityTypeManager()->getStorage('cfp_payment_request')->load(4221);
$pr->setAmountDedicatedToAp(NULL);
$pr->set('date_of_completion', NULL);
$pr->setState('under_approval');
$pr->save();


$transition_history = $pr->get('transition_history')->getValue();
array_splice($transition_history, -2);
$pr->set('transition_history', $transition_history);
$pr->set('items_approvement_closed', FALSE);
$pr->save();
/** BASE FIELD */  

  $entity_type_id = 'applicant';
  $field_name = 'main_company_address';
  $schema = \Drupal::database()->schema();
  $schema_repository = \Drupal::service('entity.last_installed_schema.repository');
  $key_value = \Drupal::keyValue('entity.storage_schema.sql');
  $key_name = $entity_type_id . '.field_schema_data.' . $field_name;
  $storage_schema = $key_value->get($key_name);
  $storage_schema['applicant']['fields']['main_company_address__cadastral_municipality'] = [
    'type' => 'varchar',
    'length' => 255,
    'not null' => FALSE,
  ];
  $key_value->set($key_name, $storage_schema);
  $schema->addField($entity_type_id, 'main_company_address__cadastral_municipality', $storage_schema['applicant']['fields']['main_company_address__cadastral_municipality']);
  $entity_field_manager = \Drupal::service('entity_field.manager');
  $field_definitions = $entity_field_manager->getBaseFieldDefinitions($entity_type_id);
  $storage = $field_definitions[$field_name]->getFieldStorageDefinition();
  $schema_repository->setLastInstalledFieldStorageDefinition($storage);


/** CONFIG FIELD */


  $entity_type_id = 'basic';
  $field_name = 'cf_settlement';
  $schema = \Drupal::database()->schema();
  $key_value = \Drupal::keyValue('entity.storage_schema.sql');
  $key_name = $entity_type_id . '.field_schema_data.' . $field_name;
  $storage_schema = $key_value->get($key_name);
  $storage_schema['basic__cf_settlement']['fields']['cf_settlement_cadastral_municipality'] = [
    'type' => 'varchar',
    'length' => 255,
    'not null' => FALSE,
  ];
  $key_value->set($key_name, $storage_schema);
  $schema->addField('basic__cf_settlement', 'cf_settlement_cadastral_municipality', $storage_schema['basic__cf_settlement']['fields']['cf_settlement_cadastral_municipality']);
/**
 * 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'