Вопрос

Учет календаря в жизненном цикле обращения

Добрый день!

В жизненном цикле количество часов в определенном статусе идет без расчета производственного календаря (рабочий день с 9:00 до 18:00, т.е. должно быть 12 минут, а не 15 часов)

Изображение удалено.Нашла  в конфигураторе, где именно происходит расчет:

var previousIntervalESQ = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "CaseLifecycle");

previousIntervalESQ.AddAllSchemaColumns();

previousIntervalESQ.AddColumn("StartDate").OrderByDesc();

previousIntervalESQ.Filters.Add(previousIntervalESQ.CreateFilterWithParameters(

    FilterComparisonType.Equal, "Case", CaseId));

previousIntervalESQ.Filters.Add(previousIntervalESQ.CreateIsNullFilter("EndDate"));

var previousIntervals = previousIntervalESQ.GetEntityCollection(UserConnection);

if (previousIntervals.Count == 0) {

    return false;

}

var lastInterval = previousIntervals[0];

lastInterval.UseAdminRights = false;

DateTime startDate = lastInterval.GetTypedColumnValue("StartDate");

TimeSpan difference = (TimeSpan)(Date-startDate);

lastInterval.SetColumnValue("EndDate", Date);

lastInterval.SetColumnValue("StateDurationInMinutes", difference.TotalMinutes);

lastInterval.SetColumnValue("StateDurationInHours", difference.TotalHours);

lastInterval.SetColumnValue("StateDurationInDays", difference.TotalDays);

lastInterval.Save();

return true;

Подскажите, что прописать в данном методе для корректного расчета с учетом календаря?

Заранее спасибо за ответ!

Нравится

6 комментариев
Лучший ответ
var lastInterval = previousIntervals[0];
TimeSpan workingS = new TimeSpan(9, 0, 0);
TimeSpan workingL = new TimeSpan(18, 0, 0);
lastInterval.UseAdminRights = false;
DateTime startDate = lastInterval.GetTypedColumnValue<DateTime>("StartDate");
//TimeSpan difference = (TimeSpan)(Date-startDate);
TimeSpan difference = ((TimeSpan)(workingL - finishDate.TimeOfDay)) + ((TimeSpan)(startDate.TimeOfDay - workingS));
lastInterval.SetColumnValue("EndDate", Date);
lastInterval.SetColumnValue("StateDurationInMinutes", difference.TotalMinutes);
lastInterval.SetColumnValue("StateDurationInHours", difference.TotalHours);
lastInterval.SetColumnValue("StateDurationInDays", difference.TotalDays);
lastInterval.Save();
return true;

Если нужно будет учитывать выходные дни, если задачи могут длиться большего чем один рабочий день, то  придется дорабатывать логику, а если исключительно как в примере, то тогда так.

var lastInterval = previousIntervals[0];
TimeSpan workingS = new TimeSpan(9, 0, 0);
TimeSpan workingL = new TimeSpan(18, 0, 0);
lastInterval.UseAdminRights = false;
DateTime startDate = lastInterval.GetTypedColumnValue<DateTime>("StartDate");
//TimeSpan difference = (TimeSpan)(Date-startDate);
TimeSpan difference = ((TimeSpan)(workingL - finishDate.TimeOfDay)) + ((TimeSpan)(startDate.TimeOfDay - workingS));
lastInterval.SetColumnValue("EndDate", Date);
lastInterval.SetColumnValue("StateDurationInMinutes", difference.TotalMinutes);
lastInterval.SetColumnValue("StateDurationInHours", difference.TotalHours);
lastInterval.SetColumnValue("StateDurationInDays", difference.TotalDays);
lastInterval.Save();
return true;

Если нужно будет учитывать выходные дни, если задачи могут длиться большего чем один рабочий день, то  придется дорабатывать логику, а если исключительно как в примере, то тогда так.

Литвинко Павел,

Спасибо большое за комментарий, не могли бы вы ещё подсказать, хотя бы схематично, пути доработки логики, так как выходные действительно нужно учитывать?

Васильева Светлана Александровна,

(Немного обновил код) Ну, если на вскидку, то к той логике, что у вас уже есть, поле расчета переменной difference убираем, добавляем примерно такую логику:

Получаем разницу в днях и циклом пробегаемся по каждому дню, первый и последний проверяем отдельно, а все остальные если это не выходные, то прибавляем 9 рабочих часов,

difference указываем разницу между первым и последним днем, и добавляем полученное кол-во часов, к этой разнице.

 

TimeSpan difference = new TimeSpan();
            //((TimeSpan)(workingL - finishDate.TimeOfDay)) + ((TimeSpan)(startDate.TimeOfDay - workingS));
            TimeSpan span = finishDate - startDate;
            int relative = span.Days;
            int workHours = 0;
            int HoursFL = 0;
            int MinutesFL = 0;
            for (int i = 0; i <= relative; i++)
            {
                DateTime temp = new DateTime();
                temp = startDate.AddDays(i);
 
                if (startDate.Date == temp.Date)
                {
                    var tempHour = workingL - temp.TimeOfDay;
                    HoursFL += tempHour.Hours;
                    MinutesFL += tempHour.Minutes;
                }
                else if (finishDate.Date == temp.Date)
                {
                    var tempHour = workingL - finishDate.TimeOfDay;
                    HoursFL += tempHour.Hours;
                    MinutesFL += tempHour.Minutes;
                }
                else
                {
                    string tempDay = temp.DayOfWeek.ToString();
                    if (tempDay != "Saturday" && tempDay != "Sunday")
                    {
                        workHours += 9;
                    }
                }
            }
            difference = new TimeSpan(workHours + HoursFL, MinutesFL, 0);



 

 

