auctions-1.0.x-dev/modules/auctions_core/src/Service/AuctionTools.php

modules/auctions_core/src/Service/AuctionTools.php
<?php

namespace Drupal\auctions_core\Service;

use Drupal\auctions_core\AuctionToolsTrait;
use Drupal\auctions_core\Entity\AuctionAutobidInterface;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageDefault;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationManager;
use Symfony\Component\Mime\Header\UnstructuredHeader;

/**
 * An informational helper.
 *
 * @ingroup auctions_core
 */
class AuctionTools {

  use StringTranslationTrait;
  use AuctionToolsTrait;

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

  /**
   * Core module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * Core module handler.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  public $configFactory;

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

  /**
   * The language default.
   *
   * @var \Drupal\Core\Language\LanguageDefault
   */
  protected $languageDefault;

  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  public $languageManager;

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

  /**
   * The renderer service.
   *
   * This is not injected because that would result in a circular dependency.
   * Instead, the renderer should pass itself to the ThemeManager when it is
   * constructed, using the setRenderer() method.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  public $renderer;

  /**
   * The UUID service.
   *
   * @var \Drupal\Component\Uuid\UuidInterface
   */
  public $uuidService;

  /**
   * AuctionTools constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The core module handler.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The configuration factory.
   * @param \Drupal\Core\Session\AccountProxyInterface $currentUser
   *   The current user account proxy.
   * @param \Drupal\Core\Language\LanguageDefault $language_default
   *   The default language manager.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Drupal\Core\Database\Connection $database
   *   The database connection.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer.
   * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
   *   The UUID service.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $configFactory, AccountProxyInterface $currentUser, LanguageDefault $language_default, LanguageManagerInterface $language_manager, Connection $database, RendererInterface $renderer, UuidInterface $uuid_service) {
    $this->entityTypeManager = $entityTypeManager;
    $this->moduleHandler = $module_handler;
    $this->configFactory = $configFactory;
    $this->currentUser = $currentUser;
    $this->languageDefault = $language_default;
    $this->languageManager = $language_manager;
    $this->database = $database;
    $this->renderer = $renderer;
    $this->uuidService = $uuid_service;
  }

  /**
   * Wapper for service lookup.
   */
  public function hasModule($moduleName = 'commerce_price') {
    return $this->moduleHandler->moduleExists($moduleName);
  }

  /**
   * {@inheritdoc}
   */
  public function getCommerceCurrencies() {
    $active_currencies = [];
    if ($this->hasModule('commerce_price')) {
      /** @var \Drupal\commerce_price\Entity\CurrencyInterface[] $currencies */
      $currencies = $this->entityTypeManager->getStorage('commerce_currency')->loadMultiple();

      foreach ($currencies as $currency) {
        if ($currency->status()) {
          $active_currencies[$currency->getCurrencyCode()] = $currency->getName();
        }
      }
    }
    return $active_currencies;
  }

  /**
   * Get active currencies as per the settings.
   *
   * @return array
   *   An array of active currency options.
   */
  public function getActiveItemCurrencies() {
    $currencyList = self::currencyList();
    // Note: activeCurrenciesConfig is a workaround for installation, as
    // config may not be installed before the entity is initialized.
    $activeCurrenciesConfig = $this->configFactory->get('auctions.item_settings')->get('active-currency');
    $activeCurrencies = $activeCurrenciesConfig ? $activeCurrenciesConfig : ['USD' => 'United States Dollar'];
    $activeCurrency = array_flip($activeCurrencies);
    unset($activeCurrency[0]);
    $options = [];
    foreach ($activeCurrency as $currency) {
      $options[$currency] = $currencyList[$currency];
    }
    return $options;
  }

  /**
   * Masks user name by leaving only the first and last letter.
   *
   * @return string
   *   The masked user name.
   */
  public function activeCurrencyList() {
    $hasCommercePrice = self::hasModule('commerce_price');
    $currencyCodes = self::getActiveItemCurrencies();
    if ($hasCommercePrice) {
      $currencyCodes = self::getCommerceCurrencies();
    }
    return $currencyCodes;
  }

