]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Рефакторинг и организация кода feature_fomichev_erp-363_week_forecast
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Mon, 9 Jun 2025 14:30:20 +0000 (17:30 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Mon, 9 Jun 2025 14:30:20 +0000 (17:30 +0300)
erp24/controllers/AutoPlannogrammaController.php
erp24/services/AutoPlannogrammaService.php
erp24/services/StorePlanService.php

index f70bb5f2dd2642c75244bdda8823767ff41551c3..5e44bd9bb5579f5082a64992c4a573806f518b09 100644 (file)
@@ -906,10 +906,11 @@ class AutoPlannogrammaController extends BaseController
             $productSalesForecast = $service->calculateProductForecastInPiecesProductsWithHistory(
                 $filters['store_id'],
                 $filters['month'],
-                $productSalesShare,
+                $filters['year'],
                 $goals,
-                $filters['subcategory'],
+                $productSalesShare,
                 $filters['category'],
+                $filters['subcategory'],
                 $filters['species']
             );
 
@@ -1037,11 +1038,13 @@ class AutoPlannogrammaController extends BaseController
             $productSalesForecast = $service->calculateProductForecastInPiecesProductsWithHistory(
                 $filters['store_id'],
                 $filters['month'],
-                $productSalesShare,
+                $filters['year'],
                 $goals,
-                $filters['subcategory'],
+                $productSalesShare,
                 $filters['category'],
+                $filters['subcategory'],
                 $filters['species']
+
             );
 
             $matrixForecast = MatrixBouquetForecast::find()
@@ -1285,7 +1288,9 @@ class AutoPlannogrammaController extends BaseController
             $filters['plan_date'] = $filters['year'] . '-' . str_pad($filters['month'], 2, '0', STR_PAD_LEFT) . '-01';
             // var_dump( $filters['plan_date']); die();
             $service = new AutoPlannogrammaService();
-//$goals = $service->calculateFullGoalChain($filters);
+            //$goals = $service->calculateFullGoalChain($filters);
+            //$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);
@@ -1303,18 +1308,19 @@ class AutoPlannogrammaController extends BaseController
             }
 
 
-            $result = StorePlanService::calculateHistoricalShare(
+           /* $result = StorePlanService::calculateHistoricalShare(
                 $filters['store_id'],
                 $filters['month'],
                 $filters['year'],
-                $filters['category'],
-                $filters['subcategory'],
-                $filters['species']
-            );
+                null,
+                null,
+                null
+            );*/
 
             $noHistoryProductData = $service->calculateSpeciesForecastForProductsWithoutHistory($filters['plan_date'], $filters);
+            $historyProductData = $service->calculateSpeciesForecastForProductsWithHistory($filters['plan_date'], $filters, $goals);
 
-            $productSalesShare = StorePlanService::calculateProductSalesShareProductsWithHistory(
+           /* $productSalesShare = StorePlanService::calculateProductSalesShareProductsWithHistory(
                 $filters['store_id'],
                 $filters['month'],
                 $result['with_history']
@@ -1328,7 +1334,7 @@ class AutoPlannogrammaController extends BaseController
                 $filters['subcategory'],
                 $filters['category'],
                 $filters['species']
-            );
+            );*/
 
             $matrixForecast = MatrixBouquetForecast::find()
                 ->where(['year' => $filters['year'], 'month' => $filters['month']])
@@ -1356,7 +1362,7 @@ class AutoPlannogrammaController extends BaseController
             $cleanedSpeciesGoals = $service->subtractSpeciesGoals($goals, $bouquetSpeciesForecast, $noHistoryProductData);
 
 
-            $salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $productSalesForecast);
+            $salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $historyProductData);
 
             $productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals);
 
@@ -1365,7 +1371,6 @@ class AutoPlannogrammaController extends BaseController
 
             $weeklySalesForecast = $service->calculateWeeklyProductForecastPieces($productForecastSpecies, $weeklySales);
 
