$service = new AutoPlannogrammaService();
//$goals = $service->calculateFullGoalChain($filters);
//$forecast = $service->calculateFullForecastForWeek($filters);
+ //$noHistoryProductData = $service->calculateSpeciesForecastForProductsWithoutHistory($filters['plan_date'], $filters);
+
+ $historyProductData = $service->calculateSpeciesForecastForProductsWithHistory($filters['plan_date'], $filters, []);
+ var_dump($historyProductData); die();
+ $salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $historyProductData);
+
+ $productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals);
+
+
$monthCategoryShare = $service->getMonthCategoryShareOrWriteOff($filters['plan_date'], $filters);
+
$monthCategoryGoal = $service->getMonthCategoryGoal($monthCategoryShare, $filters['plan_date']);
+
$monthSubcategoryShare = $service->getMonthSubcategoryShareOrWriteOff($filters['plan_date'], $filters);
+
$monthSubcategoryGoal = $service->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
$monthSpeciesShare = $service->getMonthSpeciesShareOrWriteOff($filters['plan_date'], $filters);
$goals = $service->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
$cleanedSpeciesGoals = $service->subtractSpeciesGoals($goals, [], []);
$salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $historyProductData);
-
+ var_dump($salesProductForecastShare); die();
$productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals);
$weeklySales = $service->getHistoricalSpeciesShareByWeek($filters['plan_date'], $filters);
$salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $productSalesForecast);
-
+ var_dump($salesProductForecastShare);die();
$productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals);
'subcategory' => 'main.subcategory',
'total_sum' => 'main.total_sum',
'percent' => new Expression('ROUND(CAST(main.total_sum AS DECIMAL) / NULLIF(totals.total, 0), 4)'),
- 'type' => new Expression(':type', ['type' => $type]),
+ 'type' => new Expression(':type', [':type' => $type]),
'totals_total' => 'totals.total',
])
->from([
foreach ($storeIds as $sid) {
foreach ($periods as $dt) {
- $items = $this->getProductsComponentsInCategory(
- $sid,
- (int)$dt->format('n'),
- (int)$dt->format('Y'),
- $type
- );
- if (empty($items)) {
+ try {
+ $items = $this->getProductsComponentsInCategory(
+ $sid,
+ (int)$dt->format('n'),
+ (int)$dt->format('Y'),
+ $type
+ );
+ } catch (\Throwable $e) {
continue;
}
- $filtered = match ($mode) {
- 'offline' => array_filter($items, fn($it) => in_array($it['order_id'], ['', '0'], true)),
- 'online' => array_filter($items, fn($it) => !in_array($it['order_id'], ['', '0'], true)),
- default => $items, // для writeOffs — всё
+ if (empty($items) || !is_array($items)) {
+ continue;
+ }
+
+ $normalizeOrderId = static function($row): string {
+ if (!is_array($row)) return '';
+ $oid = $row['order_id'] ?? '';
+ if ($oid === null) return '';
+ return (string)$oid;
};
- $allComponentsProductIds[$sid] = array_merge(
- $allComponentsProductIds[$sid] ?? [],
- array_column($filtered, 'product_id')
- );
+ try {
+ $filtered = match ($mode) {
+ 'offline' => array_filter($items, fn($it) => in_array($normalizeOrderId($it), ['', '0'], true)),
+ 'online' => array_filter($items, fn($it) => !in_array($normalizeOrderId($it), ['', '0'], true)),
+ default => $items,
+ };
+ } catch (\Throwable $e) {
- $sums = $this->sumProductsComponentsByGroup($filtered, $type, 'subcategory');
+ $filtered = $items;
+ }
- $allComponentsProductIds[$sid] = array_values(array_unique($allComponentsProductIds[$sid]));
+
+ $productIds = [];
+ foreach ($filtered as $row) {
+ if (is_array($row) && array_key_exists('product_id', $row)) {
+ $productIds[] = $row['product_id'];
+ }
+ }
+ $allComponentsProductIds[$sid] = array_values(array_unique(array_merge(
+ $allComponentsProductIds[$sid] ?? [],
+ $productIds
+ )));
+
+ $sums = $this->sumProductsComponentsByGroup($filtered, $type, 'subcategory') ?? [];
foreach ($sums as $sr) {
- $cat = $sr['category'];
- $subcat = $sr['subcategory'];
- $val = $sr['sum'];
+ if (!is_array($sr)) { continue; }
+ $cat = $sr['category'] ?? null;
+ $subcat = $sr['subcategory'] ?? null;
+ $val = (float)($sr['sum'] ?? 0);
- $componentAdds[$sid][$cat][$subcat] =
- ($componentAdds[$sid][$cat][$subcat] ?? 0) + $val;
+ if ($cat === null || $subcat === null) { continue; }
+
+ $componentAdds[$sid][$cat][$subcat] = ($componentAdds[$sid][$cat][$subcat] ?? 0) + $val;
+ $componentAddsSumAll[$sid][$cat] = ($componentAddsSumAll[$sid][$cat] ?? 0) + $val;
- $componentAddsSumAll[$sid][$cat] =
- ($componentAddsSumAll[$sid][$cat] ?? 0) + $val;
}
}
}
}
$productSalesShare = StorePlanService::calculateProductSalesShareProductsWithHistory(
- $filters['store_id'],
- $filters['month'],
+ $storeId,
+ $month,
$productsWithHistory
);
}
$productSalesForecast = $this->calculateProductForecastInPiecesProductsWithHistory(
- $filters['store_id'],
- $filters['month'],
+ $storeId,
+ $month,
$year,
$goals,
$productSalesShare
foreach ($productSalesShare as $productId => $data) {
$share = $data['share'] ?? 0.0;
+ $avgWeightedSumm = $data['avgWeightedSum'] ?? 0.0;
if ($share <= 0.0) {
continue;
$goalsMap[$storeId],
$goalsMap[$storeId][$cat],
$goalsMap[$storeId][$cat][$sub],
- $goalsMap[$storeId][$cat][$sub][$spec]
+ // $goalsMap[$storeId][$cat][$sub][$spec]
)
) {
continue;
}
-
- $goal = $goalsMap[$storeId][$cat][$sub][$spec];
-
- $forecastSum = $goal * $share;
+ $isGoalExists = isset($goalsMap[$storeId][$cat][$sub][$spec]);
+ $goal = $goalsMap[$storeId][$cat][$sub][$spec] ?? 0;
+ if ($goal !== 0) {
+ $forecastSum = $goal * $share;
+ } else {
+ $forecastSum = $avgWeightedSumm;
+ }
$forecastCount = $forecastSum / $price;
'category' => $data['category'],
'subcategory' => $data['subcategory'],
'species' => $data['species'],
+ 'is_goal_exists' => $isGoalExists
];
}
class StorePlanService
{
+ const PERIOD_COUNT = 3;
+
/**
* @param int $month
* @param int $year
$baseDate = strtotime("{$selectedYear}-{$selectedMonth}-01");
// Получаем периоды за 3 предыдущих месяца.
- $periods = self::getPeriods($baseDate, 3, true);
+ $periods = self::getPeriods($baseDate, self::PERIOD_COUNT, true);
// Получаем историю продаж для каждого периода.
$salesHistory = self::getSalesHistory($storeId, $periods, $category, $subcategory, $species);
public static function calculateMedianSalesForProductsWithoutHistoryExtended($storeId, $selectedMonth, $selectedYear, $productsWithoutHistory)
{
$targetDate = strtotime("{$selectedYear}-{$selectedMonth}-01");
- $periods = self::getPeriods($targetDate, 3);
+ $periods = self::getPeriods($targetDate, self::PERIOD_COUNT);
$medianSalesResults = [];
array $productsWithoutHistory
): array {
$targetDate = strtotime("{$selectedYear}-{$selectedMonth}-01");
- $periods = self::getPeriods($targetDate, 3);
+ $periods = self::getPeriods($targetDate, self::PERIOD_COUNT);
$results = [];
foreach ($productsWithoutHistory as $prod) {
{
$targetDate = strtotime(date('Y') . "-$selectedMonth-01");
- $weightedPeriods = self::getPeriods($targetDate, 3, false, true);
+ $weightedPeriods = self::getPeriods($targetDate, self::PERIOD_COUNT, false, true);
$productsData = [];
$globalTotal = 0;
foreach ($productsData as $guid => &$data) {
$data['share'] = ($globalTotal > 0) ? $data['weightedSum'] / $globalTotal : 0;
+ if (!empty($weightedPeriods)) {
+ $countPeriods = count($weightedPeriods);
+ } else {
+ $countPeriods = self::PERIOD_COUNT;
+ }
+ $data['avgWeightedSum'] = $data['weightedSum'] ? $data['weightedSum'] / $countPeriods : 0;
}
unset($data);