  /**
   * Setup to send mail.
   *
   * @return bool
   *   TRUE if the mail was sent successfully, otherwise FALSE.
   */
  public function sendMail($to, $from, $reply, $subject, array $body, array $params = []) {

    if (empty($to)) {
      return FALSE;
    }

    $default_params = [
      'headers' => [
        'Content-Type' => 'text/html; charset=iso-8859-1',
        'MIME-Version' => '1.0',
        'To' => $to,
        'From' => $from,
        'Reply-To' => $reply,
        'X-Mailer' => 'Drupal Auction',
      ],
      'to' => $to,
      'subject' => $subject,
      'langcode' => $this->languageManager->getCurrentLanguage()->getId(),
      'body' => $body,
    ];
    if (!empty($params['cc'])) {
      $default_params['headers']['Cc'] = $params['cc'];
    }
    if (!empty($params['bcc'])) {
      $default_params['headers']['Bcc'] = $params['bcc'];
    }
    $params = \array_replace($default_params, $params);

    // Change the active language to ensure the email is properly translated.
    if ($params['langcode'] != $default_params['langcode']) {
      $this->changeActiveLanguage($params['langcode']);
    }

    $message = $this->mail($params);
    // Revert back to the original active language.
    if ($params['langcode'] != $default_params['langcode']) {
      $this->changeActiveLanguage($default_params['langcode']);
    }

    return (bool) $message;
  }

  /**
   * Sends an email message. this fn is 'forked' from  phpMail to allow html.
   *
   * @param array $message
   *   A message array, as described in hook_mail_alter().
   *
   * @return bool
   *   TRUE if the mail was successfully accepted, otherwise FALSE.
   */
  protected function mail(array $message) {
    $mail_result = FALSE;
    $request = \Drupal::request();
    $line_endings = Settings::get('mail_line_endings', PHP_EOL);

    // If 'Return-Path' isn't already set in php.ini, we pass it separately
    // as an additional parameter instead of in the header.
    if (isset($message['headers']['Return-Path'])) {
      $return_path_set = \strpos(\ini_get('sendmail_path'), ' -f');
      if (!$return_path_set) {
        $message['Return-Path'] = $message['headers']['Return-Path'];
        unset($message['headers']['Return-Path']);
      }
    }

    $mimeheaders = [];
    foreach ($message['headers'] as $name => $value) {
      $mimeheaders[] = (new UnstructuredHeader('name', $value))->getBodyAsString();
    }
    $mail_headers = \implode("\n", $mimeheaders);

    // Prepare mail commands.
    $mail_subject = (new UnstructuredHeader('subject', $message['subject']))->getBodyAsString();

    // Reminder: email uses CRLF for line-endings. PHP's API requires LF
    // on Unix and CRLF on Windows. Drupal automatically guesses the
    // line-ending format appropriate for your system. If you need to
    // override this, adjust $settings['mail_line_endings'] in settings.php.
    $render_body = '<html><body>' . $this->renderer->render($message['body']) . '</body></html>';
    $mail_body = \preg_replace('@\r?\n@', $line_endings, $render_body);
    // For headers, PHP's API suggests that we use CRLF normally,
    // but some MTAs incorrectly replace LF with CRLF. See #234403.
    // We suppress warnings and notices from mail() because of issues on some
    // hosts. The return value of this method will still indicate whether mail
    // was sent successfully.
    if (!$request->server->has('WINDIR') && \strpos($request->server->get('SERVER_SOFTWARE'), 'Win32') === FALSE) {
      // On most non-Windows systems, the "-f" option to the sendmail command
      // is used to set the Return-Path. There is no space between -f and
      // the value of the return path.
      // We validate the return path, unless it is equal to the site mail, which
      // we assume to be safe.
      $site_mail = $this->configFactory->get('system.site')->get('mail');
      $additional_headers = isset($message['Return-Path']) && ($site_mail === $message['Return-Path'] || static::isShellSafe($message['Return-Path'])) ? '-f' . $message['Return-Path'] : '';
      $mail_result = @mail(
        $message['to'],
        $mail_subject,
        $mail_body,
        $mail_headers,
        $additional_headers
      );
    }
    else {
      // On Windows, PHP will use the value of sendmail_from for the
      // Return-Path header.
      $old_from = \ini_get('sendmail_from');
      $returnPath = $message['Return-Path'] ?? '';
      ini_set('sendmail_from', $returnPath);
      $mail_result = @mail(
        $message['to'],
        $mail_subject,
        $mail_body,
        $mail_headers
      );
      \ini_set('sendmail_from', $old_from);
    }

    return $mail_result;
  }

