From: marina Date: Tue, 17 Jun 2025 07:50:50 +0000 (+0300) Subject: ERP-360 Сборка страницы автопм X-Git-Url: https://gitweb.erp-flowers.ru/?a=commitdiff_plain;h=90bf3b60ec4af1b87179b110bf49e288af5ecb12;p=erp24_rep%2Fyii-erp24%2F.git ERP-360 Сборка страницы автопм --- diff --git a/erp24/controllers/AutoPlannogrammaController.php b/erp24/controllers/AutoPlannogrammaController.php index 42aed70e..7a3dc18b 100644 --- a/erp24/controllers/AutoPlannogrammaController.php +++ b/erp24/controllers/AutoPlannogrammaController.php @@ -1784,17 +1784,7 @@ return $forecast; $grouped = []; - 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']; - - $grouped[$weekItem][$storeItem][$guid][$type][$group] = $forecastValue; - } - + if ($week !== null) { $week = (int)$week; diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php index 17eff12b..810fc9b5 100644 --- a/erp24/services/AutoPlannogrammaService.php +++ b/erp24/services/AutoPlannogrammaService.php @@ -125,7 +125,7 @@ class AutoPlannogrammaService 'percent' => new Expression('ROUND(CAST(main.total_sum AS DECIMAL) / NULLIF(totals.total, 0), 4)'), 'type' => new Expression(':type', ['type' => $type]), 'totals_total' => 'totals.total', - 'products_list'=> 'main.products_list', + 'products_list' => 'main.products_list', ]) ->from([ 'main' => (new Query()) @@ -188,7 +188,7 @@ class AutoPlannogrammaService $type ); - if(!$items) { + if (!$items) { continue; } foreach ($items as $it) { @@ -231,7 +231,7 @@ class AutoPlannogrammaService 'new_total_store' => $newDenominator, 'base_total_store' => $baseTotal, 'products_list' => $r['products_list'], - 'products_components_list' => implode(',',$allComponentsProductIds[$sid] ?? []), + 'products_components_list' => implode(',', $allComponentsProductIds[$sid] ?? []), ]; } @@ -305,7 +305,7 @@ class AutoPlannogrammaService $productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id'; $field = $type === self::TYPE_WRITE_OFFS ? 'w.date' : 's.date'; - $base = new \DateTime($dateFrom); + $base = new \DateTime($dateFrom); $months = [ [ @@ -377,8 +377,8 @@ class AutoPlannogrammaService (clone $base)->modify('-2 year'), (clone $base)->modify('-1 year'), ]; - $componentAdds = []; - $componentAddsSumAll = []; + $componentAdds = []; + $componentAddsSumAll = []; $allComponentsProdIds = []; foreach ($storeIds as $sid) { @@ -402,9 +402,9 @@ class AutoPlannogrammaService $allComponentsProdIds[$sid] = array_unique($allComponentsProdIds[$sid]); foreach ($sums as $sr) { - $cat = $sr['category']; + $cat = $sr['category']; $subcat = $sr['subcategory']; - $val = $sr['sum']; + $val = $sr['sum']; $componentAdds[$sid][$cat][$subcat] = ($componentAdds[$sid][$cat][$subcat] ?? 0) + $val; @@ -417,17 +417,17 @@ class AutoPlannogrammaService $result = []; foreach ($rows as $r) { - $sid = $r['store_id']; - $cat = $r['category']; - $subcat = $r['subcategory']; + $sid = $r['store_id']; + $cat = $r['category']; + $subcat = $r['subcategory']; - $subTotal = (float)$r['total_sum']; - $catTotal = (float)$r['totals_total']; + $subTotal = (float)$r['total_sum']; + $catTotal = (float)$r['totals_total']; - $addCat = $componentAddsSumAll[$sid][$cat] ?? 0.0; - $addSubcat = $componentAdds[$sid][$cat][$subcat] ?? 0.0; + $addCat = $componentAddsSumAll[$sid][$cat] ?? 0.0; + $addSubcat = $componentAdds[$sid][$cat][$subcat] ?? 0.0; $newCatTotal = $catTotal + $addCat; @@ -439,15 +439,15 @@ class AutoPlannogrammaService : 0.0; $result[] = [ - 'store_id' => $sid, - 'category' => $cat, - 'subcategory' => $subcat, - 'old_total_sum' => $subTotal, - 'total_sum' => $newSubTotal, - 'percent' => $percent, - 'type' => $type, - 'new_total_store' => $newCatTotal, - 'base_total_store' => $catTotal, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $subcat, + 'old_total_sum' => $subTotal, + 'total_sum' => $newSubTotal, + 'percent' => $percent, + 'type' => $type, + 'new_total_store' => $newCatTotal, + 'base_total_store' => $catTotal, 'products_components_list' => implode(',', $allComponentsProdIds[$sid] ?? []), ]; } @@ -529,7 +529,7 @@ class AutoPlannogrammaService $productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id'; $field = $type === self::TYPE_WRITE_OFFS ? 'w.date' : 's.date'; - $base = new \DateTime($dateFrom); + $base = new \DateTime($dateFrom); $months = [ [ 'between', @@ -636,9 +636,9 @@ class AutoPlannogrammaService (clone $base)->modify('-1 year'), ]; - $finalResult = []; - $componentAdds = []; - $componentAddsSumAll = []; + $finalResult = []; + $componentAdds = []; + $componentAddsSumAll = []; $allComponentsProdIds = []; foreach ($storeIds as $sid) { @@ -662,10 +662,10 @@ class AutoPlannogrammaService $allComponentsProdIds[$sid] = array_unique($allComponentsProdIds[$sid]); foreach ($sums as $sr) { - $cat = $sr['category']; + $cat = $sr['category']; $subcat = $sr['subcategory']; $spec = $sr['species']; - $val = $sr['sum']; + $val = $sr['sum']; $componentAdds[$sid][$cat][$subcat][$spec] = ($componentAdds[$sid][$cat][$subcat][$spec] ?? 0) + $val; @@ -678,17 +678,17 @@ class AutoPlannogrammaService foreach ($result as $r) { - $sid = $r['store_id']; - $cat = $r['category']; - $subcat = $r['subcategory']; - $spec = $r['species']; + $sid = $r['store_id']; + $cat = $r['category']; + $subcat = $r['subcategory']; + $spec = $r['species']; - $specTotal = (float)$r['total_sum']; - $subcatTotal = (float)$r['totals_total']; + $specTotal = (float)$r['total_sum']; + $subcatTotal = (float)$r['totals_total']; - $addSubcat = $componentAddsSumAll[$sid][$cat][$subcat] ?? 0.0; - $addSpecies = $componentAdds[$sid][$cat][$subcat][$spec] ?? 0.0; + $addSubcat = $componentAddsSumAll[$sid][$cat][$subcat] ?? 0.0; + $addSpecies = $componentAdds[$sid][$cat][$subcat][$spec] ?? 0.0; $newSubcatTotal = $subcatTotal + $addSubcat; @@ -700,16 +700,16 @@ class AutoPlannogrammaService : 0.0; $finalResult[] = [ - 'store_id' => $sid, - 'category' => $cat, - 'subcategory' => $subcat, - 'species' => $spec , - 'old_total_sum' => $specTotal, - 'total_sum' => $newSpecTotal, - 'percent' => $percent, - 'type' => $type, - 'new_total_store' => $newSubcatTotal, - 'base_total_store' => $subcatTotal, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $subcat, + 'species' => $spec, + 'old_total_sum' => $specTotal, + 'total_sum' => $newSpecTotal, + 'percent' => $percent, + 'type' => $type, + 'new_total_store' => $newSubcatTotal, + 'base_total_store' => $subcatTotal, 'products_components_list' => implode(',', $allComponentsProdIds[$sid] ?? []), ]; } @@ -816,10 +816,10 @@ class AutoPlannogrammaService /** * Получает суммы продаж или списаний по видам (species) для каждой недели указанного месяца. * - * @param string $monthYear месяц-год в формате MM-YYYY, например '03-2025' - * @param array|null $filters опциональные фильтры ['store_id'=>...] - * @param array|null $productFilter опциональный фильтр по product_id - * @param string $type 'sales' или 'writeOffs' + * @param string $monthYear месяц-год в формате MM-YYYY, например '03-2025' + * @param array|null $filters опциональные фильтры ['store_id'=>...] + * @param array|null $productFilter опциональный фильтр по product_id + * @param string $type 'sales' или 'writeOffs' * @return array массив строк: [ * ['week'=>1,'store_id'=>...,'category'=>...,'subcategory'=>...,'species'=>...,'sum'=>...], * ... @@ -830,16 +830,17 @@ class AutoPlannogrammaService ?array $filters = null, ?array $productFilter = null, string $type = 'sales' - ): array { - [$yearStr, $monthStr ] = explode('-', $monthYear); + ): array + { + [$yearStr, $monthStr] = explode('-', $monthYear); $month = (int)$monthStr; - $year = (int)$yearStr; + $year = (int)$yearStr; $dateFrom = strtotime(sprintf('%04d-%02d-01 00:00:00', $year, $month)); - $dateTo = strtotime('+1 month -1 second', $dateFrom); + $dateTo = strtotime('+1 month -1 second', $dateFrom); - $stores = $this->getVisibleStores(); - $storeIds = array_map(fn($s)=>$s->id, $stores); + $stores = $this->getVisibleStores(); + $storeIds = array_map(fn($s) => $s->id, $stores); if (!empty($filters['store_id'])) { $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]); } @@ -860,13 +861,13 @@ class AutoPlannogrammaService $wkEnd = $dateTo; } $periodStart = max($wkStart, $dateFrom); - $periodEnd = min($wkEnd, $dateTo); - $daysInMonth = floor(($periodEnd - $periodStart) / 86400) + 1; + $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 23:59:59', $wkEnd), + 'end' => date('Y-m-d 23:59:59', $wkEnd), ]; } } @@ -876,12 +877,12 @@ class AutoPlannogrammaService foreach ($weekRanges as $range) { $exprWeek = new Expression((string)$range['index']); $query = (new Query())->select([ - 'week' => $exprWeek, - 'store_id' => 'ex.entity_id', - 'category' => 'p1c.category', + 'week' => $exprWeek, + 'store_id' => 'ex.entity_id', + 'category' => 'p1c.category', 'subcategory' => 'p1c.subcategory', - 'species' => 'p1c.species', - 'total_sum' => new Expression( + 'species' => 'p1c.species', + 'total_sum' => new Expression( $type === 'writeOffs' ? 'SUM(CAST(wop.summ AS NUMERIC))' : 'SUM(sp.summ)' @@ -889,10 +890,10 @@ class AutoPlannogrammaService ]); 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') + $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) { @@ -900,9 +901,9 @@ class AutoPlannogrammaService } } 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') + ->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) { @@ -912,17 +913,17 @@ class AutoPlannogrammaService $query->andWhere(['ex.entity_id' => $storeIds]) ->andWhere(['<>', 'p1c.species', '']) - ->groupBy(['week','ex.entity_id','p1c.category','p1c.subcategory','p1c.species']); + ->groupBy(['week', 'ex.entity_id', 'p1c.category', 'p1c.subcategory', 'p1c.species']); $rows = $query->all(); foreach ($rows as $row) { $result[] = [ - 'week' => $row['week'], - 'store_id' => $row['store_id'], - 'category' => $row['category'], + 'week' => $row['week'], + 'store_id' => $row['store_id'], + 'category' => $row['category'], 'subcategory' => $row['subcategory'], - 'species' => $row['species'], - 'sum' => (float)$row['total_sum'], + 'species' => $row['species'], + 'sum' => (float)$row['total_sum'], ]; } } @@ -934,16 +935,16 @@ class AutoPlannogrammaService * Возвращает диапазоны недель (index, start, end) для указанного года и месяца, * взятые по правилу «неделя считается, если в неё входит ≥4 дня из этого месяца». * - * @param int $year Год (например, 2025) + * @param int $year Год (например, 2025) * @param int $month Месяц (1–12) * @return array */ public function getWeekRangesForMonth(int $year, int $month): array { $dateFrom = strtotime(sprintf('%04d-%02d-01 00:00:00', $year, $month)); - $dateTo = strtotime('+1 month -1 second', $dateFrom); + $dateTo = strtotime('+1 month -1 second', $dateFrom); - $dayOfWeek = (int)date('N', $dateFrom); + $dayOfWeek = (int)date('N', $dateFrom); $firstMonday = $dayOfWeek === 1 ? $dateFrom : strtotime('next monday', $dateFrom); @@ -953,14 +954,14 @@ class AutoPlannogrammaService $wkEnd = $wkStart + 6 * 86400; $periodStart = max($wkStart, $dateFrom); - $periodEnd = min($wkEnd, $dateTo); + $periodEnd = min($wkEnd, $dateTo); $daysInMonth = floor(($periodEnd - $periodStart) / 86400) + 1; if ($daysInMonth >= 4) { $ranges[] = [ 'index' => (int)date('W', $wkStart), // ISO-неделя от $wkStart 'start' => date('Y-m-d H:i:s', $wkStart), // “год-месяц-день 00:00:00” - 'end' => date('Y-m-d 23:59:59', $wkEnd), // “год-месяц-день 23:59:59” + 'end' => date('Y-m-d 23:59:59', $wkEnd), // “год-месяц-день 23:59:59” ]; } } @@ -969,15 +970,16 @@ class AutoPlannogrammaService } public function getHistoricalSpeciesShareByWeek( - string $monthYear, - ?array $filters = null, - string $type = self::TYPE_SALES - ): array { + string $monthYear, + ?array $filters = null, + string $type = self::TYPE_SALES + ): array + { [$yearStr, $monthStr, $day] = explode('-', $monthYear); - $year = (int)$yearStr; + $year = (int)$yearStr; $month = (int)$monthStr; - $stores = $this->getVisibleStores(); + $stores = $this->getVisibleStores(); $storeIds = array_map(fn($s) => $s->id, $stores); if (!empty($filters['store_id'])) { $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]); @@ -991,30 +993,30 @@ class AutoPlannogrammaService : 'SUM(sp.quantity)'; // Таблицы и условия join - $fromTable = $type === self::TYPE_WRITE_OFFS ? ['w' => 'write_offs'] : ['s' => 'sales']; - $alias = key($fromTable); + $fromTable = $type === self::TYPE_WRITE_OFFS ? ['w' => 'write_offs'] : ['s' => 'sales']; + $alias = key($fromTable); - $productTableJoin = $type === self::TYPE_WRITE_OFFS ? ['wp' => 'write_offs_products'] : ['sp' => 'sales_products']; - $productAlias = key($productTableJoin); // 'wp' или 'sp' - $productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id'; - $storeJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'ex.export_val = w.store_id' : 'ex.export_val = s.store_id_1c'; + $productTableJoin = $type === self::TYPE_WRITE_OFFS ? ['wp' => 'write_offs_products'] : ['sp' => 'sales_products']; + $productAlias = key($productTableJoin); // 'wp' или 'sp' + $productTableJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'wp.write_offs_id = w.id' : 'sp.check_id = s.id'; + $storeJoinCondition = $type === self::TYPE_WRITE_OFFS ? 'ex.export_val = w.store_id' : 'ex.export_val = s.store_id_1c'; $monthQtyBySpecies = []; - $weekQtyByPos = []; + $weekQtyByPos = []; foreach ([$year - 2, $year - 1] as $histYear) { $histMonthStart = sprintf('%04d-%02d-01 00:00:00', $histYear, $month); - $histMonthEnd = date('Y-m-d 23:59:59', strtotime("$histMonthStart +1 month -1 second")); + $histMonthEnd = date('Y-m-d 23:59:59', strtotime("$histMonthStart +1 month -1 second")); $monthQuery = (new Query()) ->select([ - 'store_id' => 'ex.entity_id', - 'category' => 'p1c.category', + 'store_id' => 'ex.entity_id', + 'category' => 'p1c.category', 'subcategory' => 'p1c.subcategory', - 'species' => 'p1c.species', - 'month_sum' => new Expression($sumExpression), + 'species' => 'p1c.species', + 'month_sum' => new Expression($sumExpression), ]) ->from($fromTable) ->leftJoin($productTableJoin, $productTableJoinCondition) @@ -1030,11 +1032,11 @@ class AutoPlannogrammaService $monthRows = $monthQuery->all(); foreach ($monthRows as $row) { - $sid = $row['store_id']; - $cat = $row['category']; - $sub = $row['subcategory']; - $spec = $row['species']; - $qty = (float)$row['month_sum']; + $sid = $row['store_id']; + $cat = $row['category']; + $sub = $row['subcategory']; + $spec = $row['species']; + $qty = (float)$row['month_sum']; $monthQtyBySpecies[$sid][$cat][$sub][$spec] = ($monthQtyBySpecies[$sid][$cat][$sub][$spec] ?? 0.0) + $qty; @@ -1061,11 +1063,11 @@ class AutoPlannogrammaService $weekQuery = (new Query()) ->select([ - 'store_id' => 'ex.entity_id', - 'category' => 'p1c.category', + 'store_id' => 'ex.entity_id', + 'category' => 'p1c.category', 'subcategory' => 'p1c.subcategory', - 'species' => 'p1c.species', - 'week_sum' => new Expression($sumExpression), + 'species' => 'p1c.species', + 'week_sum' => new Expression($sumExpression), ]) ->from($fromTable) ->leftJoin($productTableJoin, $productTableJoinCondition) @@ -1079,18 +1081,18 @@ class AutoPlannogrammaService "{$alias}.date BETWEEN :wstart AND :wend", [ ':wstart' => $startDt->format('Y-m-d H:i:s'), - ':wend' => $endDt->format('Y-m-d H:i:s'), + ':wend' => $endDt->format('Y-m-d H:i:s'), ] )) ->groupBy(['ex.entity_id', 'p1c.category', 'p1c.subcategory', 'p1c.species']); $weekRows = $weekQuery->all(); foreach ($weekRows as $row) { - $sid = $row['store_id']; - $cat = $row['category']; - $sub = $row['subcategory']; - $spec = $row['species']; - $qty = (float)$row['week_sum']; + $sid = $row['store_id']; + $cat = $row['category']; + $sub = $row['subcategory']; + $spec = $row['species']; + $qty = (float)$row['week_sum']; if (!isset($weekQtyByPos[$weekPos])) { $weekQtyByPos[$weekPos] = []; @@ -1124,7 +1126,7 @@ class AutoPlannogrammaService $result = []; foreach ($targetRanges as $posIndex => $range) { - $weekPos = $posIndex + 1; + $weekPos = $posIndex + 1; $isoWeekNumber = $range['index']; if (!isset($shareByPos[$weekPos])) { @@ -1135,12 +1137,12 @@ class AutoPlannogrammaService foreach ($bySub as $sub => $bySpec) { foreach ($bySpec as $spec => $share) { $result[] = [ - 'store_id' => $sid, - 'category' => $cat, + 'store_id' => $sid, + 'category' => $cat, 'subcategory' => $sub, - 'species' => $spec, - 'week' => $isoWeekNumber, - 'share' => $share, + 'species' => $spec, + 'week' => $isoWeekNumber, + 'share' => $share, 'sumMonth' => $monthQtyBySpecies[$sid][$cat][$sub][$spec] ?? 0.0, 'sumWeek' => $weekQtyByPos[$weekPos][$sid][$cat][$sub][$spec] ?? 0.0 ]; @@ -1176,15 +1178,16 @@ class AutoPlannogrammaService public function calculateWeeklyProductForecastPieces( array $productForecastSpecies, array $weeklySales - ): array { + ): array + { $forecastMap = []; foreach ($productForecastSpecies as $item) { - $sid = $item['store_id']; - $cat = $item['category']; - $sub = $item['subcategory']; - $spec = $item['species']; - $pid = $item['product_id']; + $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; @@ -1193,15 +1196,15 @@ class AutoPlannogrammaService $result = []; foreach ($weeklySales as $w) { - $sid = $w['store_id']; - $cat = $w['category']; - $sub = $w['subcategory']; - $spec = $w['species']; - $week = $w['week']; + $sid = $w['store_id']; + $cat = $w['category']; + $sub = $w['subcategory']; + $spec = $w['species']; + $week = $w['week']; $wShare = (float)$w['share']; if ( - ! isset( + !isset( $forecastMap[$sid], $forecastMap[$sid][$cat], $forecastMap[$sid][$cat][$sub], @@ -1217,12 +1220,12 @@ class AutoPlannogrammaService $forecastWeekPieces = round($piecesMon * $wShare, 2); $result[] = [ - 'week' => $week, - 'store_id' => $sid, - 'category' => $cat, - 'subcategory' => $sub, - 'species' => $spec, - 'product_id' => $pid, + 'week' => $week, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $spec, + 'product_id' => $pid, 'forecast_month_pieces' => $piecesMon, 'forecast_week_pieces' => $forecastWeekPieces, ]; @@ -1248,16 +1251,16 @@ class AutoPlannogrammaService if (!isset($grouped[$key])) { $grouped[$key] = [ - 'store_id' => $row['store_id'], - 'category' => $row['category'], - 'subcategory' => $row['subcategory'], - 'species' => $row['species'], - 'product_id' => $row['product_id'], - 'forecast_month_pieces' => $row['forecast_month_pieces'], + 'store_id' => $row['store_id'], + 'category' => $row['category'], + 'subcategory' => $row['subcategory'], + 'species' => $row['species'], + 'product_id' => $row['product_id'], + 'forecast_month_pieces' => $row['forecast_month_pieces'], ]; } - $weekNum = (int)$row['week']; + $weekNum = (int)$row['week']; $fieldName = 'week' . $weekNum; $grouped[$key][$fieldName] = (float)$row['forecast_week_pieces']; @@ -1269,9 +1272,9 @@ class AutoPlannogrammaService } $allWeekFields = array_keys($allWeekFields); - foreach ($grouped as &$group) { + foreach ($grouped as &$group) { foreach ($allWeekFields as $fieldName) { - if (! isset($group[$fieldName])) { + if (!isset($group[$fieldName])) { $group[$fieldName] = 0.0; } } @@ -1285,10 +1288,10 @@ class AutoPlannogrammaService /** * Исторический недельный отчёт и доли по видам с учётом store_id. * - * @param string $monthYear месяц-год в формате MM-YYYY - * @param array|null $filters - * @param array|null $productFilter - * @param string $type + * @param string $monthYear месяц-год в формате MM-YYYY + * @param array|null $filters + * @param array|null $productFilter + * @param string $type * @return array{ 'weeksData': array } * возвращает плоский список строк: * [ @@ -1301,30 +1304,31 @@ class AutoPlannogrammaService ?array $filters = null, ?array $productFilter = null, string $type = 'sales' - ): array { + ): array + { [$monthStr, $yearStr] = explode('-', $monthYear); $month = (int)$monthStr; - $year = (int)$yearStr; + $year = (int)$yearStr; $yearData = []; $historical = []; for ($yr = $year - 2; $yr < $year; $yr++) { - $mYear = sprintf('%04d-%02d',$yr, $month); + $mYear = sprintf('%04d-%02d', $yr, $month); $weeklyData = $this->getWeeklySpeciesDataForMonth( $mYear, $filters, $productFilter, $type ); $yearData[$mYear] = $weeklyData; foreach ($weeklyData as $row) { - $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] ??= []; + $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; @@ -1332,21 +1336,21 @@ class AutoPlannogrammaService } $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->getMonthSpeciesShareOrWriteOff( $dateFrom, $filters, $type ); $monthMap = []; foreach ($monthWeighted as $m) { - $sid = $m['store_id']; - $cat = $m['category']; - $sub = $m['subcategory']; - $spec = $m['species']; + $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] ??= []; + $monthMap[$sid][$cat] ??= []; + $monthMap[$sid][$cat][$sub] ??= []; $monthMap[$sid][$cat][$sub][$spec] = ($monthMap[$sid][$cat][$sub][$spec] ?? 0) + $sumMonth; } @@ -1360,7 +1364,7 @@ class AutoPlannogrammaService foreach ($byCat as $cat => $bySub) { foreach ($bySub as $sub => $bySpec) { foreach ($bySpec as $spec => $value) { - $speciesList[] = compact('sid','cat','sub','spec'); + $speciesList[] = compact('sid', 'cat', 'sub', 'spec'); } } } @@ -1369,9 +1373,9 @@ class AutoPlannogrammaService $rows = []; foreach ($speciesList as $comb) { - $sid = $comb['sid']; - $cat = $comb['cat']; - $sub = $comb['sub']; + $sid = $comb['sid']; + $cat = $comb['cat']; + $sub = $comb['sub']; $spec = $comb['spec']; $sumMonth = $monthMap[$sid][$cat][$sub][$spec] ?? 0; @@ -1384,13 +1388,13 @@ class AutoPlannogrammaService $percent = $sumWeek > 0 ? round($sumWeek / $sumMonth, 4) : null; $rows[] = [ - 'week' => $week, - 'store_id' => $sid, - 'category' => $cat, + 'week' => $week, + 'store_id' => $sid, + 'category' => $cat, 'subcategory' => $sub, - 'species' => $spec, - 'sumWeek' => $sumWeek, - 'percent' => $percent, + 'species' => $spec, + 'sumWeek' => $sumWeek, + 'percent' => $percent, 'sumMonth' => $sumMonth ]; } @@ -1416,7 +1420,7 @@ class AutoPlannogrammaService } } - return $rows; + return $rows; } @@ -1432,39 +1436,40 @@ class AutoPlannogrammaService public function calculateWeeklySpeciesGoals( array $weeksShareData, array $monthSpeciesGoals - ): array { + ): array + { $monthSpeciesGoalsMap = []; foreach ($monthSpeciesGoals as $monthSpeciesGoal) { $monthSpeciesGoalsMap[$monthSpeciesGoal['store_id']] [$monthSpeciesGoal['category']] [$monthSpeciesGoal['subcategory']] - [$monthSpeciesGoal['species']] = $monthSpeciesGoal['goal'] ; + [$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']; + $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); + $weeklyGoal = round($percent * $monthlyGoal, 4); } $result[] = [ - 'week' => $week, - 'store_id' => $sid, - 'category' => $cat, - 'subcategory' => $sub, - 'species' => $spec, - 'percent' => $percent, + 'week' => $week, + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $sub, + 'species' => $spec, + 'percent' => $percent, 'monthly_goal' => $monthlyGoal, - 'weekly_goal' => $weeklyGoal, + 'weekly_goal' => $weeklyGoal, ]; } return $result; @@ -1473,8 +1478,8 @@ class AutoPlannogrammaService /** * Возвращает дату понедельника ISO-недели в формате YYYY-MM-DD * - * @param int $year ISO-год (может отличаться от календарного в границах года) - * @param int $week номер ISO-недели (1–53) + * @param int $year ISO-год (может отличаться от календарного в границах года) + * @param int $week номер ISO-недели (1–53) * @return string дата понедельника, например '2025-03-10' */ public static function getIsoWeekStart(int $year, int $week): string @@ -1510,7 +1515,7 @@ class AutoPlannogrammaService : BouquetComposition::REGION_NN; } - $pricesMap = self::buildPricesMap($productsIds, $region); + $pricesMap = self::buildPricesMap($productsIds, $region); foreach ($pricesMap as $id => $price) { if ($goal == 0 || (float)$price == 0) { @@ -1539,8 +1544,8 @@ class AutoPlannogrammaService /** * Общий расчёт плана для заданной категории товаров без истории. * - * @param int $storeId - * @param string $yearMonth строка "YYYY-MM", например "2025-03" + * @param int $storeId + * @param string $yearMonth строка "YYYY-MM", например "2025-03" * @param string $category * @param string|null $subcategory * @param string|null $species @@ -1676,7 +1681,8 @@ class AutoPlannogrammaService return $result; } - public function mapGoalsBySpecies(array $rows): array { + public function mapGoalsBySpecies(array $rows): array + { $mapped = []; foreach ($rows as $row) { $mapped[$row['store_id']][$row['category']][$row['subcategory']][$row['species']] = $row['goal']; @@ -1684,7 +1690,8 @@ class AutoPlannogrammaService return $mapped; } - public function subtractSpeciesGoals(array $data, array $bouquetForecast, array $noHistory): array { + public function subtractSpeciesGoals(array $data, array $bouquetForecast, array $noHistory): array + { $bouquetForecastMap = $this->mapGoalsBySpecies($bouquetForecast); $noHistoryMap = $this->mapGoalsBySpecies($noHistory); $result = []; @@ -1724,7 +1731,8 @@ class AutoPlannogrammaService string $year, array $speciesGoals, array $productSalesShare - ): array { + ): array + { $result = []; if (empty($speciesGoals)) { @@ -1746,7 +1754,7 @@ class AutoPlannogrammaService : BouquetComposition::REGION_NN; } - $pricesMap = self::buildPricesMap(array_keys($productSalesShare), $region); + $pricesMap = self::buildPricesMap(array_keys($productSalesShare), $region); foreach ($productSalesShare as $productId => $data) { $share = $data['share'] ?? 0.0; @@ -1760,12 +1768,12 @@ class AutoPlannogrammaService } $storeId = $data['store_id']; - $cat = $data['category']; - $sub = $data['subcategory']; - $spec = $data['species']; - $price = $pricesMap[$productId]; + $cat = $data['category']; + $sub = $data['subcategory']; + $spec = $data['species']; + $price = $pricesMap[$productId]; if ( - ! isset( + !isset( $goalsMap[$storeId], $goalsMap[$storeId][$cat], $goalsMap[$storeId][$cat][$sub], @@ -1804,25 +1812,26 @@ class AutoPlannogrammaService * * Объединяет прогнозы без истории и с историей для полного списка товаров. * - * @param array $pieciesForecastProductsNoHistyory Результат calculateSpeciesForecastForProductsWithoutHistory + * @param array $pieciesForecastProductsNoHistyory Результат calculateSpeciesForecastForProductsWithoutHistory * @param array $pieciesForecastProductWithHistory Результат calculateProductForecastInPiecesProductsWithHistory * @return array */ public function calculateProductForecastShare( array $pieciesForecastProductsNoHistyory, array $pieciesForecastProductWithHistory - ): array { + ): array + { $groupedForecasts = []; // Обработка товаров без истории foreach ($pieciesForecastProductsNoHistyory as $group) { $base = [ - 'store_id' => $group['store_id'], - 'month' => $group['month'], - 'year' => $group['year'], - 'category' => $group['category'], + 'store_id' => $group['store_id'], + 'month' => $group['month'], + 'year' => $group['year'], + 'category' => $group['category'], 'subcategory' => $group['subcategory'], - 'species' => $group['species'], + 'species' => $group['species'], ]; foreach ($group['forecasts'] as $productId => $forecast) { @@ -1832,9 +1841,9 @@ class AutoPlannogrammaService $groupedForecasts[$key]['meta'] = $base; $groupedForecasts[$key]['products'][$productId] = [ - 'product_id' => $productId, - 'forecast_pieces' => (float)$forecast, - 'history_status' => 'No history', + 'product_id' => $productId, + 'forecast_pieces' => (float)$forecast, + 'history_status' => 'No history', ]; } } @@ -1846,18 +1855,18 @@ class AutoPlannogrammaService ]); $groupedForecasts[$key]['meta'] = [ - 'store_id' => $item['store_id'], - 'month' => $item['month'], - 'year' => $item['year'], - 'category' => $item['category'], + 'store_id' => $item['store_id'], + 'month' => $item['month'], + 'year' => $item['year'], + 'category' => $item['category'], 'subcategory' => $item['subcategory'], - 'species' => $item['species'], + 'species' => $item['species'], ]; $groupedForecasts[$key]['products'][$item['product_id']] = [ - 'product_id' => $item['product_id'], + 'product_id' => $item['product_id'], 'forecast_pieces' => (float)$item['forecast_pieces'], - 'history_status' => 'With history', + 'history_status' => 'With history', ]; } @@ -1877,10 +1886,10 @@ class AutoPlannogrammaService $share = round($product['forecast_pieces'] / $totalForecast, 4); $result[] = array_merge($meta, [ - 'product_id' => $product['product_id'], + 'product_id' => $product['product_id'], 'forecast_pieces' => $product['forecast_pieces'], - 'share' => $share, - 'history_status' => $product['history_status'], + 'share' => $share, + 'history_status' => $product['history_status'], ]); } } @@ -1891,14 +1900,15 @@ class AutoPlannogrammaService /** * Рассчитывает продажи по каждому товару внутри вида на основе долей и очищенной цели вида. * - * @param array $productShares Результат calculateProductForecastShare - * @param array $speciesGoals Массив целей по видам с ключом 'goal' + * @param array $productShares Результат calculateProductForecastShare + * @param array $speciesGoals Массив целей по видам с ключом 'goal' * @return array */ public static function calculateProductSalesBySpecies( array $productShares, array $speciesGoals - ): array { + ): array + { $result = []; $goalsMap = []; foreach ($speciesGoals as $item) { @@ -1919,7 +1929,7 @@ class AutoPlannogrammaService ->all(); $products = ArrayHelper::getColumn($productShares, 'product_id'); - $pricesMap = self::buildPricesMap($products); + $pricesMap = self::buildPricesMap($products); foreach ($productShares as $shareItem) { $storeId = $shareItem['store_id']; @@ -1939,21 +1949,21 @@ class AutoPlannogrammaService $shareItem['species'] ]); - $cleanGoal = $goalsMap[$key] ?? 0; + $cleanGoal = $goalsMap[$key] ?? 0; $productSales = $shareItem['share'] * $cleanGoal; $productSalesPieces = round($productSales / $price, 2); $result[] = [ - 'store_id' => $shareItem['store_id'], - 'month' => $shareItem['month'] ?? null, - 'year' => $shareItem['year'] ?? null, - 'category' => $shareItem['category'], - 'subcategory' => $shareItem['subcategory'], - 'species' => $shareItem['species'], - 'product_id' => $shareItem['product_id'], + 'store_id' => $shareItem['store_id'], + 'month' => $shareItem['month'] ?? null, + 'year' => $shareItem['year'] ?? null, + 'category' => $shareItem['category'], + 'subcategory' => $shareItem['subcategory'], + 'species' => $shareItem['species'], + 'product_id' => $shareItem['product_id'], 'forecast_pieces' => $shareItem['forecast_pieces'], - 'share' => round($shareItem['share'], 4), + 'share' => round($shareItem['share'], 4), 'history_status' => $shareItem['history_status'], 'cleanGoal' => $cleanGoal, 'product_sales' => round($productSales, 2), @@ -1993,7 +2003,7 @@ class AutoPlannogrammaService $monthStart = sprintf('%04d-%02d-01 00:00:00', $year, $month); $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year); - $monthEnd = sprintf('%04d-%02d-%02d 23:59:59', $year, $month, $daysInMonth); + $monthEnd = sprintf('%04d-%02d-%02d 23:59:59', $year, $month, $daysInMonth); $componentProducts = []; if ($type == self::TYPE_SALES) { @@ -2021,8 +2031,8 @@ class AutoPlannogrammaService ['nom' => Products1cNomenclature::tableName()], 'nom.id = sp.product_id' ) - ->andWhere(['s.store_id' => $storeId]) - ->andWhere(['not', ['s.operation' => ['Удален', 'Удаление']]]) + ->andWhere(['s.store_id' => $storeId]) + ->andWhere(['not', ['s.operation' => ['Удален', 'Удаление']]]) ->andWhere(['between', 's.date', $monthStart, $monthEnd]) ->andWhere(['not', ['p1c.components' => '']]) ->asArray() @@ -2066,7 +2076,7 @@ class AutoPlannogrammaService } $componentGuids = []; - $componentDataRecords = []; + $componentDataRecords = []; foreach ($componentProducts as $cp) { $js = trim($cp['components']); if ($js === '' || $js[0] !== '{') { @@ -2083,15 +2093,15 @@ class AutoPlannogrammaService } $componentGuids[$guid] = true; $componentDataRecords[] = [ - 'record_id' => $cp['id'] ?? $cp['write_off_id'], - 'sale_date' => $cp['date'] ?? $cp['write_off_date'], - 'product_id' => $cp['product_id'], - 'product_name' => $cp['name'] ?? $cp['product_name'], + 'record_id' => $cp['id'] ?? $cp['write_off_id'], + 'sale_date' => $cp['date'] ?? $cp['write_off_date'], + 'product_id' => $cp['product_id'], + 'product_name' => $cp['name'] ?? $cp['product_name'], 'quantity_product' => $cp['quantity_product'], - 'component_guid' => $guid, - 'quantity' => $qty, - 'type' => $type, - 'operation' => $cp['operation'] ?? '', + 'component_guid' => $guid, + 'quantity' => $qty, + 'type' => $type, + 'operation' => $cp['operation'] ?? '', ]; } } @@ -2111,7 +2121,7 @@ class AutoPlannogrammaService ->andWhere(['region_id' => $region]) ->andWhere(['product_id' => array_values(ArrayHelper::getColumn($nomenclatures, 'id'))]) ->andWhere(['<=', 'date_from', $monthEnd]) - ->andWhere(['>=', 'date_to', $monthStart]) + ->andWhere(['>=', 'date_to', $monthStart]) ->orderBy(['date_from' => SORT_DESC]) ->all(); @@ -2123,16 +2133,16 @@ class AutoPlannogrammaService $result = []; foreach ($componentDataRecords as $componentDataRecord) { - $guid = $componentDataRecord['component_guid']; - $nomenclatureData = $nomenclatures[$guid] ?? null; - $productId = $nomenclatureData?->id; + $guid = $componentDataRecord['component_guid']; + $nomenclatureData = $nomenclatures[$guid] ?? null; + $productId = $nomenclatureData?->id; $price = 0; $dailyPrices = []; foreach ($pricesByProduct[$productId] ?? [] as $priceRecordForProduct) { if (defined($heliumConstant) && $productId == constant($heliumConstant)) { $saleDay = (new \DateTime($componentDataRecord['sale_date']))->format('Y-m-d'); $fromDay = (new \DateTime($priceRecordForProduct->date_from))->modify('-1 day')->format('Y-m-d'); - $toDay = (new \DateTime($priceRecordForProduct->date_to ))->modify('+1 day')->format('Y-m-d'); + $toDay = (new \DateTime($priceRecordForProduct->date_to))->modify('+1 day')->format('Y-m-d'); if ($fromDay <= $saleDay && $saleDay <= $toDay) { $dailyPrices[] = (float)$priceRecordForProduct->price; @@ -2152,23 +2162,23 @@ class AutoPlannogrammaService $costComponent = $componentDataRecord['quantity'] * $price; $result[] = [ - 'store_id' => $storeId, - 'record_id' => $componentDataRecord['record_id'], - 'sale_date' => $componentDataRecord['sale_date'], - 'product_id' => $componentDataRecord['product_id'], - 'product_name' => $componentDataRecord['product_name'], - 'quantity_product' => $componentDataRecord['quantity_product'], - 'component_guid' => $guid, - 'component_name' => $nomenclatureData?->name, - 'component_category' => $nomenclatureData?->category, - 'component_subcategory'=> $nomenclatureData?->subcategory, - 'component_species' => $nomenclatureData?->species, - 'quantity' => $componentDataRecord['quantity'], - 'price' => $price, - 'cost' => $cost, - 'component_cost' => $costComponent, - 'type' => $type, - 'operation' => $componentDataRecord['operation'], + 'store_id' => $storeId, + 'record_id' => $componentDataRecord['record_id'], + 'sale_date' => $componentDataRecord['sale_date'], + 'product_id' => $componentDataRecord['product_id'], + 'product_name' => $componentDataRecord['product_name'], + 'quantity_product' => $componentDataRecord['quantity_product'], + 'component_guid' => $guid, + 'component_name' => $nomenclatureData?->name, + 'component_category' => $nomenclatureData?->category, + 'component_subcategory' => $nomenclatureData?->subcategory, + 'component_species' => $nomenclatureData?->species, + 'quantity' => $componentDataRecord['quantity'], + 'price' => $price, + 'cost' => $cost, + 'component_cost' => $costComponent, + 'type' => $type, + 'operation' => $componentDataRecord['operation'], ]; } @@ -2176,17 +2186,16 @@ class AutoPlannogrammaService } - public function sumProductsComponentsByGroup(array $items, string $type, string $group = 'category'): array { $aggregated = []; foreach ($items as $row) { - $storeId = $row['store_id']; - $category = $row['component_category']; + $storeId = $row['store_id']; + $category = $row['component_category']; $subcategory = $row['component_subcategory']; - $species = $row['component_species']; - $operation = $row['operation'] ?? null; + $species = $row['component_species']; + $operation = $row['operation'] ?? null; $cost = (float)$row['cost']; @@ -2214,10 +2223,10 @@ class AutoPlannogrammaService if (!isset($aggregated[$key])) { // базовая структура $aggregated[$key] = [ - 'store_id' => $storeId, - 'category' => $category, - 'sum' => 0.0, - 'type' => $type, + 'store_id' => $storeId, + 'category' => $category, + 'sum' => 0.0, + 'type' => $type, ]; if ($group === 'subcategory' || $group === 'species') { @@ -2250,7 +2259,7 @@ class AutoPlannogrammaService $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 ($speciesData as $store_id => $categoryData) { foreach ($categoryData as $category => $subcategoryData) { foreach ($subcategoryData as $subcategory => $species) { foreach ($species as $speciesInd => $row) { @@ -2280,10 +2289,6 @@ class AutoPlannogrammaService } - - - - /** * Считает взвешенную долю категорий за целевой месяц: * берёт три предыдущих к «месяцу-цели» месяца (пропуская сразу предыдущий), @@ -2334,7 +2339,6 @@ class AutoPlannogrammaService $end = date('Y-m-t 23:59:59', strtotime($start)); - $q = (new Query()) ->select([ 'store_id' => 'ex.entity_id', @@ -2416,9 +2420,9 @@ class AutoPlannogrammaService return []; } $month = (int)$dt->format('m'); - $year = (int)$dt->format('Y'); + $year = (int)$dt->format('Y'); - $stores = $this->getVisibleStores(); + $stores = $this->getVisibleStores(); $storeIds = array_map(fn($s) => $s->id, $stores); if (!empty($filters['store_id'])) { $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]); @@ -2431,10 +2435,10 @@ class AutoPlannogrammaService $query = (new Query()) ->select([ - 'store_id' => 'ex.entity_id', - 'subcategory' => 'p1c.subcategory', - 'category' => 'p1c.category', - 'total_sum' => new Expression( + 'store_id' => 'ex.entity_id', + 'subcategory' => 'p1c.subcategory', + 'category' => 'p1c.category', + 'total_sum' => new Expression( $type === 'writeOffs' ? 'SUM(CAST(wop.summ AS NUMERIC))' : 'SUM(sp.summ)' @@ -2443,10 +2447,9 @@ class AutoPlannogrammaService 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') - + ->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(['in', new Expression('EXTRACT(YEAR FROM w.date)'), $years]) ->andWhere(['=', new Expression('EXTRACT(MONTH FROM w.date)'), $month]); if ($productFilter !== null) { @@ -2454,9 +2457,9 @@ class AutoPlannogrammaService } } 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') + ->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(['in', new Expression('EXTRACT(YEAR FROM s.date)'), $years]) ->andWhere(['=', new Expression('EXTRACT(MONTH FROM s.date)'), $month]); if ($productFilter !== null) { @@ -2483,14 +2486,14 @@ class AutoPlannogrammaService $result = []; foreach ($rows as $r) { - $sid = $r['store_id']; - $cat = $r['category']; + $sid = $r['store_id']; + $cat = $r['category']; $total = $sumByStoreCategory[$sid][$cat] ?: 1; $result[] = [ - 'store_id' => $sid, - 'category' => $cat, - 'subcategory' => $r['subcategory'], - 'total_sum' => $r['total_sum'], + 'store_id' => $sid, + 'category' => $cat, + 'subcategory' => $r['subcategory'], + 'total_sum' => $r['total_sum'], 'percent_of_month' => round($r['total_sum'] / $total, 4), ]; } @@ -2507,14 +2510,14 @@ class AutoPlannogrammaService ): array { try { - $dt = new \DateTime($dateFrom); + $dt = new \DateTime($dateFrom); } catch (\Exception $e) { return []; } $month = (int)$dt->format('m'); - $year = (int)$dt->format('Y'); + $year = (int)$dt->format('Y'); - $stores = $this->getVisibleStores(); + $stores = $this->getVisibleStores(); $storeIds = array_map(fn($s) => $s->id, $stores); if (!empty($filters['store_id'])) { $storeIds = array_intersect($storeIds, [(int)$filters['store_id']]); @@ -2526,11 +2529,11 @@ class AutoPlannogrammaService $years = [$year - 2, $year - 1]; $query = (new Query())->select([ - 'store_id' => 'ex.entity_id', - 'category' => 'p1c.category', - 'subcategory'=> 'p1c.subcategory', - 'species' => 'p1c.species', - 'total_sum' => new Expression( + 'store_id' => 'ex.entity_id', + 'category' => 'p1c.category', + 'subcategory' => 'p1c.subcategory', + 'species' => 'p1c.species', + 'total_sum' => new Expression( $type === 'writeOffs' ? 'SUM(CAST(wop.summ AS NUMERIC))' : 'SUM(sp.summ)' @@ -2539,21 +2542,21 @@ class AutoPlannogrammaService 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') + ->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(['IN', new Expression('EXTRACT(YEAR FROM w.date)'), $years]) - ->andWhere(['=', new Expression('EXTRACT(MONTH FROM w.date)'), $month]); + ->andWhere(['=', new Expression('EXTRACT(MONTH FROM w.date)'), $month]); 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') + ->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(['IN', new Expression('EXTRACT(YEAR FROM s.date)'), $years]) - ->andWhere(['=', new Expression('EXTRACT(MONTH FROM s.date)'), $month]); + ->andWhere(['=', new Expression('EXTRACT(MONTH FROM s.date)'), $month]); if ($productFilter !== null) { $query->andWhere(['sp.product_id' => $productFilter]); } @@ -2583,15 +2586,15 @@ class AutoPlannogrammaService $result = []; foreach ($rows as $r) { - $sid = $r['store_id']; - $sub = $r['subcategory']; + $sid = $r['store_id']; + $sub = $r['subcategory']; $total = $sumByStoreSubcategory[$sid][$sub] ?: 1; $result[] = [ - 'store_id' => $sid, - 'category' => $r['category'], - 'subcategory' => $sub, - 'species' => $r['species'], - 'total_sum' => (float)$r['total_sum'], + 'store_id' => $sid, + 'category' => $r['category'], + 'subcategory' => $sub, + 'species' => $r['species'], + 'total_sum' => (float)$r['total_sum'], 'percent_of_month' => round($r['total_sum'] / $total, 4), ]; } @@ -2602,8 +2605,6 @@ class AutoPlannogrammaService // Недельные расчеты - - // альтернативные методы расчета списаний public function calculateFullGoalChainWeighted(array $filters): array @@ -2620,8 +2621,8 @@ class AutoPlannogrammaService $monthSpeciesShare = $this->getMonthSpeciesShareOrWriteOff($datePlan, $filters); if ($filters['type'] === 'writeOffs') { - $salesSubShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales'); - $salesSubGoal = $this->getMonthSubcategoryGoal($salesSubShare, $monthCategoryGoal); + $salesSubShare = $this->getMonthSubcategoryShareOrWriteOffWeighted($datePlan, $filters, null, 'sales'); + $salesSubGoal = $this->getMonthSubcategoryGoal($salesSubShare, $monthCategoryGoal); $catGoalMap = []; foreach ($monthCategoryGoal as $row) { @@ -2639,8 +2640,8 @@ class AutoPlannogrammaService $writeShare = $row['percent_of_month']; - $writeGoal = ($catGoalMap[$cat] ?? 0) * $writeShare; - $saleGoal = $salesSubGoalMap[$cat][$sub] ?? 0; + $writeGoal = ($catGoalMap[$cat] ?? 0) * $writeShare; + $saleGoal = $salesSubGoalMap[$cat][$sub] ?? 0; if ($saleGoal > 0 && $writeGoal > 0.1 * $saleGoal) { $row['share'] = 0.1; @@ -2654,7 +2655,7 @@ class AutoPlannogrammaService $monthSpeciesGoal = $this->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal); if ($filters['type'] === 'writeOffs') { $salesSpecShare = $this->getMonthSpeciesShareOrWriteOffWeighted($datePlan, $datePlan, $filters, null, 'sales'); - $salesSpecGoal = $this->getMonthSpeciesGoalDirty($salesSpecShare, $monthSubcategoryGoal); + $salesSpecGoal = $this->getMonthSpeciesGoalDirty($salesSpecShare, $monthSubcategoryGoal); $subGoalMap = []; foreach ($monthSubcategoryGoal as $row) { @@ -2666,13 +2667,13 @@ class AutoPlannogrammaService } foreach ($monthSpeciesShare as &$row) { - $cat = $row['category']; - $sub = $row['subcategory']; + $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; + $writeGoal = ($subGoalMap[$cat][$sub] ?? 0) * $writeShare; + $saleGoal = $salesSpecGoalMap[$cat][$sub][$spec] ?? 0; if ($saleGoal > 0 && $writeGoal > 0.1 * $saleGoal) { $row['share'] = 0.1; @@ -2713,16 +2714,17 @@ class AutoPlannogrammaService /** * Строит карту цен за последние 20 дней. * - * @param array $productIds Список product_id для выборки - * @param int|null $regionId Если указан — один регион, иначе — несколько + * @param array $productIds Список product_id для выборки + * @param int|null $regionId Если указан — один регион, иначе — несколько * @return array */ public static function buildPricesMap( - array $productIds, - ?int $regionId = null - ): array { + array $productIds, + ?int $regionId = null + ): array + { $heliumGuid = '2b72702a-792f-11e8-9edd-1c6f659fb563'; - $periodEnd = (new \DateTime())->format('Y-m-d H:i:s'); + $periodEnd = (new \DateTime())->format('Y-m-d H:i:s'); $periodStart = (new \DateTime())->modify('-20 days')->format('Y-m-d H:i:s'); @@ -2730,7 +2732,7 @@ class AutoPlannogrammaService ->select(['product_id', 'region_id', 'price', 'active', 'date_from', 'date_to']) ->where(['product_id' => $productIds]) ->andWhere(['<=', 'date_from', $periodEnd]) - ->andWhere(['>=', 'date_to', $periodStart]) + ->andWhere(['>=', 'date_to', $periodStart]) ->orderBy(['date_from' => SORT_ASC]); if ($regionId !== null) { @@ -2742,10 +2744,10 @@ class AutoPlannogrammaService $multiRegion = $regionId === null; foreach ($priceRecords as $priceRecord) { - $productId = $priceRecord['product_id']; + $productId = $priceRecord['product_id']; $price = (float)$priceRecord['price']; - $regionId = $multiRegion + $regionId = $multiRegion ? $priceRecord['region_id'] : null; @@ -2831,22 +2833,34 @@ class AutoPlannogrammaService } $weeklyForecasts[] = [ - 'store_id' => $item['store_id'], - 'category' => $item['category'], - 'subcategory' => $item['subcategory'], - 'species' => $item['species'], - 'type' => $item['type'], - 'product_guid' => $item['product_guid'], - 'week_forecast' => round($item['full_forecast'] * $weekShare['share'], 2), - 'full_forecast' => $item['full_forecast'], - 'week' => $weekShare['week'], - 'matrix_group' => $item['matrix_group'], - 'month' => $item['month'], - 'year' => $item['year'], + 'store_id' => $item['store_id'], + 'category' => $item['category'], + 'subcategory' => $item['subcategory'], + 'species' => $item['species'], + 'type' => $item['type'], + 'product_guid' => $item['product_guid'], + 'week_forecast' => round($item['full_forecast'] * $weekShare['share'], 2), + 'full_forecast' => $item['full_forecast'], + 'week' => $weekShare['week'], + 'matrix_group' => $item['matrix_group'], + 'month' => $item['month'], + 'year' => $item['year'], ]; } } - return $weeklyForecasts; + $grouped = []; + foreach ($weeklyForecasts 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']; + + $grouped[$weekItem][$storeItem][$guid][$type][$group] = $forecastValue; + } + + return $grouped; } } \ No newline at end of file