Немного полезного кода для программистов 1С

Программирование - Инструментарий

У каждого программиста за время работы накапливается полезный инструментарий, которым он привык пользоваться. Естественно и у меня он тоже имеется. И вот решено было немного поделиться с сообществом. Возможно идеи не новые. Более того, допускаю, что реализованы они не самым оптимальным образом. Но ведь для этого сообщество и существует, чтобы делиться с ним, получая обратную связь.

У каждого программиста за время работы накапливается полезный инструментарий, которым он привык пользоваться. Естественно и у меня он тоже имеется. И вот решено было немного поделиться с сообществом. Возможно идеи не новые. Более того, допускаю, что реализованы они не самым оптимальным образом. Но ведь для этого сообщество и существует, чтобы делиться с ним, получая обратную связь.

Содержание

Управление видимостью, доступностью и просмотром реквизитов формы

Преамбула. Еще в бытность работы в 7.7, а далее и в первых конфигурациях на 8.х совершенно точно не нравились процедуры и функции вроде "УстановитьВидимость" или "УстановитьДоступность" и т.п. В них собирались все элементы, по различным условиям устанавливалась видимость. Как правило в них условия выставлялись по бизнес-логике редактирования документа, например проверка по видам операций, различным условиям заполненности и т.д. В итоге один и тот же элемент мог встречаться несколько раз и конечные условия "почему он виден или доступен" приходилось вникать во всю логику. Но не сразу пришло понимание, как должно быть, как будет удобно. Были различные варианты. Но вот некоторое время назад, мы совместно с нашей командой остановились на представленном ниже варианте.

Существует основная процедура "УстановитьУсловноеОформление" (неудачное имя для метода, понимаю, но увы уже много кода "наделано" руки не дойдут изменить его везде). В качестве параметров метода выступает: ЭтотОбъект - т.е. сама управляемая форма в которой метод вызывается и ИменаРеквизитов - список имен (не обязательно) через "," для которых необходимо выполнить настройку видимости, доступности, просмотра или других свойств. При этом, есть возможность как создать произвольный набор элементов, так и не передавать список вовсе. В таком случае работать будет следующим образом:

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

    Если НЕ Обработано.Найти(ИмяРеквизита) = Неопределено Тогда
        Возврат;
    КонецЕсли;
    Обработано.Добавить(ИмяРеквизита);

    Элементы    = Форма.Элементы;
    Объект        = Форма.Объект;

    #Область Наборы
    
    Если ИмяРеквизита = "Реквизиты" ИЛИ ПустаяСтрока(ИмяРеквизита) Тогда
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, "");
    КонецЕсли;

    #КонецОбласти
    
    #Область Элементы
    
    Если ИмяРеквизита = "Ответственный" ИЛИ ПустаяСтрока(ИмяРеквизита) Тогда
        ОбщегоНазначенияКлиентСервер.УстановитьСвойствоЭлементаФормы(Элементы,
            "Ответственный", "ТолькоПросмотр", ЗначениеЗаполнено(Объект.Ответственный));
    КонецЕсли;

    #КонецОбласти 
    
    #Область ТабЧасть_Имя
    
    Если ИмяРеквизита = "ИмяТабличнойЧастиОтветственный" ИЛИ ПустаяСтрока(ИмяРеквизита) Тогда
        ОбщегоНазначенияКлиентСервер.УстановитьСвойствоЭлементаФормы(Элементы,
            "ИмяТабличнойЧастиОтветственный", "ТолькоПросмотр", ЗначениеЗаполнено(Объект.Ответственный));
    КонецЕсли;

    #КонецОбласти
    
    #Область Команды
    
    Если ИмяРеквизита = "КомандаЗаполнить" ИЛИ ПустаяСтрока(ИмяРеквизита) Тогда
        ОбщегоНазначенияКлиентСервер.УстановитьСвойствоЭлементаФормы(Элементы,
            "ТаблицаФормыЗаполнить", "Видимость", НЕ Объект.Проведен);
    КонецЕсли;

    #КонецОбласти 
    
КонецПроцедуры

