$subcategoryList = [];
$speciesList = [];
$weightedResults = [];
+ $productSalesShare = [];
$model = new DynamicModel(['storeId', 'month', 'category', 'subcategory', 'species']);
$model->addRule(['storeId', 'month', 'category'], 'required');
$result['without_history']
);
+
+ $productSalesShare = StorePlanService::calculateProductSalesShare(
+ $storeId,
+ $selectedMonth,
+ $result['with_history']
+ );
+
}
}
}
return $this->render('show-history-data', [
'result' => $result,
'weighted' => $weightedResults,
+ 'productSalesShare' => $productSalesShare,
'model' => $model,
'storeList' => $storeList,
'monthsList' => $monthsList,
use yii\db\Expression;
use yii\helpers\ArrayHelper;
use yii_app\records\Motivation;
+use yii_app\records\PricesDynamic;
use yii_app\records\Products1cAdditionalCharacteristics;
use yii_app\records\Sales;
use yii_app\records\SalesProducts;
return $weightedResults;
}
+ /**
+ * Получает цену для товара для указанного месяца.
+ *
+ * @param string $productId GUID товара.
+ * @param string $year Год (например, "2025").
+ * @param string $month Месяц в формате "mm" (например, "03").
+ * @return float Цена, если найдена запись, иначе 0.
+ */
+ public static function getPriceForProductAndMonth($productId, $year, $month)
+ {
+ // Для проверки возьмем первый день месяца
+ $dateToCheck = "$year-$month-01";
+ $priceRecord = PricesDynamic::find()
+ ->where(['product_id' => $productId, 'active' => 1])
+ ->andWhere(['<=', 'date_from', $dateToCheck])
+ ->andWhere([
+ 'or',
+ ['>=', 'date_to', $dateToCheck],
+ ['date_to' => null]
+ ])
+ ->one();
+ if ($priceRecord) {
+ return (float)$priceRecord->price;
+ }
+ return 0;
+ }
+
+ /**
+ * Вычисляет для каждого товара с историей:
+ * - месячные продажи (сумма недельных продаж за месяц),
+ * - взвешенные продажи по месяцам (с учетом цены и веса),
+ * - итоговую взвешенную сумму,
+ * - долю товара от общей взвешенной суммы по всем товарам.
+ *
+ * @param int $storeId Идентификатор магазина.
+ * @param string $selectedMonth Целевой месяц в формате "mm".
+ * @param array $productsWithHistory Массив товаров с историей (из calculateHistoricalShare), каждый элемент содержит:
+ * [
+ * 'guid' => <GUID товара>,
+ * 'weekly_sales' => [ 'mm' => [week0, week1, week2, week3], ... ]
+ * ]
+ * @return array Массив, где ключ – GUID товара, а значение – массив с данными:
+ * [
+ * 'monthlySales' => [ 'mm' => суммарные продажи за месяц, ... ],
+ * 'monthlyWeighted' => [ 'mm' => (продажи * цена * вес), ... ],
+ * 'weightedSum' => итоговая взвешенная сумма,
+ * 'share' => доля товара (от 0 до 1)
+ * ]
+ */
+ public static function calculateProductSalesShare($storeId, $selectedMonth, $productsWithHistory)
+ {
+ $year = date('Y');
+
+
+ $weightedMonths = [];
+ for ($i = 1; $i <= 3; $i++) {
+ $timestamp = strtotime("-{$i} month", strtotime("$year-$selectedMonth-01"));
+ $weightedMonths[] = [
+ 'year' => date('Y', $timestamp),
+ 'month' => date('m', $timestamp),
+ 'weight' => 4 - $i,
+ ];
+ }
+
+ $productsData = [];
+ $globalTotal = 0;
+
+
+ foreach ($productsWithHistory as $product) {
+ $guid = $product['guid'];
+ $monthlySales = [];
+ $monthlyWeighted = [];
+ $weightedSum = 0;
+
+ foreach ($weightedMonths as $mInfo) {
+ $mKey = $mInfo['month'];
+ $sales = isset($product['weekly_sales'][$mKey]) ? array_sum($product['weekly_sales'][$mKey]) : 0;
+
+ $price = self::getPriceForProductAndMonth($guid, $mInfo['year'], $mInfo['month']);
+ $monthlySales[$mKey] = $sales;
+ $weightedValue = $sales * $price * $mInfo['weight'];
+ $monthlyWeighted[$mKey] = $weightedValue;
+ $weightedSum += $weightedValue;
+ }
+ $productsData[$guid] = [
+ 'monthlySales' => $monthlySales,
+ 'monthlyWeighted' => $monthlyWeighted,
+ 'weightedSum' => $weightedSum,
+ ];
+ $globalTotal += $weightedSum;
+ }
+
+ foreach ($productsData as $guid => &$data) {
+ $data['share'] = ($globalTotal > 0) ? $data['weightedSum'] / $globalTotal : 0;
+ }
+ unset($data);
+
+ return $productsData;
+ }
+
}
<table class="table table-bordered">
<thead>
<tr>
- <th>GUID продукта</th>
+ <th>Наименование продукта / GUID</th>
<th>Недельные продажи</th>
+ <th>Взвешенная сумма продаж</th>
+ <th>Доля товара (%)</th>
+ <th>Детали по месяцам</th>
</tr>
</thead>
<tbody>
<td>
<?php
$productModel = Products1c::find()->where(['id' => $product['guid']])->one();
+ echo ($productModel !== null) ? Html::encode($productModel->name) : Html::encode($product['guid']);
?>
- <?= ($productModel !== null) ? Html::encode($productModel->name) : Html::encode($product['guid']) ?>
+ <br>
+ <?= Html::encode($product['guid']) ?>
</td>
<td>
<?php foreach ($product['weekly_sales'] as $month => $weekData): ?>
<?= implode(', ', $weekData) ?><br>
<?php endforeach; ?>
</td>
+ <td>
+ <?php
+ $salesData = isset($productSalesShare[$product['guid']]) ? $productSalesShare[$product['guid']] : null;
+ echo ($salesData !== null) ? $salesData['weightedSum'] : 0;
+ ?>
+ </td>
+ <td>
+ <?php
+ if ($salesData !== null) {
+ echo round($salesData['share'] * 100, 2) . '%';
+ } else {
+ echo '0%';
+ }
+ ?>
+ </td>
+ <td>
+ <?php if ($salesData !== null): ?>
+ <?php foreach ($salesData['monthlySales'] as $monthKey => $sales): ?>
+ <div>
+ <strong>Месяц <?= Html::encode($monthKey) ?>:</strong>
+ продажи = <?= $sales ?>,
+ взвеш. = <?= $salesData['monthlyWeighted'][$monthKey] ?>
+ </div>
+ <?php endforeach; ?>
+ <?php endif; ?>
+ </td>
</tr>
<?php endforeach; ?>
</tbody>
<tr>
<td>
<?php
- $productModel = Products1c::find()->where(['id' => $product['guid']])->one();
- echo ($productModel !== null) ? Html::encode($productModel->name) : Html::encode($product['guid']);
- ?>
+ $productModel = Products1c::find()->where(['id' => $product['guid']])->one(); ?>
+ <?= ($productModel !== null) ? Html::encode($productModel->name) : Html::encode($product['guid']); ?> <br>
+
+ <?= Html::encode($product['guid']); ?>
+
</td>
<td>
<?php foreach ($product['weekly_sales'] as $month => $weekData): ?>