  /**
   * Disallows potentially unsafe shell characters.
   *
   * Functionally similar to PHPMailer::isShellSafe() which resulted from
   * CVE-2016-10045. Note that escapeshellarg and escapeshellcmd are inadequate
   * for this purpose.
   *
   * @param string $string
   *   The string to be validated.
   *
   * @return bool
   *   True if the string is shell-safe.
   *
   * @see https://github.com/PHPMailer/PHPMailer/issues/924
   * @see https://github.com/PHPMailer/PHPMailer/blob/v5.2.21/class.phpmailer.php#L1430
   *
   * @todo Rename to ::isShellSafe() and/or discuss whether this is the correct
   *   location for this helper.
   */
  protected static function isShellSafe($string) {
    if (\escapeshellcmd($string) !== $string || !\in_array(\escapeshellarg($string), [
      "'{$string}'",
      "\"{$string}\"",
    ])) {
      return FALSE;
    }
    if (\preg_match('/[^a-zA-Z0-9@_\\-.]/', $string) !== 0) {
      return FALSE;
    }
    return TRUE;
  }

  /**
   * Changes the active language for translations.
   *
   * @param string $langcode
   *   The langcode.
   */
  private function changeActiveLanguage($langcode) {
    if (!$this->languageManager->isMultilingual()) {
      return;
    }
    $language = $this->languageManager->getLanguage($langcode);
    if (!$language) {
      return;
    }
    // The language manager has no method for overriding the default
    // language, like it does for config overrides. We have to change the
    // default language service's current language.
    // @see https://www.drupal.org/project/drupal/issues/3029010
    $this->languageDefault->set($language);
    $this->languageManager->setConfigOverrideLanguage($language);
    $this->languageManager->reset();

    // The default string_translation service, TranslationManager, has a
    // setDefaultLangcode method. However, this method is not present on
    // either of its interfaces. Therefore we check for the concrete class
    // here so that any swapped service does not break the application.
    // @see https://www.drupal.org/project/drupal/issues/3029003
    $string_translation = $this->getStringTranslation();
    if ($string_translation instanceof TranslationManager) {
      $string_translation->setDefaultLangcode($language->getId());
      $string_translation->reset();
    }
  }

  /**
   * Return a general list of recommended Bid Step increments.
   *
   * @param int $auctionItemID
   *   The ID of the auction item.
   * @param bool $relistCount
   *   (Optional) The relist count to filter by.
   *
   * @return array
   *   An array of unique user IDs.
   *
   *    # relist group: Allows to GET ALL, ever...
   */
  public function uniqueUserList($auctionItemID, $relistCount = FALSE) {
    $auctionUsers = $this->database->select('auction_bid', 'bids');
    $auctionUsers->fields('bids', ['user_id']);
    $auctionUsers->distinct();
    $auctionUsers->condition('bids.item', $auctionItemID, '=');
    if ($relistCount) {
      $auctionUsers->condition('bids.relist_group', $relistCount, '=');
    }
    $result = $auctionUsers->execute()->fetchCol();

    return $result;
  }

  /**
   * Return a general list of users who have autobid max opt-ins.
   *
   * @param int $auctionItemID
   *   The ID of the auction item.
   * @param int $amountMax
   *   The minimum amount max for opt-ins.
   * @param int $uidExclude
   *   The user ID to exclude.
   * @param bool $relistCount
   *   (Optional) The relist count to filter by.
   *
   * @return array|false
   *   An array of users who have autobid max opt-ins if found,FALSE if not .
   *
   * @relist group:
   *   Allows to GET ALL, ever...
   */
  public function autobidsGroupedByUser($auctionItemID, $amountMax, $uidExclude, $relistCount = FALSE) {
    $conf = $this->configFactory->get('auctions.item_settings');
    $autobidsOrdering = $conf->get('autobids-ordering');
    $autobidsDirection = $conf->get('autobids-direction');

    $auctionUsers = $this->database->select('auction_autobid', 'auto');
    $auctionUsers->fields('auto', ['id', 'item', 'amount_max', 'user_id']);
    $auctionUsers->condition('auto.item', $auctionItemID, '=');
    $auctionUsers->condition('auto.status', 1, '=');
    $auctionUsers->condition('auto.user_id', $uidExclude, '!=');  /* exclude self */
    $auctionUsers->condition('auto.amount_max', $amountMax, '>=');
    if ($relistCount) {
      $auctionUsers->condition('auto.relist_group', $relistCount, '=');
    }
    // Reminder: Ordered by who opted in first.
    $auctionUsers->orderBy($autobidsOrdering, $autobidsDirection);
    $result = $auctionUsers->execute()->fetchAll(\PDO::FETCH_ASSOC);
    $groupby = $result ? $this->groupResultBy('user_id', $result) : FALSE;
    return $groupby;
  }