Ну и туда же можно добавить еще проверку на государственные праздники, которые можно например в детали хранить, заполняя раз в год

Васильева Светлана Александровна,

здравствуйте, возникла похожая задача, не могли бы вы написать название сервиса в котором рассчитываются значения ?

t.vladislav,

Владислав, решила не трогать базовую логику, поэтому для своей задачи создала объект в виде представления в БД, и создала VIEW с расчетной колонкой. Календарь взяла из сервисного договора.

Предварительно создала функцию

CREATE FUNCTION [dbo].[WorkTime] 

(

    @StartDate DATETIME,

    @FinishDate DATETIME,

    @CalendarId UNIQUEIDENTIFIER = 'F0FF1F0E-F46B-1410-1787-0026185BFCD3' --Типовой календарь

)

RETURNS BIGINT

AS

BEGIN

    DECLARE @Temp BIGINT = 0

    DECLARE @FirstDay  DATE  = CONVERT(DATE, @StartDate, 112)

    DECLARE @LastDay   DATE  = CONVERT(DATE, @FinishDate, 112)

    DECLARE @StartTime TIME  = CONVERT(TIME, @StartDate)

    DECLARE @FinishTime TIME = CONVERT(TIME, @FinishDate)



    DECLARE @WorkStart TIME  = '09:00'

    DECLARE @WorkFinish TIME = '18:00'

    -- получаем рабочее время по случайному дню в выбранном календаре

    SELECT @WorkStart=From, @WorkFinish=To FROM [WorkingTimeInterval] WHERE [DayInCalendarId] = (

        SELECT TOP 1 [Id] FROM [DayInCalendar]  WHERE [CalendarId] = @CalendarId

    )

    

    DECLARE @DailyWorkTime BIGINT = DATEDIFF(MINUTE, @WorkStart, @WorkFinish)

    DECLARE @DayOff TABLE (

      [Date] date

    )

    INSERT INTO @DayOff

    SELECT [Date] FROM DayOff WHERE CalendarId = @CalendarId AND DayTypeId = '078c9b1e-9312-43ef-b890-e5298db62827' -- выходной

    --TODO: calc short days too

    IF (@StartTime<@WorkStart)

    BEGIN

        SET @StartTime = @WorkStart

    END

    IF (@FinishTime>@WorkFinish)

    BEGIN

        SET @FinishTime=@WorkFinish

    END

    IF (@FinishTime<@WorkStart)

    BEGIN

        SET @FinishTime=@WorkStart

    END

    IF (@StartTime>@WorkFinish)

    BEGIN

        SET @StartTime = @WorkFinish

    END

    DECLARE @CurrentDate DATE

    SET @CurrentDate = @FirstDay

    DECLARE @LastDate DATE

    SET @LastDate = @LastDay

    WHILE(@CurrentDate<=@LastDate)

    BEGIN       

        IF (DATEPART(dw, @CurrentDate)!=1 AND DATEPART(dw, @CurrentDate)!=7) AND NOT EXISTS (SELECT 1 FROM @DayOff WHERE [Date] = @CurrentDate)

        BEGIN

            IF (@CurrentDate!=@FirstDay) AND (@CurrentDate!=@LastDay)

            BEGIN

                SET @Temp = @Temp + @DailyWorkTime

            END

            --IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes

            ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate!=@LastDay)

            BEGIN

                SET @Temp = @Temp + DATEDIFF(MINUTE, @StartTime, @WorkFinish)

            END

            ELSE IF (@CurrentDate!=@FirstDay) AND (@CurrentDate=@LastDay)

            BEGIN

                SET @Temp = @Temp + DATEDIFF(MINUTE, @WorkStart, @FinishTime)

            END

            --IF it starts and finishes in the same date

            ELSE IF (@CurrentDate=@FirstDay) AND (@CurrentDate=@LastDay)

            BEGIN

                SET @Temp = DATEDIFF(MINUTE, @StartTime, @FinishTime)

            END

        END

        SET @CurrentDate = DATEADD(day, 1, @CurrentDate)

    END

    -- Return the result of the function

    IF @Temp<0

    BEGIN

        SET @Temp=0

    END

    RETURN @Temp

END

_____________________________________

create view ITdsVwCaseLifecycle as 

SELECT 

    NEWID() as [Id]

      ,[CaseLifecycle].[CreatedOn] as [CreatedOn]

      ,[CaseLifecycle].[CreatedById] as [CreatedById]

      ,[CaseLifecycle].[ModifiedOn] as [ModifiedOn]

      ,[CaseLifecycle].[ModifiedById] as [ModifiedById]

      ,null as [ProcessListeners]

      ,[dbo].[WorkTime]([CaseLifecycle].[StartDate], [CaseLifecycle].[EndDate], [ServicePact].[CalendarId]) as [ITdsTiming] -- рассчитывает время в минутах

      ,([dbo].[WorkTime] ([CaseLifecycle].[StartDate], [CaseLifecycle].[EndDate], [ServicePact].[CalendarId]) / 60.0) as [ITdsTimingInHour] -- рассчитывает время в часах

      ,[Case].[ID] as [ITdsCaseId]

      ,[CaseLifecycle].[Id] as [ITdsCaseLIfeCycleId]

      ,[ServicePact].[Id] as [ServicePactId]

  FROM [DEV].[dbo].[CaseLifecycle] -- жизненный цикл обращения

  left join [Case] on [Case].[ID] = [CaseLifecycle].[CaseId] -- обращения

  left join [ServicePact] on [Case].[ServicePactId] = [ServicePact].[Id] -- сервисный договор

Показать все комментарии