From: fomichev Date: Mon, 2 Jun 2025 15:39:01 +0000 (+0300) Subject: Доля продаж недельи для вида X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=9e8c1c0959e0d4d7024c40eb332b5cf173f602d2;p=erp24_rep%2Fyii-erp24%2F.git Доля продаж недельи для вида --- diff --git a/erp24/controllers/AutoPlannogrammaController.php b/erp24/controllers/AutoPlannogrammaController.php index 24215970..c362cba8 100644 --- a/erp24/controllers/AutoPlannogrammaController.php +++ b/erp24/controllers/AutoPlannogrammaController.php @@ -1075,8 +1075,8 @@ class AutoPlannogrammaController extends BaseController $productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals); - $weeklySales = $service->getHistoricalSpeciesShareByWeek($filters['plan_date'], $filters); - var_dump($weeklySales); die(); + + //var_dump($productForecastSpecies); die(); @@ -1260,4 +1260,136 @@ class AutoPlannogrammaController extends BaseController } + public function actionWeekSalesProductsForecast() + { + $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['plan_date']); 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'], + $productSalesShare, + $goals, + $filters['subcategory'], + $filters['category'], + $filters['species'] + ); + + $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); + + + $weeklySales = $service->getHistoricalSpeciesShareByWeek($filters['plan_date'], $filters); + + $weeklySalesForecast = $service->calculateWeeklyProductForecastPieces($productForecastSpecies, $weeklySales); + + //var_dump($weeklySalesForecast); die(); + + + $flatData = array_filter($weeklySalesForecast, 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('week-sales-products_forecast', [ + 'dataProvider' => $dataProvider, + 'filters' => $filters, + ]); + } + + } diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index c94aedb3..74ea14ab 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -18,6 +18,7 @@ use yii_app\records\Products1cNomenclature; use yii_app\records\Sales; use yii_app\records\SalesProducts; use yii_app\records\SalesWriteOffsPlan; +use yii_app\records\StorePlan; use yii_app\records\WriteOffs; use yii_app\records\WriteOffsProducts; @@ -1169,6 +1170,67 @@ class AutoPlannogrammaService return $result; } + + public function calculateWeeklyProductForecastPieces( + array $productForecastSpecies, + array $weeklySales + ): array { + + $forecastMap = []; + foreach ($productForecastSpecies as $item) { + $sid = $item['store_id']; + $cat = $item['category']; + $sub = $item['subcategory']; + $spec = $item['species']; + $pid = $item['product_id']; + $piecesMon = (float)$item['product_sales_pieces']; + + $forecastMap[$sid][$cat][$sub][$spec][$pid] = $piecesMon; + } + + $result = []; + + foreach ($weeklySales as $w) { + $sid = $w['store_id']; + $cat = $w['category']; + $sub = $w['subcategory']; + $spec = $w['species']; + $week = $w['week']; + $wShare = (float)$w['share']; + + if ( + ! isset( + $forecastMap[$sid], + $forecastMap[$sid][$cat], + $forecastMap[$sid][$cat][$sub], + $forecastMap[$sid][$cat][$sub][$spec] + ) + ) { + continue; + } + + $productsInSpec = $forecastMap[$sid][$cat][$sub][$spec]; + + foreach ($productsInSpec as $pid => $piecesMon) { + $forecastWeekPieces = round($piecesMon * $wShare, 2); + + $result[] = [ + 'week' => $week, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $spec, + 'product_id' => $pid, + 'forecast_week_pieces' => $forecastWeekPieces, + ]; + } + } + + return $result; + } + + + /** * Исторический недельный отчёт и доли по видам с учётом store_id. * @@ -1874,20 +1936,48 @@ class AutoPlannogrammaService $goalsMap[$key] = $item['goal']; } } + $regions = CityStoreParams::find() + ->select(['store_id', 'address_region']) + ->indexBy('store_id') + ->asArray() + ->all(); + $products = ArrayHelper::getColumn($productShares, 'product_id'); + + $prices = PricesDynamic::find() + ->select(['product_id', 'price', 'region_id']) + ->where(['product_id' => $products]) + ->andWhere(['active' => 1]) + ->asArray() + ->all(); + $pricesMap = []; + foreach ($prices as $price) { + $pricesMap[$price['product_id']][$price['region_id']][] = $price['price']; + } foreach ($productShares as $shareItem) { + $storeId = $shareItem['store_id']; + + $region = $regions[$storeId]['address_region'] + ?? BouquetComposition::REGION_NN; + + $priceList = $pricesMap[$shareItem['product_id']][$region] ?? null; + $price = is_array($priceList) && count($priceList) > 0 + ? $priceList[0] ?? 1 + : 1; + + $key = implode('|', [ $shareItem['store_id'], $shareItem['category'], $shareItem['subcategory'], $shareItem['species'] ]); - if (!isset($goalsMap[$key])) { - continue; - } - $cleanGoal = $goalsMap[$key]; + + $cleanGoal = $goalsMap[$key] ?? 0; $productSales = $shareItem['share'] * $cleanGoal; + $productSalesPieces = round($productSales / $price, 2); + $result[] = [ 'store_id' => $shareItem['store_id'], 'month' => $shareItem['month'] ?? null, @@ -1901,6 +1991,7 @@ class AutoPlannogrammaService 'history_status' => $shareItem['history_status'], 'cleanGoal' => $cleanGoal, 'product_sales' => round($productSales, 2), + 'product_sales_pieces' => $productSalesPieces ]; } diff --git a/erp24/views/auto-plannogramma/month-products-species-forecast.php b/erp24/views/auto-plannogramma/month-products-species-forecast.php index 7385b452..f9a0db4b 100644 --- a/erp24/views/auto-plannogramma/month-products-species-forecast.php +++ b/erp24/views/auto-plannogramma/month-products-species-forecast.php @@ -112,6 +112,7 @@ $columns = [ ['attribute' => 'share', 'label' => 'Доля', 'pageSummary' => true, 'format' => ['percent', 2]], ['attribute' => 'cleanGoal', 'label' => 'Цель вида очищенная', 'pageSummary' => true, 'format' => ['decimal', 2]], ['attribute' => 'product_sales', 'label' => 'Прогноз в стоимости внутри вида', 'pageSummary' => true, 'format' => ['decimal', 2]], + ['attribute' => 'product_sales_pieces', 'label' => 'Прогноз в стоимости внутри вида шт', 'pageSummary' => true, 'format' => ['decimal', 2]], ['attribute' => 'history_status', 'label' => 'Статус товара', ], ]; diff --git a/erp24/views/auto-plannogramma/week-sales-products_forecast.php b/erp24/views/auto-plannogramma/week-sales-products_forecast.php new file mode 100644 index 00000000..321c84fc --- /dev/null +++ b/erp24/views/auto-plannogramma/week-sales-products_forecast.php @@ -0,0 +1,136 @@ + +
+ +

+ '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']) ?> +
+
+ + +
+ + + + 'week', + 'label' => 'Неделя', + ], + [ + 'attribute' => 'store_id', + 'label' => 'Магазин', + 'value' => function ($data) { + return CityStore::findOne($data['store_id'])->name ?? $data['store_id']; + }, + ], + [ + 'attribute' => 'category', + 'label' => 'Категория', + ], + [ + 'attribute' => 'subcategory', + 'label' => 'Подкатегория', + ], + [ + 'attribute' => 'species', + 'label' => 'Тип', + ],['attribute'=>'week','label'=>'Неделя'], + [ + 'attribute'=>'store_id','label'=>'Магазин', + 'value'=>function($data){ return CityStore::findOne($data['store_id'])->name ?? $data['store_id']; } + ], + + ['attribute'=>'product_id','label'=>'GUID Товара'], + ['attribute'=>'forecast_week_pieces','label'=>'Прогноз (шт.)','format'=>['decimal',2]], +]; + +echo GridView::widget([ + 'dataProvider' => $dataProvider, + 'showPageSummary' => true, + 'columns' => $columns, +]); +