From: Vladimir Fomichev Date: Wed, 28 May 2025 10:47:14 +0000 (+0300) Subject: Прогноз товаров внутри вида X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=1bffb931a6e93d587739a5c3beafe19ebbe1578d;p=erp24_rep%2Fyii-erp24%2F.git Прогноз товаров внутри вида --- diff --git a/erp24/controllers/AutoPlannogrammaController.php b/erp24/controllers/AutoPlannogrammaController.php index 18f9eb21..f9aebf2d 100644 --- a/erp24/controllers/AutoPlannogrammaController.php +++ b/erp24/controllers/AutoPlannogrammaController.php @@ -660,7 +660,7 @@ class AutoPlannogrammaController extends BaseController ]); } - public function action81() + public function actionMonthProductsSpeciesShare() { $request = Yii::$app->request; @@ -784,7 +784,138 @@ class AutoPlannogrammaController extends BaseController 'pagination' => ['pageSize' => 100], ]); } - return $this->render('81', [ + return $this->render('month-products-species-share', [ + 'dataProvider' => $dataProvider, + 'filters' => $filters, + ]); + } + + + public function actionMonthProductsSpeciesForecast() + { + $request = Yii::$app->request; + + $filters = [ + 'category' => $request->get('category'), + 'subcategory' => $request->get('subcategory') ?? null, + 'species' => $request->get('species') ?? null, + 'store_id' => $request->get('store_id') ?? [], + 'year' => $request->get('year'), + 'month' => $request->get('month'), + 'type' => $request->get('type'), + ]; + + $dataProvider = new ArrayDataProvider([ + 'allModels' => [], + 'pagination' => ['pageSize' => 100], + ]); + + $bouquetSpeciesForecast = []; + // Обработка даты на год и месяц + if (!empty($filters['year']) && !empty($filters['month'])) { + $filters['plan_date'] = $filters['year'] . '-' . str_pad($filters['month'], 2, '0', STR_PAD_LEFT) . '-01'; + //var_dump($filters); die(); + $service = new AutoPlannogrammaService(); + + //$goals = $service->calculateFullGoalChain($filters); + $monthCategoryShare = $service->getMonthCategoryShareOrWriteOff($filters['plan_date'], $filters); + $monthCategoryGoal = $service->getMonthCategoryGoal($monthCategoryShare, $filters['plan_date']); + $monthSubcategoryShare = $service->getMonthSubcategoryShareOrWriteOff($filters['plan_date'], $filters); + $monthSubcategoryGoal = $service->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal); + $monthSpeciesShare = $service->getMonthSpeciesShareOrWriteOff($filters['plan_date'], $filters); + $goals = $service->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal); + + if ($filters['type'] == AutoPlannogrammaService::TYPE_WRITE_OFFS) { + $monthCategoryWriteOffsShare = $service->getMonthCategoryShareOrWriteOff($filters['plan_date'], $filters, $filters['type']); + $monthCategoryWriteOffsGoal = $service->getMonthCategoryGoal($monthCategoryWriteOffsShare, $filters['plan_date'], $filters['type']); + $monthSubcategoryWriteOffsShare = $service->getMonthSubcategoryShareOrWriteOff($filters['plan_date'], $filters, $filters['type']); + $monthSubcategoryWriteOffsGoals = $service->getMonthSubcategoryGoal($monthSubcategoryWriteOffsShare, $monthCategoryWriteOffsGoal, $filters['type']); + $monthSpeciesWriteOffShare = $service->getMonthSpeciesShareOrWriteOff($filters['plan_date'], $filters, $filters['type']); + $goals = $service->getMonthSpeciesGoalDirty($monthSpeciesWriteOffShare, $monthSubcategoryWriteOffsGoals, $filters['type'], $data); + } + + + $result = StorePlanService::calculateHistoricalShare( + $filters['store_id'], + $filters['month'], + $filters['year'], + $filters['category'], + $filters['subcategory'], + $filters['species'] + ); + + $noHistoryProductData = $service->calculateSpeciesForecastForProductsWithoutHistory($filters['plan_date'], $filters); + + $productSalesShare = StorePlanService::calculateProductSalesShareProductsWithHistory( + $filters['store_id'], + $filters['month'], + $result['with_history'] + ); + + $productSalesForecast = $service->calculateProductForecastInPiecesProductsWithHistory( + $filters['store_id'], + $filters['month'], + $filters['category'], + $filters['subcategory'], + $filters['species'], + $productSalesShare, + $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 = $service->subtractSpeciesGoals($goals, $bouquetSpeciesForecast, $noHistoryProductData); + + + $salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $productSalesForecast); + + $productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals); + + //var_dump($salesProductForecastShare); die(); + + + + + + $flatData = array_filter($productForecastSpecies, function ($row) use ($filters) { + foreach ($filters as $key => $value) { + if (empty($value)) continue; + if (!isset($row[$key])) continue; + + if (stripos((string)$row[$key], (string)$value) === false) { + return false; + } + } + return true; + }); + + $dataProvider = new ArrayDataProvider([ + 'allModels' => $flatData, + 'pagination' => ['pageSize' => 100], + ]); + } + return $this->render('month-products-species-forecast', [ 'dataProvider' => $dataProvider, 'filters' => $filters, ]); @@ -857,7 +988,7 @@ class AutoPlannogrammaController extends BaseController } //var_dump($bouquetSpeciesForecast); die(); $noHistoryProductData = $service->calculateSpeciesForecastForProductsWithoutHistory($filters['plan_date'], $filters); - //var_dump($noHistoryProductData); die(); + // var_dump($noHistoryProductData); die(); $cleanedSpeciesGoals = $service->subtractSpeciesGoals($data, $bouquetSpeciesForecast, $noHistoryProductData); //var_dump($cleanedSpeciesGoals); die(); diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index 2731cbd0..fd5308b0 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -573,7 +573,6 @@ class AutoPlannogrammaService */ public function calculateSpeciesForecastForProductsWithoutHistory($dateFrom, $filters): array { - $t0 = hrtime(true); // Получение ID видимых магазинов $storeIds = array_map(fn($store) => $store->id, $this->getVisibleStores()); @@ -589,8 +588,7 @@ class AutoPlannogrammaService $year = $date->format('Y'); $result = []; - $initTime = (hrtime(true) - $t0) / 1e6; // миллисекунды - Yii::warning( "Init (getVisibleStores + filters): {$initTime} ms\n"); + foreach ($storeIds as $storeId) { $t1 = hrtime(true); $histResult = StorePlanService::calculateHistoricalShare( @@ -601,8 +599,6 @@ class AutoPlannogrammaService $subcategory, $species ); - $dur = (hrtime(true) - $t1) / 1e6; - Yii::warning( "calculateHistoricalShare for store {$storeId}: {$dur} ms\n"); $productsWithoutHistory = $histResult['without_history'] ?? []; if (empty($productsWithoutHistory)) { @@ -610,33 +606,26 @@ class AutoPlannogrammaService } // ——————— WEIGHTED SALES ———————— - $t2 = hrtime(true); + $medianResults = StorePlanService::calculateMedianSalesForProductsWithoutHistoryExtended( $storeId, $month, $year, $productsWithoutHistory ); - $dur = (hrtime(true) - $t2) / 1e6; - Yii::warning("calculateMedianSalesForProductsWithoutHistory for store {$storeId}: {$dur} ms\n"); if (empty($medianResults)) { continue; } // ——————— COST CALCULATION ———————— - $t3 = hrtime(true); + $costs = StorePlanService::calculateCostForProductsWithoutHistory( $storeId, $month, $year, $medianResults ); - $dur = (hrtime(true) - $t3) / 1e6; - Yii::warning( "calculateCostForProductsWithoutHistory for store {$storeId}: {$dur} ms\n"); if (!empty($costs)) { $result = array_merge($result, $costs); } } - $totalTime = (hrtime(true) - $t0) / 1e6; - Yii::warning( "Total calculateSpeciesForecastForProductsWithoutHistory: {$totalTime} ms\n"); - return $result; } diff --git a/erp24/services/StorePlanService.php b/erp24/services/StorePlanService.php index ccf53f78..5d3f38dd 100755 --- a/erp24/services/StorePlanService.php +++ b/erp24/services/StorePlanService.php @@ -317,24 +317,13 @@ class StorePlanService * * @param int $storeId * @param array $periods Массив периодов, сформированный функцией getPeriods. - * @param string $category + * @param string|null $category * @param string|null $subcategory * @param string|null $species * - * @return array Структура данных: - * [ - * 'product_guid1' => [ - * 'YYYY-MM' => [ - * 0 => salesCount_week1, - * 1 => salesCount_week2, - * ... - * ], - * ... - * ], - * ... - * ] + * @return array */ - private static function getSalesHistory($storeId, $periods, $category, $subcategory = null, $species = null) + private static function getSalesHistory($storeId, $periods, $category = null, $subcategory = null, $species = null) { $salesHistory = []; @@ -349,34 +338,40 @@ class StorePlanService $query = Sales::find()->alias('s') ->select([ 'p1c.id as product_guid', - 'COUNT(*) as sales_count' + 'COUNT(*) as sales_count', + 'p1c.category', + 'p1c.subcategory', + 'p1c.species', ]) ->innerJoin('sales_products sp', 's.id = sp.check_id') ->innerJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id') ->where(['s.store_id' => $storeId]) ->andWhere(['between', 's.date', $dateStart, $dateEnd]) ->andWhere(['order_id' => ['', '0']]) - ->andWhere(['p1c.category' => $category]); - - if ($subcategory !== null) { - $query->andWhere(['p1c.subcategory' => $subcategory]); - } - if ($species !== null) { - $query->andWhere(['p1c.species' => $species]); - } + ->andFilterWhere(['p1c.category' => $category]) + ->andFilterWhere(['p1c.subcategory' => $subcategory]) + ->andFilterWhere(['p1c.species' => $species]) + ->groupBy([ + 'p1c.id', + 'p1c.category', + 'p1c.subcategory', + 'p1c.species', + ]); - $query->groupBy('p1c.id'); $results = $query->asArray()->all(); + foreach ($results as $row) { + $guid = $row['product_guid']; - foreach ($results as $result) { - $guid = $result['product_guid']; if (!isset($salesHistory[$guid])) { - $salesHistory[$guid] = []; - } - if (!isset($salesHistory[$guid][$periodKey])) { - $salesHistory[$guid][$periodKey] = []; + $salesHistory[$guid] = [ + 'category' => $row['category'], + 'subcategory' => $row['subcategory'], + 'species' => $row['species'], + 'data' => [], + ]; } - $salesHistory[$guid][$periodKey][$weekIndex] = (int)$result['sales_count']; + + $salesHistory[$guid]['data'][$periodKey][$weekIndex] = (int)$row['sales_count']; } } } @@ -397,33 +392,36 @@ class StorePlanService * 'without_history' => [...], * ] */ - private static function analyzeHistory($salesHistory, $periods) + private static function analyzeHistory(array $salesHistory, array $periods) { $productsWithHistory = []; $productsWithoutHistory = []; - foreach ($salesHistory as $guid => $monthsData) { + foreach ($salesHistory as $guid => $info) { + $monthsData = $info['data']; + $metaFields = [ + 'category' => $info['category'], + 'subcategory' => $info['subcategory'], + 'species' => $info['species'], + ]; $hasHistoryInAllPeriods = true; - $weeklySalesData = []; + $weeklySalesData = []; foreach ($periods as $periodKey => $periodData) { $weeksCount = count($periodData['weeks']); if (!isset($monthsData[$periodKey])) { $hasHistoryInAllPeriods = false; - // Заполняем пустыми значениями столько недель, сколько их есть в периоде. $weekData = array_fill(0, $weeksCount, 0); } else { - $weekData = []; + $weekData = []; $activeWeeks = 0; - for ($weekIndex = 0; $weekIndex < $weeksCount; $weekIndex++) { - $salesCount = isset($monthsData[$periodKey][$weekIndex]) ? $monthsData[$periodKey][$weekIndex] : 0; - $weekData[$weekIndex] = $salesCount; - if ($weekIndex == 4) { - continue; //пропускаем 5 неполную неделю в учете активных продаж - } - if ($salesCount > 0) { + for ($i = 0; $i < $weeksCount; $i++) { + $count = $monthsData[$periodKey][$i] ?? 0; + $weekData[$i] = $count; + + if ($i < 4 && $count > 0) { $activeWeeks++; } } @@ -432,18 +430,20 @@ class StorePlanService $hasHistoryInAllPeriods = false; } } + $weeklySalesData[$periodKey] = $weekData; } - $productData = [ - 'guid' => $guid, - 'weekly_sales' => $weeklySalesData, - ]; + $item = array_merge( + ['guid' => $guid], + $metaFields, + ['weekly_sales' => $weeklySalesData] + ); if ($hasHistoryInAllPeriods) { - $productsWithHistory[] = $productData; + $productsWithHistory[] = $item; } else { - $productsWithoutHistory[] = $productData; + $productsWithoutHistory[] = $item; } } @@ -454,6 +454,7 @@ class StorePlanService } + /** * Метод вычисляет значение продаж для товаров без истории. * @@ -470,48 +471,43 @@ class StorePlanService */ public static function calculateMedianSalesForProductsWithoutHistoryExtended($storeId, $selectedMonth, $selectedYear, $productsWithoutHistory) { - $t0 = hrtime(true); $targetDate = strtotime("{$selectedYear}-{$selectedMonth}-01"); $periods = self::getPeriods($targetDate, 3); $medianSalesResults = []; - $initTime = (hrtime(true) - $t0) / 1e6; // миллисекунды - Yii::warning( "Init (periods): {$initTime} ms\n"); + foreach ($productsWithoutHistory as $product) { $guid = $product['guid']; - $t1 = hrtime(true); + + $similarProductIds = self::getSimilarProductIDs($guid); if (empty($similarProductIds)) { $medianSalesResults[$guid] = 0; continue; } - $dur = (hrtime(true) - $t1) / 1e6; - Yii::warning( "getSimilarProductIDs for product {$guid} {$storeId}: {$dur} ms\n"); + $medianSales = []; $salesValuesForEachMonth = []; - $t2 = hrtime(true); + foreach ($periods as $periodKey => $monthInfo) { list($median, $salesValues) = self::calculateMedianSalesForPeriod($storeId, $similarProductIds, $monthInfo); $medianSales[$periodKey] = $median; $salesValuesForEachMonth[$periodKey] = $salesValues; } - $dur = (hrtime(true) - $t2) / 1e6; - Yii::warning("calculateMedianSalesForPeriod for product {$guid} {$storeId}: {$dur} ms\n"); - $t3 = hrtime(true); $median3 = self::calculateMedianSalesOverPeriods($storeId, $similarProductIds, $periods); $medianSalesResults[$guid] = [ + 'store_id' => $storeId, + 'category' => $product['category'], + 'subcategory' => $product['subcategory'], + 'species' => $product['species'], 'weightedValue' => $median3, 'medianSales' => $medianSales, 'salesValues' => $salesValuesForEachMonth, ]; - $dur = (hrtime(true) - $t3) / 1e6; - Yii::warning( "computeWeightedValue for product {$guid} {$storeId}: {$dur} ms\n"); - } - $totalTime = (hrtime(true) - $t0) / 1e6; - Yii::warning( "Total calculateWeightedSalesForProductsWithoutHistory: {$totalTime} ms\n"); + } return $medianSalesResults; } @@ -683,8 +679,8 @@ class StorePlanService $guidToGroup = []; - foreach ($medianProductsWithoutHistory as $guid => $qty) { - $q = (float)$qty; + foreach ($medianProductsWithoutHistory as $guid => $data) { + $q = (float)$data['weightedValue']; if ($q <= 0) continue; $price = self::getPriceForProductAtOffsetMonthWeekly( @@ -692,10 +688,9 @@ class StorePlanService ); $prices[$guid] = $price; - $nom = Products1cNomenclature::findOne($guid); - $cat = $nom->category ?? ''; - $sub = $nom->subcategory ?? ''; - $sp = $nom->species ?? ''; + $cat = $data['category']; + $sub = $data['subcategory']; + $sp = $data['species']; $groupKey = implode('|', [$cat,$sub,$sp]); $guidToGroup[$guid] = $groupKey; diff --git a/erp24/views/auto-plannogramma/81.php b/erp24/views/auto-plannogramma/81.php deleted file mode 100644 index 36934c01..00000000 --- a/erp24/views/auto-plannogramma/81.php +++ /dev/null @@ -1,125 +0,0 @@ - -
- -

- 'get']); ?> -
-
- 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('Категория') ?> -
-
- 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('Подкатегория') ?> -
-
- 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('Товар') ?> -
-
- 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('Магазин') ?> -
-
- field(new \yii\base\DynamicModel(['month' => $filters['month'] ?? '']), 'month')->dropDownList(\yii_app\helpers\DateHelper::MONTH_NUMBER_NAMES, [ - 'prompt' => 'Месяц', - 'name' => 'month', - ])->label('Плановый месяц') ?> -
- -
- field(new \yii\base\DynamicModel(['year' => $filters['year'] ?? '']), 'year')->dropDownList(['2025' => 2025, '2026' => 2026], [ - 'prompt' => 'Год', - 'name' => 'year', - ])->label('Плановый год') ?> -
-
- field(new \yii\base\DynamicModel(['type' => $filters['type'] ?? '']), 'type')->widget(Select2::class, [ - 'data' => [ - 'writeOffs' => 'Списания', - 'sales' => 'Продажи' - ], - 'options' => ['placeholder' => 'Тип', 'name' => 'type'], - 'pluginOptions' => ['allowClear' => true], - ])->label('По дефолту продажи!') ?> -
-
- 'btn btn-primary']) ?> -
-
- 'btn btn-default']) ?> -
-
- - -
- - - - 'store_id', 'label' => 'Магазин', 'value' => function ($data) { - return CityStore::findOne($data['store_id'])->name ?? null; - }], - ['attribute' => 'category', 'label' => 'Категория'], - ['attribute' => 'subcategory', 'label' => 'Подкатегория'], - ['attribute' => 'species', 'label' => 'Тип'], - ['attribute' => 'product_id', 'label' => 'GUID Товара', ], - ['attribute' => 'product_id', 'label' => 'Имя Товара', - 'value' => function ($data) { - return \yii_app\records\Products1c::findOne($data['product_id'])->name ?? null; - }, - ], - ['attribute' => 'forecast_pieces', 'label' => 'Прогноз в шт', 'format' => ['decimal', 2]], - ['attribute' => 'share', 'label' => 'Доля', - - 'format' => ['percent', 2]], - - ['attribute' => 'history_status', 'label' => 'Статус товара', ], -]; - - -?> - $dataProvider, - 'columns' => $columns, -]); ?> - -
- -

