]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Правки по расчету автопланнограммы
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 24 Jul 2025 19:34:10 +0000 (22:34 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 24 Jul 2025 19:34:10 +0000 (22:34 +0300)
erp24/commands/CronController.php
erp24/controllers/AutoPlannogrammaController.php
erp24/services/AutoPlannogrammaService.php
erp24/services/StorePlanService.php
erp24/views/auto-plannogramma/calculate.php [new file with mode: 0644]

index bc6275a385be65e27e6ab404bfaad265ca346619..088667e44afbbb4df3561a69efcc0f58c1b3b978 100644 (file)
@@ -1593,7 +1593,7 @@ class CronController extends Controller
     public function actionAutoplannogrammaCalculate(): void
     {
         $date = new DateTime();
-        $date->modify('+2 months');
+        $date->modify('+0 months');
         $planDate = $date->format('Y-m-01');
         $month = (int)$date->format('m');
         $year = (int)$date->format('Y');
@@ -1617,8 +1617,18 @@ class CronController extends Controller
                     'species' => null,
                     'plan_date' => $planDate
                 ];
-
+                $this->stdout("Рассчитана автопланограмма для магазина " .  json_encode($forecastParams, JSON_UNESCAPED_UNICODE) . "\n", BaseConsole::FG_GREEN);
                 $forecast = $service->calculateFullForecastForWeek($forecastParams);
+                $logDir = Yii::getAlias('@runtime/logs/autoplannogramma');
+                if (!is_dir($logDir)) {
+                    mkdir($logDir, 0755, true);
+                }
+                $file = $logDir . '/forecast_store_' . $store->id . '_' . date('Ymd_His') . '.json';
+                file_put_contents(
+                    $file,
+                    json_encode($forecast, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
+                );
+                $this->stdout("Forecast сохранён в $file\n", BaseConsole::FG_BLUE);
                 $writeOffsForecast = $service->getWeeklyProductsWriteoffsForecast($month, $year, $forecast, $store->id);
                 $salesForecast = $service->getWeeklyBouquetProductsSalesForecast($month, $year, $store->id);
 
@@ -1640,11 +1650,16 @@ class CronController extends Controller
                     $productId = $item['product_id'];
                     $week = $item['week'];
                     $quantity = (float)($item['forecast_week_pieces'] ?? 0);
-
+                    if ($productId == '0fb4e768-8191-11ef-84ea-ac1f6b1b7573') {
+                        $this->stdout("Сохраняем: product={$productId} week={$week} quantity={$quantity}\n", BaseConsole::FG_CYAN);
+                        $this->stdout($item['forecast_week_pieces']  . "\n", BaseConsole::FG_CYAN);
+                    }
                     $details = [];
                     $total = $quantity;
 
-                    if (!empty($writeOffsForecast[$productId][$week]['writeOffs'])) {
+                    if (!empty($writeOffsForecast[$productId]) &&
+                        !empty($writeOffsForecast[$productId][$week]) &&
+                        array_key_exists('writeOffs', $writeOffsForecast[$productId][$week])) {
                         $writeOffs = $writeOffsForecast[$productId][$week]['writeOffs'];
                         $details['writeOffs']['quantity'] = $writeOffs;
                         $total += is_array($writeOffs) ? array_sum($writeOffs) : (float)$writeOffs;
@@ -1700,7 +1715,7 @@ class CronController extends Controller
                             'calculate' => $quantity,
                             'modify' => null,
                             'total' => ceil($total)
-                        ]);
+                        ], false);
 
                         if (!$model->save()) {
                             $errors = implode('; ', array_map(
@@ -1717,7 +1732,9 @@ class CronController extends Controller
                 $this->stdout("Сохранена автопланограмма для магазина {$store->name}\n", BaseConsole::FG_GREEN);
             } catch (Throwable $e) {
                 $this->stderr("Ошибка при расчёте прогноза: {$e->getMessage()}\n", BaseConsole::FG_RED);
-                Yii::error("Ошибка при расчёте прогноза: " . $e->getMessage(), __METHOD__);
+                $trace = $e->getTraceAsString();
+                Yii::error("Ошибка при расчёте прогноза: " . $e->getMessage() . "\nTrace:\n" . $trace, __METHOD__);
+                $this->stderr("Трассировка:\n$trace\n", BaseConsole::FG_RED);
                 continue;
             }
         }
index 1ee5b578f48374b72b109032b602ef4663ab17f1..7dcf9bcae9d4267c047f2683d93fcc798749ae99 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace app\controllers;
 
+use DateTime;
+use Throwable;
 use Yii;
 use yii\base\DynamicModel;
 use yii\data\ArrayDataProvider;
@@ -1355,8 +1357,8 @@ class AutoPlannogrammaController extends BaseController
 
             $service = new AutoPlannogrammaService();
             //$goals = $service->calculateFullGoalChain($filters);
-            //$forecast = $service->calculateFullForecastForWeek($filters);
-            //var_dump( $forecast); die();
+            $forecast = $service->calculateFullForecastForWeek($filters);
+            var_dump( $forecast); die();
             $monthCategoryShare = $service->getMonthCategoryShareOrWriteOff($filters['plan_date'], $filters);
             $monthCategoryGoal = $service->getMonthCategoryGoal($monthCategoryShare, $filters['plan_date']);
             $monthSubcategoryShare = $service->getMonthSubcategoryShareOrWriteOff($filters['plan_date'], $filters);
@@ -1462,216 +1464,6 @@ class AutoPlannogrammaController extends BaseController
     }
 
 