&НаКлиентеНаСервереБезКонтекста
Процедура УстановитьУсловноеОформление(Форма, ИменаРеквизитов = "")

    Если ТипЗнч(ИменаРеквизитов) = Тип("Строка") Тогда
        Если ПустаяСтрока(ИменаРеквизитов) Тогда
            МассивИмен = Новый Массив;
            МассивИмен.Добавить("");
        Иначе
            МассивИмен = СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(ИменаРеквизитов, ",");
        КонецЕсли;
    ИначеЕсли ТипЗнч(ИменаРеквизитов) = Тип("Массив") Тогда
        МассивИмен = ОбщегоНазначенияКлиентСервер.СкопироватьМассив(ИменаРеквизитов);
    Иначе
        Возврат;
    КонецЕсли;
 
    //Форма.ТолькоПросмотр = (Форма.СостоянияЗаблокировано.Найти(Форма.СведенияОЗаявкеСостояние) <> Неопределено);

    Обработано = Новый Массив;
    Для Каждого ИмяРеквизита Из МассивИмен Цикл
        УстановитьУсловноеОформлениеРеквизита(Форма, Обработано, СокрЛП(ИмяРеквизита));
    КонецЦикла;

КонецПроцедуры

// как использовать
УстановитьУсловноеОформление(ЭтотОбъект, "ИмяРеквизита");

Преимущества: по каждому элементу формы можно определить логику его настройки, при этом не вникая во все зависимости.
Недостатки: большое количество кода, если настраиваемых элементов формы много.
Итог: по последним наблюдениям, ошибок с настройкой вида форм стало значительно меньше. Программист, не владеющий достаточно логикой работы формы, тем не менее способен внести изменения согласно заданию аналитической службы. При этом, даже не программист способен понять каким образом выполнена настройка отдельно взятого элемента формы.

Проверка изменений значений реквизитов формы

Преамбула. Регулярно при изменении значений реквизитов формы необходимо выполнять те или иные действия. Часто приходится с клиента вызывать сервер. В таком случае, необходимо минимизировать вызовы. Да и вообще иногда надо проверить какое значение имел реквизит, чтобы выдать предупреждение о выборе некорретного значения и вернуть старое значение. Обычно для таких целей создают реквизиты формы куда записывают текущее значение и "ПриИзменении" проверяют выбранное значение и текущее, выполняют обработку или возвращают старое значение. Меня лично это крайне напрягает, более того когда надо проверку повесть на множество реквизитов, тогда набор реквизитов формы становится невыносим большим. А чем больше реквизитов, тем больше вероятность ошибки в будущем.

В итоге нами был реализован ряд методов, которые располагаются в форме + методы для общих модулей. При инициализации формы программа проверяет наличие служебного реквизита, создает его и хранит в нем текущие значения реквизитов, указанных пользователем. При изменении в форме, вызывается небольшой набор кода: проверка выбранного значения и возврат если значение не изменилось; обработка каких либо действий; вызов повторного копирования данных формы.

В примере приведен только код для формы, все общие модули в прикрепленном файле.

// Описание использования
//
//    1. Разместить команды из процедуры ИнициализацияФормы в соответствующую по смыслу процедуру формы (или вызвать метод из ПриСозданииНаСервере, ПриЧтенииНаСервере)
//    2. Добавить все сохраняемые реквизиты в процедуре СнятьКопиюОбъекта
//    3. Назначить обработчики ПриИзменении на сохраняемые реквизиты (см. ИмяРеквизитаПриИзменении)
//

&НаСервере
Процедура ИнициализацияФормы()
    
    РаботаСФормами.СоздатьРеквизитХраненияКопииДанныхФормы(ЭтаФорма);
    
    // прочие обработки
    <?>
    
    СнятьКопиюОбъекта(ЭтаФорма);
    
КонецПроцедуры

&НаКлиенте
Процедура ИмяРеквизитаПриИзменении(Элемент)
    Если СравнитьСКопиейОбъекта(ЭтаФорма, "Объект.ИмяРеквизита") Тогда
        Возврат;
    КонецЕсли;
    
    // прочие обработки
    
    СнятьКопиюОбъекта(ЭтаФорма);
КонецПроцедуры

#Область СлужебныеПроцедурыИФункции_КопияДанныхФормы