  /**
   * Return a general list of bids grouped by user.
   *
   * @param int $auctionItemID
   *   The ID of the auction item.
   * @param bool $relistCount
   *   (Optional) The relist count to filter by.
   *
   * @return array|false
   *   An array of bids grouped by user if found, or FALSE if not found.
   *
   * @relist group:
   *   Allows to GET ALL, ever...
   */
  public function bidsGroupedByUser($auctionItemID, $relistCount = FALSE) {
    $auctionUsers = $this->database->select('auction_bid', 'bids');
    $auctionUsers->fields('bids', [
      'id',
      'item',
      'amount',
      'relist_group',
      'user_id',
      'type',
    ]);
    $auctionUsers->condition('bids.item', $auctionItemID, '=');
    if ($relistCount) {
      $auctionUsers->condition('bids.relist_group', $relistCount, '=');
    }
    $auctionUsers->orderBy('id', 'DESC');
    $result = $auctionUsers->execute()->fetchAll(\PDO::FETCH_ASSOC);

    $groupby = $result ? $this->groupResultBy('user_id', $result) : FALSE;
    return $groupby;
  }

  /**
   * Internal:  grouping for bidsGroupedByUser(),.
   */
  private function groupResultBy($key, $data) {
    $result = [];
    foreach ($data as $val) {
      if (\array_key_exists($key, $val)) {
        $result[$val[$key]][] = $val;
      }
    }
    return $result;
  }

  /**
   * Return Auction Item Winner.
   *
   * @param $auctionItemID
   *   Auction Item ID.
   * @param $relistCount
   *   Relist count.
   */
  public function getAuctionItemWinner($auctionItemID, $relistCount = FALSE) {
    $query = $this->database->select('auction_bid', 'bids');
    $query->fields('bids', ['id', 'item', 'amount', 'relist_group', 'user_id', 'type']);
    $query->condition('bids.item', $auctionItemID, '=');

    if ($relistCount) {
      $query->condition('bids.relist_group', $relistCount, '=');
    }

    $query->orderBy('amount', 'DESC');
    $users = $query->execute()->fetchAllAssoc('user_id');
    $winner = reset($users);

    if (!empty($winner)) {
      return $this->entityTypeManager->getStorage('user')->load($winner->user_id);
    }

    return FALSE;
  }

  /**
   * Process Auction Bid for the Auction Item.
   */
  public function handleBid($uid, $item, $amount, $typeAuto = FALSE, $close = FALSE) {
    $values = [
      'user_id' => $uid,
      'item' => $item->getId(),
      'relist_group' => $item->getRelistCount(),
      'type' => $typeAuto ? 'auto' : 'standard',
      'purchase_offer' => 0,
      'amount' => $amount,
    ];

    // Save the Bid.
    if ($close) {
      $bid = $this->saveBid($values, TRUE);
    }
    else {
      $bid = $this->saveBid($values);

      // Tirggers Autobid if the Autobit price is higher.
      if (!$typeAuto) {
        $autobid = $this->getHighestItemAutoBid($item->getId(), $item->getRelistCount());

        if ($autobid instanceof AuctionAutobidInterface &&
            $autobid->getOwnerId() !== $uid &&
            $autobid->getAmountMax() > $amount) {

          $autoAmount = $amount + $item->getBidStep();
          $autoAmount = $autoAmount < $autobid->getAmountMax() ? $autoAmount : $autobid->getAmountMax();
          $this->handleBid($autobid->getOwnerId(), $item, $autoAmount, TRUE);
        }
      }
    }

    return $bid;
  }

  /**
   * Saves a bid for an auction item.
   *
   * This method creates and saves bid entity for specific auction item based on
   * the provided values. It also updates the corresponding item entity and sets
   * the workflow state if needed.
   *
   * @param array $value
   *   An associative array containing the bid values, including 'user_id',
   *   'item','relist_group', 'type', 'purchase_offer', and 'amount'.
   * @param bool $close
   *   (Optional) A boolean indicating whethr bid should close the auction item.
   *   Defaults to FALSE.
   *
   * @return \Drupal\Core\Entity\EntityInterface
   *   The saved bid entity.
   */
  public function saveBid($values, $close = FALSE) {
    $bid = $this->entityTypeManager->getStorage('auction_bid')->create([
      'user_id' => $values['user_id'],
      'item' => ['target_id' => $values['item']],
      'relist_group' => $values['relist_group'],
      'type' => $values['type'],
      'purchase_offer' => $values['purchase_offer'],
    ]);
    $bid->setAmount($values['amount']);
    $bid->save();

    // Corresponding entity ref.
    if ($bid->id()) {
      $item = $bid->getItemEntity();
      $item->get('bids')->appendItem([
        'target_id' => $bid->id(),
      ]);

      if ($close) {
        $item->setWorkflow(3);
      }

      $item->save();
    }

    return $bid;
  }

