]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Анализ товаров без истории
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Sun, 6 Apr 2025 20:16:13 +0000 (23:16 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Sun, 6 Apr 2025 20:16:13 +0000 (23:16 +0300)
erp24/controllers/CategoryPlanController.php
erp24/services/StorePlanService.php
erp24/views/category-plan/show-history-data.php

index 33a3110e128a96ec4d06864f22ddd0209ae71870..3e60567d9ce805b5ad984bd0a5fad10dccae2a49 100644 (file)
@@ -349,12 +349,19 @@ class CategoryPlanController extends Controller {
                         $subcategory,
                         $species
                     );
+                    $weightedResults = StorePlanService::calculateWeightedSalesForProductsWithoutHistory(
+                        $storeId,
+                        $selectedMonth,
+                        $result['without_history']
+                    );
+
                 }
             }
         }
 
         return $this->render('show-history-data', [
             'result'          => $result,
+            'weighted'          => $weightedResults,
             'model'           => $model,
             'storeList'       => $storeList,
             'monthsList'      => $monthsList,
index 2b7f1477dad88bbb7cdde52b7405ec3f5c630689..58c56ce4213ce5c44cae5de0820635cf73f51a49 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\Products1cAdditionalCharacteristics;
 use yii_app\records\Sales;
 use yii_app\records\SalesProducts;
 use yii_app\records\StorePlan;
@@ -316,5 +317,127 @@ class StorePlanService
         ];
     }
 
+
+    /**
+     * Метод вычисляет взвешенное значение продаж для товаров без истории.
+     *
+     * @param int   $storeId Идентификатор магазина.
+     * @param string $selectedMonth Выбранный месяц в формате "mm" (целевой месяц).
+     * @param array  $productsWithoutHistory Массив товаров без истории, где каждый элемент имеет вид:
+     *           [
+     *              'guid' => <GUID товара>,
+     *              'weekly_sales' => [ 'YYYY-mm' => [ ... ], ... ]
+     *           ]
+     * @return array Возвращает массив, где ключ – GUID товара, а значение – рассчитанное взвешенное значение продаж.
+     */
+    public static function calculateWeightedSalesForProductsWithoutHistory($storeId, $selectedMonth, $productsWithoutHistory)
+    {
+
+        $year = date('Y');
+        $targetDate = strtotime("$year-$selectedMonth-01");
+
+        $months = [];
+        for ($i = 1; $i <= 3; $i++) {
+            $monthTimestamp = strtotime("-{$i} month", $targetDate);
+            $m = date('m', $monthTimestamp);
+            $y = date('Y', $monthTimestamp);
+            $months[] = ['year' => $y, 'month' => $m];
+        }
+
+        $weightedResults = [];
+
+        foreach ($productsWithoutHistory as $product) {
+            $guid = $product['guid'];
+            //var_dump( $guid); die();
+            $myChars = Products1cAdditionalCharacteristics::find()
+                ->where(['product_id' => $guid])
+                ->asArray()
+                ->all();
+
+            $mySet = [];
+            foreach ($myChars as $char) {
+                $mySet[$char['property_id']] = $char['value'];
+            }
+            ksort($mySet);
+           // var_dump($mySet); die();
+            $countChars = count($mySet);
+           // var_dump($countChars); die();
+            if ($countChars == 0) {
+                $weightedResults[$guid] = 0;
+                continue;
+            }
+
+            $conditions = [];
+            foreach ($mySet as $propId => $val) {
+                $conditions[] = [$propId, $val];
+            }
+            $query = Products1cAdditionalCharacteristics::find()
+                ->select('product_id')
+                ->where(new \yii\db\Expression('(property_id, value) IN (' . implode(', ', array_map(function($pair) {
+                        return "('" . implode("','", $pair) . "')";
+                    }, $conditions)) . ')'))
+                ->groupBy('product_id')
+                ->having('COUNT(*) = :cnt', [':cnt' => $countChars]);
+
+            $similarProductIds = $query->column();
+           // var_dump($similarProductIds); die();
+            if (empty($similarProductIds)) {
+                $weightedResults[$guid] = 0;
+                continue;
+            }
+
+            $medianSales = [];
+            $salesValuesForEachMonth = [];
+            foreach ($months as $monthInfo) {
+                $startDate = sprintf('%04d-%02d-01', $monthInfo['year'], $monthInfo['month']);
+                $endDate = sprintf('%04d-%02d-%02d',
+                    $monthInfo['year'],
+                    $monthInfo['month'],
+                    cal_days_in_month(CAL_GREGORIAN, $monthInfo['month'], $monthInfo['year']));
+
+                $salesValues = [];
+
+                foreach ($similarProductIds as $simProdId) {
+                    $sales = Sales::find()->alias('s')
+                        ->innerJoin('sales_products sp', 's.id = sp.check_id')
+                        ->innerJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id')
+                        ->where(['p1c.id' => $simProdId])
+                        ->andWhere(['s.store_id' => $storeId])
+                        ->andWhere(['between', 's.date', $startDate . ' 00:00:00', $endDate . ' 23:59:59'])
+                        // Условие для чеков: order_id равен '' или '0'
+                        ->andWhere(['order_id' => ['', '0']])
+                        ->count();
+                    $salesValues[] = (int)$sales;
+                }
+                $salesValuesForEachMonth[] = $salesValues;
+                $nonZeroSales = array_filter($salesValues, function($val) {
+                    return $val > 0;
+                });
+                sort($nonZeroSales, SORT_NUMERIC);
+                $n = count($nonZeroSales);
+                if ($n === 0) {
+                    $median = 0;
+                } elseif ($n % 2 == 1) {
+                    $median = $nonZeroSales[floor($n / 2)];
+                } else {
+                    $median = ($nonZeroSales[$n / 2 - 1] + $nonZeroSales[$n / 2]) / 2;
+                }
+                $medianSales[] = $median;
+            }
+
+            $weights = [3, 2, 1];
+            $weightedValue = 0;
+            for ($i = 0; $i < count($medianSales); $i++) {
+                $weightedValue += $medianSales[$i] * $weights[$i];
+            }
+            $weightedResults[$guid] = [
+                'weightedValue' => $weightedValue,
+                'medianSales'   => $medianSales,
+                'salesValues'   => $salesValuesForEachMonth,
+            ];
+        }
+        return $weightedResults;
+    }
+
 }
 
