From: fomichev Date: Mon, 12 May 2025 14:55:23 +0000 (+0300) Subject: Расчет целей недели X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=94cce08fc4dbfd7d6ee7d6a395fe47c92e6fbb1a;p=erp24_rep%2Fyii-erp24%2F.git Расчет целей недели --- diff --git a/erp24/controllers/AutoPlannogrammaController.php b/erp24/controllers/AutoPlannogrammaController.php index 5c099c2a..ffd94059 100644 --- a/erp24/controllers/AutoPlannogrammaController.php +++ b/erp24/controllers/AutoPlannogrammaController.php @@ -285,7 +285,7 @@ class AutoPlannogrammaController extends BaseController $monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']] [$monthSpeciesGoal['category']] [$monthSpeciesGoal['subcategory']] - [$monthSpeciesGoal['product_name']] = $monthSpeciesGoal['goal'] ; // переименовать + [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'] ; // переименовать } @@ -345,8 +345,11 @@ class AutoPlannogrammaController extends BaseController } $weeksShareResult = $service->getHistoricalWeeklySpeciesShare($model->month, $filters, null, 'writeOffs'); - $weeksData = $weeksShareResult['weeksData']; - // $weeksShareResult = $weeksShareResult['weeksShare']; + $weeksData = $service->calculateWeeklySpeciesGoals($weeksShareResult['weeksData'], $monthSpeciesGoals) ; + + + + @@ -358,7 +361,7 @@ class AutoPlannogrammaController extends BaseController 'model' => $model, 'result' => $monthResult, 'weeksData' => $weeksData, - // 'weeksShareResult' => $weeksShareResult, + //'monthGoal' => $monthSpeciesGoalsMap, // 'weeksGoalResult' => $weeksGoalResult, 'totals' => $totals, 'storeList' => $storeList, @@ -368,4 +371,6 @@ class AutoPlannogrammaController extends BaseController } + + } diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index 74de0b0e..5926c844 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -165,7 +165,7 @@ var_dump($totals); die(); $baseMonth = strtotime("{$month}-01"); - $monthOffsets = [2, 3, 4]; + $monthOffsets = [3, 4, 5]; $monthWeights = [3, 2, 1]; @@ -365,12 +365,7 @@ var_dump($totals); die(); return []; } - $startYear = 2020; - $endYear = $year - 1; - if ($endYear < $startYear) { - return []; - } - $years = range($startYear, $endYear); + $years = [$year - 2, $year - 1]; $query = (new Query()) ->select([ @@ -550,7 +545,7 @@ var_dump($totals); die(); $result[] = [ 'category' => $species['category'], 'subcategory' => $species['subcategory'], - 'product_name' => $species['species'], + 'species' => $species['species'], 'store_id' => $species['store_id'], 'store_name' => $storeNamesMap[$species['store_id']] ?? 'Неизвестный магазин', // Добавляем название магазина 'goal' => round($species['percent_of_month'] * $goal, 2), @@ -567,16 +562,20 @@ var_dump($totals); die(); $dateFromForCategory = (new \DateTime($datePlan))->modify('-12 months')->format('Y-m-d'); $dateFrom = (new \DateTime($datePlan))->modify('-3 months')->format('Y-m-d'); - //$monthCategoryShare = $this->getMonthCategoryShareOrWriteOff($dateFromForCategory, $filters); $monthCategoryShare = $this->getMonthCategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']); $monthCategoryGoal = $this->getMonthCategoryGoal($monthCategoryShare, $datePlan, $filters); - - // $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOff($dateFrom, $filters); $monthSubcategoryShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, $filters['type']); $monthSubcategoryGoal = $this->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal); - //$monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOff($dateFrom, $filters); - //$monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOffDate($dateFrom, $datePlan, $filters, null, $filters['type']); + if ($filters['type'] == 'writeOffs') { + $monthCategorySalesShare = $this->getMonthCategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales'); + $monthCategorySalesGoal = $this->getMonthCategoryGoal($monthCategorySalesShare, $datePlan, $filters); + $monthSubcategorySalesShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales'); + $monthSubcategorySalesGoal = $this->getMonthSubcategoryGoal($monthSubcategorySalesShare, $monthCategorySalesGoal); + + + } + $monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, $filters['type']); $monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal); // var_dump($monthSpeciesGoal); @@ -743,12 +742,7 @@ var_dump($totals); die(); return []; } - $startYear = 2020; - $endYear = $year - 1; - if ($endYear < $startYear) { - return []; - } - $years = range($startYear, $endYear); + $years = [$year - 2, $year - 1]; $query = (new Query())->select([ 'store_id' => 'ex.entity_id', @@ -928,8 +922,8 @@ var_dump($totals); die(); $month = (int)$monthStr; $year = (int)$yearStr; - $dateFrom = sprintf('%04d-%02d-01 00:00:00', $year, $month); - $dateTo = date('Y-m-d H:i:s', strtotime($dateFrom . ' +1 month -1 second')); + $dateFrom = strtotime(sprintf('%04d-%02d-01 00:00:00', $year, $month)); + $dateTo = strtotime('+1 month -1 second', $dateFrom); //var_dump($monthYear); die(); $stores = $this->getVisibleStores(); $storeIds = array_map(fn($s)=>$s->id, $stores); @@ -940,20 +934,36 @@ var_dump($totals); die(); return []; } - $result = []; + $dayOfWeek = (int)date('N', $dateFrom); + $firstMonday = $dayOfWeek === 1 + ? $dateFrom + : strtotime('next monday', $dateFrom); - foreach (range(1, 5) as $ind) { - $weekStart = date("Y-m-d 00:00:00", strtotime("+" . (($ind - 1) * 7) . ' days', strtotime($dateFrom))); - $weekEnd = date("Y-m-d 23:59:59", strtotime("+" . ($ind * 7 - 1) . ' days', strtotime($dateFrom))); - if ($weekEnd > $dateTo) { - $weekEnd = $dateTo; + + $weekRanges = []; + for ($wkStart = $firstMonday; $wkStart <= $dateTo; $wkStart += 7 * 86400) { + $wkEnd = $wkStart + 6 * 86400; + if ($wkEnd > $dateTo) { + $wkEnd = $dateTo; } - if ($weekStart > $dateTo) { - continue; + $periodStart = max($wkStart, $dateFrom); + $periodEnd = min($wkEnd, $dateTo); + $daysInMonth = floor(($periodEnd - $periodStart) / 86400) + 1; + if ($daysInMonth >= 4) { + $weekRanges[] = [ + 'index' => (int)date('W', $wkStart), + 'start' => date('Y-m-d H:i:s', $wkStart), + 'end' => date('Y-m-d H:i:s', $wkEnd), + ]; } + } + + $result = []; + foreach ($weekRanges as $range) { + $exprWeek = new Expression((string)$range['index']); $query = (new Query())->select([ - 'week' => new Expression((string)$ind), + 'week' => $exprWeek, 'store_id' => 'ex.entity_id', 'category' => 'p1c.category', 'subcategory' => 'p1c.subcategory', @@ -966,22 +976,22 @@ var_dump($totals); die(); ]); if ($type === 'writeOffs') { - $query->from(['w' => 'write_offs']) - ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = w.store_id') - ->leftJoin(['wop'=> 'write_offs_products'], 'wop.write_offs_id = w.id') - ->leftJoin(['p1c'=> 'products_1c_nomenclature'], 'p1c.id = wop.product_id') - ->andWhere(['>=', 'w.date', $weekStart]) - ->andWhere(['<=', 'w.date', $weekEnd]); + $query->from(['w' => 'write_offs']) + ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = w.store_id') + ->leftJoin(['wop'=> 'write_offs_products'], 'wop.write_offs_id = w.id') + ->leftJoin(['p1c'=> 'products_1c_nomenclature'], 'p1c.id = wop.product_id') + ->andWhere(['>=', 'w.date', $range['start']]) + ->andWhere(['<=', 'w.date', $range['end']]); if ($productFilter !== null) { $query->andWhere(['wop.product_id' => $productFilter]); } } else { $query->from(['s' => 'sales']) - ->leftJoin(['sp' => 'sales_products'], 'sp.check_id = s.id') - ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c') - ->leftJoin(['p1c'=> 'products_1c_nomenclature'], 'p1c.id = sp.product_id') - ->andWhere(['>=', 's.date', $weekStart]) - ->andWhere(['<=', 's.date', $weekEnd]); + ->leftJoin(['sp' => 'sales_products'], 'sp.check_id = s.id') + ->leftJoin(['ex' => 'export_import_table'], 'ex.export_val = s.store_id_1c') + ->leftJoin(['p1c'=> 'products_1c_nomenclature'], 'p1c.id = sp.product_id') + ->andWhere(['>=', 's.date', $range['start']]) + ->andWhere(['<=', 's.date', $range['end']]); if ($productFilter !== null) { $query->andWhere(['sp.product_id' => $productFilter]); } @@ -994,7 +1004,7 @@ var_dump($totals); die(); $rows = $query->all(); foreach ($rows as $row) { $result[] = [ - 'week' => $ind, + 'week' => $row['week'], 'store_id' => $row['store_id'], 'category' => $row['category'], 'subcategory' => $row['subcategory'], @@ -1008,16 +1018,18 @@ var_dump($totals); die(); } /** - * Исторический недельный отчёт и доли по видам без вложенных циклов. + * Исторический недельный отчёт и доли по видам с учётом store_id. * * @param string $monthYear месяц-год в формате MM-YYYY * @param array|null $filters * @param array|null $productFilter * @param string $type - * @return array{ - * 'historicalWeekly' => array, - * 'weeklyShare' => array - * } + * @return array{ 'weeksData': array } + * возвращает плоский список строк: + * [ + * ['week'=>1, 'store_id'=>2, 'category'=>'...', 'subcategory'=>'...', 'species'=>'...', 'percent'=>0.32], + * ... + * ] */ public function getHistoricalWeeklySpeciesShare( string $monthYear, @@ -1025,82 +1037,161 @@ var_dump($totals); die(); ?array $productFilter = null, string $type = 'sales' ): array { - [$monthStr, $yearStr] = explode('-', $monthYear); $month = (int)$monthStr; $year = (int)$yearStr; $historical = []; - for ($yr = 2020; $yr < $year; $yr++) { + for ($yr = $year-2; $yr < $year; $yr++) { $mYear = sprintf('%02d-%d', $month, $yr); - $weeklyData = $this->getWeeklySpeciesDataForMonth($mYear, $filters, $productFilter, $type); + $weeklyData = $this->getWeeklySpeciesDataForMonth( + $mYear, $filters, $productFilter, $type + ); foreach ($weeklyData as $row) { - $idx = $row['week']; - $historical[$idx] ??= []; - $cat = $row['category']; - $sub = $row['subcategory']; - $spec = $row['species']; - - $historical[$idx][$cat][$sub][$spec] = - ($historical[$idx][$cat][$sub][$spec] ?? 0) + $row['sum']; + $week = $row['week']; + $sid = $row['store_id']; + $cat = $row['category']; + $sub = $row['subcategory']; + $spec = $row['species']; + $sumWeek = $row['sum']; + + $historical[$week] ??= []; + $historical[$week][$sid] ??= []; + $historical[$week][$sid][$cat] ??= []; + $historical[$week][$sid][$cat][$sub] ??= []; + $historical[$week][$sid][$cat][$sub][$spec] = + ($historical[$week][$sid][$cat][$sub][$spec] ?? 0) + $sumWeek; } } - $dateFrom = sprintf('%04d-%02d-01 00:00:00', $year, $month); - $dateTo = date('Y-m-d H:i:s', strtotime($dateFrom . ' +1 month -1 second')); + $dateTo = date('Y-m-d H:i:s', strtotime("$dateFrom +1 month -1 second")); $monthWeighted = $this->getMonthSpeciesShareOrWriteOffWeighted( $dateFrom, $dateTo, $filters, $productFilter, $type ); - $monthMap = []; foreach ($monthWeighted as $m) { - $cat = $m['category']; - $sub = $m['subcategory']; - $spec = $m['species']; - $monthMap[$cat][$sub][$spec] = ($monthMap[$cat][$sub][$spec] ?? 0) + $m['total_sum']; + $sid = $m['store_id']; + $cat = $m['category']; + $sub = $m['subcategory']; + $spec = $m['species']; + $sumMonth = $m['total_sum']; + + $monthMap[$sid] ??= []; + $monthMap[$sid][$cat] ??= []; + $monthMap[$sid][$cat][$sub]??= []; + $monthMap[$sid][$cat][$sub][$spec] = + ($monthMap[$sid][$cat][$sub][$spec] ?? 0) + $sumMonth; } - $flatRows = []; - foreach ($historical as $week => $byCat) { + $weeksList = array_keys($historical); + sort($weeksList, SORT_NUMERIC); + + $speciesList = []; + foreach ($monthMap as $sid => $byCat) { foreach ($byCat as $cat => $bySub) { foreach ($bySub as $sub => $bySpec) { - foreach ($bySpec as $spec => $sumWeek) { - $sumMonth = $monthMap[$cat][$sub][$spec] ?? 0; - if ($sumMonth <= 0) { - continue; - } - $flatRows[] = [ - 'week' => $week, - 'category' => $cat, - 'subcategory' => $sub, - 'species' => $spec, - 'percent' => round($sumWeek / $sumMonth, 4), - ]; + foreach ($bySpec as $spec => $_) { + $speciesList[] = compact('sid','cat','sub','spec'); } } } } - usort($flatRows, function(array $a, array $b): int { - $cmp = strcmp($a['category'], $b['category']); - if ($cmp !== 0) { - return $cmp; + + + $rows = []; + foreach ($speciesList as $comb) { + $sid = $comb['sid']; + $cat = $comb['cat']; + $sub = $comb['sub']; + $spec = $comb['spec']; + + $sumMonth = $monthMap[$sid][$cat][$sub][$spec] ?? 0; + if ($sumMonth <= 0) { + continue; // нет месячного итога } - $cmp = strcmp($a['subcategory'], $b['subcategory']); - if ($cmp !== 0) { - return $cmp; + + foreach ($weeksList as $week) { + $sumWeek = $historical[$week][$sid][$cat][$sub][$spec] ?? 0; + $percent = $sumWeek > 0 ? round($sumWeek / $sumMonth, 4) : null; + + $rows[] = [ + 'week' => $week, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $spec, + 'percent' => $percent, + ]; } - $cmp = strcmp($a['species'], $b['species']); - if ($cmp !== 0) { - return $cmp; + } + + return ['weeksData' => $rows]; + } + + + /** + * Рассчитывает недельную цель для каждого вида (species) по данным недельных долей + * и целям месяца. + * @param array $weeksShareData + * @param array $monthSpeciesGoals + * @return array + * Плоский массив строк с полями: week, store_id, category, subcategory, + * species, percent, monthly_goal, weekly_goal + */ + public function calculateWeeklySpeciesGoals( + array $weeksShareData, + array $monthSpeciesGoals + ): array { + $monthSpeciesGoalsMap = []; + foreach ($monthSpeciesGoals as $monthSpeciesGoal) { + $monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']] + [$monthSpeciesGoal['category']] + [$monthSpeciesGoal['subcategory']] + [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'] ; + } + $result = []; + foreach ($weeksShareData as $row) { + $week = $row['week']; + $sid = $row['store_id']; + $cat = $row['category']; + $sub = $row['subcategory']; + $spec = $row['species']; + $percent = $row['percent']; + + $monthlyGoal = $monthSpeciesGoalsMap[$sid][$cat][$sub][$spec] ?? null; + + $weeklyGoal = 0; + if ($monthlyGoal !== null && $percent !== null) { + $weeklyGoal = round($percent * $monthlyGoal, 4); } - return $a['week'] <=> $b['week']; - }); - return [ - 'weeksData' => $flatRows, - ]; + $result[] = [ + 'week' => $week, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $spec, + 'percent' => $percent, + 'monthly_goal' => $monthlyGoal, + 'weekly_goal' => $weeklyGoal, + ]; + } + return $result; + } + + /** + * Возвращает дату понедельника ISO-недели в формате YYYY-MM-DD + * + * @param int $year ISO-год (может отличаться от календарного в границах года) + * @param int $week номер ISO-недели (1–53) + * @return string дата понедельника, например '2025-03-10' + */ + public static function getIsoWeekStart(int $year, int $week): string + { + $iso = $year . 'W' . str_pad($week, 2, '0', STR_PAD_LEFT) . '1'; + return date('Y-m-d', strtotime($iso)); } diff --git a/erp24/views/auto-plannogramma/control-species.php b/erp24/views/auto-plannogramma/control-species.php index b73ac84f..a92d97b2 100644 --- a/erp24/views/auto-plannogramma/control-species.php +++ b/erp24/views/auto-plannogramma/control-species.php @@ -101,6 +101,8 @@ use yii_app\records\Products1c; Вид Неделя Доля (%) + Цель месяца + Цель недели @@ -109,8 +111,16 @@ use yii_app\records\Products1c; - - formatter->asPercent($r['percent'], 1) ?> + неделя - начало + + + — + + formatter->asPercent($r['percent'], 1) ?> + + + +