22 private const OPTION_NAME =
'cost_price_calculation_method';
24 private static string $method;
25 private static bool $isUsedInventoryManagement;
37 $this->batchManager = $batchManager;
39 $this->isCurrencyModuleIncluded = Loader::includeModule(
'currency');
49 self::$method ??= Option::get(
'catalog', self::OPTION_NAME);
61 public static function setMethod(
string $method): void
65 Option::set(
'catalog', self::OPTION_NAME, self::$method);
68 private static function isUsedInventoryManagement(): bool
70 static::$isUsedInventoryManagement ??= State::isUsedInventoryManagement();
72 return static::$isUsedInventoryManagement;
75 private function getCatalogPurchasingFields(): ?array
77 if ($this->productFields ===
null)
80 'filter' => [
'=ID' => $this->batchManager->getProductId()],
81 'select' => [
'PURCHASING_PRICE',
'PURCHASING_CURRENCY'],
82 'cache' => [
'ttl' => 3600]
90 private function getCatalogPurchasingPrice(): float
92 return (
float)($this->getCatalogPurchasingFields()[
'PURCHASING_PRICE'] ?? 0);
95 private function getCatalogPurchasingCurrency(): string
97 return (
string)($this->getCatalogPurchasingFields()[
'PURCHASING_CURRENCY'] ??
'');
109 public function calculate(
float $quantity,
int $storeId,
string $currency =
null): float
111 if ($quantity <= 0 || !self::isUsedInventoryManagement())
113 $catalogPrice = $this->getCatalogPurchasingPrice();
117 || !$this->isCurrencyModuleIncluded
118 || $currency === $this->getCatalogPurchasingCurrency()
121 return $catalogPrice;
124 return \CCurrencyRates::convertCurrency($catalogPrice, $this->getCatalogPurchasingCurrency(), $currency);
127 if (self::getMethod() === self::METHOD_FIFO)
129 return $this->calculateFifo($quantity, $storeId, $currency);
132 return $this->calculateAverage($storeId, $currency);
135 private function calculateFifo(
float $quantity,
int $storeId,
string $currency =
null): float
139 foreach ($this->batchManager->getAvailableStoreCollection($storeId) as $item)
141 $itemAvailableAmount = $item->getAvailableAmount();
142 $itemPurchasingPrice = $item->getPurchasingPrice();
143 if ($this->isCurrencyModuleIncluded && $currency && $item->getPurchasingCurrency() !== $currency)
145 $itemPurchasingPrice = \CCurrencyRates::convertCurrency(
146 $itemPurchasingPrice,
147 $item->getPurchasingCurrency(),
152 if ($itemAvailableAmount >= $quantity)
154 $commonAmount += $quantity;
155 $commonSum += ($itemPurchasingPrice * $quantity);
160 $quantity -= $itemAvailableAmount;
161 $commonAmount += $itemAvailableAmount;
162 $commonSum += ($itemPurchasingPrice * $itemAvailableAmount);
165 if ($commonAmount === 0)
167 return $this->getCatalogPurchasingPrice();
170 return $this->roundCalculation($commonSum / $commonAmount);
173 private function calculateAverage(
int $storeId,
string $currency =
null): float
175 $batchCollection = $this->batchManager->getAvailableStoreCollection($storeId);
176 $batch = $batchCollection->current();
180 $itemPurchasingPrice = $this->getCatalogPurchasingPrice();
181 $itemPurchasingCurrency = $this->getCatalogPurchasingCurrency();
185 $itemPurchasingPrice = $batch->getPurchasingPrice();
186 $itemPurchasingCurrency = $batch->getPurchasingCurrency();
190 $itemPurchasingPrice > 0
191 && $this->isCurrencyModuleIncluded
193 && $itemPurchasingCurrency !== $currency
196 $itemPurchasingPrice = \CCurrencyRates::convertCurrency(
197 $itemPurchasingPrice,
198 $itemPurchasingCurrency,
203 return $this->roundCalculation($itemPurchasingPrice);
206 private function roundCalculation(
float $value): float
208 return round($value, $this->getRoundPrecision());
211 private function getRoundPrecision(): int
213 return (
int)Option::get(
'sale',
'value_precision', 2);
static getRow(array $parameters)