- 'get']); ?> -
-
- 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('Категория') ?> -
-
- 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('Подкатегория') ?> -
-
- 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('Товар') ?> -
-
- 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('Магазин') ?> -
-
- field(new \yii\base\DynamicModel(['month' => $filters['month'] ?? '']), 'month')->dropDownList(\yii_app\helpers\DateHelper::MONTH_NUMBER_NAMES, [ - 'prompt' => 'Месяц', - 'name' => 'month', - ])->label('Плановый месяц') ?> -
- -
- field(new \yii\base\DynamicModel(['year' => $filters['year'] ?? '']), 'year')->dropDownList(['2025' => 2025, '2026' => 2026], [ - 'prompt' => 'Год', - 'name' => 'year', - ])->label('Плановый год') ?> -
-
- field(new \yii\base\DynamicModel(['type' => $filters['type'] ?? '']), 'type')->widget(Select2::class, [ - 'data' => [ - 'writeOffs' => 'Списания', - 'sales' => 'Продажи' - ], - 'options' => ['placeholder' => 'Тип', 'name' => 'type'], - 'pluginOptions' => ['allowClear' => true], - ])->label('По дефолту продажи!') ?> -
-
- 'btn btn-primary']) ?> -
-
- 'btn btn-default']) ?> -
-
- - -
- - - - 'store_id', 'label' => 'Магазин', 'value' => function ($data) { - return CityStore::findOne($data['store_id'])->name ?? null; - }], - ['attribute' => 'category', 'label' => 'Категория'], - ['attribute' => 'subcategory', 'label' => 'Подкатегория'], - ['attribute' => 'species', 'label' => 'Тип'], - ['attribute' => 'product_id', 'label' => 'GUID Товара', ], - ['attribute' => 'product_id', 'label' => 'Имя Товара', - 'value' => function ($data) { - return \yii_app\records\Products1c::findOne($data['product_id'])->name ?? null; - }, - ], - ['attribute' => 'forecast_pieces', 'label' => 'Прогноз в шт', 'format' => ['decimal', 2]], - ['attribute' => 'share', 'label' => 'Доля', - - 'format' => ['percent', 2]], - ['attribute' => 'cleanGoal', 'label' => 'Цель вида очищенная', 'format' => ['decimal', 2]], - ['attribute' => 'product_sales', 'label' => 'Прогноз в стоимости внутри вида', 'format' => ['decimal', 2]], - ['attribute' => 'history_status', 'label' => 'Статус товара', ], -]; - - -?> - $dataProvider, - 'columns' => $columns, -]); ?> - +
+ +

