{
return $this->hasOne(Products1c::class, ['id' => 'product_guid']);
}
+
+ public function getProfitability(): float
+ {
+ // Получаем среднюю себестоимость за последние 2 недели
+ $averageCost = SelfCostProduct::find()
+ ->where(['product_guid' => $this->product_guid])
+ ->andWhere(['>=', 'date', date('Y-m-d', strtotime('-14 days'))])
+ ->average('price');
+
+ if ($averageCost === null) {
+ return 0;
+ }
+
+ // Получаем актуальные розничные цены (РЦ) за последние 2 недели
+ $retailPrices = PricesDynamic::find()
+ ->where(['product_id' => $this->product_guid])
+ ->andWhere(['active' => 1]) // Только активные цены
+ ->andWhere(['<=', 'date_from', date('Y-m-d')]) // Цена начала действия
+ ->andWhere(['>', 'date_to', date('Y-m-d')]) // Цена еще актуальна
+ ->orderBy('date_from ASC')
+ ->asArray()
+ ->all();
+
+ if (empty($retailPrices)) {
+ return 0;
+ }
+
+ $profitMargins = [];
+
+ foreach ($retailPrices as $price) {
+ if ($price['price'] > 0) {
+ $grossProfit = $price['price'] - $averageCost;
+ $profitability = ($grossProfit / $price['price']) * 100;
+ $profitMargins[] = $profitability;
+ }
+ }
+
+ if (empty($profitMargins)) {
+ return 0;
+ }
+
+ // Сортируем массив рентабельности для вычисления медианы
+ sort($profitMargins);
+ $count = count($profitMargins);
+
+ // Вычисляем медиану рентабельности
+ $medianProfitability = ($count % 2) ?
+ $profitMargins[floor($count / 2)] :
+ (($profitMargins[$count / 2 - 1] + $profitMargins[$count / 2]) / 2);
+
+ return round($medianProfitability, 2);
+ }
+
+ public function getBuildPercentage(): float
+ {
+ $totalAssemblies = BouquetCompositionProducts::find()->count();
+ $assembliesWithProduct = BouquetCompositionProducts::find()
+ ->where(['product_guid' => $this->product_guid])
+ ->count();
+
+ return $totalAssemblies ? round(($assembliesWithProduct / $totalAssemblies) * 100, 2) : 0;
+ }
+
+ public function getAverageNumberOfPieces(): float
+ {
+ $totalCount = BouquetCompositionProducts::find()
+ ->where(['product_guid' => $this->product_guid])
+ ->sum('count');
+
+ $assembliesWithProduct = BouquetCompositionProducts::find()
+ ->where(['product_guid' => $this->product_guid])
+ ->count();
+
+ return $assembliesWithProduct ? round($totalCount / $assembliesWithProduct, 2) : 0;
+ }
+
+ public function getWriteOffPercentage(): float
+ {
+ // Получаем GUID продуктов из состава букетов
+ $productGuids = BouquetCompositionProducts::find()
+ ->select('product_guid')
+ ->where(['product_guid' => $this->product_guid])
+ ->column();
+
+ if (empty($productGuids)) {
+ return 0;
+ }
+
+ // Находим ID номенклатуры по полученным GUID
+ $productIds = Products1cNomenclature::find()
+ ->select('id')
+ ->where(['id' => $productGuids]) // Исправлено: в номенклатуре ID = product_guid из букетов
+ ->column();
+
+ if (empty($productIds)) {
+ return 0;
+ }
+
+ // Считаем сумму списаний за последний месяц по найденным товарам
+ $writeOffs = WriteOffs::find()
+ ->where(['>=', 'date', date('Y-m-01', strtotime('-1 month'))])
+ ->andWhere(new Expression("
+ EXISTS (
+ SELECT 1 FROM jsonb_array_elements(items::jsonb) item
+ WHERE item->>'product_id' IN (:productIds)
+ )
+ ", [':productIds' => implode("','", $productIds)]))
+ ->sum('summ');
+
+
+ $sales = SalesProducts::find()
+ ->alias('sp')
+ ->innerJoin('sales s', 'CAST(s.id AS VARCHAR) = sp.check_id') // Приведение типов в другую сторону
+ ->where(['>=', 's.date', date('Y-m-01', strtotime('-1 month'))]) // Фильтр по дате
+ ->andWhere(['sp.product_id' => $productIds]) // Фильтр по продукту
+ ->sum('sp.summ');
+
+ return $sales ? round(($writeOffs / $sales) * 100, 2) : 0;
+ }
+
+
}
\ No newline at end of file
<div class="col-md-4 text-center font-weight-bold">Название</div>
<div class="col-md-2 text-center font-weight-bold">Кол-во</div>
<div class="col-md-1 text-center font-weight-bold">% списания</div>
- <div class="col-md-2 text-center font-weight-bold">мрж-ть</div>
+ <div class="col-md-2 text-center font-weight-bold">рен-ть</div>
<div class="col-md-1 text-center font-weight-bold">% в сборке</div>
<div class="col-md-2 text-center font-weight-bold">ср.шт. в сборке</div>
</div>
use yii\helpers\Html;
use yii\helpers\Url;
use yii_app\records\BouquetComposition;
+ use yii_app\records\BouquetCompositionProducts;
foreach ($bouquetCompositionProducts as $product) { ?>
<div class="d-flex border-bottom ms-1 py-2" style="gap: 0;">
- <div class="text-center" style="width: 33.33%;"><?= $product->product->name ?></div>
- <div class="text-center" style="width: 16.66%;"><?= $product->count ?></div>
- <div class="text-center" style="width: 8.33%;">10%</div>
- <div class="text-center" style="width: 16.66%;">30%</div>
- <div class="text-center" style="width: 8.33%;">10%</div>
- <div class="text-center" style="width: 16.66%;">3.2%</div>
+ <div class="text-center" style="width: 33.33%;"><?= Html::encode($product->product->name) ?></div>
+ <div class="text-center" style="width: 16.66%;"><?= Html::encode($product->count) ?></div>
+ <div class="text-center" style="width: 8.33%;"><?= Html::encode($product->getWriteOffPercentage()) ?></div>
+ <div class="text-center" style="width: 16.66%;"><?= Html::encode($product->getProfitability())?> </div>
+ <div class="text-center" style="width: 16.66%;"><?= Html::encode($product->getBuildPercentage()) ?>%</div>
+ <div class="text-center" style="width: 8.33%;"><?= Html::encode($product->getAverageNumberOfPieces()) ?></div>
</div>
<?php } ?>
</div>