index 4c29a4ef5facf598d65bc6dd09d7377e8b238624..3db4f027cf4187a3bb5574b50cefe628e4281117 100644 (file)
@@ -4,12 +4,14 @@ use yii\helpers\ArrayHelper;
 use yii\widgets\ActiveForm;
 use yii\base\DynamicModel;
 use yii_app\records\CityStore;
+use yii_app\records\Products1c;
 use yii_app\records\Products1cNomenclature;
 
 
 
 /* @var $this yii\web\View */
 /* @var $result array|null */
+/* @var $weighted array|null */
 /* @var $model DynamicModel */
 /* @var $storeList array */
 /* @var $monthsList array */
@@ -130,20 +132,46 @@ JS;
         <table class="table table-bordered">
             <thead>
             <tr>
-                <th>GUID продукта</th>
+                <th>Наименование продукта</th>
                 <th>Недельные продажи</th>
+                <th>Средневзвешенные продажи</th>
+                <th>Отладочные данные</th>
+                <th>Вычесленная стоимость<br> (средневзвешенное кол-во Х цена)</th>
             </tr>
             </thead>
             <tbody>
             <?php foreach ($result['without_history'] as $product): ?>
                 <tr>
-                    <td><?= Html::encode($product['guid']) ?></td>
+                    <td>
+                        <?php
+                        $productModel = Products1c::find()->where(['id' => $product['guid']])->one();
+                        echo ($productModel !== null) ? Html::encode($productModel->name) : Html::encode($product['guid']);
+                        ?>
+                    </td>
                     <td>
                         <?php foreach ($product['weekly_sales'] as $month => $weekData): ?>
                             <strong><?= Html::encode($month) ?>:</strong>
                             <?= implode(', ', $weekData) ?><br>
                         <?php endforeach; ?>
                     </td>
+                    <td>
+                        <?= isset($weighted[$product['guid']]) ? $weighted[$product['guid']]['weightedValue'] : 0 ?>
+                    </td>
+                    <td>
+                        <?php if(isset($weighted[$product['guid']])): ?>
+                            <strong>Медианы продаж:</strong> <?= implode(', ', $weighted[$product['guid']]['medianSales']) ?><br>
+                            <strong>Продажи за месяц:</strong><br>
+                            <?php foreach ($weighted[$product['guid']]['salesValues'] as $monthIndex => $salesArray): ?>
+                                Месяц <?= $monthIndex+1 ?>: <?= implode(', ', $salesArray) ?><br>
+                            <?php endforeach; ?>
+                        <?php endif; ?>
+                    </td>
+                    <td>
+                        <?= isset($weighted[$product['guid']]) ? (\yii_app\records\PricesDynamic::find()
+                            ->where(['product_id' => $product['guid']])
+                            ->andWhere(['active' => 1])->one()->price) *
+                            $weighted[$product['guid']]['weightedValue'] : 0 ?>
+                    </td>
                 </tr>
             <?php endforeach; ?>
             </tbody>
@@ -151,6 +179,5 @@ JS;
     <?php else: ?>
         <p>Нет товаров без истории продаж.</p>
     <?php endif; ?>
-
 <?php endif; ?>
 </div>