+ 'get']); ?> +
+
+ 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('Категория') ?> +
+
+ 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('Подкатегория') ?> +
+
+ 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('Товар') ?> +
+
+ 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('Магазин') ?> +
+
+ field(new \yii\base\DynamicModel(['month' => $filters['month'] ?? '']), 'month')->dropDownList(\yii_app\helpers\DateHelper::MONTH_NUMBER_NAMES, [ + 'prompt' => 'Месяц', + 'name' => 'month', + ])->label('Плановый месяц') ?> +
+ +
+ field(new \yii\base\DynamicModel(['year' => $filters['year'] ?? '']), 'year')->dropDownList(['2025' => 2025, '2026' => 2026], [ + 'prompt' => 'Год', + 'name' => 'year', + ])->label('Плановый год') ?> +
+
+ field(new \yii\base\DynamicModel(['type' => $filters['type'] ?? '']), 'type')->widget(Select2::class, [ + 'data' => [ + 'writeOffs' => 'Списания', + 'sales' => 'Продажи' + ], + 'options' => ['placeholder' => 'Тип', 'name' => 'type'], + 'pluginOptions' => ['allowClear' => true], + ])->label('По дефолту продажи!') ?> +
+
+ 'btn btn-primary']) ?> +
+
+ 'btn btn-default']) ?> +
+
+ + +
+ + + + 'store_id', 'label' => 'Магазин', 'value' => function ($data) { + return CityStore::findOne($data['store_id'])->name ?? null; + }], + ['attribute' => 'category', 'label' => 'Категория'], + ['attribute' => 'subcategory', 'label' => 'Подкатегория'], + ['attribute' => 'species', 'label' => 'Тип'], + ['attribute' => 'product_id', 'label' => 'GUID Товара', ], + ['attribute' => 'product_id', 'label' => 'Имя Товара', + 'value' => function ($data) { + return \yii_app\records\Products1c::findOne($data['product_id'])->name ?? null; + }, + ], + ['attribute' => 'forecast_pieces', 'label' => 'Прогноз в шт', 'format' => ['decimal', 2]], + ['attribute' => 'share', 'label' => 'Доля', + + 'format' => ['percent', 2]], + ['attribute' => 'cleanGoal', 'label' => 'Цель вида очищенная', 'format' => ['decimal', 2]], + ['attribute' => 'product_sales', 'label' => 'Прогноз в стоимости внутри вида', 'format' => ['decimal', 2]], + ['attribute' => 'history_status', 'label' => 'Статус товара', ], +]; + + +?> + $dataProvider, + 'columns' => $columns, +]); ?> + +
+ +

