protected override void selectSmoFomsReportData(Worksheet ws, AccountCorrectParams args, 
                List<FomsReportRowData> formReportData, string smoCode, FomsReportRowType rowType)
            {
                var data = formReportData.FirstOrDefault(x => x.SmoCode == smoCode && x.RowType == rowType);
                if (data == null)
                {
                    data = new FomsReportRowData() { SmoCode = smoCode, RowType = rowType };
                    formReportData.Add(data);
                }
                var row = (Range)ws.Rows[totalOmsRowIndex];

                var colIndex = 0;

                if (rowType == FomsReportRowType.Plan)
                    colIndex = 9;
                if (rowType == FomsReportRowType.Fact)
                    colIndex = 15;

                data.ProfilacticCount = row.GetCellValue<int>(1, colIndex) + row.GetCellValue<int>(1, colIndex + 1);
                colIndex += 2;

                data.VisitCount = row.GetCellValue<int>(1, colIndex) + row.GetCellValue<int>(1, colIndex + 1);
                colIndex += 2;

                data.TreatmentCount = row.GetCellValue<int>(1, colIndex) + row.GetCellValue<int>(1, colIndex + 1);
                colIndex += 2;

                row = (Range)ws.Rows[emergencyRowIndex];
                if (rowType == FomsReportRowType.Plan)
                    colIndex = 11;
                if (rowType == FomsReportRowType.Fact)
                    colIndex = 17;

                data.EmergencyCount = row.GetCellValue<int>(1, colIndex) + row.GetCellValue<int>(1, colIndex + 1);
            }
        /// <summary>
        /// Заполняем сведения о выполнении заказ-задания
        /// </summary>
        /// <param name="context"></param>
        /// <param name="args"></param>
        public static void FillFomsReport(IBackgroundContext context, AccountCorrectParams args)
        {
            context.ProgressSeparator();
            context.ReportProgress(@"ФОРМИРОВАНИЕ СВЕДЕНИЙ О ВЫПОЛНЕНИИ ЗАКАЗ-ЗАДАНИЯ");
            context.ProgressSeparator();

            // книга планов
            var excelFileName = args.AccountCheckPlanFileName;
            if (File.Exists(excelFileName))
            {
                var excel = new Application();
                excel.Visible = true;

                var reportData = getFomsReportData(context, args, excelFileName, excel);

                if (reportData.Count > 0)
                {

                    excel.ScreenUpdating = false;
                    using (var dataContext = new VistaMedDataContext(context))
                    {
                        dataContext.VistaMed.Connection.Open();

                        var templateName = Path.Combine(Path.GetFullPath(@".\"), @"AccountFomsReport.xls");

                        Workbook newDoc = excel.Workbooks.Add(templateName);
                        newDoc.Names.Item(@"Период").RefersToRange.Value2 =
                            Utils.GetPeriodName(args.DateBegin, args.DateEnd);

                        var lpu = dataContext.VistaMed.Organisations.FirstOrDefault(x => x.MiacCode == args.LpuCode);
                        if (lpu != null)
                        {
                            newDoc.Names.Item("ЛПУ").RefersToRange.Value2 = lpu.FullName;
                            newDoc.Names.Item("ФИОРуководителя").RefersToRange.Value2 = lpu.Chief;
                            //newDoc.Names.Item("ФИОГлавбуха").RefersToRange.Value2 = lpu.Accountant;
                        }

                        try
                        {
                            var ws = (Worksheet)newDoc.Worksheets[1];
                            for (int rowIndex = 8; rowIndex < 19; rowIndex++)
                            {
                                var row = (Range)ws.Rows[rowIndex];
                                var rowName = row.GetCellValue<string>(1, 1);
                                var smoCode = row.GetCellValue<string>(1, 8);
                                if (!string.IsNullOrEmpty(smoCode))
                                {
                                    var rowDataType = (rowName.StartsWith("Плановое")
                                        ? FomsReportRowType.Plan
                                        : FomsReportRowType.Fact);

                                    var rowData = reportData.FirstOrDefault(x => x.SmoCode == smoCode
                                                                                 && x.RowType == rowDataType);

                                    if (rowData != null)
                                    {
                                        var colIndex = 2;
                                        row.SetCellValue(1, colIndex++, rowData.StacFullCount + rowData.StacApuCount);
                                        row.SetCellValue(1, colIndex++, rowData.StacDayCount);
                                        row.SetCellValue(1, colIndex++, rowData.EmergencyCount);
                                        row.SetCellValue(1, colIndex++, rowData.TreatmentCount);
                                        row.SetCellValue(1, colIndex++, rowData.VisitCount + rowData.ProfilacticCount);
                                        row.SetCellValue(1, colIndex++, rowData.AmbulanceCount);
                                    }

                                }
                            }
                        }
                        finally
                        {
                            dataContext.VistaMed.Connection.Close();
                        }
                    }
                    excel.ScreenUpdating = true;
                }
            }
            else
                context.ReportError(@"Файл {0} не найден", excelFileName);

            context.ProgressSeparator('-');
        }
            public void SelectFomsReportData(Worksheet ws, AccountCorrectParams args,
                List<FomsReportRowData> formReportData)
            {
                string smoCode;
                if (isTagretPage(ws.Name, out smoCode))
                {
                    if (int.Parse(smoCode) > 0)
                    {
                        context.ProgressSeparator();
                        context.ReportProgress("Выбираем данные: лист {0} ...", ws.Name);

                        selectSmoFomsReportData(ws, args, formReportData, smoCode, FomsReportRowType.Plan);
                        selectSmoFomsReportData(ws, args, formReportData, smoCode, FomsReportRowType.Fact);
                    }
                }
            }
 protected abstract void selectSmoFomsReportData(Worksheet ws, AccountCorrectParams args,
     List<FomsReportRowData> formReportData, string smoCode, FomsReportRowType rowType);
        private static List<FomsReportRowData> getFomsReportData(IBackgroundContext context, AccountCorrectParams args, string excelFileName,
            Application excel)
        {
            var result = new List<FomsReportRowData>();

            Workbook newDoc;
            try
            {
                context.ReportProgress(@"Открываем файл {0} ...", excelFileName);
                newDoc = excel.Workbooks.Open(excelFileName);
            }
            catch (Exception exception)
            {
                newDoc = null;
                context.ReportError(@"Ошибка открытия файла {0} : {1}", excelFileName, exception.Message);
            }

            if (newDoc != null)
            {
                // ищем имя КоличествоМесяцев
                var kolMonthExists = false;
                for (int i = 1; i <= newDoc.Names.Count; i++)
                {
                    if (newDoc.Names.Item(i).Name == @"КоличествоМесяцев")
                    {
                        kolMonthExists = true;
                        break;
                    }
                }

                if (kolMonthExists)
                {
                    // сравниваем КоличествоМесяцев из книги с отчетным периодом
                    int kolMonth = (int)newDoc.Names.Item(@"КоличествоМесяцев").RefersToRange.Value2;

                    if (args.DateEnd.Month == kolMonth)
                    {
                        excel.ScreenUpdating = false;
                        using (var dataContext = new VistaMedDataContext(context))
                        {
                            dataContext.VistaMed.Connection.Open();

                            try
                            {
                                // массив с объектами - типами листов книги
                                List<PageType> pageTypes = (new PageType[]
                                {
                                    new Polyclinic(context, dataContext),
                                    new Stacionar(context, dataContext),
                                    new Ambulance(context, dataContext)
                                }).ToList();

                                // сканируем все листы
                                foreach (Worksheet ws in newDoc.Worksheets.Cast<Worksheet>())
                                {
                                    // попытка обработать текущий лист для всех типов листов
                                    // фактическая работа делается в этих объектах
                                    pageTypes.ForEach(x => x.SelectFomsReportData(ws, args, result));
                                }
                            }
                            finally
                            {
                                dataContext.VistaMed.Connection.Close();
                            }
                        }
                        excel.ScreenUpdating = true;
                    }
                    else
                        context.ReportError(@"КоличествоМесяцев в файле не соответствует выбранному Вами периоду");
                }
                else
                    context.ReportError(@"Имя ячейки ""КоличествоМесяцев"" не найдено в файле {0}", excelFileName);

                newDoc.Close(false);
            }

            return result;
        }
 protected override void selectSmoFomsReportData(Worksheet ws, AccountCorrectParams args,
     List<FomsReportRowData> formReportData, string smoCode, FomsReportRowType rowType)
 {
     var data = formReportData.FirstOrDefault(x => x.SmoCode == smoCode && x.RowType == rowType);
     if (data == null)
     {
         data = new FomsReportRowData() { SmoCode = smoCode, RowType = rowType };
         formReportData.Add(data);
     }
     var doctorRow = (Range)ws.Rows[doctorEventsRowIndex];
     var feldRow = (Range)ws.Rows[feldEventsRowIndex];
     var colIndex = 0;
     if (rowType == FomsReportRowType.Plan)
         colIndex = 4;
     if (rowType == FomsReportRowType.Fact)
         colIndex = 5;
     data.AmbulanceCount = doctorRow.GetCellValue<int>(1, colIndex) + feldRow.GetCellValue<int>(1, colIndex);
 }
            /// <summary>
            /// Убрать из резервного счета (для последкющего формирования допсчета)
            /// </summary>
            /// <param name="args"></param>
            /// <param name="smoId"></param>
            /// <param name="nedo"></param>
            private void removeFromReserveAccount(AccountCorrectParams args, int smoId, List<FactPlanDelta> nedo)
            {
                if (nedo.Count > 0)
                {
                    context.ProgressSeparator();
                    context.ReportProgress(@"Обработка недовыполнения плана ({0} элементов) ...", nedo.Count);

                    var reserveAccountId = getReserveAccountId(args, smoId);
                    if (reserveAccountId > 0)
                    {
                        foreach (var delta in nedo)
                        {
                            context.ReportProgress(nedo.ToString());

                            var serviceCodes = delta.ServiceCodes.Split(',');
                            var serviceInList = string.Join(",", serviceCodes.Select(x => "'" + x + "'"));
                            var recLimit = delta.DeltaCount == 0 ? 10000000 : delta.DeltaCount;

                            // выбираем события для возмещения недовыполнения из резервного счета по кодам услуг
                            var sqlSelectEventsText = string.Format(@"SELECT DISTINCT ai.event_id AS Id
                            FROM Account a
                            JOIN Account_Item ai ON a.id = ai.master_id AND ai.deleted = 0 AND a.deleted = 0
                            JOIN rbService s ON ai.service_id = s.id
                            JOIN Event e ON e.id = ai.event_id AND e.deleted = 0
                            WHERE a.id = {0} AND s.code IN ({1}) AND ai.sum > 0
                            ORDER BY e.execDate
                            LIMIT {2}",
                                reserveAccountId,
                                serviceInList,
                                recLimit);

                            var eventIdList = dataContext.VistaMed.SelectSqlData<IdData>(sqlSelectEventsText)
                                .Select(x => x.Id).ToList();

                            context.ReportProgress(@"В резервном счете найдено {0} событий заданных типов",
                                eventIdList.Count());

                            string eventsStr = "";

                            if (delta.DeltaCount > 0)
                            {
                                // недовыполнение по количеству талонов / законченных случаев
                                eventsStr = string.Join(",", eventIdList.Select(x => x.ToString()));
                            }
                            else if (delta.DeltaKol > 0)
                            {
                                // недовыполнение по количеству койко-дней
                                var restKol = delta.DeltaKol;

                                foreach (var eventId in eventIdList)
                                {
                                    var kolSqlText = string.Format(@"SELECT SUM(ai.amount)
                                        FROM Account_Item ai
                                        JOIN rbService s ON s.id = ai.service_id AND ai.deleted = 0
                                        WHERE ai.master_id = {0} AND ai.event_id = {1}
                                        AND s.code IN ({2})", reserveAccountId, eventId, serviceInList);

                                    var eventKol = dataContext.VistaMed.CalculateSql<int>(kolSqlText);

                                    restKol -= eventKol;

                                    if (restKol >= 0)
                                    {
                                        eventsStr += "," + eventId;
                                    }
                                    else
                                        break;
                                }

                                if (!string.IsNullOrEmpty(eventsStr))
                                    eventsStr = "0" + eventsStr;
                            }

                            if (!string.IsNullOrEmpty(eventsStr))
                            {
                                // удаляем из резервного счета выбранные события
                                var sqlDeleteText = string.Format(@"DELETE FROM AccountItem ai
                                    WHERE ai.master_id = {0} AND ai.event_id IN ({1})", reserveAccountId, eventsStr);

                                context.ReportProgress(
                                    @"Из резервного счета удалено {0} событий для компенсации недовыполнения плана",
                                    dataContext.VistaMed.ExecuteSql(sqlDeleteText));

                                var sqlUpdateText =
                                    string.Format(@"UPDATE Action SET payStatus = 0 WHERE event_id IN ({0})", eventsStr);
                                dataContext.VistaMed.ExecuteSql(sqlUpdateText);

                                updateAccountTotals(reserveAccountId);

                                context.ReportProgress(@"Сформируйте дополнительный счет.");
                            }
                            else
                                context.ReportProgress(@"В резервном счете не найдены услуги с нужными кодами.");
                        }
                    }
                    else
                        context.ReportProgress(@"Счет с данными перевыполнения (резервный счет) не найден");
                }
            }
 /// <summary>
 /// Выбрать список отклонений по листу (переопределяется в наследниках)
 /// </summary>
 /// <param name="ws"></param>
 /// <param name="args"></param>
 /// <param name="smoId"></param>
 /// <returns></returns>
 protected abstract List<FactPlanDelta> selectDeltaList(Worksheet ws, AccountCorrectParams args, int smoId);
            /// <summary>
            /// Найти код резервного счета по СМО
            /// </summary>
            /// <param name="args"></param>
            /// <param name="smoId"></param>
            /// <returns></returns>
            private int getReserveAccountId(AccountCorrectParams args, int smoId)
            {
                var sqlText = string.Format(@"SELECT a.id AS Id
                                FROM Account a
                                JOIN Contract c ON a.contract_id = c.id AND a.deleted = 0 AND c.deleted = 0
                                JOIN rbFinance f ON f.id = c.finance_id AND f.name = 'омс'
                                WHERE c.payer_id = {2} AND a.number = '{1}'
                                AND a.settleDate = '{0}'",
                                    args.DateEnd.BeginOfYear().SqlStr(),
                                    ReserveAccountName,
                                    smoId);

                var data = dataContext.VistaMed.SelectSqlData<IdData>(sqlText);
                int result = 0;
                if (data.Count > 0)
                {
                    result = data.Max(x => x.Id);
                }
                return result;
            }
            /// <summary>
            /// Перемещение событий перевыполнения в резервный счет
            /// </summary>
            /// <param name="args"></param>
            /// <param name="smoId"></param>
            /// <param name="pere"></param>
            private void moveToReserveAccount(AccountCorrectParams args, int smoId, List<FactPlanDelta> pere)
            {
                // перевыполнение плана - отрицательный знак разницы
                // инвертируем разницу для простоты дальнейшей обработки данных
                pere.ForEach(x =>
                {
                    x.DeltaCount = -x.DeltaCount;
                    x.DeltaKol = -x.DeltaKol;
                });

                if (pere.Count > 0)
                {
                    context.ProgressSeparator();
                    context.ReportProgress(@"Обработка перевыполнения плана ({0} элементов) ...", pere.Count);

                    // ищем или создаем резервный счет за год по смо
                    var reserveAccountId = getReserveAccountId(args, smoId);

                    if (reserveAccountId == 0)
                        reserveAccountId = createReserveAccount(args, smoId);

                    if (reserveAccountId > 0)
                    {
                        // ищем основной счет на отчетную дату
                        var mainAccountId = getMainAccountId(args, smoId);

                        if (mainAccountId > 0)
                        {
                            // цикл по всем фактам перевыполнения
                            foreach (var delta in pere)
                            {
                                context.ReportProgress(pere.ToString());

                                // массив кодов перевыполненных услуг
                                var serviceCodes = delta.ServiceCodes.Split(',');
                                var serviceInList = string.Join(",", serviceCodes.Select(x => "'" + x + "'"));

                                // макс кол-во переносимых из основного счета событий
                                // (для перевыполнения по событиям (зак.случ.) - это кол-во перевыполнения
                                // для перевыполнения койко-дней (кол-во в событиях) - выберем всех кандидатов
                                var recLimit = delta.DeltaCount == 0 ? 10000000 : delta.DeltaCount;

                                // выбираем события для устранения перевыполнения из основного счета по кодам услуг
                                // сортируя по увеличению суммы и по дате (чтобы изымать из основного счета
                                // в первую очередь события с меньшими суммами)
                                var sqlSelectEventsText = string.Format(@"SELECT DISTINCT ai.event_id AS Id
                                    FROM Account a
                                    JOIN Account_Item ai ON a.id = ai.master_id AND ai.deleted = 0 AND a.deleted = 0
                                    JOIN rbService s ON ai.service_id = s.id
                                    JOIN Event e ON e.id = ai.event_id AND e.deleted = 0
                                    WHERE a.id = {0} AND s.code IN ({1}) AND ai.sum > 0
                                    ORDER BY ai.sum, e.execDate
                                    LIMIT {2}", mainAccountId, serviceInList, recLimit);

                                // список идентификаторов событий - кандидатов
                                // для переноса из основного счета в резервный
                                var eventIdList = dataContext.VistaMed.SelectSqlData<IdData>(sqlSelectEventsText)
                                    .Select(x => x.Id).ToList();

                                context.ReportProgress(@"В основном счете найдено {0} событий заданных типов",
                                    eventIdList.Count());

                                string eventsStr = "";

                                if (delta.DeltaCount > 0)
                                {
                                    // перевыполнение по количеству талонов / законченных случаев
                                    // переносим все события (количество ограничивается LIMIT)
                                    eventsStr = string.Join(",", eventIdList.Select(x => x.ToString()));
                                }
                                else if (delta.DeltaKol > 0)
                                {
                                    // перевыполнение по количеству койко-дней
                                    var restKol = delta.DeltaKol;

                                    // сканируем список событий - кандидатов
                                    foreach (var eventId in eventIdList)
                                    {
                                        // считаем количество койко-дней по текущему событию
                                        var kolSqlText = string.Format(@"SELECT SUM(ai.amount)
                                        FROM Account_Item ai
                                        JOIN rbService s ON s.id = ai.service_id AND ai.deleted = 0
                                        WHERE ai.master_id = {0} AND ai.event_id = {1} AND ai.sum > 0
                                        AND s.code IN ({2})", mainAccountId, eventId, serviceInList);

                                        var eventKol = dataContext.VistaMed.CalculateSql<int>(kolSqlText);

                                        // вычитаем из остатка перевыполнения
                                        restKol -= eventKol;

                                        if (restKol >= 0)
                                        {
                                            // если перевыполнение не устранено
                                            eventsStr += "," + eventId;

                                            if (restKol == 0)
                                                break;
                                        }
                                        else
                                            // перебор
                                            break;
                                    }

                                    if (!string.IsNullOrEmpty(eventsStr))
                                        eventsStr = "0" + eventsStr;
                                }

                                if (!string.IsNullOrEmpty(eventsStr))
                                {
                                    // переносим из основного счета выбранные события в резервный
                                    var sqlUpdateText = string.Format(@"UPDATE Account_Item
                                        SET master_id = {0}
                                        WHERE master_id = {1} AND event_id IN ({2})",
                                            reserveAccountId, mainAccountId, eventsStr);

                                    context.ReportProgress(
                                        @"Из основного счета перенесено {0} событий в резервный для компенсации перевыполнения плана",
                                        dataContext.VistaMed.ExecuteSql(sqlUpdateText));

                                    updateAccountTotals(reserveAccountId);
                                    updateAccountTotals(mainAccountId);
                                }
                                else
                                    context.ReportProgress(@"В основном счете не найдены услуги с нужными кодами.");
                            }
                        }
                        else
                            context.ReportError(@"Основной счет за {0} не найден",
                                args.DateEnd.ToShortDateString());
                    }
                    else
                        context.ReportError(@"Счет с данными перевыполнения (резервный счет) не найден");
                }
            }
            /// <summary>
            /// Определить главный счет за период по СМО
            /// </summary>
            /// <param name="args"></param>
            /// <param name="smoId"></param>
            /// <returns></returns>
            private int getMainAccountId(AccountCorrectParams args, int smoId)
            {
                int result = 0;

                // выбираем счета по данной смо с датой периода счета = дата окончания отчетного месяца
                // (исключая счета диспансеризации и профосмотры)
                // сортируем по сумме с убыванием
                var sqlText = string.Format(@"SELECT a.id AS Id
                    FROM Account a
                    JOIN Contract c ON a.contract_id = c.id AND a.deleted = 0 AND c.deleted = 0
                    JOIN rbFinance f ON f.id = c.finance_id AND f.name = 'омс'
                    JOIN Account_Item ai ON ai.master_id = a.id AND ai.deleted = 0
                    JOIN rbService s ON s.id = ai.service_id AND s.code NOT LIKE '_6____'
                    WHERE c.payer_id = {0} AND a.settleDate = '{1}'
                    GROUP BY a.id ORDER BY a.sum DESC",
                                  smoId, args.DateEnd.SqlStr());

                // счет с большей суммой - главный (основной), который нужно чистить от перевыполнения
                var idObj = dataContext.VistaMed.SelectSqlData<IdData>(sqlText).FirstOrDefault();

                if (idObj != null)
                    result = idObj.Id;

                return result;
            }
        /// <summary>
        /// Провести автоматическую коррекцию реестров (устранить пере(недо)выполнение) по результатам проверки
        /// </summary>
        /// <param name="context"></param>
        /// <param name="args"></param>
        public static void AutoCorrectAccounts(IBackgroundContext context, AccountCorrectParams args)
        {
            context.ProgressSeparator();
            context.ReportProgress(@"АВТОМАТИЧЕСКАЯ КОРРЕКТИРОВКА РЕЕСТРОВ В СООТВЕТСТВИИ С ПЛАНАМИ");
            context.ProgressSeparator();

            // книга планов
            var excelFileName = args.AccountCheckPlanFileName;
            if (File.Exists(excelFileName))
            {
                var excel = new Application();
                excel.Visible = true;

                Workbook newDoc;
                try
                {
                    context.ReportProgress(@"Открываем файл {0} ...", excelFileName);
                    newDoc = excel.Workbooks.Open(excelFileName);
                }
                catch (Exception exception)
                {
                    newDoc = null;
                    context.ReportError(@"Ошибка открытия файла {0} : {1}", excelFileName, exception.Message);
                }

                if (newDoc != null)
                {
                    // ищем имя КоличествоМесяцев
                    var kolMonthExists = false;
                    for (int i = 1; i <= newDoc.Names.Count; i++)
                    {
                        if (newDoc.Names.Item(i).Name == @"КоличествоМесяцев")
                        {
                            kolMonthExists = true;
                            break;
                        }
                    }

                    if (kolMonthExists)
                    {
                        // сравниваем КоличествоМесяцев из книги с отчетным периодом
                        int kolMonth = (int) newDoc.Names.Item(@"КоличествоМесяцев").RefersToRange.Value2;

                        if (args.DateEnd.Month == kolMonth)
                        {
                            excel.ScreenUpdating = false;
                            using (var dataContext = new VistaMedDataContext(context))
                            {
                                dataContext.VistaMed.Connection.Open();

                                // новая транзакция для обеспечения атомарности всех изменений БД для текущего метода
                                dataContext.VistaMed.ActiveTransaction = dataContext.VistaMed.Connection
                                    .BeginTransaction(IsolationLevel.ReadCommitted);

                                try
                                {
                                    // массив с объектами - типами листов книги
                                    List<PageType> pageTypes = (new PageType[]
                                    {
                                        new Polyclinic(context, dataContext),
                                        new Stacionar(context, dataContext),
                                        new Ambulance(context, dataContext)
                                    }).ToList();

                                    // сканируем все листы
                                    foreach (Worksheet ws in newDoc.Worksheets.Cast<Worksheet>())
                                    {
                                        // попытка обработать текущий лист для всех типов листов
                                        // фактическая работа делается в этих объектах
                                        pageTypes.ForEach(x => x.AutoCorrectAccounts(ws, args));
                                    }

                                    try
                                    {
                                        dataContext.VistaMed.ActiveTransaction.Commit();
                                        context.ReportProgress(@"Изменения записаны в БД");
                                    }
                                    catch (Exception exception)
                                    {
                                        dataContext.VistaMed.ActiveTransaction.Rollback();
                                        context.ReportProgress(@"Ошибка записи изменений в БД: {0}", exception.Message);
                                    }
                                }
                                finally
                                {
                                    dataContext.VistaMed.ActiveTransaction.Dispose();
                                    dataContext.VistaMed.ActiveTransaction = null;
                                    dataContext.VistaMed.Connection.Close();
                                }
                            }
                            excel.ScreenUpdating = true;

                        }
                        else
                            context.ReportError(@"КоличествоМесяцев в файле не соответствует выбранному Вами периоду");
                    }
                    else
                        context.ReportError(@"Имя ячейки ""КоличествоМесяцев"" не найдено в файле {0}", excelFileName);

                    newDoc.Close(false);
                }
            }
            else
                context.ReportError(@"Файл {0} не найден", excelFileName);

            context.ProgressSeparator('-');
        }
            /// <summary>
            /// Создать резервный счет (счет перевыполнения)
            /// </summary>
            /// <param name="args"></param>
            /// <param name="smoId"></param>
            /// <returns></returns>
            private int createReserveAccount(AccountCorrectParams args, int smoId)
            {
                int result = 0;

                var admin = dataContext.VistaMed.People
                    .FirstOrDefault(x => !x.Deleted && x.Login.ToLower() == @"админ");

                if (admin != null)
                {

                    var adminId = admin.Id;

                    var sqlContractSelect = string.Format(@"SELECT c.id AS Id
                            FROM Contract c
                            JOIN rbFinance f ON f.id = c.finance_id AND c.deleted = 0
                            WHERE f.name = 'омс' AND c.payer_id = {0} AND grouping = 'свои'
                            AND c.begDate <= '{1}' AND c.endDate < '{2}'",
                        smoId,
                        args.DateEnd.BeginOfYear().SqlStr(),
                        args.DateEnd.EndOfYear().AddDays(1).SqlStr());

                    var contractData = dataContext.VistaMed.SelectSqlData<IdData>(sqlContractSelect).FirstOrDefault();
                    if (contractData != null)
                    {
                        var contractId = contractData.Id;

                        var serverDateTime =
                            dataContext.VistaMed.CalculateSql<DateTime>(@"SELECT CURRENT_TIMESTAMP()");

                        var sqlText = string.Format(@"INSERT INTO Account
                                (createDatetime, createPerson_id, modifyDatetime, modifyPerson_id,
                                deleted, contract_id, orgStructure_id, payer_id,
                                settleDate, number, date, amount,
                                uet, sum, exposeDate, payedAmount,
                                payedSum, refusedAmount, refusedSum, format_id)
                                    VALUES
                                ('{0}', {1}, '{0}', {1},
                                0, {2}, NULL, {3},
                                '{4}', '{5}', '{4}', 0,
                                0, 0, NULL, 0,
                                0, 0, 0, NULL)",
                            serverDateTime.SqlStrWithTime(),
                            adminId, contractId, smoId,
                            args.DateEnd.BeginOfYear().SqlStr(),
                            ReserveAccountName);

                        dataContext.VistaMed.ExecuteSql(sqlText);

                        result = getReserveAccountId(args, smoId);
                    }
                    else
                        context.ReportError(@"Не найден договор по СМО за период");
                }
                else
                    context.ReportError(@"Не найдена учетная запись администратора");
                return result;
            }
            protected override void selectSmoFomsReportData(Worksheet ws, AccountCorrectParams args,
                List<FomsReportRowData> formReportData, string smoCode, FomsReportRowType rowType)
            {
                context.ReportProgress(@"Читаем данные стационара ...");

                (new Section(5, 39, dataContext, StacionarType.Full, context))
                    .SelectFomsReportData(ws, args, formReportData, smoCode, rowType);

                (new Section(45, 72, dataContext, StacionarType.Day, context))
                    .SelectFomsReportData(ws, args, formReportData, smoCode, rowType);

                (new Section(78, 106, dataContext, StacionarType.Apu, context))
                    .SelectFomsReportData(ws, args, formReportData, smoCode, rowType);
            }
 protected override List<FactPlanDelta> selectDeltaList(Worksheet ws, AccountCorrectParams args, int smoId)
 {
     throw new NotImplementedException();
 }
                public void SelectFomsReportData(Worksheet ws, AccountCorrectParams args,
	                List<FomsReportRowData> fomsReportData, string smoCode, FomsReportRowType rowType)
                {
                    var data = fomsReportData.FirstOrDefault(x => x.SmoCode == smoCode && x.RowType == rowType);
                    if (data == null)
                    {
                        data = new FomsReportRowData() {SmoCode = smoCode, RowType = rowType};
                        fomsReportData.Add(data);
                    }

                    var row = (Range) ws.Rows[maxRowIndex + 1];

                    var colIndex = 0;

                    if (rowType == FomsReportRowType.Plan)
                        colIndex = 7;
                    if (rowType == FomsReportRowType.Fact)
                        colIndex = 11;

                    var sumValue = row.GetCellValue<int>(1, colIndex)
                                + row.GetCellValue<int>(1, colIndex + 1)
                                + row.GetCellValue<int>(1, colIndex + 2)
                                + row.GetCellValue<int>(1, colIndex + 3);

                    switch (stacionarType)
                    {
                        case StacionarType.Apu:
                            data.StacApuCount = sumValue;
                            break;

                        case StacionarType.Full:
                            data.StacFullCount = sumValue;
                            break;

                        case StacionarType.Day:
                            data.StacDayCount = sumValue;
                            break;
                    }
                }
            /// <summary>
            /// Провести автоматическую корректировку реестров
            /// </summary>
            /// <param name="ws"></param>
            /// <param name="args"></param>
            public void AutoCorrectAccounts(Worksheet ws, AccountCorrectParams args)
            {
                string smoCode;
                if (isTagretPage(ws.Name, out smoCode))
                {
                    var smoId = getSmoId(smoCode);
                    if (smoId > 0)
                    {
                        context.ProgressSeparator();
                        context.ReportProgress("Выбираем пере(недо)выполнение планов: лист {0} ...", ws.Name);

                        var deltaList = selectDeltaList(ws, args, smoId)
                            .Where(x => x.DeltaType != FactPlanDeltaType.Zero)
                            .ToList();

                        if (deltaList.Count > 0)
                        {
                            context.ReportProgress(@"Обнаружено {0} отклонений от плана", deltaList.Count);

                            var nedo = deltaList.Where(x => x.DeltaType == FactPlanDeltaType.Nedo).ToList();
                            removeFromReserveAccount(args, smoId, nedo);

                            var pere = deltaList.Where(x => x.DeltaType == FactPlanDeltaType.Pere).ToList();
                            moveToReserveAccount(args, smoId, pere);

                        }
                        else
                            context.ReportProgress(@"Отклонения от планов не обнаружены");

                    }
                }
            }