  /**
   * Get 'any' autobid by uid, relist - order by highest (as current)
   */
  public function seekAutobid($uid, $auctionItemId, $relistGroup = FALSE) {
    $autobids = $this->entityTypeManager->getStorage('auction_autobid');
    $seekAutobid = $autobids->getQuery();
    $seekAutobid->condition('user_id', $uid);
    $seekAutobid->condition('item', $auctionItemId);
    if ($relistGroup) {
      $seekAutobid->condition('relist_group', $relistGroup);
    }
    $seekAutobid->sort('amount_max', 'DESC');
    $seekAutobid->accessCheck();
    $autobidList = $seekAutobid->execute();
    $hasAutobid = $autobids->loadMultiple($autobidList);

    return reset($hasAutobid);
  }

  /**
   * Flush thru and remove 'any' autobid triggers that could possibly exists.
   *
   * Reminder/direction:  change to handle thru publishing state @keep history..
   */
  public function removeAutobid($uid, $auctionItemId, $relistGroup = FALSE) {
    $autobids = $this->entityTypeManager->getStorage('auction_autobid');
    $seekAutobid = $autobids->getQuery();
    $seekAutobid->condition('user_id', $uid);
    $seekAutobid->condition('item', $auctionItemId);
    if ($relistGroup) {
      $seekAutobid->condition('relist_group', $relistGroup);
    }
    $seekAutobid->accessCheck();
    $autobidList = $seekAutobid->execute();
    $removals = $autobids->loadMultiple($autobidList);
    $ids = [];
    foreach ($removals as $id => $entity) {
      $entity->delete();
      $ids[$id] = $id;
    }
    return $ids;
  }

  /**
   * Get the Highest Item Autobid.
   */
  public function getHighestItemAutoBid($auctionItemId, $relistGroup = FALSE) {
    $autobidStorage = $this->entityTypeManager->getStorage('auction_autobid');
    $autobidQuery = $autobidStorage->getQuery();
    $autobidQuery->condition('item', $auctionItemId);

    if ($relistGroup) {
      $autobidQuery->condition('relist_group', $relistGroup);
    }

    $autobidQuery->sort('amount_max', 'DESC');
    $autobidQuery->accessCheck();
    $autobidList = $autobidQuery->execute();
    $autobid = $autobidStorage->loadMultiple($autobidList);

    return reset($autobid);
  }

