From 12cd23ab26d85abca2e2c5380ad9897bc6510b09 Mon Sep 17 00:00:00 2001 From: fomichev Date: Wed, 9 Apr 2025 16:24:53 +0300 Subject: [PATCH] =?utf8?q?=D0=92=D1=8B=D0=B4=D0=B5=D0=BB=D0=B5=D0=BD=D0=B8?= =?utf8?q?=D0=B5=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B9=20=D0=B8=20?= =?utf8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=BF=D0=BE=20=D0=BA?= =?utf8?q?=D0=BE=D0=B4=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- erp24/services/StorePlanService.php | 533 +++++++++++++++++++--------- 1 file changed, 364 insertions(+), 169 deletions(-) diff --git a/erp24/services/StorePlanService.php b/erp24/services/StorePlanService.php index c8b7f5ca..efe67fab 100755 --- a/erp24/services/StorePlanService.php +++ b/erp24/services/StorePlanService.php @@ -212,32 +212,116 @@ class StorePlanService return $enablePlanAdd; } - public static function calculateHistoricalShare($storeId, $selectedMonth, $category, $subcategory = null, $species = null) + /** + * Рассчитывает недельные продажи товаров за 3 предыдущих месяца относительно переданного месяца и года + * для конкретного магазина и фильтрации по категориям/подкатегориям/видам. + * + * @param int $storeId ID магазина. + * @param string $selectedMonth Месяц в формате "01" - "12". + * @param string $selectedYear Год в формате "YYYY". + * @param string $category Категория товаров. + * @param string|null $subcategory Подкатегория товаров (опционально). + * @param string|null $species Вид товаров (опционально). + * + * @return array [ + * 'with_history' => [ // товары, у которых есть данные по продажам во всех периодах и активности минимум в 2 недели каждого периода. + * [ + * 'guid' => string, // идентификатор товара. + * 'weekly_sales' => [ // продажи по неделям для каждого периода. + * '2025-01' => [int, int, ...], + * '2024-12' => [int, int, ...], + * '2024-11' => [int, int, ...], + * ] + * ], + * ... + * ], + * 'without_history' => [ // товары с неполными данными по истории продаж. + * // Структура аналогична. + * ] + * ] + */ + public static function calculateHistoricalShare($storeId, $selectedMonth, $selectedYear, $category, $subcategory = null, $species = null) + { + $baseDate = strtotime("{$selectedYear}-{$selectedMonth}-01"); + + // Получаем периоды за 3 предыдущих месяца. + $periods = self::getPeriods($baseDate, 3); + + // Получаем историю продаж для каждого периода. + $salesHistory = self::getSalesHistory($storeId, $periods, $category, $subcategory, $species); + + // Анализируем и разделяем товары по наличию полноценной истории продаж. + return self::analyzeHistory($salesHistory, $periods); + } + + /** + * Формирует периоды (месяц + недели) за заданное количество предыдущих месяцев от базовой даты. + * + * @param int $baseDate Timestamp базовой даты. + * @param int $count Количество периодов для получения (по умолчанию 3). + * + * @return array Массив периодов с ключами вида "YYYY-MM". + */ + private static function getPeriods($baseDate, $count = 3) { - $currentTimestamp = time(); - $year = date('Y', $currentTimestamp); $periods = []; - for ($i = 1; $i <= 3; $i++) { - $prevTimestamp = strtotime("-{$i} month", $currentTimestamp); - $month = date('m', $prevTimestamp); - $periods[$month] = []; - } - //var_dump($periods); die(); - foreach ($periods as $monthKey => $month) { - $days = cal_days_in_month(CAL_GREGORIAN, (int)$monthKey, (int)$year); - $weeks = $days > 28 ? 5 : 4; - for ($weekNumber = 1; $weekNumber <= $weeks; $weekNumber++) { - $range = Motivation::getWeekRange(null, $weekNumber, (int)$monthKey, $year); - $periods[$monthKey][$weekNumber - 1] = $range; + for ($i = $count; $i >= 1; $i--) { + $timestamp = strtotime("-{$i} month", $baseDate); + $month = date('m', $timestamp); + $year = date('Y', $timestamp); + + $periodKey = "{$year}-{$month}"; + $periods[$periodKey] = [ + 'year' => $year, + 'month' => $month, + 'weeks' => [], + ]; + + $daysInMonth = cal_days_in_month(CAL_GREGORIAN, (int)$month, (int)$year); + $weeksCount = $daysInMonth > 28 ? 5 : 4; + + for ($weekNumber = 1; $weekNumber <= $weeksCount; $weekNumber++) { + $range = Motivation::getWeekRange(null, $weekNumber, (int)$month, (int)$year); + $periods[$periodKey]['weeks'][$weekNumber - 1] = $range; } } - // var_dump($periods); die(); + + return $periods; + } + + /** + * Получает историю продаж за каждый период по неделям. + * + * @param int $storeId + * @param array $periods Массив периодов, сформированный функцией getPeriods. + * @param string $category + * @param string|null $subcategory + * @param string|null $species + * + * @return array Структура данных: + * [ + * 'product_guid1' => [ + * 'YYYY-MM' => [ + * 0 => salesCount_week1, + * 1 => salesCount_week2, + * ... + * ], + * ... + * ], + * ... + * ] + */ + private static function getSalesHistory($storeId, $periods, $category, $subcategory, $species) + { $salesHistory = []; - foreach ($periods as $monthKey => $weeks) { - foreach ($weeks as $weekIndex => $week) { + foreach ($periods as $periodKey => $periodData) { + $year = $periodData['year']; + $month = $periodData['month']; + + foreach ($periodData['weeks'] as $weekIndex => $week) { $dateStart = $week['start_time'] . ' 00:00:00'; - $dateEnd = $week['end_time'] . ' 23:59:59'; + $dateEnd = $week['end_time'] . ' 23:59:59'; $query = Sales::find()->alias('s') ->select([ @@ -248,7 +332,6 @@ class StorePlanService ->innerJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id') ->where(['s.store_id' => $storeId]) ->andWhere(['between', 's.date', $dateStart, $dateEnd]) - // Условие для чеков: order_id равен '' или '0' ->andWhere(['order_id' => ['', '0']]) ->andWhere(['p1c.category' => $category]); @@ -262,53 +345,74 @@ class StorePlanService $query->groupBy('p1c.id'); $results = $query->asArray()->all(); - foreach ($results as $result) { $guid = $result['product_guid']; if (!isset($salesHistory[$guid])) { $salesHistory[$guid] = []; } - if (!isset($salesHistory[$guid][$monthKey])) { - $salesHistory[$guid][$monthKey] = []; + if (!isset($salesHistory[$guid][$periodKey])) { + $salesHistory[$guid][$periodKey] = []; } - $salesHistory[$guid][$monthKey][$weekIndex] = (int)$result['sales_count']; + $salesHistory[$guid][$periodKey][$weekIndex] = (int)$result['sales_count']; } } } - $productsWithHistory = []; + + return $salesHistory; + } + + /** + * Анализирует историю продаж, разделяя товары на группы: + * - с историей продаж во всех периодах (активность не менее 2-х недель в каждом периоде), + * - без полноценной истории продаж. + * + * @param array $salesHistory Структура продаж, сформированная функцией getSalesHistory. + * @param array $periods Массив периодов, полученный функцией getPeriods. + * + * @return array [ + * 'with_history' => [...], + * 'without_history' => [...], + * ] + */ + private static function analyzeHistory($salesHistory, $periods) + { + $productsWithHistory = []; $productsWithoutHistory = []; foreach ($salesHistory as $guid => $monthsData) { - $hasHistoryInAllMonths = true; + $hasHistoryInAllPeriods = true; $weeklySalesData = []; - foreach ($periods as $monthKey => $monthData) { - if (!isset($monthsData[$monthKey])) { - $hasHistoryInAllMonths = false; - $weekData = [0, 0, 0, 0]; + foreach ($periods as $periodKey => $periodData) { + $weeksCount = count($periodData['weeks']); + $checkWeeksCount = min(4, $weeksCount); + + if (!isset($monthsData[$periodKey])) { + $hasHistoryInAllPeriods = false; + $weekData = array_fill(0, $weeksCount, 0); } else { $weekData = []; $activeWeeks = 0; - for ($weekIndex = 0; $weekIndex < 5; $weekIndex++) { - $salesCount = isset($monthsData[$monthKey][$weekIndex]) ? $monthsData[$monthKey][$weekIndex] : 0; + for ($weekIndex = 0; $weekIndex < $checkWeeksCount; $weekIndex++) { + $salesCount = isset($monthsData[$periodKey][$weekIndex]) ? $monthsData[$periodKey][$weekIndex] : 0; $weekData[$weekIndex] = $salesCount; if ($salesCount > 0) { $activeWeeks++; } } if ($activeWeeks < 2) { - $hasHistoryInAllMonths = false; + $hasHistoryInAllPeriods = false; } } - $weeklySalesData[$monthKey] = $weekData; + $weeklySalesData[$periodKey] = $weekData; } $productData = [ - 'guid' => $guid, + 'guid' => $guid, 'weekly_sales' => $weeklySalesData, ]; - if ($hasHistoryInAllMonths) { + if ($hasHistoryInAllPeriods) { $productsWithHistory[] = $productData; } else { $productsWithoutHistory[] = $productData; @@ -316,7 +420,7 @@ class StorePlanService } return [ - 'with_history' => $productsWithHistory, + 'with_history' => $productsWithHistory, 'without_history' => $productsWithoutHistory, ]; } @@ -325,66 +429,27 @@ class StorePlanService /** * Метод вычисляет взвешенное значение продаж для товаров без истории. * - * @param int $storeId Идентификатор магазина. - * @param string $selectedMonth Выбранный месяц в формате "mm" (целевой месяц). + * @param int $storeId Идентификатор магазина. + * @param string $selectedMonth Выбранный месяц в формате "mm" (целевой месяц). * @param array $productsWithoutHistory Массив товаров без истории, где каждый элемент имеет вид: * [ * 'guid' => , - * 'weekly_sales' => [ 'YYYY-mm' => [ ... ], ... ] + * 'weekly_sales' => [ 'YYYY-MM' => [ ... ], ... ] * ] + * * @return array Возвращает массив, где ключ – GUID товара, а значение – рассчитанное взвешенное значение продаж. */ public static function calculateWeightedSalesForProductsWithoutHistory($storeId, $selectedMonth, $productsWithoutHistory) { - - $year = date('Y'); - $targetDate = strtotime("$year-$selectedMonth-01"); - - $months = []; - for ($i = 1; $i <= 3; $i++) { - $monthTimestamp = strtotime("-{$i} month", $targetDate); - $m = date('m', $monthTimestamp); - $y = date('Y', $monthTimestamp); - $months[] = ['year' => $y, 'month' => $m]; - } + $targetDate = strtotime(date('Y') . "-$selectedMonth-01"); + $periods = self::generateWeightedPeriods($targetDate, 3); $weightedResults = []; foreach ($productsWithoutHistory as $product) { $guid = $product['guid']; - //var_dump( $guid); die(); - $myChars = Products1cAdditionalCharacteristics::find() - ->where(['product_id' => $guid]) - ->asArray() - ->all(); - - $mySet = []; - foreach ($myChars as $char) { - $mySet[$char['property_id']] = $char['value']; - } - ksort($mySet); - // var_dump($mySet); die(); - $countChars = count($mySet); - // var_dump($countChars); die(); - if ($countChars == 0) { - $weightedResults[$guid] = 0; - continue; - } - $conditions = []; - foreach ($mySet as $propId => $val) { - $conditions[] = [$propId, $val]; - } - $query = Products1cAdditionalCharacteristics::find() - ->select('product_id') - ->where(new \yii\db\Expression('(property_id, value) IN (' . implode(', ', array_map(function($pair) { - return "('" . implode("','", $pair) . "')"; - }, $conditions)) . ')')) - ->groupBy('product_id') - ->having('COUNT(*) = :cnt', [':cnt' => $countChars]); - - $similarProductIds = $query->column(); - // var_dump($similarProductIds); die(); + $similarProductIds = self::getSimilarProductIDs($guid); if (empty($similarProductIds)) { $weightedResults[$guid] = 0; continue; @@ -392,57 +457,168 @@ class StorePlanService $medianSales = []; $salesValuesForEachMonth = []; - foreach ($months as $monthInfo) { - $startDate = sprintf('%04d-%02d-01', $monthInfo['year'], $monthInfo['month']); - $endDate = sprintf('%04d-%02d-%02d', - $monthInfo['year'], - $monthInfo['month'], - cal_days_in_month(CAL_GREGORIAN, $monthInfo['month'], $monthInfo['year'])); - - $salesValues = []; - - foreach ($similarProductIds as $simProdId) { - $sales = Sales::find()->alias('s') - ->innerJoin('sales_products sp', 's.id = sp.check_id') - ->innerJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id') - ->where(['p1c.id' => $simProdId]) - ->andWhere(['s.store_id' => $storeId]) - ->andWhere(['between', 's.date', $startDate . ' 00:00:00', $endDate . ' 23:59:59']) - // Условие для чеков: order_id равен '' или '0' - ->andWhere(['order_id' => ['', '0']]) - ->count(); - $salesValues[] = (int)$sales; - } - $salesValuesForEachMonth[] = $salesValues; - $nonZeroSales = array_filter($salesValues, function($val) { - return $val > 0; - }); - sort($nonZeroSales, SORT_NUMERIC); - $n = count($nonZeroSales); - if ($n === 0) { - $median = 0; - } elseif ($n % 2 == 1) { - $median = $nonZeroSales[floor($n / 2)]; - } else { - $median = ($nonZeroSales[$n / 2 - 1] + $nonZeroSales[$n / 2]) / 2; - } - $medianSales[] = $median; + foreach ($periods as $periodKey => $monthInfo) { + list($median, $salesValues) = self::calculateMedianSalesForPeriod($storeId, $similarProductIds, $monthInfo); + $medianSales[$periodKey] = $median; + $salesValuesForEachMonth[$periodKey] = $salesValues; } $weights = [3, 2, 1]; - $weightedValue = 0; - for ($i = 0; $i < count($medianSales); $i++) { - $weightedValue += $medianSales[$i] * $weights[$i]; - } + $weightedValue = self::computeWeightedValue($medianSales, $weights); + $weightedResults[$guid] = [ 'weightedValue' => $weightedValue, 'medianSales' => $medianSales, 'salesValues' => $salesValuesForEachMonth, ]; } + return $weightedResults; } + /** + * Генерирует периоды для расчёта продаж. + * + * @param int $targetDate Timestamp целевой даты. + * @param int $count Количество предыдущих месяцев. + * @param bool $withWeight Если true – к каждому периоду добавляется вес (ключ 'weight'). + * + * @return array Ассоциативный массив, где ключ – "YYYY-MM", а значение – массив с ключами 'year', 'month' + * и, если $withWeight равен true, 'weight'. + */ + private static function generateWeightedPeriods($targetDate, $count = 3, $withWeight = false) + { + $periods = []; + for ($i = 1; $i <= $count; $i++) { + $timestamp = strtotime("-{$i} month", $targetDate); + $year = date('Y', $timestamp); + $month = date('m', $timestamp); + $periodKey = sprintf('%04d-%02d', $year, $month); + + $periodData = [ + 'year' => $year, + 'month' => $month, + ]; + + if ($withWeight) { + // Вес рассчитывается как 4 - $i, то есть для ближайшего месяца – вес 3, затем 2 и 1. + $periodData['weight'] = 4 - $i; + } + + $periods[$periodKey] = $periodData; + } + return $periods; + } + + /** + * Получает идентификаторы товаров, похожих на указанный товар, + * исходя из набора его дополнительных характеристик. + * + * @param string $guid Идентификатор товара. + * + * @return array Массив идентификаторов похожих товаров. + */ + private static function getSimilarProductIDs($guid) + { + $myChars = Products1cAdditionalCharacteristics::find() + ->where(['product_id' => $guid]) + ->asArray() + ->all(); + + $mySet = []; + foreach ($myChars as $char) { + $mySet[$char['property_id']] = $char['value']; + } + ksort($mySet); + $countChars = count($mySet); + if ($countChars === 0) { + return []; + } + + $conditions = []; + foreach ($mySet as $propId => $val) { + $conditions[] = [$propId, $val]; + } + $query = Products1cAdditionalCharacteristics::find() + ->select('product_id') + ->where(new \yii\db\Expression('(property_id, value) IN (' . + implode(', ', array_map(function($pair) { + return "('" . implode("','", $pair) . "')"; + }, $conditions)) . ')')) + ->groupBy('product_id') + ->having('COUNT(*) = :cnt', [':cnt' => $countChars]); + + return $query->column(); + } + + /** + * Вычисляет медианное значение продаж для похожих товаров в заданном периоде. + * + * @param int $storeId Идентификатор магазина. + * @param array $similarProductIds Массив идентификаторов похожих товаров. + * @param array $monthInfo Массив с ключами 'year' и 'month'. + * + * @return array Возвращает массив: [медианное значение продаж, массив продаж по каждому товару] + */ + private static function calculateMedianSalesForPeriod($storeId, $similarProductIds, $monthInfo) + { + $startDate = sprintf('%04d-%02d-01', $monthInfo['year'], $monthInfo['month']); + $endDate = sprintf('%04d-%02d-%02d', + $monthInfo['year'], + $monthInfo['month'], + cal_days_in_month(CAL_GREGORIAN, $monthInfo['month'], $monthInfo['year'])); + + $salesValues = []; + + foreach ($similarProductIds as $simProdId) { + $sales = Sales::find()->alias('s') + ->innerJoin('sales_products sp', 's.id = sp.check_id') + ->innerJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id') + ->where(['p1c.id' => $simProdId]) + ->andWhere(['s.store_id' => $storeId]) + ->andWhere(['between', 's.date', $startDate . ' 00:00:00', $endDate . ' 23:59:59']) + ->andWhere(['order_id' => ['', '0']]) + ->count(); + $salesValues[] = (int)$sales; + } + + $nonZeroSales = array_filter($salesValues, function($val) { + return $val > 0; + }); + sort($nonZeroSales, SORT_NUMERIC); + $n = count($nonZeroSales); + if ($n === 0) { + $median = 0; + } elseif ($n % 2 === 1) { + $median = $nonZeroSales[floor($n / 2)]; + } else { + $median = ($nonZeroSales[$n / 2 - 1] + $nonZeroSales[$n / 2]) / 2; + } + + return [$median, $salesValues]; + } + + /** + * Вычисляет взвешенное значение на основании медианных продаж по периодам. + * + * @param array $medianSales Ассоциативный массив медианных продаж, ключи – периоды ("YYYY-MM"). + * @param array $weights Массив весов (порядок должен соответствовать порядку перебора медианных продаж). + * + * @return float Взвешенное значение. + */ + private static function computeWeightedValue($medianSales, $weights) + { + $weightedValue = 0; + $i = 0; + foreach ($medianSales as $periodKey => $median) { + if (isset($weights[$i])) { + $weightedValue += $median * $weights[$i]; + } + $i++; + } + return $weightedValue; + } + /** * Получает цену для товара для указанного месяца. * @@ -465,6 +641,8 @@ class StorePlanService ['>=', 'date_to', $monthStart], ['date_to' => '2100-01-01 03:00:00+03'] ])->all(); + + if (CityStore::find()->where(['id' => $storeId])->one()->city_id == 1342) { $region = 52; } elseif (CityStore::find()->where(['id' => $storeId])->one()->city_id == 1) { @@ -472,11 +650,9 @@ class StorePlanService } else { $region = null; } - //$priceRecords->andWhere(['or',['region_id' => $region],['region_id' => null]]) //->all(); - if (!empty($priceRecords)) { $totalPrice = 0; $count = 0; @@ -491,10 +667,10 @@ class StorePlanService $averagePrice = 0; } - return $averagePrice; } + /** * Вычисляет для каждого товара с историей: * - месячные продажи (сумма недельных продаж за месяц), @@ -502,65 +678,37 @@ class StorePlanService * - итоговую взвешенную сумму, * - долю товара от общей взвешенной суммы по всем товарам. * - * @param int $storeId Идентификатор магазина. - * @param string $selectedMonth Целевой месяц в формате "mm". - * @param array $productsWithHistory Массив товаров с историей (из calculateHistoricalShare), каждый элемент содержит: - * [ + * @param int $storeId Идентификатор магазина. + * @param string $selectedMonth Целевой месяц в формате "mm". + * @param array $productsWithHistory Массив товаров с историей, где каждый элемент имеет вид: + * [ * 'guid' => , - * 'weekly_sales' => [ 'mm' => [week0, week1, week2, week3], ... ] - * ] + * 'weekly_sales' => [ 'YYYY-MM' => [ ... ], ... ] + * ] + * * @return array Массив, где ключ – GUID товара, а значение – массив с данными: * [ - * 'monthlySales' => [ 'mm' => суммарные продажи за месяц, ... ], - * 'monthlyWeighted' => [ 'mm' => (продажи * цена * вес), ... ], + * 'monthlySales' => [ 'YYYY-MM' => суммарные продажи за месяц, ... ], + * 'monthlyWeighted' => [ 'YYYY-MM' => (продажи * цена * вес), ... ], * 'weightedSum' => итоговая взвешенная сумма, - * 'share' => доля товара (от 0 до 1) + * 'share' => доля товара (от 0 до 1), + * 'monthlyPrice' => [ 'YYYY-MM' => цена ] * ] */ public static function calculateProductSalesShare($storeId, $selectedMonth, $productsWithHistory) { - $year = date('Y'); - + $targetDate = strtotime(date('Y') . "-$selectedMonth-01"); - $weightedMonths = []; - for ($i = 1; $i <= 3; $i++) { - $timestamp = strtotime("-{$i} month", strtotime("$year-$selectedMonth-01")); - $weightedMonths[] = [ - 'year' => date('Y', $timestamp), - 'month' => date('m', $timestamp), - 'weight' => 4 - $i, - ]; - } + $weightedPeriods = self::generateWeightedPeriods($targetDate, 3, true); $productsData = []; $globalTotal = 0; - foreach ($productsWithHistory as $product) { $guid = $product['guid']; - $monthlySales = []; - $monthlyWeighted = []; - $weightedSum = 0; - $monthlyPrice = []; - - foreach ($weightedMonths as $mInfo) { - $mKey = $mInfo['month']; - $sales = isset($product['weekly_sales'][$mKey]) ? array_sum($product['weekly_sales'][$mKey]) : 0; - - $price = self::getPriceForProductAndMonth($guid, $mInfo['year'], $mInfo['month'], $storeId); - $monthlySales[$mKey] = $sales; - $weightedValue = $sales * $price * $mInfo['weight']; - $monthlyWeighted[$mKey] = $weightedValue; - $monthlyPrice[$mKey] = $price; - $weightedSum += $weightedValue; - } - $productsData[$guid] = [ - 'monthlySales' => $monthlySales, - 'monthlyWeighted' => $monthlyWeighted, - 'weightedSum' => $weightedSum, - 'monthlyPrice' => $monthlyPrice, - ]; - $globalTotal += $weightedSum; + $result = self::processProductWithHistory($storeId, $product, $weightedPeriods); + $productsData[$guid] = $result; + $globalTotal += $result['weightedSum']; } foreach ($productsData as $guid => &$data) { @@ -571,5 +719,52 @@ class StorePlanService return $productsData; } + + + /** + * Обрабатывает товар с историей, вычисляя для него: + * - месячные продажи, + * - взвешенные продажи (с учетом цены и заданного веса), + * - итоговую взвешенную сумму. + * + * @param int $storeId Идентификатор магазина. + * @param array $product Массив с данными товара, содержащий 'guid' и 'weekly_sales' с ключами "YYYY-MM". + * @param array $weightedPeriods Ассоциативный массив периодов, с ключами "YYYY-MM" и весами. + * + * @return array Массив с ключами: + * - monthlySales: [ 'YYYY-MM' => суммарные продажи за период, ... ] + * - monthlyWeighted: [ 'YYYY-MM' => продажи * цена * вес, ... ] + * - weightedSum: итоговая сумма по взвешенным продажам, + * - monthlyPrice: [ 'YYYY-MM' => цена, ... ] + */ + private static function processProductWithHistory($storeId, $product, $weightedPeriods) + { + $monthlySales = []; + $monthlyWeighted = []; + $monthlyPrice = []; + $weightedSum = 0; + $guid = $product['guid']; + + foreach ($weightedPeriods as $periodKey => $periodInfo) { + $sales = isset($product['weekly_sales'][$periodKey]) + ? array_sum($product['weekly_sales'][$periodKey]) + : 0; + $price = self::getPriceForProductAndMonth($guid, $periodInfo['year'], $periodInfo['month'], $storeId); + + $monthlySales[$periodKey] = $sales; + $weightedValue = $sales * $price * $periodInfo['weight']; + $monthlyWeighted[$periodKey] = $weightedValue; + $monthlyPrice[$periodKey] = $price; + $weightedSum += $weightedValue; + } + + return [ + 'monthlySales' => $monthlySales, + 'monthlyWeighted' => $monthlyWeighted, + 'weightedSum' => $weightedSum, + 'monthlyPrice' => $monthlyPrice, + ]; + } + } -- 2.39.5