-
             $weeklySalesForecastFormated = $service->pivotWeeklyForecast($weeklySalesForecast);
 
             $flatData = array_filter($weeklySalesForecastFormated, function ($row) use ($filters) {
@@ -1382,7 +1387,7 @@ class AutoPlannogrammaController extends BaseController
 
             $dataProvider = new ArrayDataProvider([
                 'allModels' => $flatData,
-                'pagination' => ['pageSize' => 100],
+                'pagination' => ['pageSize' => 1000],
             ]);
         }
         return $this->render('week-sales-products_forecast', [
index e0ee09a241ab2d1eaaefa0f1fea8484a82d9f5c7..7ab2e47306f5cd0654bf04709eb963dc7226d89d 100644 (file)
@@ -12,6 +12,7 @@ use yii_app\records\BouquetComposition;
 use yii_app\records\CityStore;
 use yii_app\records\CityStoreParams;
 use yii_app\records\ExportImportTable;
+use yii_app\records\MatrixBouquetForecast;
 use yii_app\records\PricesDynamic;
 use yii_app\records\Products1c;
 use yii_app\records\Products1cNomenclature;
@@ -773,7 +774,7 @@ class AutoPlannogrammaService
         $dateFromForCategory = (new \DateTime($datePlan))->modify('-' . (self::CATEGORY_LOOKBACK_MONTHS + self::LOOKBACK_MONTHS) . ' months')->format('Y-m-d');
 
         $monthCategoryShare = $this->getMonthCategoryShareOrWriteOff($dateFromForCategory, $filters);
-        $monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters);
+        $monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters['type']);
 
         $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOff($datePlan, $filters);
         $monthSubcategoryGoal = $this->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
@@ -1614,6 +1615,71 @@ class AutoPlannogrammaService
         return $result;
     }
 