&НаКлиентеНаСервереБезКонтекста 
Процедура СнятьКопиюОбъекта(Форма)
    МассивРеквизитов = Новый Массив;
    МассивРеквизитов.Добавить("Объект.Дата");
    МассивРеквизитов.Добавить("Объект.Организация");
    МассивРеквизитов.Добавить("ИмяРеквизита");
    
    РаботаСФормамиКлиентСервер.СкопироватьДанныеФормы(Форма, МассивРеквизитов);
КонецПроцедуры

&НаКлиентеНаСервереБезКонтекста 
Функция СравнитьСКопиейОбъекта(Форма, ИмяРеквизита)
    Возврат РаботаСФормамиКлиентСервер.СравнитьСКопиейДанныхФормы(Форма, ИмяРеквизита);
КонецФункции

&НаКлиентеНаСервереБезКонтекста 
Функция ЗначениеИзКопииОбъекта(Форма, ИмяРеквизита)
    Возврат РаботаСФормамиКлиентСервер.ЗначениеИзКопииДанныхФормы(Форма, ИмяРеквизита);
КонецФункции

#КонецОбласти

Преимущества: нет необходимости создавать множество реквизитов в форме для хранения старых данных; возможность хранить копии значений не только реквизитов "Объекта" (основного реквизита), но и реквизитов формы; возможность расширения механизма под нужды программиста.
Недостатки: отсутствие возможности хранить значения реквизитов таб. частей; дублирование кода в форме.
Итог: легко проверить изменился ли реквизит; легко вернуть значение назад.

Послесловие

На самом деле оригинального и сверхумного ничего в представленном коде нет, да и быть не может. Что придумал один человек, второй всегда повторит. Я с удовольствием выслушаю критику и внесу изменения. Надеюсь, код подкажется полезным кому-либо. И кстати, может уже кто-то трудится над созданием репозитория с "полезным" кодом? Используйте на здоровье, модифицируйте и т.д.
Версионирование данных инструментов на текущий момент не ведется. Пока не вижу смысла. Жизнь покажет.
На картинке изображен детский набор инструментов "Fisher-Price Disney's Handy Manny Talking Tool Box". Вдруг кому интересно )))).

Скачать файлы

Наименование Файл Версия Размер
Шаблон + общие модуля
.zip 4,42Kb
24.09.17
4
.zip 4,42Kb 4 Скачать

См. также

Комментарии
1. p m (pm74) 34 24.09.17 21:43 Сейчас в теме
небольшая "подстава" от 1с , при программном изменении значений реквизитов в форме событие "приизменении" не вызывается
healplease; Светлый ум; +2 3 Ответить
2. Александр Анисков (vandalsvq) 789 24.09.17 23:15 Сейчас в теме
(1) ну это понятно, в таком случае обработка изменения уже забота программиста
Dementor; +1 Ответить
3. p m (pm74) 34 25.09.17 07:18 Сейчас в теме
(2) я просто имел в виду , было бы неплохо , если бы 1с случае любого локального события формы , генерировало какое то событие изменения данных формы которое можно было отловить
rpgshnik; +1 Ответить
4. Максим Б (Xershi) 280 25.09.17 07:40 Сейчас в теме
(3) Флаг модифицированность для этого!
5. p m (pm74) 34 25.09.17 07:50 Сейчас в теме
(4) и ? в какой момент его считывать ?
rpgshnik; +1 Ответить
9. Максим Б (Xershi) 280 25.09.17 09:30 Сейчас в теме
(5) какой алгоритм заложите такой и будет!
6. Oleg Space (spacecraft) 25.09.17 07:54 Сейчас в теме
(3) какое такое событие изменения реквизитов формы нужно отдельно отлавливать? Если программно изменяете реквизит, то и программно нужно запустить нужные обработчики, при необходимости.
Dementor; +1 Ответить
7. p m (pm74) 34 25.09.17 08:04 Сейчас в теме
(6) автор правильно указал на проблему , если вы меняете реквизит у вас нет старого значения реквизита ,даже если он фактически не изменился генерируется событие при изменении
8. Oleg Space (spacecraft) 25.09.17 08:07 Сейчас в теме
(7) не ответили на вопрос. Какое событие нужно отлавливать отдельно? Если меняете программно реквизит, значит можете предусмотреть обработку логики изменения этого реквизита.
16. Mike (air_mike) 25 27.09.17 08:59 Сейчас в теме
(7) Старое значение не куда не денется из базы, пока вы его не записали в базу. Согласен с (6).
В УФ специально убрали процедуры ПриВыводеСтроки и ОбновлениеОтображение, т.к. по уму они не нужны и приводят только к тормозам.
15. Александр Жиличев (alexzhilichev) 141 27.09.17 04:57 Сейчас в теме
(3) Как раз наоборот это правильное поведение. Не зря ПриОткрытии(), ПриИзменении() и т.п. называются обработчиками событий.
10. Александр Цуканов (tsukanov) 37 25.09.17 13:49 Сейчас в теме
(1) Попробуйте это:
Процедура УстановитьЗначениеИнтерактивно(Значение, Элемент, Форма)
	Перем ВладелецФормы, ЗакрыватьПриВыборе;

	ВладелецФормы = Форма.ВладелецФормы;
	ЗакрыватьПриВыборе = Форма.ЗакрыватьПриВыборе;
	
	Форма.ВладелецФормы = Элемент;
	Форма.ЗакрыватьПриВыборе = Ложь;
	
	Форма.ОповеститьОВыборе(Значение);
	
	Если Форма.ВладелецФормы = Элемент Тогда
		Форма.ВладелецФормы = ВладелецФормы;
	КонецЕсли;
	
	Если Не Форма.ЗакрыватьПриВыборе Тогда
		Форма.ЗакрыватьПриВыборе = ЗакрыватьПриВыборе;
	КонецЕсли;  
	
