Три костыля. Сказ про фокусы в коде
Разработка - Механизмы платформы 1С
О костыли!
Часто
ли Вы делаете "костыли" в решении повседневных задач? Я да. И не стыжусь этого! Мы работаем на результат и главной нашей целью должно быть решение задач бизнеса. Иногда, не смотря на перфекционизм, разработчику приходится жертвовать качеством, порождать технический долг, но идти к поставленной цели.
Хорошего разработчика от плохого отличает не то, что его решения всегда идеальные с технической точки зрения. Самое ценное и важное качество - это доводить поставленную задачу до конца в разумные для бизнеса сроки, при этом не забывая о техническом долге и возвращаясь к нему в будущем. Не важно по какой причине было выбрано подобного рода решение. Важно, чтобы работа над улучшением продукта продолжалась, ведь вопросы архитектуры и качества технических решений актуальны всегда.
Сегодня, мы рассмотрим пару таких "костылей", которые могут быть Вам интересны. Добро пожаловать!

Итоги в динамическом списке
Иногда разработчики увлекаются
в "игре" с механизмами платформы, а бывает и заказчики желают получить от программы чего-то из ряда вон выходящее (для платформы, но привычное для пользователей).
Этот костыль из этой "оперы". На одном из проектов заказчик пожелал видеть итоги в динамическом списке. Все доводы, что список для этого не предназначен, а также что имеется путаница между списками и отчетами - все это было проигнорировано. "Просто возьми мои деньги и сделай!", - говорил заказчик. "Все будет хорошо работать!", - говорили менеджеры.
Результат - вот такое решение для итогов в динамическом списке управляемой формы:
- В форму списка добавляются реквизиты для хранения итогов и привязываются к подвалу соответствующих колонок списка.
- По событию "ПриПолученииДанныхНаСервере", "ПриАктивизацииСтроки" (о ужас!) или с помощью обработчика ожидания выполняем обновлением значений итогов с учетом отборов в динамическом списке.
Вся сложность заключается в том, что динамический список не получает сразу все записи, а получает их порциями. Соответственно, мы не можем сразу получить итог по всем документам по текущим данным списка, соответствующим отбору. Каким же образом его посчитать?
Итак, перейдем к решению задачи. Начнем с изменения формы, далее опишем алгоритм получения итоговых значений.
Форма и интерфейс
Для начала подготовим форму документа для отображения итоговых полей. Для этого добавим два строковых реквизита формы "Рейтинг" и "Сумма".
В данные реквизиты будут записываться итоговые значения по документам. Для отображения значений реквизитов в подвале динамического списка необходимо включить соответствующую опцию связанного элемента формы списка (см. следующий скриншот).
Далее нужно связать колонки подвала с соответствующими реквизитами формы, которые были созданы ранее. Для этого в свойствах колонок нужно установить путь к данным подвала.
Теперь нужно определиться по какому событию будет происходить обновление итогов в подвале списка. Для удобства разработки добавим команду "Обновить" и соответствующий ей элемент формы на командную панель. При выполнении этой команды будет происходить обновление итогов. В реальных задачах обновление итогов нужно делать на событиях "ПриПолученииДанныхНаСервере", "ПриАктивизацииСтроки" (о ужас!) или с помощью обработчика ожидания.
Пример Вы можете посмотреть в репозитории на GitHub. Также было добавлено событие обновления итогов при записи документа, при этом используется механизм оповещения форм. Подробнее на этом останавливаться не будем.
Алгоритм
Осталась самая проблематичная часть - нужно получить значения итогов. Поступим следующим образом: будем формировать запрос к базе данных на получение значений итоговых полей в соответствии с отбором, установленным в динамическом списке. Стоит учитывать, что в отборе может стоять сложное условие из групп.
Этапы формирования запроса для получения итогов следующие:
1. Получаем исходный запрос динамического списка.

