private static void verifyDispanDd14(IBackgroundContext context, PrevalidatorParams args, 
            IEnumerable<Event> allEvents)
        {
            if (context.CancellationPending) return;

            context.ReportProgress(@"Проверка диспансеризации детей 14 летних");

            if (dsServices == null)
            {
                loadDdServices();
            }

            var eventCodes = new[] { "ДД14лет" };
            var actionComplexPrefix = @"Комплексная услуга на";
            var regExAgeList = new Regex(@"\(.*\)");

            var ddsEvents = from e in allEvents
                            where eventCodes.Contains(e.EventType.Code)
                            select e;

            var errors = new List<string>();
            foreach (var ddsEvent in ddsEvents)
            {
                errors.Clear();
                var actComplex = (from a in ddsEvent.Actions where a.ActionType != null
                                      && a.ActionType.Name.StartsWith(actionComplexPrefix)
                                  select a).ToList();
                Action complexService = null;

                if (!actComplex.Any())
                {
                    errors.Add(string.Format(@"Не найдена ""{0}""", actionComplexPrefix));
                }
                else
                {
                    if (actComplex.Count() > 1)
                        errors.Add(@"Определено более одной комплексной услуги");
                    else
                    {
                        complexService = actComplex.First();

                        var emptyIspoln = (from a in ddsEvent.Actions
                                           where a.ActionType != null && a.Person_PersonId == null
                                           select a).ToList();
                        errors.AddRange(emptyIspoln.Select(action =>
                            string.Format(@"Не задан исполнитель услуги {0}", action.ActionType.Code)));

                        var emptySpec = (from a in ddsEvent.Actions
                                         where
                                             a.ActionType != null && a.Person_PersonId != null &&
                                             a.Person_PersonId.RbSpeciality == null
                                         select a).ToList();
                        errors.AddRange(emptySpec.Select(action =>
                            string.Format(@"Не задана специальность исполнителя услуги {0}", action.ActionType.Code)));

                        if (complexService.Person_PersonId != null && complexService.Person_PersonId.RbSpeciality != null)
                        {

                            var specCode = complexService.Person_PersonId.RbSpeciality.FederalCode;
                            var validSpecCodes = new List<string> { "1134" };

                            if (!validSpecCodes.Contains(specCode))
                                errors.Add(
                                    string.Format(@"Неверная специальность исполнителя комплексной услуги {0} {1}",
                                                  specCode, complexService.Person_PersonId.FIO));

                        }

                        var serviceName = complexService.ActionType.Name;
                        if (ddsEvent.ExecDate.HasValue)
                        {

                            if (regExAgeList.IsMatch(serviceName))
                            {
                                var ddYear = ddsEvent.ExecDate.Value.Year - 14;

                                if (ddsEvent.Client.BirthDate.Year != ddYear)
                                {
                                    errors.Add(string.Format(@"Год рождения пациента {0} не равен {1}",
                                        ddsEvent.Client.BirthDate.Year, ddYear));
                                }
                            }

                            var longServices = from a in ddsEvent.Actions where a.EndDate != a.BegDate select a;
                            errors.AddRange(longServices.Select(longService =>
                                string.Format(@"Услуга {0} имеет дату окончания не равную дате начала",
                                longService.ActionType.Code)));

                            var oldServices = (from a in ddsEvent.Actions where a.BegDate < args.DateBegin select a).ToList();

                            foreach (var oldService in oldServices)
                            {
                                var maxServiceAgeMonth = oldService.ActionType.Code.EndsWith("61021") ? 12 : 1;
                                if (oldService.BegDate != null && oldService.BegDate.Value < ddsEvent.SetDate.AddMonths(-maxServiceAgeMonth))
                                {
                                    errors.Add(string.Format(@"Анализ просрочен {0}", oldService.ActionType.Code));
                                }
                            }

                            var dupActions = (from a in ddsEvent.Actions
                                              group a by a.ActionType
                                                  into g
                                                  where g.Count() > 1
                                                  select g);

                            foreach (var dupAction in dupActions)
                            {
                                errors.Add(string.Format(@"Найдено более одного ({0}) мероприятия с кодом {1}",
                                    dupAction.Count(), dupAction.Key.Code));
                            }

                            /* объемы */
                            var ddrServiceIfo =
                                (from d in dsServices
                                 where d.EventCode == ddsEvent.EventType.Code && d.Gender == ddsEvent.Client.Sex
                                 select d).FirstOrDefault();

                            if (ddrServiceIfo != null)
                            {
                                var ageServices = (from c in ddrServiceIfo.AgeServicesList
                                                   from s in c.ServiceCodes
                                                   where 14 >= c.AgeBegin && 14 <= c.AgeEnd
                                                   select s).Distinct().ToList();

                                if (ageServices.Count > 0)
                                {
                                    var minCount = ageServices.Count;

                                    var effectiveActions = (from a in ddsEvent.Actions
                                                            where ageServices.Contains(a.ActionType.Code)
                                                            select a).ToList();
                                    if (effectiveActions.Count < minCount)
                                    {
                                        var candidates = string.Join(",",
                                            ageServices.Except(effectiveActions.Select(x => x.ActionType.Code)));

                                        errors.Add(string.Format(@"Недостаточный объем услуг {0} из {1}. Можно добавить {2}",
                                            effectiveActions.Count, minCount, candidates));
                                    }

                                    var unavailActions = (from a in ddsEvent.Actions
                                                          where !ageServices.Contains(a.ActionType.Code) && a != complexService
                                                          select a).ToList();
                                    if (unavailActions.Count > 0)
                                    {
                                        errors.Add(string.Format(@"Недопустимые(ое) мероприятия(e) {0} нужно удалить",
                                            string.Join(",", unavailActions.Select(x => x.ActionType.Code))));
                                    }
                                }
                            }

                        }
                    }
                }
                if (errors.Count > 0)
                {
                    context.ReportError(@" {0} {1}", ddsEvent.EventType.Code, ddsEvent);
                    if (complexService != null)
                        context.ReportProgress(@"  > {0}", complexService);
                    errors.ForEach(x => context.ReportProgress(@"    -> {0}", x));
                }
            }
        }
        private static void verifyDispanDdr1(IBackgroundContext context, PrevalidatorParams args, 
            IList<Event> allEvents)
        {
            if (context.CancellationPending) return;

            context.ReportProgress(@"Проверка диспансеризации работающих 2013 (1 этап)");

            if(ddServices == null)
            {
                loadDdServices();
            }

            var eventCodes = new[] {"dd2013_1"};
            const string actionComplexPrefix = @"Комплексная услуга на";
            var regExGender = new Regex(@"(мужчины|женщины)");
            var regExAgeList = new Regex(@"\(.*\)");

            var ddrEvents = from e in allEvents
                            where eventCodes.Contains(e.EventType.Code)
                            select e;

            var errors = new List<string>();

            foreach (var ddrEvent in ddrEvents)
            {
                errors.Clear();

                var actComplex = (from a in ddrEvent.Actions
                                 where a.ActionType != null && a.ActionType.Name.StartsWith(actionComplexPrefix)
                                 select a).ToList();

                var isDdr2014 = ddrEvent.SetDate < new DateTime(2015, 4, 1);

                Action complexService = null;

                if (!actComplex.Any())
                {
                    errors.Add(string.Format(@"Не найдена ""{0}""", actionComplexPrefix));
                }
                else
                {
                    if(actComplex.Count() > 1)
                        errors.Add(@"Определено более одной комплексной услуги");
                    else
                    {
                        complexService = actComplex.FirstOrDefault();

                        var emptyIspoln = (from a in ddrEvent.Actions
                                           where a.ActionType != null && a.Person_PersonId == null
                                           select a).ToList();
                        errors.AddRange(emptyIspoln.Select(action =>
                            string.Format(@"Не задан исполнитель услуги {0}", action.ActionType.Code)));

                        var emptySpec = (from a in ddrEvent.Actions
                                         where
                                             a.ActionType != null && a.Person_PersonId != null &&
                                             a.Person_PersonId.RbSpeciality == null
                                         select a).ToList();
                        errors.AddRange(emptySpec.Select(action =>
                            string.Format(@"Не задана специальность исполнителя услуги {0}", action.ActionType.Code)));

                        var specCode = "???";
                        if (complexService != null)
                        {
                            if (complexService.Person_PersonId != null)
                                specCode = complexService.Person_PersonId.RbSpeciality.FederalCode;
                            var validSpecCodes = new List<string> {"27", "16"};

                            if (!validSpecCodes.Contains(specCode))
                                errors.Add(
                                    string.Format(@"Неверная специальность исполнителя комплексной услуги {0} {1}",
                                        specCode,
                                        (complexService.Person_PersonId == null
                                            ? "???"
                                            : complexService.Person_PersonId.FIO)));
                        }

                        var serviceName = complexService == null ? "" : complexService.ActionType.Name;
                        var isNewComplex = regExGender.IsMatch(serviceName);
                        if(string.IsNullOrEmpty(serviceName) || isNewComplex || ddrEvent.ExecDate < new DateTime(2013, 5, 1))
                        {
                            int gender = ddrEvent.Client.Sex;
                            if (!string.IsNullOrEmpty(serviceName))
                            {
                                if (isNewComplex)
                                {
                                    gender = regExGender.Match(serviceName).Value == @"мужчины" ? 1 : 2;
                                }
                                if (gender > 0 && ddrEvent.Client.Sex != gender)
                                    errors.Add(@"Неверный пол пациента");

                                if (regExAgeList.IsMatch(serviceName))
                                {
                                    var strList = regExAgeList.Match(serviceName).Value;
                                    strList = strList.Replace("для возрастов - ", "");
                                    strList = strList.Replace("для возрастов ", "");
                                    strList = strList.Replace(" лет", "");
                                    strList = strList.Substring(1, strList.Length - 2);
                                    var clientYear = ddrEvent.Client.BirthDate.Year;

                                    if (strList.Contains(","))
                                    {

                                        var ageList = strList.Split(',').Select(x => int.Parse(x.Trim())).ToList();

                                        var yearList = ageList.Select(x => complexService.EndDate != null
                                            ? complexService.EndDate.Value.Year - x
                                            : 0).ToList();

                                        if (!yearList.Contains(clientYear))
                                        {
                                            errors.Add(
                                                string.Format(@"Год рождения пациента {0} не соответствует допустимым"
                                                              + @" годам для услуги {1} - {2}", clientYear,
                                                    complexService.ActionType.Code,
                                                    string.Join(",", yearList.Select(x => x.ToString()))
                                                    ));
                                        }
                                    }
                                    else if (strList.Contains("-"))
                                    {
                                         // 061050 - 061051
                                        strList = strList.Replace(" ", "");
                                        var ageBounds = strList.Split('-').Select(x => int.Parse(x.Trim())).ToList();
                                        for (int i = 0; i < ageBounds.Count; i++)
                                        {
                                            ageBounds[i] = args.DateEnd.Year - ageBounds[i];
                                        }
                                        if (clientYear > ageBounds[0] || clientYear < ageBounds[1])
                                        {
                                            errors.Add(
                                                string.Format(@"Год рождения пациента {0} не соответствует допустимым"
                                                              + @" возрастам для услуги {1} - {2}", clientYear,
                                                    complexService.ActionType.Code,
                                                    "от " + ageBounds[0] + " до " + ageBounds[1]));
                                        }
                                    }
                                }
                            }

                            var longServices = from a in ddrEvent.Actions
                                               where a.EndDate != a.BegDate && a.ActionType.Code.IsInt()
                                               select a;

                            foreach (var longService in longServices)
                            {
                                errors.Add(string.Format(@"Услуга {0} имеет дату окончания не равную дате начала", longService.ActionType.Code));
                            }

                            var oldServices = (from a in ddrEvent.Actions
                                              where a.BegDate < args.DateBegin
                                              select a).ToList();

                            var dupActions = (from a in ddrEvent.Actions group a by a.ActionType
                                                  into g where g.Count() > 1 select g);

                            foreach (var dupAction in dupActions)
                            {
                                errors.Add(string.Format(@"Найдено более одного ({0}) мероприятия с кодом {1}",
                                    dupAction.Count(), dupAction.Key.Code));
                            }

                            /* объемы */
                            var theEvent = ddrEvent;

                            var ddrServiceIfo =
                                (from d in (isDdr2014 ? ddServices2014 : ddServices)
                                 where d.EventCode == theEvent.EventType.Code && d.Gender == gender
                                 select d).FirstOrDefault();

                            if(ddrServiceIfo != null)
                            {
                                if (theEvent.ExecDate != null)
                                {
                                    var age = theEvent.ExecDate.Value.Year - ddrEvent.Client.BirthDate.Year;
                                    var ageSrv = ddrServiceIfo.AgeServicesList.FirstOrDefault(x => x.Age == age);
                                    if(ageSrv != null)
                                    {
                                        var minCount = ageSrv.ServiceCodes.Count * 0.85;

                                        if ((int)minCount < minCount)
                                            minCount = ((int)minCount) + 1;

                                        if (ageSrv.MinServicesCount > 0)
                                            minCount = ageSrv.MinServicesCount;

                                        var effectiveActions = (from a in ddrEvent.Actions
                                                                where ageSrv.ServiceCodes.Contains(a.ActionType.Code)
                                                                select a).ToList();

                                        if(effectiveActions.Count < minCount)
                                        {
                                            var candidates = string.Join(",",
                                                                         ageSrv.ServiceCodes.Except(
                                                                             effectiveActions.Select(x => x.ActionType.Code)));

                                            errors.Add(string.Format(@"Недостаточный объем услуг {0} из {1}. Можно добавить {2}",
                                                                     effectiveActions.Count, minCount, candidates));
                                        }

                                        var unavailActions = (from a in ddrEvent.Actions
                                                              where !ageSrv.ServiceCodes.Contains(a.ActionType.Code)
                                                                    && !(!a.ActionType.Code.IsInt() || a == complexService)
                                                              select a).ToList();
                                        if(unavailActions.Count > 0)
                                        {
                                            errors.Add(string.Format(@"Недопустимые(ое) мероприятия(e) {0} нужно удалить",
                                                                     string.Join(",", unavailActions.Select(x => x.ActionType.Code))));
                                        }
                                    }
                                }
                            }

                        }
                        else
                            errors.Add(string.Format(@"Комплексная услуга {0} более не используется", complexService.ActionType.Code));
                    }
                }

                var reexRes = new Regex(@"(рисвоена|пределена) .* гр.*здоровья");
                if(ddrEvent.RbResult == null)
                    errors.Add("Не определен результат обращения");
                else if(!reexRes.IsMatch(ddrEvent.RbResult.Name))
                    errors.Add("Недопустимый результат обращения (в результате нет группы здоровья)");

                if(errors.Count>0)
                {
                    context.ReportError(@" dd2013 {0}", ddrEvent);
                    if(complexService != null)
                        context.ReportProgress(@"  > {0}", complexService);
                    errors.ForEach(x => context.ReportProgress(@"    -> {0}", x));
                }
            }
        }
        private static void verifyMkb(IBackgroundContext context, PrevalidatorParams args, VistaMedDataContext dataContext)
        {
            if (context.CancellationPending) return;

            context.ReportProgress(@"Проверка на пустые МКБ");
            var emptyMkbActions = (from action in dataContext.VistaMed.ActionsNotInAccount
                                  where action.EndDate >= args.DateBegin && action.EndDate <= args.DateEnd
                                        && action.Event != null && action.Event.EventType != null
                                        && action.Event.EventType.Name.ToLower().Contains(@"стоматология")
                                        && action.MKB == null
                                  select action).ToList();
            if (emptyMkbActions.Any())
            {
                context.ReportError(@"Неопределенный МКБ для услуг");
                foreach (var action in emptyMkbActions)
                {
                    context.ReportProgress(@"    > {0} {1}", action.Event, action.ToShortString());
                    if (context.CancellationPending) return;
                }
            }

            if (context.CancellationPending) return;
            var emptyMkbDs = from d in dataContext.VistaMed.Diagnoses
                             from ds in dataContext.VistaMed.Diagnostics
                             where (d.MKB == null || string.IsNullOrEmpty(d.MKB))
                                   && ds.DiagnosisId == d.Id
                                   && ds.Event != null && ds.Event.ExecDate >= args.DateBegin &&
                                   ds.Event.ExecDate <= args.DateEnd
                                   && !dataContext.VistaMed.AccountItems.Any(x => x.EventId == ds.EventId)
                             select ds.Event;
            if (emptyMkbDs.Any())
            {
                context.ReportError(@"Неопределенный МКБ для диагнозов");
                foreach (var dsEvent in emptyMkbDs)
                {
                    context.ReportProgress(@"    > {0}", dsEvent);
                    if (context.CancellationPending) return;
                }
            }

            var unknownMkbActions = (from action in dataContext.VistaMed.ActionsNotInAccount
                                   where action.EndDate >= args.DateBegin && action.EndDate <= args.DateEnd
                                         && action.Event != null && action.Event.EventType != null
                                         && !string.IsNullOrEmpty(action.MKB)
                                         && !(from m in dataContext.VistaMed.MKBs where action.MKB == m.DiagID select m).Any()
                                   select action).ToList();

            if (context.CancellationPending) return;
            if (unknownMkbActions.Any())
            {
                context.ReportError(@"Несуществующий МКБ");
                foreach (var action in unknownMkbActions)
                {
                    context.ReportProgress(@"    > {0} {1}", action.Event, action.ToShortString());
                    if (context.CancellationPending) return;
                }
            }
        }
        private static void verifyContracts(IBackgroundContext context, PrevalidatorParams prevalidatorParams, VistaMedDataContext dataContext)
        {
            if (context.CancellationPending) return;

            var errors = (from a in dataContext.VistaMed.Actions
                          let e = a.Event
                          where e.ExecDate != null && e.Contract != null
                                && e.ExecDate >= prevalidatorParams.DateBegin
                                && e.ExecDate <= prevalidatorParams.DateEnd
                                && !dataContext.VistaMed.AccountItems.Any(x => x.EventId == e.Id)
                                && a.Contract != null && a.Contract != e.Contract
                          select new {Event = e, Action = a}).ToList();

            var invalidGroups = (from s in errors group s by s.Event into g select g).ToList();
            if (invalidGroups.Any())
            {
                context.ReportError(@"Несовпадение договора в талоне и услугах");
                foreach (var invalidGroup in invalidGroups)
                {
                    context.ReportProgress(@"    > {0}", invalidGroup.Key);
                    context.ReportProgress(@"    > договор события {0}",
                                           invalidGroup.Key.Contract.GetFullName(dataContext.VistaMed));

                    foreach (var action in invalidGroup.Select(x => x.Action))
                    {
                        var actionStr = (action.ActionType == null ? "" : action.ActionType.Code);
                        context.ReportProgress(@"    > договор мероприятия {0} {1}",
                                               actionStr, action.Contract.GetFullName(dataContext.VistaMed));
                    }
                }
            }
        }
        private static void verifyFinance(IBackgroundContext context, PrevalidatorParams args, VistaMedDataContext dataContext)
        {
            if (context.CancellationPending) return;

            var badVisits = from visit in dataContext.VistaMed.VisitsNotInAccount
                            where visit.Date >= args.DateBegin && visit.Date <= args.DateEnd
                            && visit.ServiceId != null
                            && visit.Event != null && visit.Event.Contract != null && visit.Event.Contract.RbFinance != null
                            && visit.Event.Contract.FinanceId != visit.FinanceId
                            && !dataContext.VistaMed.AccountItems.Any(x => x.EventId == visit.EventId)
                            select visit;
            if(badVisits.Select(x => x.Id).Any())
            {
                context.ReportError(@"Неверный тип финансирования для посещений");
                var badGroups = from v in badVisits.ToList() group v by v.Event into g select g;
                foreach (var badVisit in badGroups)
                {
                    context.ReportProgress(@"  > {0} тип фин.""{1}""",
                        badVisit.Key, badVisit.Key.Contract.RbFinance.Name);
                    foreach (var visit in badVisit)
                    {
                        context.ReportProgress(@"      -> {0} тип фин.""{1}""",
                            visit.ToShortString(), visit.RbFinance.Name);

                    }
                    if (context.CancellationPending) break;
                }
            }
            if (context.CancellationPending) return;
            var badActions = from action in dataContext.VistaMed.ActionsNotInAccount
                             where action.EndDate >= args.DateBegin && action.EndDate <= args.DateEnd
                            && action.ActionType != null && action.FinanceId != null
                            && action.Event != null && action.Event.Contract != null && action.Event.Contract.RbFinance != null
                            && action.Event.Contract.FinanceId != action.FinanceId
                            select action;
            if (badActions.Any())
            {
                context.ReportError(@"Неверный тип финансирования для мероприятий");
                var badGroups = from v in badActions.ToList() group v by v.Event into g select g;
                foreach (var badGroup in badGroups)
                {
                    context.ReportProgress(@"  > {0} тип фин.""{1}""",
                                           badGroup.Key, badGroup.Key.Contract.RbFinance.Name);

                    foreach (var action in badGroup)
                    {
                        context.ReportProgress(@"      -> {0} тип фин.""{1}""", action.ToShortString(), action.RbFinance.Name);

                    }
                    if (context.CancellationPending) break;
                }
            }
            if (context.CancellationPending) return;
            var budOms = (from ev in dataContext.VistaMed.EventsNotInAccount
                         where ev.ExecDate >= args.DateBegin && ev.ExecDate <= args.DateEnd
                         && ev.Client != null && ev.Client.ClientPolicies.Count == 0
                         && ev.Contract != null && ev.Contract.RbFinance != null
                         && ev.Contract.RbFinance.Code == "2"
                         select ev).ToList();
            if (budOms.Any())
            {
                context.ReportError(@"Финансирование ОМС для пациента без полисов ОМС");
                foreach (var badEvent in budOms)
                {
                    context.ReportProgress(@"  > {0}", badEvent);
                    if (context.CancellationPending) break;
                }
            }
        }
        public static void Prevalidate(IBackgroundContext context, 
            PrevalidatorParams args)
        {
            if (context.CancellationPending) return;
            context.ProgressSeparator();
            context.ReportProgress(@"ПРЕДВАРИТЕЛЬНАЯ ПРОВЕРКА ДАННЫХ Виста-Мед за период с {0} по {1}",
                args.DateBegin.ToShortDateString(), args.DateEnd.ToShortDateString());
            context.ReportProgress("");
            using (var dataContext = new VistaMedDataContext(context))
            {
                context.ReportProgress(@"Выборка талонов ...");
                var allEvents = (from e in dataContext.VistaMed.EventsNotInAccount
                                where e.EventType != null && e.ExecDate != null
                                      && e.ExecDate.Value >= args.DateBegin
                                      && e.ExecDate.Value <= args.DateEnd
                                      && e.Client != null
                                      && (args.CreatePersonId ==0 || e.CreatePersonId == args.CreatePersonId)
                                select e).ToList();

                context.ReportProgress(@"Выборка мероприятий ...");
                var allActions = (from action in dataContext.VistaMed.ActionsNotInAccount
                                  where action.EndDate >= args.DateBegin && action.EndDate <= args.DateEnd
                                        && action.Event != null && action.Event.EventType != null
                                        && !action.ActionType.Code.StartsWith("4-")
                                        && (args.CreatePersonId == 0 || action.CreatePersonId == args.CreatePersonId)
                                  select action).ToList();

                verifyPolicies(context, dataContext, allEvents);

                if(args.ValidateAll || args.ValidateContracts)
                    verifyContracts(context, args, dataContext);
                if (args.ValidateAll || args.ValidateFinance)
                    verifyFinance(context, args, dataContext);
                if (args.ValidateAll || args.ValidateMkb)
                    verifyMkb(context, args, dataContext);
                if (args.ValidateAll || args.ValidatePodrUsl)
                    verifyPodrUsl(context, allEvents, allActions);
                if (args.ValidateAll || args.ValidateIspoln)
                    verifyIspoln(context, allActions);
                if (args.ValidateAll || args.ValidateDdr)
                {
                    verifyDispanDdr1(context, args, allEvents);
                    // verifyDispanDdr2(context, args, allEvents);
                }
                if (args.ValidateAll || args.ValidateDdVet)
                {
                    verifyDispanVet(context, args, allEvents);
                }
                if (args.ValidateAll || args.ValidateDds)
                    verifyDispanDds(context, args, dataContext, allEvents);
                if (args.ValidateAll || args.ValidateDd14)
                    verifyDispanDd14(context, args, allEvents);
                if (args.ValidateAll || args.ValidateProfOsm)
                    verifyDispanProfOsm(context, args, allEvents);
                if (args.ValidateAll || args.ValidateViolet)
                    verifyViolet(context, allEvents);
                if (args.ValidateAll || args.Validate1013)
                    verify1013(context, allEvents);
                if (args.ValidateAll || args.ValidateStationar)
                    verifyStationar(context, allEvents, dataContext, allActions);

                verifyKolUsl(context, allActions, dataContext);

                //if (args.ValidateAll || args.ValidatePodrUsl)
                //    verifyNotInBill(context, allEvents, allActions, dataContext);

            }
            context.ProgressSeparator('-');
        }
        private static void verifyDispanProfOsm(IBackgroundContext context, PrevalidatorParams args,
            IList<Event> allEvents)
        {
            if (context.CancellationPending) return;

            context.ReportProgress(@"Проверка профосмотров");

            if (dpServices == null)
            {
                loadDdServices();
            }

            var eventCodes = new[] { "ПрофОсм" };
            const string actionComplexPrefix = @"Комплексная услуга на";
            var regExGender = new Regex(@"(мужчин|женщин)");
            var regExAgeList = new Regex(@"\(.*\)");

            var ddrEvents = from e in allEvents
                            where eventCodes.Contains(e.EventType.Code)
                            select e;

            var errors = new List<string>();
            foreach (var ddrEvent in ddrEvents)
            {
                errors.Clear();
                var actComplex = (from a in ddrEvent.Actions
                                  where a.ActionType != null
                                        && a.ActionType.Name.StartsWith(actionComplexPrefix)
                                  select a).ToList();
                Action complexService = null;

                if (!actComplex.Any())
                {
                    errors.Add(string.Format(@"Не найдена ""{0}""", actionComplexPrefix));
                }
                else
                {
                    if (actComplex.Count() > 1)
                        errors.Add(@"Определено более одной комплексной услуги");
                    else
                    {
                        complexService = actComplex.First();

                        var emptyIspoln = (from a in ddrEvent.Actions
                                           where a.ActionType != null && a.Person_PersonId == null
                                           select a).ToList();
                        errors.AddRange(emptyIspoln.Select(action =>
                            string.Format(@"Не задан исполнитель услуги {0}", action.ActionType.Code)));

                        var emptySpec = (from a in ddrEvent.Actions
                                         where
                                             a.ActionType != null && a.Person_PersonId != null &&
                                             a.Person_PersonId.RbSpeciality == null
                                         select a).ToList();
                        errors.AddRange(emptySpec.Select(action =>
                            string.Format(@"Не задана специальность исполнителя услуги {0}", action.ActionType.Code)));

                        var specCode = complexService.Person_PersonId.RbSpeciality.FederalCode;
                        var validSpecCodes = new List<string> { "27", "16" };

                        if (!validSpecCodes.Contains(specCode))
                            errors.Add(string.Format(@"Неверная специальность исполнителя комплексной услуги {0} {1}",
                                specCode, complexService.Person_PersonId.FIO));

                        var serviceName = complexService.ActionType.Name;
                        var isNewComplex = regExGender.IsMatch(serviceName);
                        if (isNewComplex)
                        {
                            int gender = 0;
                            if (isNewComplex)
                            {
                                gender = regExGender.Match(serviceName).Value == @"мужчин" ? 1 : 2;
                            }
                            if (gender > 0 && ddrEvent.Client.Sex != gender)
                                errors.Add(@"Неверный пол пациента");

                            if (regExAgeList.IsMatch(serviceName))
                            {
                                var strList = regExAgeList.Match(serviceName).Value;
                                var validChars = new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ','};
                                strList = new string(strList.ToArray().Where(validChars.Contains).Select(y => y).ToArray());
                                strList = strList.Replace(@"1820", "18,19,20");

                                var ageList = strList.Split(',').Select(x => int.Parse(x.Trim())).ToList();

                                var yearList = ageList.Select(x => complexService.EndDate != null ?
                                    complexService.EndDate.Value.Year - x : 0).ToList();

                                var clientYear = ddrEvent.Client.BirthDate.Year;
                                if (!yearList.Contains(clientYear))
                                {
                                    errors.Add(string.Format(@"Год рождения пациента {0} не соответствует допустимым"
                                        + @" годам для услуги {1} - {2}", clientYear,
                                        complexService.ActionType.Code,
                                        string.Join(",", yearList.Select(x => x.ToString()))
                                        ));
                                }
                            }

                            var longServices = from a in ddrEvent.Actions
                                               where a.EndDate != a.BegDate && a.ActionType.Code.IsInt()
                                               select a;
                            foreach (var longService in longServices)
                            {
                                errors.Add(string.Format(@"Услуга {0} имеет дату окончания не равную дате начала", longService.ActionType.Code));
                            }

                            var oldServices = (from a in ddrEvent.Actions
                                               where a.BegDate < args.DateBegin
                                               select a).ToList();

                            //foreach (var oldService in oldServices)
                            //{
                            //    if(oldService.BegDate != null && oldService.BegDate.Value < new DateTime(2013,1,1))
                            //    {
                            //        errors.Add(string.Format(@"Анализ просрочен {0}", oldService.ActionType.Code));
                            //    }
                            //}

                            var dupActions = (from a in ddrEvent.Actions
                                              group a by a.ActionType
                                                  into g
                                                  where g.Count() > 1
                                                  select g);

                            foreach (var dupAction in dupActions)
                            {
                                errors.Add(string.Format(@"Найдено более одного ({0}) мероприятия с кодом {1}",
                                    dupAction.Count(), dupAction.Key.Code));
                            }

                            /* объемы */
                            var theEvent = ddrEvent;
                            var ddrServiceIfo =
                                (from d in dpServices
                                 where d.EventCode == theEvent.EventType.Code && d.Gender == gender
                                 select d).FirstOrDefault();

                            if (ddrServiceIfo != null)
                            {
                                if (theEvent.ExecDate != null)
                                {
                                    var age = theEvent.ExecDate.Value.Year - ddrEvent.Client.BirthDate.Year;
                                    var ageSrv = ddrServiceIfo.AgeServicesList.FirstOrDefault(x => x.Age == age);
                                    if (ageSrv != null)
                                    {
                                        var minCount = ageSrv.ServiceCodes.Count * 0.85;
                                        minCount = Math.Round(minCount);

                                        //if ((int)minCount < minCount)
                                        //    minCount = ((int)minCount) + 1;

                                        var effectiveActions = (from a in ddrEvent.Actions
                                                                where ageSrv.ServiceCodes.Contains(a.ActionType.Code)
                                                                select a).ToList();
                                        if (effectiveActions.Count < minCount)
                                        {
                                            var candidates = string.Join(",",
                                                                         ageSrv.ServiceCodes.Except(
                                                                             effectiveActions.Select(x => x.ActionType.Code)));

                                            errors.Add(string.Format(@"Недостаточный объем услуг {0} из {1}. Можно добавить {2}",
                                                                     effectiveActions.Count, minCount, candidates));
                                        }

                                        var unavailActions = (from a in ddrEvent.Actions
                                                              where !ageSrv.ServiceCodes.Contains(a.ActionType.Code)
                                                                    && !(!a.ActionType.Code.IsInt() || a == complexService)
                                                              select a).ToList();
                                        if (unavailActions.Count > 0)
                                        {
                                            errors.Add(string.Format(@"Недопустимые(ое) мероприятия(e) {0} нужно удалить",
                                                                     string.Join(",", unavailActions.Select(x => x.ActionType.Code))));
                                        }
                                    }
                                }
                            }

                        }
                        else
                            errors.Add(string.Format(@"Комплексная услуга {0} более не используется", complexService.ActionType.Code));
                    }
                }
                if (errors.Count > 0)
                {
                    context.ReportError(@" ПрофОсм {0}", ddrEvent);
                    if (complexService != null)
                        context.ReportProgress(@"  > {0}", complexService);
                    errors.ForEach(x => context.ReportProgress(@"    -> {0}", x));
                }
            }
        }
        private static void verifyDispanDds(IBackgroundContext context, PrevalidatorParams args, 
            VistaMedDataContext dataContext, IEnumerable<Event> allEvents)
        {
            if (context.CancellationPending) return;

            if (args.DateEnd.Year != 2013) return;
            context.ReportProgress(@"Проверка диспансеризации детей сирот 2013");

            if (dsServices == null)
            {
                loadDdServices();
            }

            var eventCodes = new[] { "ДДСирот", "ДДСирТЖС" };
            var actionComplexPrefix = @"Комплексная услуга на";
            var regExAgeList = new Regex(@"\(.*\)");

            var ddsEvents = from e in allEvents
                            where eventCodes.Contains(e.EventType.Code)
                            select e;

            var errors = new List<string>();
            foreach (var ddsEvent in ddsEvents)
            {
                errors.Clear();
                var actComplex = (from a in ddsEvent.Actions
                                  where a.ActionType != null
                                  && a.ActionType.Name.StartsWith(actionComplexPrefix)
                                  select a).ToList();

                Action complexService = null;
                if (ddsEvent.ExecDate != null)
                {
                    var clientAge = dataContext.VistaMed.Age(ddsEvent.Client.BirthDate, ddsEvent.ExecDate.Value);

                    if (!actComplex.Any())
                    {
                        errors.Add(string.Format(@"Не найдена ""{0}""", actionComplexPrefix));
                    }
                    else
                    {
                        if (actComplex.Count() > 1)
                            errors.Add(@"Определено более одной комплексной услуги");
                        else
                        {
                            complexService = actComplex.First();

                            var emptyIspoln = (from a in ddsEvent.Actions
                                               where a.ActionType != null && a.Person_PersonId == null
                                               select a).ToList();
                            errors.AddRange(emptyIspoln.Select(action =>
                                                               string.Format(@"Не задан исполнитель услуги {0}", action.ActionType.Code)));

                            var emptySpec = (from a in ddsEvent.Actions
                                             where
                                                 a.ActionType != null && a.Person_PersonId != null &&
                                                 a.Person_PersonId.RbSpeciality == null
                                             select a).ToList();
                            errors.AddRange(emptySpec.Select(action =>
                                                             string.Format(@"Не задана специальность исполнителя услуги {0}", action.ActionType.Code)));

                            if (complexService.Person_PersonId != null &&
                                complexService.Person_PersonId.RbSpeciality != null)
                            {
                                var specCode = complexService.Person_PersonId.RbSpeciality.FederalCode;
                                var validSpecCodes = new List<string> {"1134"};

                                if (!validSpecCodes.Contains(specCode))
                                    errors.Add(
                                        string.Format(@"Неверная специальность исполнителя комплексной услуги {0} {1}",
                                                      specCode, complexService.Person_PersonId.FIO));
                            }

                            var serviceName = complexService.ActionType.Name;

                            if (regExAgeList.IsMatch(serviceName))
                            {
                                var strList = regExAgeList.Match(serviceName).Value;
                                strList = strList.Substring(1, strList.Length - 2);
                                var arrAge = strList.Split('-').Select(x => int.Parse(x.Trim())).ToArray();
                                var ageBeg = arrAge[0];
                                var ageEnd = arrAge[1];

                                if (clientAge < ageBeg || clientAge > ageEnd)
                                {
                                    errors.Add(string.Format(@"Возраст пациента {0} не соответствует диапазону возрастов "
                                                             + @" для услуги {1}: {2}-{3}", clientAge,
                                                             complexService.ActionType.Code,
                                                             ageBeg, ageEnd));
                                }
                            }

                            var longServices = from a in ddsEvent.Actions
                                               where a.EndDate != a.BegDate
                                               select a;
                            foreach (var longService in longServices)
                            {
                                errors.Add(string.Format(@"Услуга {0} имеет дату окончания не равную дате начала",
                                                         longService.ActionType.Code));
                            }

                            var oldServices =
                                (from a in ddsEvent.Actions where a.BegDate < args.DateBegin select a).ToList();

                            foreach (var oldService in oldServices)
                            {
                                var aAge = clientAge < 2 ? 1 : 3;
                                var maxServiceAgeMonth = oldService.ActionType.Code.EndsWith("61021") ? 12 : aAge;
                                if (oldService.BegDate != null && oldService.BegDate.Value < ddsEvent.SetDate.AddMonths(-maxServiceAgeMonth))
                                {
                                    errors.Add(string.Format(@"Анализ просрочен {0}", oldService.ActionType.Code));
                                }
                            }

                            var dupActions = (from a in ddsEvent.Actions
                                              group a by a.ActionType
                                              into g
                                              where g.Count() > 1
                                              select g);

                            foreach (var dupAction in dupActions)
                            {
                                errors.Add(string.Format(@"Найдено более одного ({0}) мероприятия с кодом {1}",
                                                         dupAction.Count(), dupAction.Key.Code));
                            }

                            /* объемы */
                            Event theEvent = ddsEvent;
                            var ddrServiceIfo =
                                (from d in dsServices
                                 where d.EventCode == theEvent.EventType.Code && d.Gender == theEvent.Client.Sex
                                 select d).FirstOrDefault();

                            if (ddrServiceIfo != null)
                            {
                                var ageServices = (from c in ddrServiceIfo.AgeServicesList
                                                   from s in c.ServiceCodes
                                                   where clientAge >= c.AgeBegin && clientAge <= c.AgeEnd
                                                   select s).Distinct().ToList();

                                if (ageServices.Count > 0)
                                {
                                    var minCount = ageServices.Count;

                                    var effectiveActions = (from a in ddsEvent.Actions
                                                            where ageServices.Contains(a.ActionType.Code)
                                                            select a).ToList();
                                    if (effectiveActions.Count < minCount)
                                    {
                                        var candidates = string.Join(",",
                                                                     ageServices.Except(
                                                                         effectiveActions.Select(x => x.ActionType.Code)));

                                        errors.Add(string.Format(
                                            @"Недостаточный объем услуг {0} из {1}. Можно добавить {2}",
                                            effectiveActions.Count, minCount, candidates));
                                    }

                                    var unavailActions = (from a in ddsEvent.Actions
                                                          where
                                                              !ageServices.Contains(a.ActionType.Code) &&
                                                              a != complexService
                                                          select a).ToList();
                                    if (unavailActions.Count > 0)
                                    {
                                        errors.Add(string.Format(@"Недопустимые(ое) мероприятия(e) {0} нужно удалить",
                                                                 string.Join(",",
                                                                             unavailActions.Select(x => x.ActionType.Code))));
                                    }
                                }
                            }
                        }
                    }
                    if (errors.Count > 0)
                    {
                        context.ReportError(@" {0} {1}", ddsEvent.EventType.Code, ddsEvent);
                        if (complexService != null)
                            context.ReportProgress(@"  > {0}", complexService);
                        context.ReportProgress(@"  > возраст на дату закрытия талона {0} полных лет", clientAge);
                        errors.ForEach(x => context.ReportProgress(@"    -> {0}", x));
                    }
                }
            }
        }
        private static void verifyDispanDdr2(IBackgroundContext context, PrevalidatorParams args,
                    IList<Event> allEvents)
        {
            if (context.CancellationPending) return;

            context.ReportProgress(@"Проверка диспансеризации работающих 2013 (2 этап)");

            if (ddServices == null)
            {
                loadDdServices();
            }

            var eventCodes = new[] { "dd2013_2" };
            const string actionComplexPrefix = @"Осмотр врача-терапевта/врача общей практики";

            var ddrEvents = from e in allEvents
                            where eventCodes.Contains(e.EventType.Code)
                            select e;

            var errors = new List<string>();
            foreach (var ddrEvent in ddrEvents)
            {
                errors.Clear();
                var actComplex = (from a in ddrEvent.Actions
                                  where a.ActionType != null
                                        && a.ActionType.Name.StartsWith(actionComplexPrefix)
                                  select a).ToList();
                Action complexService = null;

                if (!actComplex.Any())
                {
                    errors.Add(string.Format(@"Не найдена ""{0}""", actionComplexPrefix));
                }
                else
                {
                    if (actComplex.Count() > 1)
                        errors.Add(@"Определено более одной комплексной услуги");
                    else
                    {
                        complexService = actComplex.First();

                        var emptyIspoln = (from a in ddrEvent.Actions
                                           where a.ActionType != null && a.Person_PersonId == null
                                           select a).ToList();
                        errors.AddRange(emptyIspoln.Select(action =>
                                                           string.Format(@"Не задан исполнитель услуги {0}",
                                                                         action.ActionType.Code)));

                        var emptySpec = (from a in ddrEvent.Actions
                                         where
                                             a.ActionType != null && a.Person_PersonId != null &&
                                             a.Person_PersonId.RbSpeciality == null
                                         select a).ToList();
                        errors.AddRange(emptySpec.Select(action =>
                                                         string.Format(
                                                             @"Не задана специальность исполнителя услуги {0}",
                                                             action.ActionType.Code)));

                        var specCode = "???";
                        if (complexService.Person_PersonId != null)
                            specCode = complexService.Person_PersonId.RbSpeciality.FederalCode;
                        var validSpecCodes = new List<string> {"27", "16"};

                        if (!validSpecCodes.Contains(specCode))
                            errors.Add(string.Format(@"Неверная специальность исполнителя комплексной услуги {0} {1}",
                                                     specCode,
                                                     (complexService.Person_PersonId == null
                                                          ? "???"
                                                          : complexService.Person_PersonId.FIO)));

                        int gender = ddrEvent.Client.Sex;

                        var longServices = from a in ddrEvent.Actions
                                           where a.EndDate != a.BegDate && a.ActionType.Code.IsInt()
                                           select a;

                        foreach (var longService in longServices)
                        {
                            errors.Add(string.Format(@"Услуга {0} имеет дату окончания не равную дате начала",
                                                     longService.ActionType.Code));
                        }

                        var dupActions = (from a in ddrEvent.Actions
                                          group a by a.ActionType
                                          into g
                                          where g.Count() > 1
                                          select g);

                        foreach (var dupAction in dupActions)
                        {
                            errors.Add(string.Format(@"Найдено более одного ({0}) мероприятия с кодом {1}",
                                                     dupAction.Count(), dupAction.Key.Code));
                        }

                        /* объемы */
                        var theEvent = ddrEvent;
                        var ddrServiceIfo =
                            (from d in ddServices
                             where d.EventCode == theEvent.EventType.Code && d.Gender == gender
                             select d).FirstOrDefault();

                        if (ddrServiceIfo != null)
                        {
                            if (theEvent.ExecDate != null)
                            {
                                var age = theEvent.ExecDate.Value.Year - ddrEvent.Client.BirthDate.Year;
                                var ageSrv = ddrServiceIfo.AgeServicesList.FirstOrDefault(x => x.Age == age);
                                if (ageSrv != null)
                                {
                                    var minCount = ageSrv.ServiceCodes.Count*0.85;

                                    if ((int) minCount < minCount)
                                        minCount = ((int) minCount) + 1;

                                    if (ageSrv.MinServicesCount > 0)
                                        minCount = ageSrv.MinServicesCount;

                                    var effectiveActions = (from a in ddrEvent.Actions
                                                            where ageSrv.ServiceCodes.Contains(a.ActionType.Code)
                                                            select a).ToList();
                                    if (effectiveActions.Count < minCount)
                                    {
                                        var candidates = string.Join(",",
                                                                     ageSrv.ServiceCodes.Except(
                                                                         effectiveActions.Select(x => x.ActionType.Code)));

                                        errors.Add(
                                            string.Format(@"Недостаточный объем услуг {0} из {1}. Можно добавить {2}",
                                                          effectiveActions.Count, minCount, candidates));
                                    }

                                    var unavailActions = (from a in ddrEvent.Actions
                                                          where !ageSrv.ServiceCodes.Contains(a.ActionType.Code)
                                                                && !(!a.ActionType.Code.IsInt() || a == complexService)
                                                          select a).ToList();
                                    if (unavailActions.Count > 0)
                                    {
                                        errors.Add(string.Format(@"Недопустимые(ое) мероприятия(e) {0} нужно удалить",
                                                                 string.Join(",",
                                                                             unavailActions.Select(
                                                                                 x => x.ActionType.Code))));
                                    }
                                }
                            }
                        }
                    }
                }
                if (errors.Count > 0)
                {
                    context.ReportError(@" dd2013 {0}", ddrEvent);
                    if (complexService != null)
                        context.ReportProgress(@"  > {0}", complexService);
                    errors.ForEach(x => context.ReportProgress(@"    -> {0}", x));
                }
            }
        }