$baseMonth = strtotime("{$month}-01");
- $monthOffsets = [2, 3, 4];
+ $monthOffsets = [3, 4, 5];
$monthWeights = [3, 2, 1];
return [];
}
- $startYear = 2020;
- $endYear = $year - 1;
- if ($endYear < $startYear) {
- return [];
- }
- $years = range($startYear, $endYear);
+ $years = [$year - 2, $year - 1];
$query = (new Query())
->select([
$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),
$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);
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',
$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);
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',
]);
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]);
}
$rows = $query->all();
foreach ($rows as $row) {
$result[] = [
- 'week' => $ind,
+ 'week' => $row['week'],
'store_id' => $row['store_id'],
'category' => $row['category'],
'subcategory' => $row['subcategory'],
}
/**
- * Исторический недельный отчёт и доли по видам без вложенных циклов.
+ * Исторический недельный отчёт и доли по видам с учётом store_id.
*
* @param string $monthYear месяц-год в формате MM-YYYY
* @param array|null $filters
* @param array|null $productFilter
* @param string $type
- * @return array{
- * 'historicalWeekly' => array<int, array>,
- * 'weeklyShare' => array<int, array>
- * }
+ * @return array{ 'weeksData': array<int, array> }
+ * возвращает плоский список строк:
+ * [
+ * ['week'=>1, 'store_id'=>2, 'category'=>'...', 'subcategory'=>'...', 'species'=>'...', 'percent'=>0.32],
+ * ...
+ * ]
*/
public function getHistoricalWeeklySpeciesShare(
string $monthYear,
?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));
}