-    public function actionControlSpeciesOld()
-    {
-        $model = new DynamicModel([
-            'storeId', 'month', 'type',
-
-        ]);
-        $model->addRule(['month', 'type'], 'required')
-            ->addRule('storeId', 'integer');
-
-        $storeList = CityStore::find()
-            ->select(['name', 'id'])
-            ->where(['visible' => CityStore::IS_VISIBLE])
-            ->indexBy('id')
-            ->column();
-
-        $monthsList = [];
-        for ($i = 0; $i < 12; $i++) {
-            // получаем метку вида "03-2025"
-            $ts = strtotime("first day of -{$i} month");
-            $key = date('m-Y', $ts);
-            $monthsList[$key] = $key;
-        }
-
-        $monthResult = [];
-        $totals = [];
-        $weeksData = [];
-        $weeksShareResult = [];
-        $weeksGoalResult = [];
-        $monthCategoryShareResult = [];
-        $weeksProductForecast = [];
-
-        if ($model->load(Yii::$app->request->post()) && $model->validate()) {
-            $filters = [];
-
-            list($m, $y) = explode('-', $model->month);
-            $dateFrom = date("Y-m-d 00:00:00", strtotime(sprintf('%04d-%02d-01', $y, $m)));
-            $dateTo = date("Y-m-t 23:59:59", strtotime($dateFrom));
-
-            if ($model->storeId) {
-                $filters['store_id'] = $model->storeId;
-                $filters['type'] = $model->type;
-                $filters['plan_date'] = $dateFrom;
-            }
-
-            $service = new AutoPlannogrammaService();
-
-            if ($model->storeId) {
-                $totals = $service->getStoreTotals(
-                    [$model->storeId],
-                    $dateFrom,
-                    null,
-                    $model->type,
-                    $dateTo
-                );
-            }
-
-            $monthSpeciesGoals = $service->calculateFullGoalChainWeighted($filters);
-            $monthSpeciesGoalsMap = [];
-            foreach ($monthSpeciesGoals as $monthSpeciesGoal) {
-                $monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']]
-                [$monthSpeciesGoal['category']]
-                [$monthSpeciesGoal['subcategory']]
-                [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'];
-            }
-
-            $weeksShareResult = $service->getHistoricalWeeklySpeciesShare($model->month, $filters, null, 'writeOffs');
-            $weeksData = $service->calculateWeeklySpeciesGoals($weeksShareResult['weeksData'], $monthSpeciesGoals);
-
-            $datePlan = $filters['plan_date'];
-            $monthCategoryShare = $service->getMonthCategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']);
-            $monthCategoryGoal = $service->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters['type']);
-            foreach ($monthCategoryShare as $sid => $cats) {
-                foreach ($cats as $cat) {
-                    $monthCategoryShareResult[$sid][$cat['category']]['total_sum_cat'] = $cat['total_sum_cat'];
-                    $monthCategoryShareResult[$sid][$cat['category']]['share_of_total'] = $cat['share_of_total'];
-                }
-
-            }
-            foreach ($monthCategoryGoal as $cats) {
-                $monthCategoryShareResult[$cats['store_id']][$cats['category']]['goal'] = $cats['goal'];
-
-            }
-
-            $monthSubcategoryShare = $service->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']);
-            $monthSubcategoryGoal = $service->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
-
-            if ($filters['type'] === 'writeOffs') {
-                $salesSubShare = $service->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales');
-                $salesSubGoal = $service->getMonthSubcategoryGoal($salesSubShare, $monthCategoryGoal);
-
-                $catGoalMap = [];
-                foreach ($monthCategoryGoal as $row) {
-                    $catGoalMap[$row['category']] = $row['goal'];
-                }
-                $salesSubGoalMap = [];
-                foreach ($salesSubGoal as $row) {
-                    $salesSubGoalMap[$row['category']][$row['subcategory']] = $row['goal'];
-                }
-
-
-                foreach ($monthSubcategoryShare as &$row) {
-                    $cat = $row['category'];
-                    $sub = $row['subcategory'];
-
-                    $writeShare = $row['percent_of_month'];
-
-                    $writeGoal = ($catGoalMap[$cat] ?? 0) * $writeShare;
-                    $saleGoal = $salesSubGoalMap[$cat][$sub] ?? 0;
-
-                    if ($saleGoal > 0 && $writeGoal > 0.1 * $saleGoal) {
-                        $row['share'] = 0.1;
-                    }
-                }
-                unset($row);
-                $monthSubcategoryGoal = $service->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
-            }
-
-            foreach ($monthSubcategoryShare as $subcat) {
-                $monthCategoryShareResult[$subcat['store_id']][$subcat['category']][$subcat['subcategory']]['total_sum'] = $subcat['total_sum'];
-                $monthCategoryShareResult[$subcat['store_id']][$subcat['category']][$subcat['subcategory']]['percent_of_month'] = $subcat['percent_of_month'];
-            }
-            foreach ($monthSubcategoryGoal as $cats) {
-                $monthCategoryShareResult[$cats['store_id']][$cats['category']][$cats['subcategory']]['goal'] = $cats['goal'];
-
-            }
-            $monthSpeciesShare = $service->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, $filters['type']);
-            $monthSpeciesGoal = $service->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
-            if ($filters['type'] === 'writeOffs') {
-                $salesSpecShare = $service->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, 'sales');
-                $salesSpecGoal = $service->getMonthSpeciesGoalDirty($salesSpecShare, $monthSubcategoryGoal);
-
-                $subGoalMap = [];
-                foreach ($monthSubcategoryGoal as $row) {
-                    $subGoalMap[$row['category']][$row['subcategory']] = $row['goal'];
-                }
-                $salesSpecGoalMap = [];
-                foreach ($salesSpecGoal as $row) {
-                    $salesSpecGoalMap[$row['category']][$row['subcategory']][$row['species']] = $row['goal'];
-                }
-
-                foreach ($monthSpeciesShare as &$row) {
-                    $cat = $row['category'];
-                    $sub = $row['subcategory'];
-                    $spec = $row['species'];
-
-                    $writeShare = $row['percent_of_month'];
-                    $writeGoal = ($subGoalMap[$cat][$sub] ?? 0) * $writeShare;
-                    $saleGoal = $salesSpecGoalMap[$cat][$sub][$spec] ?? 0;
-
-                    if ($saleGoal > 0 && $writeGoal > 0.1 * $saleGoal) {
-                        $row['share'] = 0.1;
-                    }
-                }
-                unset($row);
-
-                $monthSpeciesGoal = $service->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
-            }
-            foreach ($monthSpeciesShare as $species) {
-                $monthCategoryShareResult[$species['store_id']][$species['category']][$species['subcategory']][$species['species']]['total_sum'] = $species['total_sum'];
-                $monthCategoryShareResult[$species['store_id']][$species['category']][$species['subcategory']][$species['species']]['percent_of_month'] = $species['percent_of_month'];
-
-            }
-            foreach ($weeksShareResult['weeksData'] as $row) {
-                $monthCategoryShareResult[$row['store_id']][$row['category']][$row['subcategory']][$row['species']][$row['week']]['sumWeek'] = $row['sumWeek'];
-
-            }
-
-
-            foreach ($weeksData as $r) {
-                $forecasts = $service->calculateWeekForecastSpeciesProducts($r['category'], $r['subcategory'], $r['species'], $r['store_id'], $r['weekly_goal']);
-                foreach ($forecasts as $forecast) {
-                    $weeksProductForecast[] = [
-                        'category' => $forecast['category'] ?? '',
-                        'subcategory' => $forecast['subcategory'] ?? '',
-                        'species' => $forecast['species'] ?? '',
-                        'product_id' => $forecast['product_id'] ?? '',
-                        'name' => $forecast['name'] ?? '',
-                        'price' => $forecast['price'] ?? '',
-                        'goal' => $forecast['goal'] ?? 0,
-                        'forecast' => $forecast['forecast'] ?? 0,
-                        'week' => $r['week'],
-                    ];
-                }
-            }
-
-            usort($weeksProductForecast, function ($a, $b) {
-                foreach (['category', 'subcategory', 'species', 'name', 'week'] as $key) {
-                    $va = $a[$key];
-                    $vb = $b[$key];
-                    if ($va < $vb) return -1;
-                    if ($va > $vb) return 1;
-                }
-                return 0;
-            });
-
-        }
-
-        return $this->render('control-species-old', [
-            'model' => $model,
-            'result' => $monthResult,
-            'weeksData' => $weeksData,
-            'monthCategoryShare' => $monthCategoryShareResult,
-            'weeksProductForecast' => $weeksProductForecast,
-            'totals' => $totals,
-            'storeList' => $storeList,
-            'monthsList' => $monthsList,
-
-        ]);
-    }
-
 
     public function actionControlSpecies()
     {
@@ -1917,4 +1709,146 @@ class AutoPlannogrammaController extends BaseController
 
 
     }