  /**
   * Handles the autobid functionality for a user.
   *
   * @param int $uid
   *   The user ID for whom the autobid is handled.
   * @param int $auctionItemId
   *   The ID of the auction item to which the autobid is related.
   * @param int $relistGroup
   *   The relist group associated with the autobid.
   * @param float $amountMax
   *   The maximum bid amount for the autobid.
   *
   * @return \Drupal\YourModule\Entity\Autobid|bool
   *   The autobid entity if created or updated, or FALSE if there was an error.
   */
  public function handleAutobid($uid, $auctionItemId, $relistGroup, $amountMax) {
    $autobid = $this->seekAutobid($uid, $auctionItemId, $relistGroup);
    $item = $this->entityTypeManager->getStorage('auction_item')->load($auctionItemId);
    $step = $item->getBidStep();
    $highest = $this->getHighestItemAutoBid($auctionItemId, $relistGroup);
    $currentHightest = $item->seekCurrentHightest();

    if ($autobid instanceof AuctionAutobidInterface) {
      $autobid->setAmountMax($amountMax);
      $autobid->setName($this->showAsCents($amountMax, '.', ','));
      $autobid->save();
    }
    else {
      $autobid = $this->createAutobid($uid, $auctionItemId, $relistGroup, $amountMax);
    }

    if (!$highest || $highest->getOwnerId() === $autobid->getOwnerId()) {
      return $autobid;
    }

    // If here are more than one active autobids, the price is raised to the
    // user placed autobid if this is lower.
    if ($autobid->getAmountMax() < $highest->getAmountMax()) {
      $currBid = $autobid->getAmountMax();
      $prevBid = $highest->getAmountMax();
      if (($currBid + $step) < $prevBid) {
        $currBidAmount = $currBid;
        $prevBidAmount = $currBid + $step;
        $currUid = $uid;
        $prevUid = $highest->getOwnerId();
      }
      else {
        $currBidAmount = $prevBid;
        $prevBidAmount = $currBid;
        $currUid = $highest->getOwnerId();
        $prevUid = $uid;
      }

      // Place the Bid (type "auto"), for the AutoBid registered.
      $this->handleBid($currUid, $item, $this->roundCents($currBidAmount), TRUE);
      $this->handleBid($prevUid, $item, $this->roundCents($prevBidAmount), TRUE);
    }
    elseif ($autobid->getAmountMax() > $highest->getAmountMax()) {
      $currBid = $autobid->getAmountMax();
      $prevBid = $highest->getAmountMax();

      if (($currBid - $step) > $prevBid) {
        $currBidAmount = $prevBid + $step;
        $prevBidAmount = $prevBid;
        $currUid = $uid;
        $prevUid = $highest->getOwnerId();
      }
      else {
        $currBidAmount = $prevBid;
        $prevBidAmount = $currBid;
        $currUid = $highest->getOwnerId();
        $prevUid = $uid;
      }

      // Place a bid for the previous higher AutoBid.
      $this->handleBid($currUid, $item, $this->roundCents($currBidAmount), TRUE);
      $this->handleBid($prevUid, $item, $this->roundCents($prevBidAmount), TRUE);
    }
    else {
      // Wins the last one autobidding. NOTE: As requirement from PM.
      $this->handleBid($highest->getOwnerId(), $item, $autobid->getAmountMax() - $step, TRUE);
      $this->handleBid($uid, $item, $autobid->getAmountMax(), TRUE);
    }

    return $autobid;
  }

  /**
   * Return a general list of recommendind Bid Step increments.
   */
  private function createAutobid($uid, $auctionItemId, $relistGroup, $amountMax) {
    $auctionAutobid = $this->entityTypeManager->getStorage('auction_autobid');
    $amount = $this->roundCents($amountMax);
    $autobid = $auctionAutobid->create([
      'user_id' => $uid,
      'name' => $this->showAsCents($amount, '.', ',') /* Reminder:  always create a name. */,
      'amount_max' => $amount,
      'item' => $auctionItemId,
      'relist_group' => $relistGroup,
      'uuid' => $this->uuidService->generate(),
      'status' => 1,
      'created' => \time(),
    ]);

    $autobid->save();

    return $autobid;
  }

  /**
   * Return a  list of recommended Bid Step increments. concept, unused @fees.
   */
  public function suggestedBidIncrements() {
    return [
      '$50 — 300' => '$25',
      '$300 — 500' => '$50',
      '$500 — 2,000' => '$100',
      '$2,000 — 5,000' => '$250',
      '$5,000 — 10,000' => '$500',
      '$10,000 — 20,000' => '$1,000',
      '$20,000 — 50,000' => '$2,500',
      '$50,000 — 100,000' => '$5,000',
      '$100,000 — 300,000' => '$10,000',
      '$300,000 — 1,000,000' => '$25,000',
      '$1,000,000 — 2,000,000' => '$50,000',
      '$2,000,000 — 3,000,000' => '$100,000',
      '$3,000,000 — 5,000,000' => '$250,000',
      '$5,000,000 — 10,000,000' => '$500,000',
      '$10,000,000+' => '$1,000,000',
    ];
  }

  /**
   * A helper to build checkbox/select list options.
   */
  public function currencyOptions() {
    $currencies = self::currencyList();
    foreach ($currencies as $k => $v) {
      $currencies[$k] = $k . ' | ' . $v;
    }
    return $currencies;
  }