+ 'get']); ?> +
+
+ 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('Категория') ?> +
+
+ 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('Подкатегория') ?> +
+
+ 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('Товар') ?> +
+
+ 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('Магазин') ?> +
+
+ field(new \yii\base\DynamicModel(['month' => $filters['month'] ?? '']), 'month')->dropDownList(\yii_app\helpers\DateHelper::MONTH_NUMBER_NAMES, [ + 'prompt' => 'Месяц', + 'name' => 'month', + ])->label('Плановый месяц') ?> +
+ +
+ field(new \yii\base\DynamicModel(['year' => $filters['year'] ?? '']), 'year')->dropDownList(['2025' => 2025, '2026' => 2026], [ + 'prompt' => 'Год', + 'name' => 'year', + ])->label('Плановый год') ?> +
+
+ field(new \yii\base\DynamicModel(['type' => $filters['type'] ?? '']), 'type')->widget(Select2::class, [ + 'data' => [ + 'writeOffs' => 'Списания', + 'sales' => 'Продажи' + ], + 'options' => ['placeholder' => 'Тип', 'name' => 'type'], + 'pluginOptions' => ['allowClear' => true], + ])->label('По дефолту продажи!') ?> +
+
+ 'btn btn-primary']) ?> +
+
+ 'btn btn-default']) ?> +
+
+ + +
+ + + + 'store_id', 'label' => 'Магазин', 'value' => function ($data) { + return CityStore::findOne($data['store_id'])->name ?? null; + }], + ['attribute' => 'category', 'label' => 'Категория'], + ['attribute' => 'subcategory', 'label' => 'Подкатегория'], + ['attribute' => 'species', 'label' => 'Тип'], + ['attribute' => 'product_id', 'label' => 'GUID Товара', ], + ['attribute' => 'product_id', 'label' => 'Имя Товара', + 'value' => function ($data) { + return \yii_app\records\Products1c::findOne($data['product_id'])->name ?? null; + }, + ], + ['attribute' => 'forecast_pieces', 'label' => 'Прогноз в шт', 'format' => ['decimal', 2]], + ['attribute' => 'share', 'label' => 'Доля', + + 'format' => ['percent', 2]], + + ['attribute' => 'history_status', 'label' => 'Статус товара', ], +]; + + +?> + $dataProvider, + 'columns' => $columns, +]); ?> +