+
+    /**
+     * Расчет автопланограммы по одному магазину и рендер GridView
+     * @param int $store_id
+     * @param string|null $category
+     * @param string|null $subcategory
+     * @param string|null $species
+     * @param int|null $month
+     * @param int|null $year
+     */
+    public function actionCalculate(    ) {
+        $request = Yii::$app->request;
+            $category = $request->get('category',null);
+            $subcategory = $request->get('subcategory') ?? null;
+            $species = $request->get('species') ?? null;
+            $store_id = $request->get('store_id',null) ;
+            $year = $request->get('year');
+            $month = $request->get('month');
+            $type = $request->get('type');
+
+
+        $dataProvider = new ArrayDataProvider([
+            'allModels' => [],
+            'pagination' => ['pageSize' => 100],
+        ]);
+
+
+        if (!empty($year) && !empty($month)) {
+            $planDate = sprintf('%04d-%02d-01', $year, $month);
+
+
+            $store = CityStore::findOne($store_id);
+            if (!$store) {
+                throw new \yii\web\NotFoundHttpException("Store with ID={$store_id} not found.");
+            }
+
+            $service = new AutoPlannogrammaService();
+
+            $params = [
+                'month' => $month,
+                'year' => $year,
+                'type' => AutoPlannogrammaService::TYPE_SALES,
+                'store_id' => $store_id,
+                'category' => $category,
+                'subcategory' => $subcategory,
+                'species' => $species,
+                'plan_date' => $planDate,
+            ];
+
+            try {
+                $forecast = $service->calculateFullForecastForWeek($params);
+                //var_dump($forecast);die();
+                $writeOffsForecast = $service->getWeeklyProductsWriteoffsForecast($month, $year, $forecast, $store_id);
+                $salesForecast = $service->getWeeklyBouquetProductsSalesForecast($month, $year, $store_id);
+
+                $existing = Autoplannogramma::find()
+                    ->where(['month' => $month, 'year' => $year, 'store_id' => $store_id, 'week' => array_unique(array_column($forecast, 'week'))])
+                    ->indexBy(fn($r) => $r->week . '_' . $r->product_id)
+                    ->all();
+
+                $rows = [];
+                foreach ($forecast as $item) {
+                    $key = $item['week'] . '_' . $item['product_id'];
+                    $model = $existing[$key] ?? new Autoplannogramma();
+                    $quantity = (float)($item['forecast_week_pieces'] ?? 0);
+                    $productId = $item['product_id'];
+                    $week = $item['week'];
+
+
+                    $details = [];
+                    $total = $quantity;
+                    if (!empty($writeOffsForecast[$productId][$week]['writeOffs'])) {
+                        $wo = $writeOffsForecast[$productId][$week]['writeOffs'];
+                        $details['writeOffs']['quantity'] = $wo;
+                        $total += is_array($wo) ? array_sum($wo) : (float)$wo;
+                    } else {
+                        $details['writeOffs']['quantity'] = 0;
+                    }
+                    foreach (['offline', 'online', 'marketplace'] as $type) {
+                        $block = ['share' => 0, 'quantity' => 0, 'groups' => []];
+                        if (isset($salesForecast[$store_id][$productId][$type]) && is_array($salesForecast[$store_id][$productId][$type])) {
+                            $share = $salesForecast[$store_id][$type]['share'] ?? 0;
+                            $block['share'] = (float)$share;
+                            $block['quantity'] = (float)sprintf('%.2f', round($quantity * $share, 2));
+                            foreach ($salesForecast[$store_id][$productId][$type] as $k => $v) {
+                                $block['groups'][$k] = (float)$v;
+                                $total += (float)$v;
+                            }
+                        }
+                        $details[$type] = $block;
+                    }
+                    $details['forecast'] = ['quantity' => $quantity];
+                    $total = (float)sprintf('%.2f', $total);
+
+                    $model->setAttributes([
+                        'month' => $month,
+                        'year' => $year,
+                        'week' => $week,
+                        'product_id' => $productId,
+                        'store_id' => $store_id,
+                        'is_archive' => false,
+                        'capacity_type' => 1,
+                        'details' => json_encode($details, JSON_UNESCAPED_UNICODE),
+                        'calculate' => $quantity,
+                        'modify' => null,
+                        'total' => ceil($total),
+                    ], false);
+                    $model->save();
+
+
+                    $rows[] = [
+                        'week' => $week,
+                        'product_id' => $productId,
+                        'calculate' => $model->calculate,
+                        'total' => $model->total,
+                        'details' => $model->details,
+                    ];
+                }
+
+            } catch (Throwable $e) {
+                Yii::error($e->getMessage(), __METHOD__);
+                throw $e;
+            }
+
+
+            $dataProvider = new ArrayDataProvider([
+                'allModels' => $rows,
+                'pagination' => ['pageSize' => 50],
+                'sort' => ['attributes' => ['week', 'product_id', 'calculate', 'total']],
+            ]);
+        }
+        return $this->render('calculate', [
+            'store'        => $store ?? null,
+            'month'        => $month,
+            'year'         => $year,
+            'category'     => $category,
+            'subcategory'  => $subcategory,
+            'species'      => $species,
+            'dataProvider' => $dataProvider,
+        ]);
+    }
+
 }
