< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.AnalysisServices.SalesForecastService
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/AnalysisServices/SalesForecastService.cs
Line coverage
100%
Covered lines: 39
Uncovered lines: 0
Coverable lines: 39
Total lines: 88
Line coverage: 100%
Branch coverage
81%
Covered branches: 13
Total branches: 16
Branch coverage: 81.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ForecastSales(...)83.33%1212100%
ExponentialSmoothing(...)75%44100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/AnalysisServices/SalesForecastService.cs

#LineLine coverage
 1using OzonDomains.Models;
 2
 3namespace OzonOrdersWeb.Areas.Studio2.AnalysisServices
 4{
 5    public class SalesForecastService
 6    {
 7        /// <summary>
 8        /// Прогнозирование продаж на основе исторических данных заказов
 9        /// Использует метод экспоненциального сглаживания с учетом сезонности (день недели)
 10        /// </summary>
 11        public Dictionary<DateTime, decimal> ForecastSales(
 12            List<Order> historicalOrders,
 13            int forecastPeriodDays = 30)
 14        {
 15            // Группируем продажи по дням
 316            var dailySales = historicalOrders
 3617                .Where(o => o.ProcessingDate.HasValue)
 3518                .GroupBy(o => o.ProcessingDate!.Value.Date)
 3519                .Select(g => new
 3520                {
 3521                    Date = g.Key,
 3522                    TotalSales = g.Sum(o => (o.Price ?? 0) * (o.TrueQuantity ?? o.Quantity ?? 0)),
 3523                    TotalQuantity = g.Sum(o => o.TrueQuantity ?? o.Quantity ?? 0)
 3524                })
 3525                .OrderBy(x => x.Date)
 326                .ToList();
 27
 328            var forecast = new Dictionary<DateTime, decimal>();
 29
 330            if (!dailySales.Any())
 231                return forecast;
 32
 3633            var lastDate = dailySales.Max(x => x.Date);
 34
 1235            for (int i = 1; i <= forecastPeriodDays; i++)
 36            {
 537                var forecastDate = lastDate.AddDays(i);
 38
 39                // Используем среднее значение за последние N дней
 540                int windowSize = 7; // неделя
 541                var recentSales = dailySales
 17542                    .Where(x => x.Date >= lastDate.AddDays(-windowSize))
 543                    .ToList();
 44
 545                if (recentSales.Any())
 46                {
 47                    // Учитываем день недели (сезонность)
 548                    var dayOfWeek = forecastDate.DayOfWeek;
 549                    var historicalSameDaySales = dailySales
 17550                        .Where(x => x.Date.DayOfWeek == dayOfWeek)
 551                        .TakeLast(4) // Последние 4 недели
 2052                        .Select(x => x.TotalSales)
 553                        .ToList();
 54
 555                    if (historicalSameDaySales.Any())
 56                    {
 57                        // Экспоненциальное сглаживание
 558                        double forecastValue = ExponentialSmoothing(
 559                            historicalSameDaySales,
 560                            alpha: 0.3);
 61
 562                        forecast[forecastDate] = (decimal)forecastValue;
 63                    }
 64                }
 65            }
 66
 167            return forecast;
 68        }
 69
 70        /// <summary>
 71        /// Экспоненциальное сглаживание временного ряда
 72        /// alpha - параметр сглаживания (0-1), чем больше - тем больше вес последних данных
 73        /// Формула: S_t = α * Y_t + (1-α) * S_{t-1}
 74        /// </summary>
 75        private double ExponentialSmoothing(List<decimal> data, double alpha)
 76        {
 577            if (data.Count == 0) return 0;
 78
 579            double smoothed = (double)data[0];
 4080            for (int i = 1; i < data.Count; i++)
 81            {
 1582                smoothed = alpha * (double)data[i] + (1 - alpha) * smoothed;
 83            }
 584            return smoothed;
 85        }
 86    }
 87}
 88