private static void AddFieldToSchedule(
            ViewSchedule viewSchedule, BuiltInParameter builtInParameter, out bool fieldAlreadyAdded)
        {
            var schedulableFields = viewSchedule.Definition.GetSchedulableFields();

            fieldAlreadyAdded = false;

            foreach (var sf in schedulableFields)
            {
                if (sf.FieldType != ScheduleFieldType.Instance)
                {
                    continue;
                }
                if (sf.ParameterId.IntegerValue != (int)builtInParameter)
                {
                    continue;
                }

                // Get all schedule field ids
                var ids = viewSchedule.Definition.GetFieldOrder();
                foreach (var id in ids)
                {
                    try
                    {
                        var scheduleField = viewSchedule.Definition.GetField(id);
                        if (scheduleField.GetSchedulableField() == sf)
                        {
                            fieldAlreadyAdded      = true;
                            scheduleField.IsHidden = false;
                            break;
                        }
                    }
                    catch
                    {
                        // Тут бывают какие-то ошибки, но мне они не важны, поэтому проще их "проглотить"
                    }
                }

                if (fieldAlreadyAdded == false)
                {
                    viewSchedule.Definition.AddField(sf);
                }
            }

            viewSchedule.RefreshData();
            viewSchedule.Document.Regenerate();
        }
        /// <summary>
        /// Получение сортированных элементов из спецификации с не установленной галочкой "Для каждого экземпляра"
        /// </summary>
        /// <param name="viewSchedule">Вид спецификации</param>
        /// <param name="elements">Элементы, полученные с этого вида</param>
        private IEnumerable <RowData <int> > GetSortedElementsFromNotItemizedScheduleBySignalValue(
            ViewSchedule viewSchedule, List <Element> elements)
        {
            var sortedElements = new List <RowData <int> >();

            var builtInParameter = GetBuiltInParameterForElements(elements);

            if (builtInParameter == null)
            {
                return(sortedElements);
            }

            // запоминаю начальные значения в параметре
            var cachedParameterValues = new Dictionary <Element, string>();

            elements.ForEach(e =>
            {
                var parameter = e.get_Parameter(builtInParameter.Value);
                cachedParameterValues.Add(e, parameter.AsString());
            });

            const string signalValue    = "$Filled$";
            var          columnNumber   = -1;
            var          startRowNumber = -1;

            bool fieldAlreadyAdded;

            using (var transaction = new SubTransaction(viewSchedule.Document))
            {
                transaction.Start();

                // всем элементам записываем в комментарий сигнальное значение в виде двух специальных символов
                elements.ForEach(e =>
                {
                    if (e.GroupId != ElementId.InvalidElementId)
                    {
                        (e.Document.GetElement(e.GroupId) as Group)?.UngroupMembers();
                    }

                    var parameter = e.get_Parameter(builtInParameter.Value);
                    parameter.Set(signalValue);
                });

                // К спецификации добавляем поле. Код из справки, за исключением try {} catch{}
                AddFieldToSchedule(viewSchedule, builtInParameter.Value, out fieldAlreadyAdded);

                // в зависимости от количества строк в таблице сразу заполняю коллекцию "болванками" и
                // нахожу номер нужной колонки и первой строки
                var sectionData = viewSchedule.GetTableData().GetSectionData(SectionType.Body);
                var rowNumber   = 0;
                for (var r = sectionData.FirstRowNumber; r <= sectionData.LastRowNumber; r++)
                {
                    for (var c = sectionData.FirstColumnNumber; c <= sectionData.LastColumnNumber; c++)
                    {
                        var cellValue = viewSchedule.GetCellText(SectionType.Body, r, c);
                        if (cellValue.Contains(signalValue))
                        {
                            rowNumber++;
                            sortedElements.Add(new RowData <int>(rowNumber));

                            if (startRowNumber == -1)
                            {
                                startRowNumber = r;
                            }
                            if (columnNumber == -1)
                            {
                                columnNumber = c;
                            }

                            break;
                        }
                    }
                }

                transaction.Commit();
            }

            // теперь выполняю итерацию по всем элементам
            for (var index = 0; index < elements.Count; index++)
            {
                var element = elements[index];
                using (var transaction = new SubTransaction(viewSchedule.Document))
                {
                    transaction.Start();

                    if (index != 0)
                    {
                        var parameter = elements[index - 1].get_Parameter(builtInParameter.Value);

                        // возвращаю элементу значение в параметр
                        parameter.Set(signalValue);
                    }

                    {
                        // элементу стираю второй символ. Первый символ нужен, чтобы идентифицировать ячейку
                        var parameter = element.get_Parameter(builtInParameter.Value);
                        parameter.Set(string.Empty);
                    }

                    // регенерирую таблицу, чтобы обновить представление
                    viewSchedule.RefreshData();
                    viewSchedule.Document.Regenerate();

                    transaction.Commit();
                }

                // теперь смотрю какая ячейка погасла
                var sectionData = viewSchedule.GetTableData().GetSectionData(SectionType.Body);
                var rowNumber   = 0;
                for (var r = startRowNumber; r <= sectionData.LastRowNumber; r++)
                {
                    rowNumber++;
                    var cellValue = viewSchedule.GetCellText(SectionType.Body, r, columnNumber);

                    /*
                     * Начиная с версии 2022 в ячейках с разными значениями пишется <варианты>. Проверять буду по скобкам
                     */

                    if (string.IsNullOrEmpty(cellValue) || (cellValue.StartsWith("<") && cellValue.EndsWith(">")))
                    {
                        var elInRow = sortedElements.FirstOrDefault(e => e.RowNumber == rowNumber);
                        elInRow?.Items.Add(element.Id.IntegerValue);

                        break;
                    }
                }
            }

            // восстанавливаю начальные значения параметров
            using (var transaction = new SubTransaction(viewSchedule.Document))
            {
                transaction.Start();

                foreach (var pair in cachedParameterValues)
                {
                    var parameter = pair.Key.get_Parameter(builtInParameter.Value);
                    parameter.Set(pair.Value);
                }

                // если поле не было добавлено изначально, то нужно его удалить
                if (!fieldAlreadyAdded)
                {
                    RemoveFieldFromSchedule(viewSchedule, builtInParameter.Value);
                }

                transaction.Commit();
            }

            return(sortedElements);
        }