namespace app\controllers;
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Yii;
+ use yii\base\DynamicModel;
use yii\data\ArrayDataProvider;
use yii\db\Expression;
use yii\db\Query;
}
+ public function actionWeekSalesSpeciesExcel()
+ {
+ $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],
+ ]);
+
+
+ return $this->render('week-sales-species-excel', [
+ 'dataProvider' => $dataProvider,
+ 'filters' => $filters,
+ ]);
+ }
+
+
+ public function actionExportExcel()
+ {
+ $rows = [];
+ /* $rows[] = [
+ 'week' => '1',
+ 'type_pm' => 'max',
+ 'shop' => 'Ванеева',
+ 'category' => 'Срезка',
+ 'subcategory' => 'Розы',
+ 'species' => 'Роза',
+ 'product_name' => 'Роза Эквадор',
+ 'quantity' => '3',
+ 'value_type' => 'offline',
+ 'group_name' => 'Оффлайн',
+ ];*/
+ $request = Yii::$app->request;
+ $this->layout = false;
+ Yii::$app->response->format = Response::FORMAT_RAW;
+
+
+ while (ob_get_level() > 0) {
+ ob_end_clean();
+ }
+
+ $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'),
+ ];
+
+
+
+ $bouquetSpeciesForecast = [];
+
+
+ $filters['plan_date'] = $filters['year'] . '-' . str_pad($filters['month'], 2, '0', STR_PAD_LEFT) . '-01';
+ // var_dump( $filters['plan_date']); die();
+ $service = new AutoPlannogrammaService();
+
+ $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);
+
+
+ foreach ($weeklySalesForecast as $fc ) {
+ $rows[] = [
+ 'week' => $fc['week'],
+ 'type_pm' => 'max',
+ 'shop' => $fc['store_id'],
+ 'category' => $fc['category'],
+ 'subcategory' => $fc['subcategory'],
+ 'species' => $fc['species'],
+ 'product_name' => $fc['product_id'],
+ 'quantity' => $fc['forecast_week_pieces'],
+ 'value_type' => 'offline',
+ 'group_name' => 'Оффлайн',
+ ];
+ }
+
+
+
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setTitle('Отчёт');
+
+ $sheet->setCellValue('A1', 'Неделя');
+ $sheet->setCellValue('B1', 'Тип п-ма');
+ $sheet->setCellValue('C1', 'Магазин');
+ $sheet->setCellValue('D1', 'Категория');
+ $sheet->setCellValue('E1', 'Подкатегория');
+ $sheet->setCellValue('F1', 'Вид');
+ $sheet->setCellValue('G1', 'Товар');
+ $sheet->setCellValue('H1', 'Кол-во');
+ $sheet->setCellValue('I1', 'Тип значения');
+ $sheet->setCellValue('J1', 'Имя группы');
+
+ $rowNum = 2;
+
+ foreach ($rows as $row) {
+ $sheet->setCellValue('A' . $rowNum, $row['week']);
+ $sheet->setCellValue('B' . $rowNum, $row['type_pm']);
+ $sheet->setCellValue('C' . $rowNum, $row['shop']);
+ $sheet->setCellValue('D' . $rowNum, $row['category']);
+ $sheet->setCellValue('E' . $rowNum, $row['subcategory']);
+ $sheet->setCellValue('F' . $rowNum, $row['species']);
+ $sheet->setCellValue('G' . $rowNum, $row['product_name']);
+ $sheet->setCellValue('H' . $rowNum, $row['quantity']);
+ $sheet->setCellValue('I' . $rowNum, $row['value_type']);
+ $sheet->setCellValue('J' . $rowNum, $row['group_name']);
+ $rowNum++;
+ }
+
+ foreach (range('A', 'J') as $col) {
+ $sheet->getColumnDimension($col)->setAutoSize(true);
+ }
+
+
+
+ $tempFile = tempnam(sys_get_temp_dir(), 'xlsx');
+ $writer = new Xlsx($spreadsheet);
+ $writer->save($tempFile);
+
+ return Yii::$app
+ ->response
+ ->sendStreamAsFile(
+ fopen($tempFile, 'rb'),
+ 'report_' . date('Ymd_His') . '.xlsx',
+ [
+ 'mimeType' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'inline' => false,
+ 'deleteFileAfterSend' => true,
+ ]
+ );
+ }
+
+
+ /**
+ * Получает JSON из тела запроса (массив строк) и экспортирует его в Excel.
+ * Если JSON не передан или невалиден, подставляет тестовые данные.
+ */
+ public function actionExportJsonToExcel()
+ {
+ $this->layout = false;
+ Yii::$app->response->format = Response::FORMAT_RAW;
+
+ while (ob_get_level() > 0) {
+ ob_end_clean();
+ }
+
+ $rawBody = Yii::$app->request->getRawBody();
+ $decoded = json_decode($rawBody, true);
+
+ if (is_array($decoded) && !empty($decoded)) {
+ $rows = $decoded;
+ } else {
+ $rows = [
+ [
+ 'week' => '1',
+ 'type_pm' => 'max',
+ 'shop' => 'Ванеева',
+ 'category' => 'Срезка',
+ 'subcategory' => 'Розы',
+ 'species' => 'Роза',
+ 'product_name' => 'Роза Эквадор',
+ 'quantity' => '3',
+ 'value_type' => 'offline',
+ 'group_name' => 'Оффлайн',
+ ],
+ [
+ 'week' => '2',
+ 'type_pm' => 'min',
+ 'shop' => 'Ленина',
+ 'category' => 'Срезка',
+ 'subcategory' => 'Гвоздики',
+ 'species' => 'Гвоздика',
+ 'product_name' => 'Гвоздика Стандарт',
+ 'quantity' => '5',
+ 'value_type' => 'online',
+ 'group_name' => 'Онлайн',
+ ],
+ ];
+ }
+
+ $spreadsheet = new Spreadsheet();
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setTitle('Отчёт из JSON');
+
+ $sheet->setCellValue('A1', 'Неделя');
+ $sheet->setCellValue('B1', 'Тип п-ма');
+ $sheet->setCellValue('C1', 'Магазин');
+ $sheet->setCellValue('D1', 'Категория');
+ $sheet->setCellValue('E1', 'Подкатегория');
+ $sheet->setCellValue('F1', 'Вид');
+ $sheet->setCellValue('G1', 'Товар');
+ $sheet->setCellValue('H1', 'Кол-во');
+ $sheet->setCellValue('I1', 'Тип значения');
+ $sheet->setCellValue('J1', 'Имя группы');
+
+ $rowNum = 2;
+ foreach ($rows as $row) {
+ $week = $row['week'] ?? '';
+ $typePm = $row['type_pm'] ?? '';
+ $shop = $row['shop'] ?? '';
+ $category = $row['category'] ?? '';
+ $subcategory = $row['subcategory'] ?? '';
+ $species = $row['species'] ?? '';
+ $productName = $row['product_name'] ?? '';
+ $quantity = $row['quantity'] ?? '';
+ $valueType = $row['value_type'] ?? '';
+ $groupName = $row['group_name'] ?? '';
+
+ $sheet->setCellValue("A{$rowNum}", $week);
+ $sheet->setCellValue("B{$rowNum}", $typePm);
+ $sheet->setCellValue("C{$rowNum}", $shop);
+ $sheet->setCellValue("D{$rowNum}", $category);
+ $sheet->setCellValue("E{$rowNum}", $subcategory);
+ $sheet->setCellValue("F{$rowNum}", $species);
+ $sheet->setCellValue("G{$rowNum}", $productName);
+ $sheet->setCellValue("H{$rowNum}", $quantity);
+ $sheet->setCellValue("I{$rowNum}", $valueType);
+ $sheet->setCellValue("J{$rowNum}", $groupName);
+
+ $rowNum++;
+ }
+
+ foreach (range('A', 'J') as $col) {
+ $sheet->getColumnDimension($col)->setAutoSize(true);
+ }
+
+ $tempFile = tempnam(sys_get_temp_dir(), 'xlsx');
+ $writer = new Xlsx($spreadsheet);
+ $writer->save($tempFile);
+
+ return Yii::$app
+ ->response
+ ->sendStreamAsFile(
+ fopen($tempFile, 'rb'),
+ 'report_' . date('Ymd_His') . '.xlsx',
+ [
+ 'mimeType' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'inline' => false,
+ 'deleteFileAfterSend' => true,
+ ]
+ );
+ }
+
+ public function actionTest()
+ {
+ $request = Yii::$app->request;
+
+ $filters = [
+ 'category' => 'Срезка',
+ 'subcategory' => 'Розы' ?? null,
+ 'species' => 'Роза' ?? null,
+ 'store_id' => 2 ?? [],
+ 'year' => 2025,
+ 'month' => 5,
+ 'type' => 'sales',
+ ];
+
+ $stores = ArrayHelper::map(
+ CityStore::findAll(['visible' => CityStore::IS_VISIBLE]),
+ 'id',
+ 'name'
+ );
+ // var_dump($stores);die();
+ $dataProvider = new ArrayDataProvider([
+ 'allModels' => [],
+ 'pagination' => ['pageSize' => 100],
+ ]);
+ $flatData =[];
+ $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);
+
+
+
+
+ $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('test', [
+ 'dataProvider' => $dataProvider,
+ 'data' => $flatData,
+ 'filters' => $filters,
+ 'stores' => $stores
+ ]);
+ }
+
+
+ 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()
+ {
+ $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();
+
+ $monthSpeciesGoals = $service->calculateFullGoalChain($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, $monthSpeciesGoals);
+
+ $datePlan = $filters['plan_date'];
+ $monthCategoryShare = $service->getMonthCategoryShareOrWriteOff($datePlan, $filters, $filters['type']);
+ $monthCategoryGoal = $service->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters['type']);
+
+ $monthCategorySalesShare = $service->getMonthCategoryShareOrWriteOff($datePlan, $filters);
+ $monthCategorySalesGoal = $service->getMonthCategoryGoal($monthCategorySalesShare, $datePlan);
+
+ foreach ($monthCategoryShare as $sid => $cats) {
+ foreach ($cats as $cat) {
+ $monthCategoryShareResult[$sid][$cat['category']]['total_sum'] = $cat['total_sum'];
+ $monthCategoryShareResult[$sid][$cat['category']]['percent'] = $cat['percent'];
+ }
+
+ }
+ foreach ($monthCategoryGoal as $cats) {
+ $monthCategoryShareResult[$cats['store_id']][$cats['category']]['goal'] = $cats['goal'];
+
+ }
+ $monthSubcategorySalesShare = $service->getMonthSubcategoryShareOrWriteOff($datePlan, $filters);
+ $monthSubcategorySalesGoal = $service->getMonthSubcategoryGoal($monthSubcategorySalesShare, $monthCategorySalesGoal);
+
+ $monthSubcategoryShare = $service->getMonthSubcategoryShareOrWriteOff($datePlan, $filters, $filters['type']);
+ $monthSubcategoryGoal = $service->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal, $filters['type'], $monthSubcategorySalesGoal);
+
+ 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'] = $subcat['percent'];
+ }
+ foreach ($monthSubcategoryGoal as $cats) {
+ $monthCategoryShareResult[$cats['store_id']][$cats['category']][$cats['subcategory']]['goal'] = $cats['goal'];
+
+ }
+ $monthSpeciesSalesShare = $service->getMonthSpeciesShareOrWriteOff($datePlan, $filters);
+ $monthSpeciesSalesGoal = $service->getMonthSpeciesGoalDirty($monthSpeciesSalesShare, $monthSubcategoryGoal);
+
+ $monthSpeciesShare = $service->getMonthSpeciesShareOrWriteOff($datePlan, $filters, $filters['type']);
+ $monthSpeciesGoal = $service->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal, $filters['type'], $monthSpeciesSalesGoal);
+
+ 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'] = $species['percent'];
+
+ }
+ foreach ($monthSpeciesGoal as $cats) {
+ $monthCategoryShareResult[$cats['store_id']][$cats['category']][$cats['subcategory']][$cats['species']]['goal'] = $cats['goal'];
+
+ }
+
+ foreach ($weeksShareResult 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', [
+ 'model' => $model,
+ 'result' => $monthResult,
+ 'weeksData' => $weeksData,
+ 'monthCategoryShare' => $monthCategoryShareResult,
+ 'weeksProductForecast' => $weeksProductForecast,
+ 'totals' => $totals,
+ 'storeList' => $storeList,
+ 'monthsList' => $monthsList,
+
+ ]);
+ }
+
+ public function actionGetSubcategories(string $category, int $year, int $week): array
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+
+ $data = Autoplannogramma::find()
+ ->alias('a')
+ ->leftJoin('products_1c_nomenclature p', 'a.product_id = p.id')
+ ->where(['p.category' => $category])
+ ->andWhere(['a.year' => $year])
+ ->andWhere(['a.week' => $week])
+ ->select([
+ 'p.subcategory as name',
+ new \yii\db\Expression("CASE WHEN COUNT(a.id) > 0 THEN 1 ELSE 0 END AS hasData")
+ ])
+ ->groupBy('p.subcategory')
+ ->asArray()
+ ->all();
+
+ return $data;
+ }
+
+ public function actionWeeklyBouquetProductsForecast()
+ {
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+
+ $request = Yii::$app->request;
+ $storeIdRequest = $request->get('storeId', null);
+ $monthRequest = $request->get('month');
+ $yearRequest = $request->get('year');
+ $weekRequest = $request->get('week');
+
+ if (!$monthRequest || !$yearRequest || $weekRequest === null) {
+ return ['success' => false, 'message' => 'Нет параметров'];
+ }
+
+ $service = new AutoPlannogrammaService();
+ $result = $service->getWeeklyBouquetProductsForecast($monthRequest, $yearRequest, $storeIdRequest);
+
+ if (!is_array($result)) {
+ return ['success' => false, 'message' => 'Ошибка структуры данных'];
+ }
+
+ $grouped = [];
+ $salesShares = [];
+
+ $plans = SalesWriteOffsPlan::find()
+ ->where(['month' => $monthRequest, 'year' => $yearRequest])
+ ->indexBy('store_id')
+ ->asArray()
+ ->all();
+ if ($plans) {
+ foreach ($plans as $storeId => $plan) {
+ $total = $plan['total_sales_plan'];
+ $offline = $plan['offline_sales_plan'];
+ $online = $plan['online_sales_shop_plan'];
+ $market = $plan['online_sales_marketplace_plan'];
+ $salesShares[$storeId]['offline'] = round($offline / $total, 4);
+ $salesShares[$storeId]['online'] = round($online / $total, 4);
+ $salesShares[$storeId]['marketplace'] = round($market / $total, 4);
+ }
+ }
+
+ foreach ($result as $item) {
+ $weekItem = (int) $item['week'];
+ $storeItem = (int) $item['store_id'];
+ $guid = (string) $item['product_guid'];
+ $group = (string) $item['matrix_group'];
+ $type = (string) $item['type'];
+ $forecastValue = (float) $item['week_forecast'];
+ if (isset($salesShares[$storeItem]) && isset($salesShares[$storeItem][$type])) {
+ $grouped[$weekItem][$storeItem][$type]['share'] = $salesShares[$storeItem][$type];
+ }
+
+ $grouped[$weekItem][$storeItem][$guid][$type][$group] = $forecastValue;
+ }
+
+
+ if ($weekRequest !== null) {
+ $week = (int) $weekRequest;
+ $grouped = isset($grouped[$week]) ? [$week => $grouped[$week]] : [];
+ }
+
+ return [
+ 'success' => true,
+ 'data' => $grouped,
+ ];
+
+
+ }
}