+
+    public function calculateSpeciesForecastForProductsWithHistory($dateFrom, $filters, $goals): array
+    {
+        // Получение ID видимых магазинов
+        $storeIds = array_map(fn($store) => $store->id, $this->getVisibleStores());
+
+        $subcategory = !empty($filters['subcategory']) ? $filters['subcategory'] : null;
+        $species = !empty($filters['species']) ? $filters['species'] : null;
+
+        // Применение фильтра по магазину, если указан
+        if (!empty($filters['store_id'])) {
+            $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]);
+        }
+        $date = new \DateTime($dateFrom);
+        $month = $date->format('m');
+        $year = $date->format('Y');
+
+        $result = [];
+
+        foreach ($storeIds as $storeId) {
+            $histResult = StorePlanService::calculateHistoricalShare(
+                $storeId,
+                $month,
+                $year,
+                $filters['category'],
+                $subcategory,
+                $species
+            );
+
+            $productsWithHistory = $histResult['with_history'] ?? [];
+            if (empty($productsWithHistory)) {
+                continue;
+            }
+
+            $productSalesShare = StorePlanService::calculateProductSalesShareProductsWithHistory(
+                $filters['store_id'],
+                $filters['month'],
+                $productsWithHistory
+            );
+
+            if (empty($productSalesShare)) {
+                continue;
+            }
+
+            $productSalesForecast = $this->calculateProductForecastInPiecesProductsWithHistory(
+                $filters['store_id'],
+                $filters['month'],
+                $year,
+                $goals,
+                $productSalesShare,
+                $filters['category'],
+                $filters['subcategory'],
+                $filters['species']
+
+            );
+
+
+            if (!empty($productSalesForecast)) {
+                $result = array_merge($result, $productSalesForecast);
+            }
+        }
+
+        return $result;
+    }
+
     public function mapGoalsBySpecies(array $rows): array {
         $mapped = [];
         foreach ($rows as $row) {
@@ -1800,12 +1866,12 @@ class AutoPlannogrammaService
     }
 
     public function calculateProductForecastInPiecesProductsWithHistory(
-        int $storeId,
-        string $month,
-        array $productSalesShare,
-        array $speciesGoals,
-        string $subcategory = null,
+        int    $storeId,
+        string $month, string $year,
+        array  $speciesGoals,
+        array  $productSalesShare,
         string $category = null,
+        string $subcategory = null,
         string $species = null
     ): array {
         $result = [];
@@ -1869,6 +1935,8 @@ class AutoPlannogrammaService
             $forecastCount = $forecastSum / $priceRecord->price;
 
             $result[] = [
+                'month' => $month,
+                'year' => $year,
                 'product_id' => $productId,
                 'goal' => $goal,
                 'goal_share' => round($forecastSum, 2),
@@ -1897,68 +1965,80 @@ class AutoPlannogrammaService
         array $pieciesForecastProductsNoHistyory,
         array $pieciesForecastProductWithHistory
     ): array {
-        $shareResult = [];
+        $groupedForecasts = [];
+
+        // Обработка товаров без истории
+        foreach ($pieciesForecastProductsNoHistyory as $group) {
+            $base = [
+                'store_id'    => $group['store_id'],
+                'month'       => $group['month'],
+                'year'        => $group['year'],
+                'category'    => $group['category'],
+                'subcategory' => $group['subcategory'],
+                'species'     => $group['species'],
+            ];
 
-        $info = $pieciesForecastProductsNoHistyory[0] ?? null;
-        if (!$info) {
-            return [];
-        }
+            foreach ($group['forecasts'] as $productId => $forecast) {
+                $key = implode('|', [
+                    $base['store_id'], $base['category'], $base['subcategory'], $base['species']
+                ]);
 
-        $noHistoryMap = $info['forecasts'] ?? [];
+                $groupedForecasts[$key]['meta'] = $base;
+                $groupedForecasts[$key]['products'][$productId] = [
+                    'product_id'       => $productId,
+                    'forecast_pieces'  => (float)$forecast,
+                    'history_status'   => 'No history',
+                ];
+            }
+        }
 
-        $piecesMap = [];
+        // Обработка товаров с историей
         foreach ($pieciesForecastProductWithHistory as $item) {
-            if (isset($item['product_id'], $item['forecast_pieces'])) {
-                $piecesMap[$item['product_id']] = $item['forecast_pieces'];
-            }
+            $key = implode('|', [
+                $item['store_id'], $item['category'], $item['subcategory'], $item['species']
+            ]);
+
+            $groupedForecasts[$key]['meta'] = [
+                'store_id'    => $item['store_id'],
+                'month'       => $item['month'],
+                'year'        => $item['year'],
+                'category'    => $item['category'],
+                'subcategory' => $item['subcategory'],
+                'species'     => $item['species'],
+            ];
+
+            $groupedForecasts[$key]['products'][$item['product_id']] = [
+                'product_id'      => $item['product_id'],
+                'forecast_pieces' => (float)$item['forecast_pieces'],
+                'history_status'  => 'With history',
+            ];
         }
 
-        $allProductIds = array_merge(
-                array_keys($noHistoryMap),
-                array_keys($piecesMap)
-        );
+        $result = [];
 
-        $quantityMap = [];
-        foreach ($allProductIds as $pid) {
-            if (isset($piecesMap[$pid])) {
-                $quantityMap[$pid] = $piecesMap[$pid];
-            } elseif (isset($noHistoryMap[$pid])) {
-                $quantityMap[$pid] = (float)$noHistoryMap[$pid];
-            } else {
-                $quantityMap[$pid] = 0;
+        foreach ($groupedForecasts as $group) {
+            $meta = $group['meta'];
+            $products = $group['products'];
+
+            $totalForecast = array_sum(array_column($products, 'forecast_pieces'));
+
+            if ($totalForecast <= 0) {
+                continue;
             }
-        }
 
-        $totalPieces = array_sum($quantityMap);
-        if ($totalPieces <= 0) {
-            return [];
-        }
+            foreach ($products as $product) {
+                $share = round($product['forecast_pieces'] / $totalForecast, 4);
 
-        $storeId     = $info['store_id'];
-        $month       = $info['month'];
-        $year        = $info['year'];
-        $category    = $info['category'];
-        $subcategory = $info['subcategory'];
-        $species     = $info['species'];
-
-        foreach ($quantityMap as $pid => $count) {
-            $share = $count / $totalPieces;
-
-            $shareResult[] = [
-                'store_id'       => $storeId,
-                'month'          => $month,
-                'year'           => $year,
-                'category'       => $category,
-                'subcategory'    => $subcategory,
-                'species'        => $species,
-                'product_id'     => $pid,
-                'forecast_pieces'=> $count,
-                'share'          => round($share, 4),
-                 'history_status'  => in_array($pid,  array_keys($noHistoryMap)) ? 'No history' : 'With history'
-            ];
+                $result[] = array_merge($meta, [
+                    'product_id'      => $product['product_id'],
+                    'forecast_pieces' => $product['forecast_pieces'],
+                    'share'           => $share,
+                    'history_status'  => $product['history_status'],
+                ]);
+            }
         }
 
-        return $shareResult;
+        return $result;
     }
 
     /**
@@ -2305,4 +2385,47 @@ class AutoPlannogrammaService
     }
 
 
+    public function calculateFullForecastForWeek(array $filters): array
+    {
+        $bouquetSpeciesForecast = [];
+        $goals = $this->calculateFullGoalChain($filters);
+        $noHistoryProductData = $this->calculateSpeciesForecastForProductsWithoutHistory($filters['plan_date'], $filters);
+        $historyProductData = $this->calculateSpeciesForecastForProductsWithHistory($filters['plan_date'], $filters, $goals);
+        $matrixForecast = MatrixBouquetForecast::find()
+            ->where(['year' => $filters['year'], 'month' => $filters['month']])
+            ->asArray()
+            ->all();
+        $matrixGroups = array_unique(ArrayHelper::getColumn($matrixForecast, 'group'));
+        $bouquetForecast = StorePlanService::getBouquetSpiecesMonthGoalFromForecast($filters['month'], $filters['year'], $filters['store_id'], $matrixGroups);
+        $speciesData = $bouquetForecast['final'];
+        foreach ($speciesData as $store_id  => $categoryData) {
+            foreach ($categoryData as $category => $subcategoryData) {
+                foreach ($subcategoryData as $subcategory => $species) {
+                    foreach ($species as $speciesInd => $row) {
+                        $bouquetSpeciesForecast[] = [
+                            'category' => $category,
+                            'subcategory' => $subcategory,
+                            'store_id' => $store_id,
+                            'species' => $speciesInd,
+                            'goal' => $row
+                        ];
+                    }
+                }
+            }
+
+        }
+        $cleanedSpeciesGoals = $this->subtractSpeciesGoals($goals, $bouquetSpeciesForecast, $noHistoryProductData);
+
+        $salesProductForecastShare = $this->calculateProductForecastShare($noHistoryProductData, $historyProductData);
+
+        $productForecastSpecies = $this->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals);
+
+        $weeklySales = $this->getHistoricalSpeciesShareByWeek($filters['plan_date'], $filters);
+
+        $weeklySalesForecast = $this->calculateWeeklyProductForecastPieces($productForecastSpecies, $weeklySales);
+
+        return $weeklySalesForecast;
+    }
+
+
 }
\ No newline at end of file
index 9a11fcfb23d3daa1d1e2c952aaf20affc9ceb133..8ae908919daa90c27fb548ba946812668185ad33 100755 (executable)
@@ -367,6 +367,7 @@ class StorePlanService
 
                     if (!isset($salesHistory[$guid])) {
                         $salesHistory[$guid] = [
+                            'store_id'    => $storeId,
                             'category'    => $row['category'],
                             'subcategory' => $row['subcategory'],
                             'species'     => $row['species'],
@@ -403,6 +404,7 @@ class StorePlanService
         foreach ($salesHistory as $guid => $info) {
             $monthsData    = $info['data'];
             $metaFields    = [
+                'store_id' => $info['store_id'],
                 'category'    => $info['category'],
                 'subcategory' => $info['subcategory'],
                 'species'     => $info['species'],