Как мы видим, запрос выбирает все реквизиты документа. Для небольшого усложнения добавил собственное поле "УровеньРейтинга", формируемое конструкцией "ВЫБОР".
2. Формируем текст условий запроса (секция "ГДЕ") и подставляем в исходный запрос.
К полученному исходному тексту запроса нам необходимо добавить условия в соответствии с настроенным отбором динамического списка.
Для этого нужно рекурсивно обойти все используемые элементы отбора, включая группы, и сформировать текст условия. Подробно описывать созданный алгоритм не буду, его Вы можете посмотреть в тестовой конфигурации, опишу лишь важные моменты.
Рекурсивный обход элементов отбора осуществляется следующим способом.
// Устанавливаем условия запроса в соответствии с установленным отбором
ТекстУсловийЗапроса = "ГДЕ ";
Для Каждого Эл Из МассивИспользуемыхПолейОтбора Цикл
// Обрабатываем текущий элемент отбора верхнего уровня
ОбработатьЭлементОтбора(Эл, ТекстУсловийЗапроса, "И",
СтруктураТиповПолей, Запрос, ТекстЗапроса);
КонецЦикла;
ТекстУсловийЗапроса = УбратьЛишнийОператорУсловия(ТекстУсловийЗапроса);
Здесь самым интересным является процедура "ОбработатьЭлементыОтбора", которая дописывает в строковую переменную "ТекстУсловийЗапроса" условия в соответствии с текущем элементом. Рассмотрим код данной процедуры.
// Процедура обрабатывает переданный элемент отбора
// Параметры:
// 1. Эл - элемент отбора (группа или поле отбора)
// 2. ТекстУсловийЗапроса - строковая переменная, в которую добисываются условия запроса
// 3. ВидСравненияУсловия - вид сравнения в условии (зависит от группы элементов отбора)
// 4. СтруктураТиповПолей - структура, содержащая описание типов полей для полей отбора
// 5. Запрос - класс запроса, который будет в дальнейшем выполнен для получения результата
// 6. ТекстЗапроса - исходный текст запроса динамического списка
//
&НаСервереБезКонтекста
Процедура ОбработатьЭлементОтбора(Эл, ТекстУсловийЗапроса, ВидСравненияУсловияВерхнийУровень, СтруктураТиповПолей, Запрос, ТекстЗапроса)
// Если это группа элементов отбора
Если ТипЗнч(Эл) = Тип("ГруппаЭлементовОтбораКомпоновкиДанных") Тогда
ВидСравненияУсловия = "";
Отрицание = "";
Если Эл.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ Тогда
ВидСравненияУсловия = "И";
ИначеЕсли Эл.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли Тогда
ВидСравненияУсловия = "ИЛИ";
ИначеЕсли Эл.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаНе Тогда
ВидСравненияУсловия = "И";
Отрицание = " НЕ ";
КонецЕсли;
ТекстУсловийЗапроса = ТекстУсловийЗапроса + Отрицание +"(";
ЭлементыГруппы = Эл.Элементы;
МассивЭлементовОтбора = ПолучитьМассивВключенныхЭлементов(ЭлементыГруппы);
Если МассивЭлементовОтбора.Количество() = 0 Тогда
ТекстУсловийЗапроса = ТекстУсловийЗапроса + "ИСТИНА"
Иначе
Для Каждого ЭлГр Из Эл.Элементы Цикл
Если ЭлГр.Использование Тогда
ОбработатьЭлементОтбора(ЭлГр, ТекстУсловийЗапроса, ВидСравненияУсловия, СтруктураТиповПолей, Запрос, ТекстЗапроса);
КонецЕсли;
КонецЦикла;
ТекстУсловийЗапроса = Лев(ТекстУсловийЗапроса, СтрДлина(ТекстУсловийЗапроса)-(СтрДлина(ВидСравненияУсловия)+1));
КонецЕсли;
ТекстУсловийЗапроса = ТекстУсловийЗапроса + ") " + ВидСравненияУсловияВерхнийУровень;
Иначе // Если это поле отбора
ИмяПоляОтбора = Строка(Эл.ЛевоеЗначение);
ТекстУсловийЗапроса = ТекстУсловийЗапроса
+ " " +ПолучитьТекстПоляПоПредставлению(ТекстЗапроса, ИмяПоляОтбора) // Получаем текст поля, по которому делается отбор
// Получаем выражение сравнения для полученного ранее поля отбора
+ " " + ПолучитьВидСравненияИЗначение(Эл.ВидСравнения, Эл.ПравоеЗначение, ИмяПоляОтбора, ВидСравненияУсловияВерхнийУровень);
// Если "ВидСравнения" - "Заполнено" или "Не заполнено", тогда в условие сравнения подставляем пустое значение для поля отбора по его типу
Если Эл.ВидСравнения = ВидСравненияКомпоновкиДанных.Заполнено ИЛИ
Эл.ВидСравнения = ВидСравненияКомпоновкиДанных.НеЗаполнено Тогда
Запрос.УстановитьПараметр("Заполнено"+ИмяПоляОтбора,
СтруктураТиповПолей[СтрЗаменить(ИмяПоляОтбора, "Заполнено", "")].ПривестиЗначение());
Иначе // Иначе устанавливаем значение параметра в соответствии с переданным значением
// Получаем значение для передачи в запрос параметром
Запрос.УстановитьПараметр(СтрЗаменить(ИмяПоляОтбора, ".", ""), ПолучитьЗначениеДляПараметра(Эл.ПравоеЗначение, ИмяПоляОтбора));
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В зависимости от типа переданного элемента отбора (группа или элемент отбора) формирует соответствующий текст условия. Все условия в группе обрамляются скобками, входящие группу также обрамляются круглыми скобками. Условия между выражениями зависят от вышестоящей группы (между верхними в иерархии элементами ставится условие "И").
Если у элемента установлен флаг использования (свойство "Использование") тогда элемент обрабатывается. Формируемый текст зависит также от условия сравнения (Равно, не равно, в списке и прочее). Зависимость формируемого текста условия от вида сравнения можно увидеть в следующей функции.
// Получаем вид сравнения по имени поля и значению отбора, а также виду сравнения в сусловии
// Параметры:
// 1. ВидСравненияОтбор - вид сравнения в поле отбора (тип "ВидСравненияКомпоновкиДанных")
// 2. ЗначениеСравнения - ПравоеЗначение из элемента отбора
// 3. ИмяПоляОтбора - имя поля в отборе
// 4. ВидСравненияУсловия - вид сравнения условия в зависимости от текущей родительской
// группы элементов в отборе (корневая группа - условие "И")
//
&НаСервереБезКонтекста
Функция ПолучитьВидСравненияИЗначение(ВидСравненияОтбор, ЗначениеСравнения,
ИмяПоляОтбора, ВидСравненияУсловия)
ДопИмя = СтрЗаменить(Строка(ВидСравненияОтбор), " ", "");
ИмяПоляОтбора = СтрЗаменить(ИмяПоляОтбора, ".", "")+ДопИмя;
Если ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.Равно Тогда
Возврат "= &" + ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.НеРавно Тогда
Возврат "<> &" + ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.ВИерархии Тогда
Возврат "В ИЕРАРХИИ (&" + ИмяПоляОтбора + ") "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.НеВИерархии Тогда
Возврат "НЕ В ИЕРАРХИИ (&" + ИмяПоляОтбора + ") "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.Меньше Тогда
Возврат "< &" + ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно Тогда
Возврат "<= &" + ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.Больше Тогда
Возврат "> &" + ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.БольшеИлиРавно Тогда
Возврат ">= &" + ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.ВСписке Тогда
Возврат "В (&" + ИмяПоляОтбора + ") "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.НеВСписке Тогда
Возврат "НЕ В (&" + ИмяПоляОтбора + ") "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.Содержит Тогда
Возврат "ПОДОБНО (""%" + ЗначениеСравнения + "%"") "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.НеСодержит Тогда
Возврат "НЕ ПОДОБНО (""%" + ЗначениеСравнения + "%"") "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.Заполнено Тогда
Возврат "<> &" + "Заполнено"+ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
ИначеЕсли ВидСравненияОтбор = ВидСравненияКомпоновкиДанных.НеЗаполнено Тогда
Возврат "= &" + "Заполнено"+ИмяПоляОтбора + " "+ВидСравненияУсловия+" ";
КонецЕсли;
КонецФункции
Еще одной интересной, на мой взгляд, функцией является "ПолучитьТекстПоляПоПредставлению". Нужна она для того, чтобы подставлять в условия запроса поля, которые формируются выражениями языка запроса. Выше в исходный запрос мной было добавлено поле "УровеньРейтинга". Если пользователь будет использовать его в отборе, то в условие запроса нужно подставлять полностью все выражение. Данная функция получает текст поля из запроса по его представлению. Для таких сложных полей она вернет полностью весь текст выражения.
Подробнее алгоритм смотрите в тестовой конфигурации, приложенной к статье. Ниже приведу скриншот настроек отбора и сформированных для них условий запроса.
Сформированный текст условий присоединяется к исходному запросу динамического списка. Результат запроса помещается во временную таблицу.
3. Первый запрос помещаем во временную таблицу и выполняем группировку по итоговым полям с необходимыми агрегатными функциями.
Напомню, что нам нужно получить среднее значение по полю "Рейтинг" и общую сумму по полю "Сумма". Запрос с учетом отборов мы уже сформировали, осталось произвести подсчет итоговых значений. Делается это следующим запросом:
После выполнения запроса обрабатываем полученный результат и записываем в реквизиты формы, которые мы создали ранее. В конечном счете, мы получили отображение итогов в подвале динамического списка.
Представленное решение далеко не идеальное и может быть улучшено:
- Оптимизация запроса для получения итогов.
- Клиент-серверный вызов для получения итогов.
- Кэширование данных.
- Отказ от "ручного" сбора запроса.
- И многое другое.
Но общий принцип должен быть понятен.
Согласитесь, это тот еще костыль!
Решение не единственное, но достаточно очевидное. Ссылки на другие способы решения этой же задачи:
"Костыль" высшего уровня. Причем многим он и костылем не казался, а пользователи вообще были счастливы! Счастье длилось, пока в базе было немного документов, а пользователи не ставили сложных отборов в списке ... ведь запросы для расчета итогов могут быть очень тяжелыми при большом количестве записей и особых отборах.
Никогда! Никогда не делайте итоги в динамическом списке! А если и делаете, то позаботьтесь, чтобы запрос для их расчета был простым во всех случаях.
Проверка наличия свойства
"Поле объекта не обнаружено" - все разработчики "мечтают" увидеть это сообщение от платформы! Особенно после развертывания релиза, применения новых доработок.
Однажды у клиента возникла проблема в интеграции, когда в функцию передавались объекты разных типов и для них выполнялись различные преобразования. В один прекрасный момент фоновые задания начали "вываливаться" с ошибкой "Поле объекта не обнаружено", нужно было срочное решение! Отладки на сервере нет, больше никакой информации об ошибке получить не удалось, клиент в стадии "всем конец".
Пришло "гениальное" решение - написать функцию, которая бы проверяла наличие поля или свойства у любого типа значения. Сказано - сделано!
Функция ПеременнаяСодержитСвойство(Переменная, ИмяСвойства)
// Инициализируем структуру для теста
// с ключом (значение переменной "ИмяСвойства")
// и значением произвольного GUID'а
GUIDПроверка = Новый УникальныйИдентификатор;
СтруктураПроверка = Новый Структура;
СтруктураПроверка.Вставить(ИмяСвойства, GUIDПроверка);
// Заполняем созданную структуру из переданного
// значения переменной
ЗаполнитьЗначенияСвойств(СтруктураПроверка, Переменная);
// Если значение для свойства структуры осталось
// NULL, то искомое свойство не найдено,
// и наоборот.
Если СтруктураПроверка[ИмяСвойства] = GUIDПроверка Тогда
Возврат Ложь;
Иначе
Возврат Истина;
КонецЕсли;
КонецФункции
Пример использования функции ниже.
Процедура ПроверитьСвойствоДокумента()
РеквизитыДокумента = Метаданные.Документы.ТестовыйДокумент.Реквизиты;
ИмяРеквизита = "Комментарий";
Если ПеременнаяСодержитСвойство(РеквизитыДокумента, ИмяРеквизита) Тогда
Сообщить("Реквизит """ + ИмяРеквизита + """ найден!");
Иначе
Сообщить("Реквизит """ + ИмяРеквизита + """ НЕ найден!");
КонецЕсли;
КонецПроцедуры
В тестовой конфигурации, исходный код которой Вы можете найти на GitHub, результатом будет сообщение:
>> Реквизит "Комментарий" найден!
Все взлетело!
В итоге проверку в алгоритмы обмена добавили, и проблема решилась. Спустя неделю добрались до истины - проблема была в плохих данных. Но "костыль" работает до сих пор.
Думаете, что это все? Нет! Эта функция переехала в общий модуль и со временем ее начали использовать другие разработчики! Не важно, позволяет ли платформа проверять наличие свойства для типа или нет, проще использовать универсальную функцию и не беспокоиться!
Сейчас это решение используют до сих пор. "Костыль" стал удобным решением, позволяющим обходить некоторые ограничения платформы. Минусы у этого подхода очевидные:
- Игнорирование стандартного функционала платформы 1С усложняет сопровождение.
- Снижение производительности по сравнению со стандартными методами. На сколько происходит замедление зависит от конкретной ситуации.
А Вы используете такую "костылефичу"?
Чтение наборов записей запросом
На одном из проектов по оптимизации производительности также пришлось использовать одно
сомнительное решение. Всем известно, что при выполнении конструкции "НаборЗаписей.Прочитать()" в транзакции, платформа 1С устанавливает разделяемую управляемую блокировку на прочитанные данные до конца транзакции. Этот момент отлично описан в статье "Ускорение в 100 раз. Решаем проблему блокировок" от Андрея Бурмистрова. Почему это может быть плохо? При параллельной работе пользователей это может привести к управляемым взаимоблокировкам. Опять же, посмотрите статью.
Решение этой проблемы обычно простое - нужно отказаться от использования объектной техники работы с данными там, где они нужны только для чтения. Лучше всего использовать запросы. Если же нужно использовать такой способ работы, то перед вызовом "Прочитать()" необходимо установить исключительную управляемую блокировку.
Но что, если исправлять нужно сотни таких мест в конфигурации, а то и больше. Что если в некотором отраслевом решении (чисто гипотетически) объектную технику при работе с наборами данных регистров используют повсеместно. И правильно! Зачем эти запросы, так же проще!
Как Вы догадались, времени на исправление всех мест просто не было и было найдено быстрое решение - чтение набора записей с помощью запроса.
Ниже полный листинг кода для чтения наборов запросом. Кстати, там внизу используется "костыль" для проверки наличия свойств, о котором мы ранее говорили.
Процедура ПрочитатьНаборРегистраЗапросом(НаборРегистра) Экспорт
// Получаем имя типа регистра и имя его метаданных
МетаданныеРегистра = НаборРегистра.Метаданные();
ВидОбъекта = Неопределено;
ЭтоРегистрБухгалтерии = Ложь;
Если ОбщегоНазначения.ЭтоРегистрНакопления(МетаданныеРегистра) Тогда
ВидОбъекта = "РегистрНакопления";
ИначеЕсли ОбщегоНазначения.ЭтоРегистрСведений(МетаданныеРегистра) Тогда
ВидОбъекта = "РегистрСведений";
ИначеЕсли ОбщегоНазначения.ЭтоРегистрБухгалтерии(МетаданныеРегистра) Тогда
ВидОбъекта = "РегистрБухгалтерии";
ЭтоРегистрБухгалтерии = Истина;
ИначеЕсли ОбщегоНазначения.ЭтоРегистрРасчета(МетаданныеРегистра) Тогда
ВидОбъекта = "РегистрРасчета";
КонецЕсли;
// Если переданный объект не является регистром, то чтение не выполняем
Если ВидОбъекта = Неопределено Тогда
Возврат;
КонецЕсли;
// Формируем текст запроса для получения записей регистра
ЗапросДанныхРегистра = Новый Запрос;
Если ЭтоРегистрБухгалтерии Тогда
ЗапросДанныхРегистра.Текст =
"Выбрать *
| Из " + ВидОбъекта + "." + МетаданныеРегистра.Имя + ".ДвиженияССубконто(, , {УсловияОтбора}, , )
|ГДЕ
| {УсловияОтбора}";
Иначе
ЗапросДанныхРегистра.Текст =
"Выбрать *
| Из " + ВидОбъекта + "." + МетаданныеРегистра.Имя + "
|ГДЕ
| {УсловияОтбора}";
КонецЕсли;
// Условия отбора регистра формируем отдельно по коллекции элементов
// отбора переданного набора записей
ТекстОтбора = "";
Для Каждого ЭлОтбора Из НаборРегистра.Отбор Цикл
ОбработатьУсловиеОтбора(ЭлОтбора, ЗапросДанныхРегистра, ТекстОтбора);
КонецЦикла;
Если ЗапросДанныхРегистра.Параметры.Количество() > 0 Тогда
ЗапросДанныхРегистра.Текст = СтрЗаменить(ЗапросДанныхРегистра.Текст, "{УсловияОтбора}", ТекстОтбора);
Иначе
ЗапросДанныхРегистра.Текст = СтрЗаменить(ЗапросДанныхРегистра.Текст, "{УсловияОтбора}", " ИСТИНА ");
КонецЕсли;
// Получаем результат запроса в виде выборки и заполняем
// набор записей
ВыборкаДанныхРегистра = ЗапросДанныхРегистра.Выполнить().Выбрать();
НаборРегистра.Очистить();
Если ЭтоРегистрБухгалтерии Тогда
Пока ВыборкаДанныхРегистра.Следующий() Цикл
// Заполняем таблицу движений
НоваяЗапись = НаборРегистра.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, ВыборкаДанныхРегистра);
// Заполняем таблицу субконто
ЕстьПоляСубконто = Истина;
НомерСубконто = 1;
Пока ЕстьПоляСубконто Цикл
ИмяПоляСубконтоКт = "СубконтоКт"+Формат(НомерСубконто,"ЧГ=0");
ИмяПоляСубконтоДт = "СубконтоДт"+Формат(НомерСубконто,"ЧГ=0");
ЕстьСубконтоКт = СодержитСвойство(ВыборкаДанныхРегистра, ИмяПоляСубконтоКт);
ЕстьСубконтоДт = СодержитСвойство(ВыборкаДанныхРегистра, ИмяПоляСубконтоДт);
Если ЕстьСубконтоДт ИЛИ ЕстьСубконтоКт Тогда
Если ЕстьСубконтоДт Тогда
ЗначениеСубконто = ВыборкаДанныхРегистра[ИмяПоляСубконтоДт];
ВидСубконто = ВыборкаДанныхРегистра["Вид"+ИмяПоляСубконтоДт];
НоваяЗапись.СубконтоДт[ВидСубконто] = ЗначениеСубконто;
КонецЕсли;
Если ЕстьСубконтоКт Тогда
ЗначениеСубконто = ВыборкаДанныхРегистра[ИмяПоляСубконтоКт];
ВидСубконто = ВыборкаДанныхРегистра["Вид"+ИмяПоляСубконтоКт];
НоваяЗапись.СубконтоКт[ВидСубконто] = ЗначениеСубконто;
КонецЕсли;
Иначе
ЕстьПоляСубконто = Ложь;
КонецЕсли;
НомерСубконто = НомерСубконто + 1;
КонецЦикла;
КонецЦикла;
Иначе
Пока ВыборкаДанныхРегистра.Следующий() Цикл
НовСтр = НаборРегистра.Добавить();
ЗаполнитьЗначенияСвойств(НовСтр, ВыборкаДанныхРегистра);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
// Формируем текст условия запроса с учетом элементов отбора регистра, а также
// устанавливаем все необходимые параметры запроса
// Параметры:
// ЭлОтбора - элемент обора из коллекции "НаборЗаписейРегистра.Отбор"
// ЗапросДанныхРегистра - объект типа "Запрос", с помощью которого будет
// выполнено получение данных записей регистра
// ТекстОтбора - переменная, в которую будут записаны сформированные
// условия запроса строкой
//
Функция ОбработатьУсловиеОтбора(ЭлОтбора, ЗапросДанныхРегистра, ТекстОтбора)
ТекстОтбора = ?(ЗапросДанныхРегистра.Параметры.Количество() > 0, " И ", "") +
ТекстОтбора +
ЭлОтбора.ПутьКДанным;
УсловиеОтбора = " = ";
Если ЭлОтбора.ВидСравнения = ВидСравнения.Больше Тогда
УсловиеОтбора = " > (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.БольшеИлиРавно Тогда
УсловиеОтбора = " >= (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.ВИерархии Тогда
УсловиеОтбора = " В ИЕРАРХИИ (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.ВСписке Тогда
УсловиеОтбора = " В (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.ВСпискеПоИерархии Тогда
УсловиеОтбора = " В ИЕРАРХИИ (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.Интервал Тогда
УсловиеОтбора = " МЕЖДУ &" + ЭлОтбора.Имя+"ЗначениеС И &" + ЭлОтбора.Имя+"ЗначениеПо";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеС", ЭлОтбора.ЗначениеС);
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеПо", ЭлОтбора.ЗначениеПо);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.ИнтервалВключаяГраницы Тогда
УсловиеОтбора = " " + ЭлОтбора.Имя + " >= &"+ЭлОтбора.Имя+"ЗначениеС И " + ЭлОтбора.Имя + " <= &"+ЭлОтбора.Имя+"ЗначениеПо ";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеС", ЭлОтбора.ЗначениеС);
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеПо", ЭлОтбора.ЗначениеПо);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.ИнтервалВключаяНачало Тогда
УсловиеОтбора = " " + ЭлОтбора.Имя + " >= &"+ЭлОтбора.Имя+"ЗначениеС И " + ЭлОтбора.Имя + " < &"+ЭлОтбора.Имя+"ЗначениеПо ";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеС", ЭлОтбора.ЗначениеС);
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеПо", ЭлОтбора.ЗначениеПо);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.ИнтервалВключаяОкончание Тогда
УсловиеОтбора = " " + ЭлОтбора.Имя + " > &"+ЭлОтбора.Имя+"ЗначениеС И " + ЭлОтбора.Имя + " <= &"+ЭлОтбора.Имя+"ЗначениеПо ";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеС", ЭлОтбора.ЗначениеС);
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя+"ЗначениеПо", ЭлОтбора.ЗначениеПо);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.Меньше Тогда
УсловиеОтбора = " < (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.МеньшеИлиРавно Тогда
УсловиеОтбора = " <= (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.НеВИерархии Тогда
УсловиеОтбора = " НЕ В ИЕРАРХИИ (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.НеВСписке Тогда
УсловиеОтбора = " НЕ В (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.НеВСпискеПоИерархии Тогда
УсловиеОтбора = " НЕ В ИЕРАРХИИ (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.НеРавно Тогда
УсловиеОтбора = " <> (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.НеСодержит Тогда
УсловиеОтбора = " НЕ ПОДОБНО (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.Равно Тогда
УсловиеОтбора = " = (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
ИначеЕсли ЭлОтбора.ВидСравнения = ВидСравнения.Содержит Тогда
УсловиеОтбора = " ПОДОБНО (&" + ЭлОтбора.Имя + ")";
ЗапросДанныхРегистра.УстановитьПараметр(ЭлОтбора.Имя, ЭлОтбора.Значение);
КонецЕсли;
ТекстОтбора = ТекстОтбора +
УсловиеОтбора;
КонецФункции
// Универсальная функция для проверки наличия свойств у значения любого типа данных
// Переменные:
// 1. Переменная - переменная любого типа, для которой необходимо проверить наличие свойства
// 2. ИмяСвойства - переменная типа "Строка", содержащая искомое свойства
//
Функция СодержитСвойство(Переменная, ИмяСвойства) Экспорт
// Инициализируем структуру для теста с ключом (значение переменной "ИмяСвойства") и значением произвольного GUID'а
GUIDПроверка = Новый УникальныйИдентификатор;
СтруктураПроверка = Новый Структура;
СтруктураПроверка.Вставить(ИмяСвойства, GUIDПроверка);
ЗаполнитьЗначенияСвойств(СтруктураПроверка, Переменная);
// Если значение для свойства структуры осталось NULL, то искомое свойство не найдено, и наоборот.
Если СтруктураПроверка[ИмяСвойства] = GUIDПроверка Тогда
Возврат Ложь;
Иначе
Возврат Истина;
КонецЕсли;
КонецФункции
Много, много кода. Все сводится к тому, что в зависимости от типа регистра, его настроек, установленных отборов и некоторых других особенностей формируется текст запроса для получения данных. После данные из выборки запроса переносятся в сам набор.
Для удобства использования можно перенести функции в общий модуль.
Фактически, мы получаем данные запросов и на их основе заполняем набор записей. Как это использовать? Например, стандартный способ работы с набором записей такой.
НаборДвиженияНоменклатураНоменклатура = РегистрыНакопления.ДвиженияНоменклатураНоменклатура.СоздатьНаборЗаписей();
// "Документ" - ссылка на документ регистратор
НаборДвиженияНоменклатураНоменклатура.Отбор.Регистратор.Установить(Документ);
НаборДвиженияНоменклатураНоменклатура.Прочитать();
Если же использовать "костыль", то код будет немного отличаться.
НаборДвиженияНоменклатураНоменклатура = РегистрыНакопления.ДвиженияНоменклатураНоменклатура.СоздатьНаборЗаписей();
// "Документ" - ссылка на документ регистратор
НаборДвиженияНоменклатураНоменклатура.Отбор.Регистратор.Установить(Документ);
// Вместо вызова метода "Прочитать()" используем собственную процедуру
ПрочитатьНаборРегистраЗапросом(НаборДвиженияНоменклатураНоменклатура);
Результат - набор записей прочитан, заполнен. При этом никаких блокировок в транзакции платформа не установит, ведь все записи прочитаны запросом. Новый способ чтения наборов работает для любых регистров - накопления, бухгалтерии, сведений, расчета.
Такой "костыль" помог исправить проблемы производительности и возникающие взаимоблокировки, но не решил множество других связанных проблем...
Костыли в оптимизации! Кто бы мог подумать!
Мы человеки!
Считаете, что всегда нужно делать идеальные решения? Качество важнее сроков? Процесс важнее результата?
Мы все ошибаемся, делаем спорные решения и можем сомневаться в их правильности. Уж про себя все это я могу с уверенностью подтвердить.
Будем честными к себе и окружающим.
Если и у Вас есть истории о "костылях" и готовность ими поделиться - добро пожаловать в комментарии!
Какой самый "лучший костыль" был у тебя!?
Другие ссылки
Вступайте в нашу телеграмм-группу Инфостарт
костыль велосипед качество быстрое решение ужас
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.
См. также
Механизмы платформы 1С Разработчик Бесплатно (free)
Разберем 15 мифов о работе платформы «1С:Предприятие 8» – как распространенных, так и малоизвестных. Начнем с классики: «Код, написанный в одну строку, работает быстрее, чем многострочный». Так ли это на самом деле?
16.07.2025 45555 TitanLuchs 109
Работа с интерфейсом WEB-интеграция Механизмы платформы 1С Бесплатно (free)
Веб-интерфейсы повышают качество внешнего вида приложений 1С, предсказуемы с точки зрения верстки, позволяют организовать удобные и высокопроизводительные рабочие места для пользователей. Расскажем об особенностях разработки веб-интерфейсов на React внутри 1С, двустороннем взаимодействии 1С и JavaScript, а также сборке веб-приложения в одностраничный файл.
19.06.2025 18581 zeegin 15
Механизмы платформы 1С Работа с интерфейсом Разработчик Стажер 1С:Предприятие 8 Бесплатно (free)
Про ООП в 1С и о том, как сделать свой код более кратким и выразительным при помощи использования текучего интерфейса (fluent interface).
03.02.2025 25964 bayselonarrend 127
Механизмы платформы 1С Разработчик 1С:Предприятие 8 Бесплатно (free)
В этой статье подробно рассматривается работа с JSON в XDTO в 1С:Предприятие. Вы узнаете, как сериализовать и десериализовать объекты XDTO в JSON, интегрировать 1С с веб-сервисами и API, а также корректно обрабатывать данные при обмене. Разбираются особенности работы с коллекциями, использование функций восстановления и частые ошибки при работе с JSON и XDTO.
30.01.2025 33172 user2122906 10
Механизмы платформы 1С Файловый обмен (TXT, XML, DBF), FTP Разработчик 1С:Предприятие 8 Бесплатно (free)
Этот материал познакомит вас с механизмом XDTO (XML Data Transfer Objects) в 1С и научит эффективно использовать его возможности. Мы разберёмся, как работать с XML-схемами, создавать модели данных, манипулировать объектами XDTO, а также сериализовать и десериализовать их в XML. Вы узнаете, как использовать XDTO для интеграции с внешними системами, избегать типичных ошибок и оптимизировать код. К концу вы будете уверенно применять XDTO для решения сложных задач обмена данными и автоматизации процессов.
17.01.2025 55529 user2122906 13
Механизмы платформы 1С WEB-интеграция Разработчик 1С:Предприятие 8 Бесплатно (free)
В платформе 8.3.27 появилась возможность использовать WebSocket-клиент. Давайте посмотрим, как это все устроено и чем оно нам полезно.
14.01.2025 46673 dsdred 110
Механизмы платформы 1С Разработчик 1С:Предприятие 8 Бесплатно (free)
Особенности "Знач" и разбор документации к нему.
10.01.2025 26365 VAAngelov 60
Механизмы платформы 1С Разработчик Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)
Эта небольшая статья - некоторого рода шпаргалка по файловым потокам: как и зачем с ними работать, какие преимущества это дает.
23.06.2024 40361 bayselonarrend 24
Основная беда - именно структуризация кода. Именно она позволяет делать быстрые и удобные модификации и откаты.
ПС: недавно видел процедуру на несколько тысяч строк. И это в решении, которое висит на releases.1c. А количество повторов кусков кода - десятки. Совместимо, чё ((
ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(Объект, ИмяРеквизита)
Функция ЕстьРеквизитИлиСвойствоОбъекта(Объект, ИмяРеквизита) Экспорт
КлючУникальности = Новый УникальныйИдентификатор;
СтруктураРеквизита = Новый Структура(ИмяРеквизита, КлючУникальности);
ЗаполнитьЗначенияСвойств(СтруктураРеквизита, Объект);
Возврат СтруктураРеквизита[ИмяРеквизита] <> КлючУникальности;
КонецФункции
Костыль с проверкой свойства через ГУИД использую тоже очень много лет. Насколько знаю, аналог в типовых появился примерно после Вашей публикации.
.
Из нюансов - такой костыль работать будет не со всеми объектами. Например объект типа Соответствие упадет с исключением при попытке его обработать этим кодом:
ЗаполнитьЗначенияСвойств(СтруктураПроверка, Переменная);
Мой вариант костыля выглядит чуть сложнее (видимо потому что активно используется и костылём не считается):
// Позволяет определить есть ли среди реквизитов объекта, реквизит с переданным именем
//
// Параметры
//
// пОбъект - Именованнная структура данных - любой объект имеющий именованные свойства
// пИмяРек - Строка - имя искомого реквизита
// пТипЗначения - Тип - Ожидаемый тип реквизита. Если параметр задан, то производится проверка типа
// имеет смысл, если просто наличие реквизита не может считаться достаточным, и реквизит должен иметь строго определенный тип
//
// Возвращаемое значение:
// Булево - Истина если реквизит существует (общая проверка) и соответствует переданному типу (опциональная проверка)
//
Функция ЕстьРеквизитОбъекта(пОбъект, пИмяРек, пТипЗначения = Неопределено) Экспорт
КонтрольноеЗначение = Новый УникальныйИдентификатор;
Попытка
СтруктураРеквизита = Новый Структура(пИмяРек, КонтрольноеЗначение);
Исключение
Возврат Ложь;
КонецПопытки;
Если СтруктураРеквизита.Количество() > 1 Тогда
Возврат Ложь; // если передан список реквизитов, то создастся структура со списком ключей, что сигнатурой метода не предусмотрено
КонецЕсли;
ПроверятьТип = ТипЗНЧ(пТипЗначения) = Тип("Тип");
ЗаполнитьЗначенияСвойств(СтруктураРеквизита, пОбъект);
Результат = Ложь;
Значение = СтруктураРеквизита[пИмяРек];
Если Значение <> КонтрольноеЗначение Тогда
Если ПроверятьТип Тогда
Результат = пТипЗначения = ТипЗНЧ(Значение);
Иначе
Результат = Истина;
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции Показатьбаги порождают костыли
особенности платформы позволяют воспользоваться нетипичными решениями
надо добавить "%" перед и после ЭлОтбора.Имя
1)В девяностые приходилось переносить файлы с компа на комп на дискетах и, порою, в виде многотомных архивов. Между дисководами часто наблюдался момент частичной несовместимости, то есть непереносимости того или иного файла (ну не читается). Компы источник и приёмник зачастую бывали разнесены в разные концы города и не имели ни общей сети естественно ни интернета никакого (а то бы зачем переносить на дискетах). Приходилось записывать какой из файлов или томов архива не скопировался на машину приемник и приехав к компу источнику опять его переписывать. В 1988 ом году на одной из первых ввезённых в СССР IBM XT коллега владевший в отличии от меня английским показал функцию Нортон Командера сравнивающую панели директорий между собой и выделяющих отличия, то есть те файлы которых нет во второй папки. Под это я и написал на своём любимом ассемблере костылик. Моя программа генерировала пустые файлы нулевой длинны по входящему перечню файлов, то есть строго говоря одни имена по списку того что удалось перекопировать на компьютер приемник. В итоге на все доступные дискеты с приемника переписывались эти пустые списки. Придя на компьютер с которого ты копировал исходные файлы ты тупо сравнивал в Нортон Командере папки с исходными файлами компьютера источника с тем перечнем который тебе сгенерировала моя утилита. В итоге у тебя в папке источнике на компьютере источнике тупо выделялось автоматом средствами Нортона то что ранее не удалось перенести на приёмник.
2)Вот многие не помнят раздрай и сыр бор по проблеме 2000. Какая была повсеместная паника и как те же американцы вербовали к себе в америцу для решения проблемы 2000 всех до кого могли дотянуться. Меня в тот период пригласили на семинар который проходил в два дня с участием компаний Microsoft и Novell в налоговой с привлечением представителей всех налоговых инспекций с присутствием зам министра. Контора которая мен туда протащила представляла налоговой проекционный монитор которого у той не было для проведения семинара, ну вот нас в благодарность и позвали тоже на первый день. Когда представитель Майкрософта спрашивал есть ли те кто считает что проблема надуманная поднялось всего две руки одна из которых была моя. Тем не менее. С проблемой 2000 я столкнулся на одном из старых компьютеров несколько позже. БИОС компьютера ничего не знал о следующем веке и тупо пересчитывал дату в начало века перед этим, но дата была верна и время верное. В итоге я написал на том же ассемблере маленький костылик. Логика поведения костылика зависела, как сейчас помню, от его командной строки - если строка была пустая, то программа посылала в консоль enter, а в противном случае пыталась проинтерпретировать цифры как цифры начала века, на всякий случай (а вдруг и ещё через сто лет компьютером будут пользоваться). В итоге у компьютера в autoexec.bat была записана последовательность команд с использованием моего пача которая корректировала дату операционной системы с выводом команды date и перенаправлением ей же исходной даты с подменой века
Ну и возвращаясь к костылям 1С. Давеча разрабатывал довольно таки объёмный запрос, ну как объёмный, всё ж относительно, ну примерно 1200 строк и 48 Кб соответственно. Дело шло туго ибо много всяких промежуточных таблиц и значений в итоге и мозги уже не те и проблемно было в голове всё это ворочать. К слову сказать данный приём/костыль хорош и для работы со всяческими типорвыми и их запросами когда проблемно скормить запрос отладочной команды консоли
НужныеПакеты=Новый Массив;
НужныеПакеты.Добавить(11);
НужныеПакеты.Добавить(12);
НужныеПакеты.Добавить(17);
Для СчПакет=0 По НужныеПакеты.Количество()-1 Цикл
РезультатПакета = МВТ.Таблицы.Получить(НужныеПакеты.Получить(СчПакет)).ПолучитьДанные();
Подстрочник=Новый ТабличныйДокумент;
КолонкаИнд=0;
Линия=Новый Линия(ТипЛинииЯчейкиТабличногоДокумента.Сплошная,2);
Для Каждого Колонка из РезультатПакета.Колонки Цикл
КолонкаИнд=КолонкаИнд+1;
Обл=Подстрочник.Область(1,КолонкаИнд,1,КолонкаИнд);
Обл.Текст=Колонка.Имя;
Обл.Обвести(Линия,Линия,Линия,Линия);
КонецЦикла;
Выборка=РезультатПакета.Выбрать();
Строка=2;
Пока Выборка.Следующий() Цикл
Для Инд=1 по КолонкаИнд Цикл
//Выборка.Получить(Инд);
Обл=Подстрочник.Область(Строка,Инд,Строка,Инд);
Обл.Текст=Выборка[Инд-1];
Обл.Обвести(Линия,Линия,Линия,Линия);
КонецЦикла;
Строка=Строка+1;
КонецЦикла;
Подстрочник.Показать("Подстрочник "+НужныеПакеты.Получить(СчПакет)+" = "+МВТ.Таблицы.Получить(НужныеПакеты.Получить(СчПакет)).ПолноеИмя);
Подстрочник=Неопределено;
КонецЦикла;
ПоказатьТаким образом мы выводим интересующую нас временную таблицу в табличный документ. Далее табличный документ мы можем сохранить в екселевском формате и работать с интересующей нас временной таблицей в екселе. С несложной модификацией данного кода мы получим вариант вывода не временной таблицы в ексель, а скажем подзапроса. Прекрасная альтернатива использованию консоли запросов для просмотра временных таблиц и подзапросов
Приём с выводом временной таблицы по-быстрому в моксель я сделал хз сколько лет назад (и кстати не областями, а через ПостроительОтчета, оно быстрее в разы), но я же этот жуткий костылище не публиковал...
И Ваши публикации, и автора ИМХО "популяризируют" внимательное отношение к инструменту.
Да, согласен с тем, что код на 1С писать так уж и трудно, но более ценное качество это умение за ограниченный срок и бюджет на изначально кривом решении решить поставленную задачу, ничего сильно сразу не поломав и чтобы потом оно работало. И отлично, что у автора есть чувство "что накостылил". Это позволяет двигаться вперед! Все мы иногда "костылим", что уж там!
Но то, что многие не изучают, например, подсистемы и функции БСП, а фигачат свои сомнительные аналоги, не изучают хотя бы типовые решения с их архитектурой, чтобы оттуда что-нибудь скопировать (хотя бы проверенную идею), а также костылят без зазрения совести - это плохой знак!
Не могу спорить на счет каких-то специфических функций, просто нужно знать, что есть готовые простые функции, которые нормально работают. А вот поиск функций в БСП - часто проблема, что-то просто запоминаешь, но если тебе нужно найти какую-то вроде простую ф-ю, порой это очень непросто сделать.
(30)
Верно, методы не из "интерфейса", строго говоря, запрещено использовать.
И это тоже. Ещё момент - если видишь "велосипед" у кого-то, то совсем не факт, что это он его повторно изобрёл, а не в БСП наконец включили аналог :)) как у автора, кстати
А изложенное тут - ну, кое-что платформа уже научилась делать, но так очень многое можно счесть костылями. Сколько ухищрений я делал ради оптимизации запросов динамических списков, которые не понимали составные запросы и временные таблицы - да полно. Сколько чисто архитектурных решений родилось, потому что иначе платформа не позволяла... Пока функций СКД и вызова общих модулей не было... Пока управляемые формы развивались... Да любой наверняка выкручивался как мог. Сейчас это кажется постыдными костылями, но тогда ведь помогло решить задачу, и не всегда было непроизводительным... Чего стыдиться-то? Некоторые вещи в рамках одного события УФ нельзя сделать, приходится запускать одноразовую обработку ожидания на 0.1 секунды, это, к примеру, костыль?)
Стыдиться надо криворуким кодерам, которые простейшие вещи сделать не могут нормально, и авторам типовых, которые собственные стандарты не соблюдают.
1. ДОСовая программа собирала нужные данные и выкладывала в определённую папку с набором данных для каждого отчёта;
2. Были написаны несколько макросов(по количеству отчётов) в MS WORD;
2. На DELPHI была написана прога, висевшая в трее, которая "слушала" этот каталог, если что ))) - запускала WORD с нужным макросом;
В итоге все довольны. Проработал сей костыль исправно до покупки 1С.Бухгалтерия пару лет.
Прям сейчас - задание написать простой для пользователей-администраторов баз и файлов "архиватор-администратор-обновлятор" с максимальными данными на главной форме для оперативного принятия решений с минимальным количеством кликов. Вот уж пришлось костылей нагородить от души )))) Ни одна существующая прога им не подошла. Просили попроще и чтобы всё было максимально автоматизировано(больше интеллекта). Написал КАК ХОТЕЛИ пользователи ))))
из доса печатали в файлы, а другая прога отправляла все на новехонький лазерный притер
а вот чем все закончилось не знаю. может до сих пор пользуют ))
такие дела
"5.3.1. Временные таблицы в динамических списках рекомендуется использовать только тогда, когда они содержат заведомо небольшое количество записей. Иначе их использование неэффективно, т.к. значения временных таблиц в динамическом списке НЕ кешируются, а формируется при каждом считывании данных для заполнения списка."
Пример такого безобразия - управление холдингом , журнал заявок на расход. с добавленными к нему колонками дополнительной инфы собранной по оплатам. В стандарте худо бедно еле еле работает, а как добавишь инфы -дохнет. на 20000 заявок уже прикидывается трупиком.
Компания с территориально удалёнными розничными точками. Нетиповая конфигурация на обычном приложении. При открытии очередного магазина обнаружилось, что чек на ккм Печатается неприемлемо долго. На исправление ситуации была ночь до открытия. В итоге: толстый клиент запускается в сервисе терминалов. При необходимости печати чека, нужные данные формируются в текстовый файл, который кладётся в специальную папку на клиентской машине. На клиентской машине запускается самописная конфигурация, которая умеет работать с ккм. Она периодически смотрит папку на наличие файла, и если находит, запускает процедуру печати. Данные файла сохраняет в себя, сам файл после этого удаляется.
Это уже костылил сам
Та же компания, та же конфигурация. Удалённые магазины работают через сервер терминалов. Возникла задача заполнять накладные из ТСД, через атоловский драйвер. Причём взаимодействие с ТСД должно быть по сети. В документации к драйверу жирным по белому написано, что использовать на сервере терминалов не рекомендуется. Но деваться опять было некуда. Для работы с ТСД драйвер стартует службу, которая уже общается непосредственно с аппаратом. Соответственно схема выгрузки выглядела так. Толстый клиент 1С -> Служба Драйвера -> ТСД. Причём служба драйвера стартует под каждым пользователем, начавшим сеанс в машине. На сервере терминалов привело к эффекту, что запрос на получение данных отправлял один экземпляр службы, а получить мог совсем другой. Тогда закостылил батник, который с правами администратора убивал все процессы службы драйвера, кроме того, что запустился от текущего пользователя. Выгрузка должна была пройти, пока процессы не перезапустились. Так работало пока пользователей, которые работали с выгрузкой, не отселили на персональные виртуалки.
Один из последних костылей это "квазиасинхронная" свертка/развертка до нужного уровня дерева значений на управляемой форме без блокирования работы пользователя и выводом индикатора прогресса))
Костыль из необычного если так можно назвать делал такой:
Задача сделать справочник цветов (в RGB), он по большей части статический (pantone можете посмотреть что такое), но цветов там прям много, порядка 10тыс! Самое главное что в форме выбора в динамическом списке должна была показываться колонка с визуальным отображением этого цвета (реального цвета). В итоге решил через библиотеку картинок, сделал обработку которая по заполненному справочнику цветов, брала RGB каждого цвета, рисовала картинку 16х16, далее следующий элемент, опять картинку 16х16, далее склеивала эти тысячи картинок в одну огромную картинку 16хNNNNNNN, индекс картинки соответствовал коду элемента справочника цветов, далее картинка загружалась в библиотеку картинок, в форме выбора вывести картинку по индексу - штатный механизм.
Еще один костыль, то же картинки в динамическом списке, только на этот раз это прикрепленные картинки к элементам справочника. Когда пользователь открывает справочник (это справочник всякой фурнитуры для одежды), он должен увидеть название (код) фурнитуры и ее картинку в отдельной колонке, задача что бы получить сразу все картинки. Оказалось что достаточно в колонку дин списка передавать навигационную ссылку, и все выводит. В итоге написаная процедура по массовому получению навигационных ссылок картинок всего справочника, которая записывала их во временный кэш в регистре, а дин список уже просто выгребал данные из регистра кэша. На удивление работало довольно шустро )
Тогда вообще можно не вклинивать в транзакцию запрос, прочитав набор для движения в процедуре "ПередЗаписью" (проверив что РежимЗаписи = Проведение) и сохранив его в дополнительные свойства Объекта.
СхемаКомпоновкиДанных = элементы.Список.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
НастройкиКомпоновкиДанных = Элементы.Список.ПолучитьИсполняемыеНастройкиКомпоновкиДанных();
КомпановщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпановщикМакета.Выполнить(СхемаКомпоновкиДанных,НастройкиКомпоновкиДанных,,,Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
ТаблицаЗначений = Новый ТаблицаЗначений;
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаЗначений);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Итого = ТаблицаЗначений.Итог("ИтоговыйСтолбец"); Показатьи выводим в реквизит формы, а реквизит в путьКПодвалу
"ИЛИ" в запросе, действительно тормозит выполнение. 1с предлагает через ОБЪЕДИНИТЬ. Хотя надо заметить в типовых сплошь и рядом используется "ИЛИ".
Выбрала другой вариант. Вместо
Условие1 или Условие2
Делаю
Выбрать
Когда Условие1 Тогда Истина
Когда Условие2 Тогда Истина
Иначе Ложь
Конец :)
Оператор "ИЛИ" не всегда тормозит выполнение запроса.
Для условий одного типа данных оптимизатор понимает.
Напротив, конструкция "Выбрать" в условии соединения всегда провоцирует Nested Loops Join,
что на больших таблицах ощутимо медленнее, чем MERGE JOIN или HASH JOIN.
В условии "ГДЕ" конструкция "Выбрать" всегда провоцирует Table Scan.
Проверьте еще раз свои расчеты ?
&НаСервереБезКонтекста
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
ХранилищеСистемныхНастроек.Сохранить("Документ.ФормаСписка", "Изменение", Истина);
КонецПроцедурыПри активизации строки считываем флаг изменения, выполняем необходимые действия, затем сбрасываем флаг. Можно дополнительно настроить ПодключитьОбработчикОжидания() для формы
Процедура СписокПриАктивизацииСтроки(Элемент)
Изменение = ВосстановитьЗначениеСервер("Документ.ФормаСписка", "Изменение");
Если Изменение Тогда
// Необходимые вычисления, например получаем количество записей
//в динамическом списке и подставляем его в итоги
ПоместитьВХранилище("Документ.ФормаСписка", "Изменение", Ложь);
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ПоместитьВХранилище(ИмяОбъект, ИмяПараметр, Значение)
ХранилищеСистемныхНастроек.Сохранить(ИмяОбъект, ИмяПараметр, Значение);
КонецПроцедуры ПоказатьИтоги в динамическом спискеТакое есть в бп в банковских выписках. МОжет реализовано не совсем так, но суть такая. Правда там суммы за день по контрагенту и организации
Проверка наличия свойстване знаю как давно, но в бсп давно есть методы для проверки. При чем текст будто от туда скопирован
Чтение наборов записей запросомВ реализации даже в тексте костыль. Давно есть Метаданные().ПолноеИмя()
Для меня самый большой костыль это когда я нашел возможность дорабатывать формы в расширении без захвата самой формы. С тех пор стал везде делать такой механизм. Удобно. Все в 1 месте. Получилось даже назначать обработчики событий на элементы, правда там есть нюанс, но если он не нужен то можно на почти на все. В целом механизм максимально простой, но вот новичок прорастет разбираться
А так костыли не делаю. Рано или поздно понадобится туда залезть и допилить и тогда начнутся большие проблемы
Вы можете заказать платную консультацию или разработку у автора. Будет создан приватный заказ на «Бирже заказов» для автора.
№ 1100169
Создание 03.09.19 08:00
Обновление 03.09.19 08:00
Просмотры 41172
Загрузки 0
Рейтинг
193
Комментарии 83
Код открыт Да
Рубрики Механизмы платформы 1С
Кому Разработчик
Тип файла Нет файла
Платформа 1С:Предприятие 8
Конфигурация Универсальные
Операционная система Не имеет значения
Страна Не имеет значения
Отрасль Не имеет значения
Налоги Не имеет значения
Вид учета Не имеет значения
Доступ к файлу Бесплатно (free)