]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Товары с историей feature_fomichev_erp-366_calculate_history_share_product_species
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 8 Apr 2025 07:14:13 +0000 (10:14 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 8 Apr 2025 07:14:13 +0000 (10:14 +0300)
erp24/controllers/CategoryPlanController.php
erp24/services/StorePlanService.php
erp24/views/category-plan/show-history-data.php

index f3dac776e7af02d5b4dde3d01f47fd0ee992c137..054cfb90325c36113b1447079b4489daebd093f7 100644 (file)
@@ -305,6 +305,7 @@ class CategoryPlanController extends Controller {
         $subcategoryList = [];
         $speciesList = [];
         $weightedResults = [];
+        $productSalesShare = [];
         $model = new DynamicModel(['storeId', 'month', 'category', 'subcategory', 'species']);
         $model->addRule(['storeId', 'month', 'category'], 'required');
 
@@ -355,6 +356,13 @@ class CategoryPlanController extends Controller {
                         $result['without_history']
                     );
 
+
+                    $productSalesShare = StorePlanService::calculateProductSalesShare(
+                        $storeId,
+                        $selectedMonth,
+                        $result['with_history']
+                    );
+
                 }
             }
         }
@@ -362,6 +370,7 @@ class CategoryPlanController extends Controller {
         return $this->render('show-history-data', [
             'result'          => $result,
             'weighted'          => $weightedResults,
+            'productSalesShare' => $productSalesShare,
             'model'           => $model,
             'storeList'       => $storeList,
             'monthsList'      => $monthsList,
index 58c56ce4213ce5c44cae5de0820635cf73f51a49..dd088a6dea4da3d6581c2d60664d839dc6ff07c2 100755 (executable)
@@ -6,6 +6,7 @@ use DateTime;
 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;
@@ -439,5 +440,105 @@ class StorePlanService
         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;
+    }
+
 }
 
index c408532b90cf6943ee957e8b497babcbab5e5049..bd79da791592175304a8da2da5ce856d5777a2ec 100644 (file)
@@ -105,8 +105,11 @@ JS;
         <table class="table table-bordered">
             <thead>
             <tr>
-                <th>GUID продукта</th>
+                <th>Наименование продукта / GUID</th>
                 <th>Недельные продажи</th>
+                <th>Взвешенная сумма продаж</th>
+                <th>Доля товара (%)</th>
+                <th>Детали по месяцам</th>
             </tr>
             </thead>
             <tbody>
@@ -115,8 +118,10 @@ JS;
                     <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): ?>
@@ -124,6 +129,32 @@ JS;
                             <?= 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>
@@ -149,9 +180,11 @@ JS;
                 <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): ?>