From 27c6bc114059cf673eec126ad062d8040463121e Mon Sep 17 00:00:00 2001 From: Vladimir Fomichev Date: Mon, 4 Aug 2025 17:00:58 +0300 Subject: [PATCH] =?utf8?q?=D0=A3=D0=B1=D0=B8=D1=80=D0=B0=D0=B5=D0=BC=20?= =?utf8?q?=D0=BE=D1=87=D0=B8=D1=81=D1=82=D0=BA=D1=83=20=D1=86=D0=B5=D0=BB?= =?utf8?q?=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/commands/CronController.php | 8 +- .../AutoPlannogrammaController.php | 52 +++--- erp24/services/AutoPlannogrammaService.php | 154 ++++++++++-------- 3 files changed, 114 insertions(+), 100 deletions(-) diff --git a/erp24/commands/CronController.php b/erp24/commands/CronController.php index 95844dba..8cb8cd1d 100644 --- a/erp24/commands/CronController.php +++ b/erp24/commands/CronController.php @@ -1696,7 +1696,7 @@ class CronController extends Controller $forecasts = array_merge($forecasts, $forecast); } - $logDir = Yii::getAlias('@runtime/logs/autoplannogramma'); + /*$logDir = Yii::getAlias('@runtime/logs/autoplannogramma'); if (!is_dir($logDir)) { mkdir($logDir, 0755, true); } @@ -1704,7 +1704,7 @@ class CronController extends Controller file_put_contents( $file, json_encode($forecasts, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) - ); + );*/ $this->stdout("Forecast сохранён в $file\n", BaseConsole::FG_BLUE); $writeOffsForecast = $service->getWeeklyProductsWriteoffsForecast($month, $year, $forecasts, $store->id); $salesForecast = $service->getWeeklyBouquetProductsSalesForecast($month, $year, $store->id); @@ -1727,10 +1727,6 @@ class CronController extends Controller $productId = $item['product_id']; $week = $item['week']; $quantity = (float)($item['forecast_week_pieces'] ?? 0); - if ($productId == '0fb4e768-8191-11ef-84ea-ac1f6b1b7573') { - $this->stdout("Сохраняем: product={$productId} week={$week} quantity={$quantity}\n", BaseConsole::FG_CYAN); - $this->stdout($item['forecast_week_pieces'] . "\n", BaseConsole::FG_CYAN); - } $details = []; $total = $quantity; diff --git a/erp24/controllers/AutoPlannogrammaController.php b/erp24/controllers/AutoPlannogrammaController.php index 4666036b..9c58b574 100644 --- a/erp24/controllers/AutoPlannogrammaController.php +++ b/erp24/controllers/AutoPlannogrammaController.php @@ -1107,7 +1107,7 @@ class AutoPlannogrammaController extends BaseController $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); + $goals = $service->getMonthSpeciesGoalDirty($monthSpeciesWriteOffShare, $monthSubcategoryWriteOffsGoals, $filters['type'], $goals); } @@ -1410,31 +1410,31 @@ class AutoPlannogrammaController extends BaseController $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); - +// $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, [], []); $salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $historyProductData); diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index 45fb204c..01396249 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -862,59 +862,28 @@ class AutoPlannogrammaService ->asArray() ->all(); - $rawGoals = []; + $rawGoalsSales = []; + $rawGoalsWriteOffs = []; foreach ($categoryPlan as $categoryName => $category) { - if ($filters['type'] == AutoPlannogrammaService::TYPE_SALES) { - $rawGoals[$categoryName] = (float)$category['offline'] + (float)$category['internet_shop'] + (float)$category['marketplace']; - } else { - $rawGoals[$categoryName] = (float)$category['write_offs']; - } + $rawGoalsSales[$categoryName] = (float)$category['offline'] + (float)$category['internet_shop'] + (float)$category['marketplace']; + $rawGoalsWriteOffs[$categoryName] = (float)$category['write_offs']; } - $matrixGoal = $rawGoals['Матрица'] ?? 0.0; - $nonMatrixGoals = array_filter( - $rawGoals, - fn($v, $k) => $k !== 'Матрица', - ARRAY_FILTER_USE_BOTH - ); - $sumWithoutMatrix = array_sum($nonMatrixGoals); - - if ($sumWithoutMatrix <= 0) { - foreach ($nonMatrixGoals as $categoryName => $_) { - $monthCategoryGoal[] = [ - 'category' => $categoryName, - 'store_id' => $filters['store_id'], - 'share' => 0.0, - 'goal' => 0.0, - ]; - } - } else { - foreach ($nonMatrixGoals as $categoryName => $value) { - $share = $value / $sumWithoutMatrix; - - if ($filters['type'] == AutoPlannogrammaService::TYPE_SALES) { - $distributable = max(0.0, $sumWithoutMatrix - $matrixGoal); - } else { - $distributable = $sumWithoutMatrix; - } - - $newGoal = $share * $distributable; - - $monthCategoryGoal[] = [ - 'category' => $categoryName, - 'store_id' => $filters['store_id'], - 'share' => $share, - 'goal' => $newGoal, - ]; - } - } + $monthCategoryGoalSales = $this->buildCategoryGoals($rawGoalsSales, true, $filters['store_id']); // для продаж: вычитаем матрицу + $monthCategoryGoalWriteOffs = $this->buildCategoryGoals($rawGoalsWriteOffs, false, $filters['store_id']); // для списаний: без вычитания $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOff($datePlan, $filters); - $monthSubcategoryGoal = $this->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal); - + $monthSubcategoryGoal = $this->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoalSales); $monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOff($datePlan, $filters); $monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal); + if ($filters['type'] == AutoPlannogrammaService::TYPE_WRITE_OFFS) { + $monthSubcategoryWriteOffsShare = $this->getMonthSubcategoryShareOrWriteOff($datePlan, $filters, $filters['type']); + $monthSubcategoryWriteOffsGoals = $this->getMonthSubcategoryGoal($monthSubcategoryWriteOffsShare, $monthCategoryGoalWriteOffs, $filters['type'], $monthSubcategoryGoal); + $monthSpeciesWriteOffShare = $this->getMonthSpeciesShareOrWriteOff($datePlan, $filters, $filters['type']); + $monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesWriteOffShare, $monthSubcategoryWriteOffsGoals, $filters['type'], $monthSpeciesGoal); + } + $filtered = array_filter($monthSpeciesGoal, function ($row) use ($filters) { foreach ($filters as $key => $value) { if ($value === null || $value === '') { @@ -941,6 +910,54 @@ class AutoPlannogrammaService return array_values($filtered); } + /** + * @param array $rawGoals + * @param bool $subtractMatrix если true — для распределения вычитается "Матрица" + * @param int $storeId + * @return array + */ +private function buildCategoryGoals(array $rawGoals, bool $subtractMatrix, int $storeId): array { + $result = []; + + $matrixGoal = $rawGoals['Матрица'] ?? 0.0; + + $nonMatrixGoals = array_filter( + $rawGoals, + fn($v, $k) => $k !== 'Матрица', + ARRAY_FILTER_USE_BOTH + ); + $sumWithoutMatrix = array_sum($nonMatrixGoals); + + if ($sumWithoutMatrix <= 0) { + foreach ($nonMatrixGoals as $categoryName => $_) { + $result[] = [ + 'category' => $categoryName, + 'store_id' => $storeId, + 'share' => 0.0, + 'goal' => 0.0, + ]; + } + return $result; + } + + $distributableBase = $subtractMatrix + ? max(0.0, $sumWithoutMatrix - $matrixGoal) + : $sumWithoutMatrix; + + foreach ($nonMatrixGoals as $categoryName => $value) { + $share = $value / $sumWithoutMatrix; + $newGoal = $share * $distributableBase; + + $result[] = [ + 'category' => $categoryName, + 'store_id' => $storeId, + 'share' => $share, + 'goal' => $newGoal, + ]; + } + + return $result; +} // Недельные расчеты @@ -2424,30 +2441,31 @@ class AutoPlannogrammaService $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); +// $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, [], []); $salesProductForecastShare = $this->calculateProductForecastShare($noHistoryProductData, $historyProductData); -- 2.39.5