index 927a24d84e2cd007a73006963abf34cf30257080..04e286c8ad1ac447efa743fc59604d74110d3c15 100644 (file)
@@ -1875,10 +1875,38 @@ class AutoPlannogrammaService
         foreach ($groupedForecasts as $group) {
             $meta = $group['meta'];
             $products = $group['products'];
+            $query = Products1cNomenclature::find()
+                ->select('id')
+                ->where(['category' => $meta['category']])
+                ->andWhere(['not in', 'category', ['', 'букет', 'сборка', 'сервис']])
+                ->andFilterWhere([
+                    'subcategory' => $meta['subcategory'],
+                    'species'     => $meta['species'],
+                ]);
+            $allProductIds = $query->column();
+
+
+            foreach ($allProductIds as $pid) {
+                if (!isset($products[$pid])) {
+                    $products[$pid] = [
+                        'product_id'      => $pid,
+                        'forecast_pieces' => 0.0,
+                        'history_status'  => 'No history', // или 'Missing'
+                    ];
+                }
+            }
 
             $totalForecast = array_sum(array_column($products, 'forecast_pieces'));
 
             if ($totalForecast <= 0) {
+                foreach ($products as $p) {
+                    $result[] = array_merge($meta, [
+                        'product_id'      => $p['product_id'],
+                        'forecast_pieces' => $p['forecast_pieces'],
+                        'share'           => 0.0,
+                        'history_status'  => $p['history_status'],
+                    ]);
+                }
                 continue;
             }
 
index 308ebfdce40e74dc211e5831e7619acd4362679a..9d2200e64198f3518278c7d5b832e2da59a1da8f 100755 (executable)
@@ -349,8 +349,8 @@ class StorePlanService
                     ->where(['s.store_id' => $storeId])
                     ->andWhere(['between', 's.date', $dateStart, $dateEnd])
                     ->andWhere(['order_id' => ['', '0']])
-                    //->andWhere(['p1.components' => ''])
-                    ->andWhere(['not in', 'p1c.category', ['', 'букет', 'сборка', 'сервис']])
+                    ->andWhere(['p1.components' => ''])
+                    ->andWhere(['not in', 'p1c.category', ['',  'сервис']])
                     ->andFilterWhere(['p1c.category'    => $category])
                     ->andFilterWhere(['p1c.subcategory' => $subcategory])
                     ->andFilterWhere(['p1c.species'     => $species])
@@ -380,6 +380,22 @@ class StorePlanService
             }
         }
 