КонецПроцедуры // УстановитьЗначениеИнтерактивно()
Показать


ps Написал по памяти. Не проверял. Мог косякнуть.
pps Это типа универсальная общая процедура. Подходит не для всего конечно
11. Maxim Kolkin (the1) 284 26.09.17 14:31 Сейчас в теме
(10) Что за удивительная абстрактная фигня?
12. Александр Цуканов (tsukanov) 37 26.09.17 14:47 Сейчас в теме
(11) Эта фигня делает то, что хочет автор первого коммента. Я ответил на ваш вопрос?
13. Сергей Старых (tormozit) 4485 26.09.17 15:03 Сейчас в теме
(10) Лучше брать эту процедуру из модуля ирОбщий.ИнтерактивноЗаписатьВЭлементУправленияЛкс инструментов разработчика. https://infostart.ru/public/16985/
user600603_v.soldatova; +1 Ответить
14. Александр Цуканов (tsukanov) 37 26.09.17 15:10 Сейчас в теме
17. Сергей Смирнов (protexprotex) 135 27.09.17 12:11 Сейчас в теме
Было бы полезным если бы фирма 1С сделала как в C++ (например как в C++) - отделила бы определение функции от самой функции. Т.е. типа h и cpp файлов. По моему, удобно было бы.
18. Владимир Безфамильный (Vovan1975) 14 27.09.17 12:19 Сейчас в теме
(17) ну ну. Удобно ога. Добавил параметр в процедуру и незабудь еще в объявлении его зафигачить.
Было это на 7.7, никакого удобства нет
20. Сергей Смирнов (protexprotex) 135 27.09.17 12:26 Сейчас в теме
(18) На 7.7. это было сделано через клавиатуру :-). h - файлы нужны для экспортируемых функций - т.е. можно было хранить объявления функций в одном файле, а сами функции - в другом. И к своему проекту подсоединять только h - файлы - и если Вы используете функцию, то она только и компилится в проекте -> проект меньше и работает быстрее. А 1С при открытии модуля все это хозяйство переводит в байт - код -> вывод - ускорение было бы работы 1С.
19. Anatolii Karasev (KapasMordorov) 408 27.09.17 12:20 Сейчас в теме
21. Дмитрий К. (Dementor) 9 28.09.17 15:27 Сейчас в теме
(17)
Т.е. типа h и cpp файлов. По моему, удобно было бы.

Ага, так это удобно, что мои знакомые программисты сишники на небольших проектах все фигачат сразу в *.hpp файлы :)


(20)
А 1С при открытии модуля все это хозяйство переводит в байт - код -> вывод - ускорение было бы работы 1С.

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