  /**
   * Return a list of known global currencies.
   */
  public function currencyList() {
    $currencies = [
      "AED" => $this->t("United Arab Emirates dirham"),
      "AFN" => $this->t("Afghan afghani"),
      "ALL" => $this->t("Albanian lek"),
      "AMD" => $this->t("Armenian dram"),
      "ANG" => $this->t("Netherlands Antillean guilder"),
      "AOA" => $this->t("Angolan kwanza"),
      "ARS" => $this->t("Argentine peso"),
      "AUD" => $this->t("Australian Dollar"),
      "AWG" => $this->t("Aruban florin"),
      "AZN" => $this->t("Azerbaijani manat"),
      "BAM" => $this->t("Bosnia and Herzegovina convertible mark"),
      "BBD" => $this->t("Barbados Dollar"),
      "BDT" => $this->t("Bangladeshi taka"),
      "BGN" => $this->t("Bulgarian lev"),
      "BHD" => $this->t("Bahraini dinar"),
      "BIF" => $this->t("Burundian franc"),
      "BMD" => $this->t("Bermudian Dollar"),
      "BND" => $this->t("Brunei Dollar"),
      "BOB" => $this->t("Boliviano"),
      "BRL" => $this->t("Brazilian real"),
      "BSD" => $this->t("Bahamian Dollar"),
      "BTN" => $this->t("Bhutanese ngultrum"),
      "BWP" => $this->t("Botswana pula"),
      "BYN" => $this->t("New Belarusian ruble"),
      "BYR" => $this->t("Belarusian ruble"),
      "BZD" => $this->t("Belize Dollar"),
      "CAD" => $this->t("Canadian Dollar"),
      "CDF" => $this->t("Congolese franc"),
      "CHF" => $this->t("Swiss franc"),
      "CLF" => $this->t("Unidad de Fomento"),
      "CLP" => $this->t("Chilean peso"),
      "CNY" => $this->t("Renminbi|Chinese yuan"),
      "COP" => $this->t("Colombian peso"),
      "CRC" => $this->t("Costa Rican colon"),
      "CUC" => $this->t("Cuban convertible peso"),
      "CUP" => $this->t("Cuban peso"),
      "CVE" => $this->t("Cape Verde escudo"),
      "CZK" => $this->t("Czech koruna"),
      "DJF" => $this->t("Djiboutian franc"),
      "DKK" => $this->t("Danish krone"),
      "DOP" => $this->t("Dominican peso"),
      "DZD" => $this->t("Algerian dinar"),
      "EGP" => $this->t("Egyptian pound"),
      "ERN" => $this->t("Eritrean nakfa"),
      "ETB" => $this->t("Ethiopian birr"),
      "EUR" => $this->t("Euro"),
      "FJD" => $this->t("Fiji Dollar"),
      "FKP" => $this->t("Falkland Islands pound"),
      "GBP" => $this->t("Pound sterling"),
      "GEL" => $this->t("Georgian lari"),
      "GHS" => $this->t("Ghanaian cedi"),
      "GIP" => $this->t("Gibraltar pound"),
      "GMD" => $this->t("Gambian dalasi"),
      "GNF" => $this->t("Guinean franc"),
      "GTQ" => $this->t("Guatemalan quetzal"),
      "GYD" => $this->t("Guyanese Dollar"),
      "HKD" => $this->t("Hong Kong Dollar"),
      "HNL" => $this->t("Honduran lempira"),
      "HRK" => $this->t("Croatian kuna"),
      "HTG" => $this->t("Haitian gourde"),
      "HUF" => $this->t("Hungarian forint"),
      "IDR" => $this->t("Indonesian rupiah"),
      "ILS" => $this->t("Israeli new shekel"),
      "INR" => $this->t("Indian rupee"),
      "IQD" => $this->t("Iraqi dinar"),
      "IRR" => $this->t("Iranian rial"),
      "ISK" => $this->t("Icelandic króna"),
      "JMD" => $this->t("Jamaican Dollar"),
      "JOD" => $this->t("Jordanian dinar"),
      "JPY" => $this->t("Japanese yen"),
      "KES" => $this->t("Kenyan shilling"),
      "KGS" => $this->t("Kyrgyzstani som"),
      "KHR" => $this->t("Cambodian riel"),
      "KMF" => $this->t("Comoro franc"),
      "KPW" => $this->t("North Korean won"),
      "KRW" => $this->t("South Korean won"),
      "KWD" => $this->t("Kuwaiti dinar"),
      "KYD" => $this->t("Cayman Islands Dollar"),
      "KZT" => $this->t("Kazakhstani tenge"),
      "LAK" => $this->t("Lao kip"),
      "LBP" => $this->t("Lebanese pound"),
      "LKR" => $this->t("Sri Lankan rupee"),
      "LRD" => $this->t("Liberian Dollar"),
      "LSL" => $this->t("Lesotho loti"),
      "LYD" => $this->t("Libyan dinar"),
      "MAD" => $this->t("Moroccan dirham"),
      "MDL" => $this->t("Moldovan leu"),
      "MGA" => $this->t("Malagasy ariary"),
      "MKD" => $this->t("Macedonian denar"),
      "MMK" => $this->t("Myanmar kyat"),
      "MNT" => $this->t("Mongolian tögrög"),
      "MOP" => $this->t("Macanese pataca"),
      "MRO" => $this->t("Mauritanian ouguiya"),
      "MUR" => $this->t("Mauritian rupee"),
      "MVR" => $this->t("Maldivian rufiyaa"),
      "MWK" => $this->t("Malawian kwacha"),
      "MXN" => $this->t("Mexican peso"),
      "MXV" => $this->t("Mexican Unidad de Inversion"),
      "MYR" => $this->t("Malaysian ringgit"),
      "MZN" => $this->t("Mozambican metical"),
      "NAD" => $this->t("Namibian Dollar"),
      "NGN" => $this->t("Nigerian naira"),
      "NIO" => $this->t("Nicaraguan córdoba"),
      "NOK" => $this->t("Norwegian krone"),
      "NPR" => $this->t("Nepalese rupee"),
      "NZD" => $this->t("New Zealand Dollar"),
      "OMR" => $this->t("Omani rial"),
      "PAB" => $this->t("Panamanian balboa"),
      "PEN" => $this->t("Peruvian Sol"),
      "PGK" => $this->t("Papua New Guinean kina"),
      "PHP" => $this->t("Philippine peso"),
      "PKR" => $this->t("Pakistani rupee"),
      "PLN" => $this->t("Polish złoty"),
      "PYG" => $this->t("Paraguayan guaraní"),
      "QAR" => $this->t("Qatari riyal"),
      "RON" => $this->t("Romanian leu"),
      "RSD" => $this->t("Serbian dinar"),
      "RUB" => $this->t("Russian ruble"),
      "RWF" => $this->t("Rwandan franc"),
      "SAR" => $this->t("Saudi riyal"),
      "SBD" => $this->t("Solomon Islands Dollar"),
      "SCR" => $this->t("Seychelles rupee"),
      "SDG" => $this->t("Sudanese pound"),
      "SEK" => $this->t("Swedish krona"),
      "SGD" => $this->t("Singapore Dollar"),
      "SHP" => $this->t("Saint Helena pound"),
      "SLL" => $this->t("Sierra Leonean leone"),
      "SOS" => $this->t("Somali shilling"),
      "SRD" => $this->t("Surinamese Dollar"),
      "SSP" => $this->t("South Sudanese pound"),
      "STD" => $this->t("São Tomé and Príncipe dobra"),
      "SVC" => $this->t("Salvadoran colón"),
      "SYP" => $this->t("Syrian pound"),
      "SZL" => $this->t("Swazi lilangeni"),
      "THB" => $this->t("Thai baht"),
      "TJS" => $this->t("Tajikistani somoni"),
      "TMT" => $this->t("Turkmenistani manat"),
      "TND" => $this->t("Tunisian dinar"),
      "TOP" => $this->t("Tongan paʻanga"),
      "TRY" => $this->t("Turkish lira"),
      "TTD" => $this->t("Trinidad and Tobago Dollar"),
      "TWD" => $this->t("New Taiwan Dollar"),
      "TZS" => $this->t("Tanzanian shilling"),
      "UAH" => $this->t("Ukrainian hryvnia"),
      "UGX" => $this->t("Ugandan shilling"),
      "USD" => $this->t("United States Dollar"),
      "UYI" => $this->t("Uruguay Peso en Unidades Indexadas"),
      "UYU" => $this->t("Uruguayan peso"),
      "UZS" => $this->t("Uzbekistan som"),
      "VEF" => $this->t("Venezuelan bolívar"),
      "VND" => $this->t("Vietnamese đồng"),
      "VUV" => $this->t("Vanuatu vatu"),
      "WST" => $this->t("Samoan tala"),
      "XAF" => $this->t("Central African CFA franc"),
      "XCD" => $this->t("East Caribbean Dollar"),
      "XOF" => $this->t("West African CFA franc"),
      "XPF" => $this->t("CFP franc"),
      "XXX" => $this->t("No currency"),
      "YER" => $this->t("Yemeni rial"),
      "ZAR" => $this->t("South African rand"),
      "ZMW" => $this->t("Zambian kwacha"),
      "ZWL" => $this->t("Zimbabwean Dollar"),
    ];
    return $currencies;
  }

}

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

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