commerce-8.x-2.8/modules/order/src/PriceSplitter.php
modules/order/src/PriceSplitter.php
<?php namespace Drupal\commerce_order; use Drupal\commerce_order\Entity\OrderInterface; use Drupal\commerce_price\Calculator; use Drupal\commerce_price\Price; use Drupal\commerce_price\RounderInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; class PriceSplitter implements PriceSplitterInterface { /** * The currency storage. * * @var \Drupal\Core\Entity\EntityStorageInterface */ protected $currencyStorage; /** * The rounder. * * @var \Drupal\commerce_price\RounderInterface */ protected $rounder; /** * Constructs a new PriceSplitter object. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. * @param \Drupal\commerce_price\RounderInterface $rounder * The rounder. */ public function __construct(EntityTypeManagerInterface $entity_type_manager, RounderInterface $rounder) { $this->currencyStorage = $entity_type_manager->getStorage('commerce_currency'); $this->rounder = $rounder; } /** * {@inheritdoc} */ public function split(OrderInterface $order, Price $amount, $percentage = NULL) { if (!$percentage) { // The percentage is intentionally not rounded, for maximum precision. $percentage = Calculator::divide($amount->getNumber(), $order->getSubtotalPrice()->getNumber()); } // Calculate the initial per-order-item amounts using the percentage. // Round down to ensure that their sum isn't larger than the full amount. $amounts = []; foreach ($order->getItems() as $order_item) { if (!$order_item->getTotalPrice()->isZero()) { $individual_amount = $order_item->getTotalPrice()->multiply($percentage); $individual_amount = $this->rounder->round($individual_amount, PHP_ROUND_HALF_DOWN); $amounts[$order_item->id()] = $individual_amount; $amount = $amount->subtract($individual_amount); } } // The individual amounts don't add up to the full amount, distribute // the reminder among them. if (!$amount->isZero()) { /** @var \Drupal\commerce_price\Entity\CurrencyInterface $currency */ $currency = $this->currencyStorage->load($amount->getCurrencyCode()); $precision = $currency->getFractionDigits(); // Use the smallest rounded currency amount (e.g. '0.01' for USD). $smallest_number = Calculator::divide('1', pow(10, $precision), $precision); $smallest_amount = new Price($smallest_number, $amount->getCurrencyCode()); while (!$amount->isZero()) { foreach ($amounts as $order_item_id => $individual_amount) { $amounts[$order_item_id] = $individual_amount->add($smallest_amount); $amount = $amount->subtract($smallest_amount); if ($amount->isZero()) { break 2; } } } } return $amounts; } }