+        foreach ($salesHistory as &$history) {
+            foreach ($periods as $periodKey => $periodData) {
+                if (!isset($history['data'][$periodKey])) {
+                    $history['data'][$periodKey] = [];
+                }
+
+                foreach (array_keys($periodData['weeks']) as $weekIndex) {
+                    if (!isset($history['data'][$periodKey][$weekIndex])) {
+                        $history['data'][$periodKey][$weekIndex] = 0;
+                    }
+                }
+                ksort($history['data'][$periodKey]);
+            }
+        }
+        unset($history);
+
         return $salesHistory;
     }
 
@@ -678,25 +694,14 @@ class StorePlanService
         int   $selectedYear,
         array $medianProductsWithoutHistory
     ): array {
-        $accumulator  = [];
-        $prices        = [];
-        $guidToGroup   = [];
-
+        $accumulator = [];
 
         foreach ($medianProductsWithoutHistory as $guid => $data) {
-            $q = (float)$data['weightedValue'];
-            if ($q <= 0) continue;
-
-            $price = self::getPriceForProductAtOffsetMonthWeekly(
-                $guid, $selectedYear, $selectedMonth, $storeId, 2
-            );
-            $prices[$guid] = $price;
-
-            $cat = $data['category'];
-            $sub = $data['subcategory'];
-            $sp  = $data['species'];
-            $groupKey = implode('|', [$cat,$sub,$sp]);
-            $guidToGroup[$guid] = $groupKey;
+            $cat      = $data['category'];
+            $sub      = $data['subcategory'];
+            $sp       = $data['species'];
+            $groupKey = implode('|', [$cat, $sub, $sp]);
+            $q        = (float)$data['weightedValue'];
 
             if (!isset($accumulator[$groupKey])) {
                 $accumulator[$groupKey] = [
@@ -710,15 +715,15 @@ class StorePlanService
                     'forecasts'   => [],
                 ];
             }
-            $accumulator[$groupKey]['goal'] += $q * $price;
-        }
 
-        foreach ($medianProductsWithoutHistory as $guid => $qty) {
-            $groupKey = $guidToGroup[$guid];
-            $goal     = $accumulator[$groupKey]['goal'];
-            $price    = $prices[$guid] ?? 0.0;
-            $accumulator[$groupKey]['forecasts'][$guid] = $qty['weightedValue'];
+            $accumulator[$groupKey]['forecasts'][$guid] = $q;
 
+            if ($q > 0) {
+                $price = self::getPriceForProductAtOffsetMonthWeekly(
+                    $guid, $selectedYear, $selectedMonth, $storeId, 2
+                );
+                $accumulator[$groupKey]['goal'] += $q * $price;
+            }
         }
 
         return array_values($accumulator);
diff --git a/erp24/views/auto-plannogramma/calculate.php b/erp24/views/auto-plannogramma/calculate.php
new file mode 100644 (file)
index 0000000..a6b4c39
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+/* @var yii\data\ArrayDataProvider $dataProvider */
+$allModels = $dataProvider->getModels();
+
+?>
+<div class="filter-form" style="margin-bottom: 20px;">
+    <?php
+    use kartik\date\DatePicker;
+    use kartik\grid\GridView;
+    use kartik\select2\Select2;
+    use yii\helpers\ArrayHelper;
+    use yii\helpers\Html;
+    use yii\widgets\ActiveForm;
+    use yii_app\records\CityStore;
+    use yii_app\records\Products1cNomenclature;
+    ?>
+    <h1><?= Html::encode($this->title) ?></h1>
+    <?php
+    $form = ActiveForm::begin(['method' => 'get']); ?>
+    <div class="row p-3">
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['category' => $filters['category'] ?? '']), 'category')->widget(Select2::class, [
+                'data' => ArrayHelper::map(
+                    Products1cNomenclature::find()->select('category')->distinct()->asArray()->all(),
+                    'category',
+                    'category'
+                ),
+                'options' => ['placeholder' => 'Категория', 'name' => 'category'],
+                'pluginOptions' => ['allowClear' => true],
+            ])->label('Категория') ?>
+        </div>
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['subcategory' => $filters['subcategory'] ?? '']), 'subcategory')->widget(Select2::class, [
+                'data' => ArrayHelper::map(
+                    Products1cNomenclature::find()->select('subcategory')->distinct()->asArray()->all(),
+                    'subcategory',
+                    'subcategory'
+                ),
+                'options' => ['placeholder' => 'Подкатегория', 'name' => 'subcategory'],
+                'pluginOptions' => ['allowClear' => true],
+            ])->label('Подкатегория') ?>
+        </div>
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['species' => $filters['species'] ?? '']), 'species')->widget(Select2::class, [
+                'data' => ArrayHelper::map(
+                    Products1cNomenclature::find()->select('species')->distinct()->asArray()->all(),
+                    'species',
+                    'species'
+                ),
+                'options' => ['placeholder' => 'Тип товара', 'name' => 'species'],
+                'pluginOptions' => ['allowClear' => true],
+            ])->label('Товар') ?>
+        </div>
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['store_id' => $filters['store_id'] ?? '']), 'store_id')->widget(Select2::class, [
+                'data' => ArrayHelper::map(
+                    CityStore::findAll(['visible' => CityStore::IS_VISIBLE]),
+                    'id',
+                    'name'
+                ),
+                'options' => ['placeholder' => 'Магазин', 'name' => 'store_id'],
+                'pluginOptions' => ['allowClear' => true],
+            ])->label('Магазин') ?>
+        </div>
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['month' => $filters['month'] ?? '']), 'month')->dropDownList(\yii_app\helpers\DateHelper::MONTH_NUMBER_NAMES, [
+                'prompt' => 'Месяц',
+                'name' => 'month',
+            ])->label('Плановый месяц') ?>
+        </div>
+
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['year' => $filters['year'] ?? '']), 'year')->dropDownList(['2025' => 2025, '2026' => 2026], [
+                'prompt' => 'Год',
+                'name' => 'year',
+            ])->label('Плановый год') ?>
+        </div>
+        <div class="col-md">
+            <?= $form->field(new \yii\base\DynamicModel(['type' => $filters['type'] ?? '']), 'type')->widget(Select2::class, [
+                'data' => [
+                    'writeOffs' => 'Списания',
+                    'sales' => 'Продажи'
+                ],
+                'options' => ['placeholder' => 'Тип', 'name' => 'type'],
+                'pluginOptions' => ['allowClear' => true],
+            ])->label('По дефолту продажи!') ?>
+        </div>
+        <div class="col-md">
+            <?= Html::submitButton('Фильтровать', ['class' => 'btn btn-primary']) ?>
+        </div>
+        <div class="col-md">
+            <?= Html::a('Сбросить', ['auto-plannogramma/8'], ['class' => 'btn btn-default']) ?>
+        </div>
+    </div>
+
+    <?php ActiveForm::end(); ?>
+</div>
+<div class="autoplannogramma-calc">
+
+
+    <?= GridView::widget([
+        'dataProvider' => $dataProvider,
+        'columns' => [
+            ['attribute' => 'week', 'label' => 'Неделя'],
+            ['attribute' => 'product_id', 'label' => 'Product GUID'],
+            ['attribute' => 'calculate', 'label' => 'Calculate'],
+            ['attribute' => 'total', 'label' => 'Total'],
+            [
+                'attribute' => 'details',
+                'label'     => 'Details',
+                'format'    => 'raw',
+                'value'     => function($model) {
+                    return '<pre>' . Html::encode(json_encode($model['details'], JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT)) . '</pre>';
+                }
+            ],
+        ],
+    ]); ?>