Я тут на днях решил таки легендарного Брукса почитать. Так он в своей статье про использование современных ЯП (на те годы это были APL, Algol, Fortran и прочие) говорит, что не смотря на некоторое увеличение времени на компиляцию все же, в общем итоге, выгодно использовать ЯП высокого уровня, так как это в разы экономит программистам их время, уменьшает затраты на разработку, а оптимизацию выполнения может на себя взять компилятор. Это я к чему вспомнил? Главное удобство программиста при разработке, а в продакшине (сервер или файловая уже не важно) все равно будет крутится закешированный байт-код.
22. Сергей Смирнов (protexprotex) 135 28.09.17 15:36 Сейчас в теме
(21)
Это не просто удобно. Это нужно. Т.к. если Вы захотите импортировать в свой проект чужие наработки, то hpp файлы не импортируют, а импортируют именно h - файлы - заголовки->lib - ы и т.д. А то что знакомые сишники все hpp файлы "фигачат", так это не относится к языку программирования. Т.к. если они пишут проекты типа Hello World - то да - тут не спорю. А если это распределенный проект между командой разработчиков, то руководитель проекта вас за такие вот вольности по головке не погладит. Но к 1С это все равно не относится.
24. Дмитрий К. (Dementor) 9 28.09.17 16:43 Сейчас в теме
(22)
Т.к. если они пишут проекты типа Hello World - то да - тут не спорю. А если это распределенный проект между командой разработчиков, то руководитель проекта вас за такие вот вольности по головке не погладит.

Нет, они писали более сложные программы - решение транспортных задач для разнородного транспортного парка с десятками ограничений и графом дорог, который являлся функцией от самих машин (тонажа, наличия прицепов, купленных разрешений на проезды в закрытые части городов) и от времени (часто грузовики в города пускают только ночью, а днем можно пикапиками и газельками возить, а про работу мостов по часам я уже вообще молчу - не успел проскочить на второй берег, уже считай полдня потерял), а так же программы прогнозирования спроса и прочие математические модели.
Нет, у руководителя проекта никаких возражений не было. Общие *.hpp для разных проектов отлично компилировались в промежуточные *.a файлы, которые далее уже больше не пересобирались без надобности и использовались в линковке различных бинарников. Вся магия в правильно настроенных мейк-файлах.
Да, к 1С не относится и слава Богу :)
27. eugenie zheludkov (eugeniezheludkov) 32 29.09.17 09:27 Сейчас в теме
(22) вот я понимаю зачем нужен интерфейс в ООП языках типа ява, шарп, но вот зачем нужно дублировать информацию при помощи .h не понимаю! пока разрабатываешь без ТЗ, сигнатуру метода класса перепишешь миллион раз и каждый раз нужно лезть в этот .h и переписывать его там причем не так как в cpp файле, честно - это ужасно.
конечно тут все дело в волшебных ИДЕ которые должны сами лезть и исправлять все в .h , но например ArduinoIDE, vi , nano к таким не относится (((
28. Сергей Смирнов (protexprotex) 135 29.09.17 09:37 Сейчас в теме
(27) h - файлы нужны для линкования ваших разработок со сторонними проектами. Делаете include "My.h" и все. И это определение делаете во всех модулях в которых используется функции из My.cpp. И линковщик соберет все. А если без h - файла, то cpp - файл Вы не сможете объявить во всех модулях. Так устроено.
23. Сергей Смирнов (protexprotex) 135 28.09.17 15:36 Сейчас в теме
(21) А по поводу ошибок - там синтаксический контроль есть. Он об таких ошибках сообщит.
25. Дмитрий К. (Dementor) 9 28.09.17 16:48 Сейчас в теме
(23) Есть такой. Помню как-то запустил синтаксический контроль по типовой торговле - насыпалась куча ошибок про отсутствие назначенных обработчиков и прочая мелочь. А ведь это писала "элита 1С" :) Рассматривайте визуальную часть формы как заголовочный файл, где в событиях прописаны названия функций и предопределен состав реквизитов. Вот как сейчас платформа позволяет делать ошибки, так и с внедрение еще одного промежуточного слоя ничего не изменится!
26. Сергей Смирнов (protexprotex) 135 28.09.17 17:17 Сейчас в теме
(25) Ну, может быть. В общем, для 1С (пока) это не надо. Но именно создатель С++ очень трепетно относился к такому разделению :-)
Оставьте свое сообщение