В начале 2014 г. на моем новом рабочем месте произошел программный "переворот" - предприятие перешло с самописной ACCESS'подобной программы на 1С "Управление торговлей" 11 (собственно для этого и взяли в штат меня). Актуальный релиз на тот момент был 11.1.2.х (не помню точно какой), который, несмотря на довольно серьезные доработки, примерно через полгода работы удалось обновить до версии 11.1.2.31, в коем виде программа и остается до сих пор. В связи с тем, что помимо основного предприятия необходимо внедрять (т.е. переводить с самописной на 1С) то же самое и в многочисленных филиалах (в декабре этого года наконец-то "осчастливим" последний филиал), времени и свободных человеческих ресурсов на регулярные обновления просто не хватало. Однако 1С не стоит на месте, в новых релизах исправлены баги (например, с закрытием месяца, которым у нас пользуются очень активно), сделаны серьезные и интересные доработки (резервирование товаров, напр.), потому идея обновления до актуального релиза постоянно витала в воздухе.
Такова краткая предыстория. Естественно, сразу встал вопрос о том, чтобы упростить дальнейшее обновление, т.е. постараться перенести дописанный код в максимально "независимые" от обновлений места. Про подписки на события при записи, проведении, заполнении объектов и т.д. писать не буду, меня интересуют доработки именно форм (справочников, документов...), поскольку именно их сложнее всего править независимо. На момент начала процесса обновления актуальным релизом являлся 11.1.10.153, потому о нем и речь.
Итак, в релизе 11.1.10 программистами 1С создан механизм, который позволяет нам существенно упростить доработки форм. А именно: во многих модулях форм (а может и во всех - не проверял) в процедурах "ПриСозданииНаСервере", "ПриЧтенииНаСервере", "ПередЗаписьюНаСервере" и т.д. добавлены вызовы общих модулей, ответственных за упрощение доработки конфигурации:
Поскольку эти процедуры с суффиксом "Переопределяемый", в них мы можем дописывать свой функционал, не боясь "перетирания" кода при обновлении релизов.
Начнем по порядку, с какими задачами пришлось столкнуться, и как их удалось решить с наименьшими потерями.
В основном пришлось внести изменения в модуль "МодификацияКонфигурацииПереопределяемый", поэтому по тексту, если нет оговорок, следует понимать именно этот общий модуль.
1. Замена стандартного реквизита на свой.
Пример - в нашей фирме было принято решение отказаться от сложного механизма перемещения товара между организациями, поскольку, если честно, мы просто испугались, что за этим последует: постоянно отслеживать остатки товаров в наличии в той или иной организации, делать какие-то мудреные перемещения товаров между ними и как весь этот "геморрой" отразится на работу Закрытия месяца... В итоге мы (отдел ИТ) решили по-другому эту проблему (не скажу, что было просто, но это уже другая тема). Мы добавили в документы еще один реквизит "ОрганизацияДляПечати", а на форме документов и списков подменили им старый реквизит "Организация".
Решение. В модуле "МодификацияКонфигурацииПереопределяемый" создаем процедуру замены реквизита "Организация" (поскольку она используется во многих документах):
Процедура ПереопределитьОрганизацию(Форма)
//Форма.Элементы.Организация.ПутьКДанным = "ОрганизацияДляПечати"; // не работает
Форма.Элементы.Организация.Видимость = Ложь;
Элемент = ДобавитьРеквизит(Форма, "ОрганизацияДляПечати", Форма.Элементы.Организация.Родитель.Имя, "Организация", "Организация", "Объект.ОрганизацияДляПечати");
Элемент.ПоложениеЗаголовка = ПоложениеЗаголовкаЭлементаФормы.Авто;
Элемент.Ширина = Форма.Элементы.Организация.Ширина;
Элемент.РастягиватьПоГоризонтали = Форма.Элементы.Организация.РастягиватьПоГоризонтали;
КонецПроцедуры
в закомментированной строке указан первый пришедший в голову метод решения проблемы, однако он не сработал, потому пришлось сделать чуть "по-сложнее" - делаем невидимым стандартный реквизит, добавляем наш, а параметры отображения берем из свойств "старого" реквизита.
Процедура создания реквизита на форме также вынесена в отдельную функцию:
Функция ДобавитьРеквизит(Форма, Имя, Родитель=Неопределено, ВставитьПеред, Заголовок, ПутьКДанным)
Если Родитель = Неопределено Тогда
Элемент = Форма.Элементы.Вставить(Имя, Тип("ПолеФормы"), Форма, Форма.Элементы[ВставитьПеред]);
Иначе
Элемент = Форма.Элементы.Вставить(Имя, Тип("ПолеФормы"), Форма.Элементы[Родитель], Форма.Элементы[ВставитьПеред]);
КонецЕсли;
Элемент.Вид = ВидПоляФормы.ПолеВвода;
Элемент.Заголовок = Заголовок;
Элемент.ПутьКДанным = ПутьКДанным;
Элемент.ПоложениеЗаголовка = ПоложениеЗаголовкаЭлементаФормы.Авто;
Элемент.Видимость = Истина;
Элемент.Доступность = Истина;
Элемент.ТолькоПросмотр = Ложь;
Элемент.КнопкаСпискаВыбора = Неопределено;
Элемент.КнопкаВыбора = Неопределено;
Элемент.КнопкаОчистки = Неопределено;
Элемент.КнопкаРегулирования = Неопределено;
Элемент.КнопкаОткрытия = Неопределено;
Элемент.РежимВыбораИзСписка = Ложь;
Элемент.ЦветТекста = Новый Цвет(); //"Авто";
Элемент.ГоризонтальноеПоложение = ГоризонтальноеПоложениеЭлемента.Авто;
Элемент.Ширина = 0;
Возврат Элемент;
КонецФункции
Смысл действий, думаю, понятен - создается реквизит формы, связывается с реквизитом объекта, устанавливаются "координаты" положения (перед каким реквизитом) и параметры самого реквизита по-умолчанию. Соответственно, в процедуре "ПриСозданииНаСервере" (того же модуля, а не модуля формы документа!) пишем примерно так:
#Область ВводОстатков
// ВВОД ОСТАТКОВ
Если Форма.ИмяФормы = "Документ.ВводОстатков.Форма.ФормаРасчетыСПартнерами" Тогда
ПереопределитьОрганизацию(Форма);
КонецЕсли;
#КонецОбласти
Готово, новый реквизит теперь для пользователя неотличим от старого, однако программно несет совсем другой смысл.
2. Добавление реквизитов формы (НЕ реквизитов объекта)
Задача возникает тогда, когда мы хотим хранить какие-то "глобальные" для формы переменные в контексте самой формы, или, к примеру, добавить в шапку формы или ее табличную часть динамически обновляемые поля. Например:
Реквизиты = Новый Массив;
Реквизиты.Добавить(Новый РеквизитФормы("Рентабельность", Новый ОписаниеТипов("Число"), "Объект.Товары", "% рент.", Истина));
Реквизиты.Добавить(Новый РеквизитФормы("мВидЦеныПС", Новый ОписаниеТипов("СправочникСсылка.ВидыЦен"), "", "ВидЦеныПС", Истина));
Реквизиты.Добавить(Новый РеквизитФормы("МинимальныйПроцентНаценкиПродажи", Новый ОписаниеТипов("Число"), "", "МинимальныйПроцентНаценкиПродажи", Истина));
Реквизиты.Добавить(Новый РеквизитФормы("СвойствоОрганизация", Новый ОписаниеТипов("ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения"), "", "СвойствоОрганизация", Истина));
Реквизиты.Добавить(Новый РеквизитФормы("КартинкаСостоянияДокумента", Новый ОписаниеТипов("Число"), "", "КартинкаСостоянияДокумента", Истина));
Форма.ИзменитьРеквизиты(Реквизиты);
в ТЧ Товары добавлено поле Рентабельность (рассчитывается динамически), а на самой форме (пустой 3-й параметр метода Новый РеквизитФормы()) несколько переменных, инициализируемых при создании документа и используемых в дальнейших расчетах плюс реквизит для отображения картинки. Метод формы "ИзменитьРеквизиты()" размещает массив реквизитов на форме.
Далее инициализируем наши переменные:
Форма.мВидЦеныПС = Справочники.ВидыЦен.ПлановаяСебестоимость;
Форма.МинимальныйПроцентНаценкиПродажи = Константы.МинимальныйПроцентНаценкиПродажи.Получить();
Форма.СвойствоОрганизация = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Организация (Клиенты)");
поскольку мы находимся на стороне сервера, то можем так сделать.
3. Добавление картинки на форме
Выше мы уже добавили числовой реквизит "КартинкаСостоянияДокумента", в котором будет храниться номер нашей картинки (чтобы видеть в форме документа его состояние - проведен, не проведен, помечен на удаление). Теперь нам надо сделать следующее:
// Добавляем картинку состояния документа
ДобавитьКартинкуСостоянияДокумента(Форма, "ДокументОснование");
// Обновляем изображение
Альянс.ОбновитьКартинкуПроведенияДокумента(Форма.Объект, Форма.КартинкаСостоянияДокумента);
Сперва добавим на форму реквизит вида "ПолеКартинки" (используя стандартную картинку из Библиотеки картинок):
Процедура ДобавитьКартинкуСостоянияДокумента(Форма, Перед, Родитель=Неопределено)
Если Родитель=Неопределено Тогда
Элемент = Форма.Элементы.Вставить("КартинкаСостоянияДокумента", Тип("ПолеФормы"), , Форма.Элементы[Перед]);
Иначе
Элемент = Форма.Элементы.Вставить("КартинкаСостоянияДокумента", Тип("ПолеФормы"), Форма.Элементы[Родитель], Форма.Элементы[Перед]);
КонецЕсли;
Элемент.Вид = ВидПоляФормы.ПолеКартинки;
Элемент.ПутьКДанным = "КартинкаСостоянияДокумента";
Элемент.ПоложениеЗаголовка = ПоложениеЗаголовкаЭлементаФормы.Нет;
Элемент.Видимость = Истина;
Элемент.Доступность = Истина;
Элемент.КартинкаЗначений = БиблиотекаКартинок.СостоянияДокумента;
Элемент.Ширина = 2;
Элемент.Высота = 1;
Элемент.РастягиватьПоГоризонтали = Ложь;
Элемент.РастягиватьПоВертикали = Ложь;
Элемент.РазмерКартинки = РазмерКартинки.РеальныйРазмер;
Элемент.Рамка = Новый Рамка(ТипРамкиЭлементаУправления.БезРамки);
КонецПроцедуры
затем обновим наш реквизит, отвечающий за номер изображения:
Процедура ОбновитьКартинкуПроведенияДокумента(Объект, КартинкаСостоянияДокумента) Экспорт
Если Объект.Ссылка.Пустая() Тогда
КартинкаСостоянияДокумента = 3;
Иначе
Если Объект.Проведен Тогда
КартинкаСостоянияДокумента = 0;
Иначе
Если Объект.ПометкаУдаления Тогда
КартинкаСостоянияДокумента = 1;
Иначе
КартинкаСостоянияДокумента = 2;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
в итоге имеем следующее:
4. Добавление реквизитов в табличную часть (на форме)
В п.1 мы добавили новый реквизит "в шапку" формы документа, а теперь добавим в ТЧ "Товары":
// Добавляем новые реквизиты формы
Элемент = ДобавитьРеквизит(Форма, "ТоварыПлановаяСебестоимость", "Товары", "ТоварыЦена", "План. с-сть", "Объект.Товары.ПлановаяСебестоимость");
Элемент.Видимость = Альянс.ВидимостьСебестоимости();
Элемент.Доступность = РольДоступна("ДобавлениеИзменениеЦенНоменклатуры") ИЛИ РольДоступна("ПолныеПрава");
Элемент = ДобавитьРеквизит(Форма, "ТоварыРентабельность", "Товары", "ТоварыЦена", "% рент.", "Объект.Товары.Рентабельность");
Элемент.Видимость = Альянс.ВидимостьСебестоимости();
Элемент.Доступность = Ложь;
Элемент.ТолькоПросмотр = Истина;
Элемент.Формат = "ЧДЦ=2";
Элемент.ВыделятьОтрицательные = Истина;
используя нашу функцию, возвращающую реквизит формы. Первый реквизит присутствует в составе объекта, второй - только на форме (создан в п.2), однако работа с ними в данном случае однотипна.
Теперь мы можем пересчитать и обновить нашу рентабельность (при первом открытии документа). В моем случае это так:
Процедура ОбновитьРентабельность(Товары)
Для каждого ТекущаяСтрока Из Товары Цикл
Если ТекущаяСтрока.Цена = 0 Тогда
ТекущаяСтрока.Рентабельность = 0;
Иначе
ТекущаяСтрока.Рентабельность = (ТекущаяСтрока.Цена - ТекущаяСтрока.ПлановаяСебестоимость) / ТекущаяСтрока.Цена * 100;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
реквизит "ПлановаяСебестоимость" "завязан" на номенклатуру и обновляется при ее изменении, об этом ниже.
5. Переопределение обработчиков элементов формы
Теперь нам надо изменить логику работы обработчиков элементов формы, установить новые действия для тех или иных событий элементов формы. Т.е нам необходимо сделать что-то такое:
// Переопределение обработчиков элементов формы
Форма.Элементы.Партнер.УстановитьДействие("ПриИзменении", "Альянс_ПартнерПриИзменении");
Форма.Элементы.ТоварыНоменклатура.УстановитьДействие("ПриИзменении", "Альянс_ТоварыНоменклатураПриИзменении");
Форма.Элементы.ТоварыВидЦены.УстановитьДействие("ПриИзменении", "Альянс_ТоварыВидЦеныПриИзменении");
Форма.Элементы.ТоварыЦена.УстановитьДействие("ПриИзменении", "Альянс_ТоварыЦенаПриИзменении");
Форма.Элементы.ТоварыСумма.УстановитьДействие("ПриИзменении", "Альянс_ТоварыСуммаПриИзменении");
Форма.Команды.ОткрытьПодбор.Действие = "Альянс_ОткрытьПодбор";
Форма.Элементы.НеОтгружатьЧастями.УстановитьДействие("ПриИзменении", "Альянс_НеОтгружатьЧастямиПриИзменении");
Форма.УстановитьДействие("ОбработкаВыбора", "Альянс_ОбработкаВыбора");
а вот реализовать новые алгоритмы, к сожалению, мы можем только в самой форме документа. Добавляем в конец модуля формы:
сами алгоритмы не привожу, это не важно. Важно то, что все-таки наши новые обработчики событий элементов формы мы добавляем в самом модуле формы документа, вынести их "вовне" не получится. Поэтому при обновлении, ежели они "затрутся", нам надо будет эти блоки переносить вручную.
6. Добавление табличной части
В документе "Поступление товаров и услуг" у меня добавлена новая ТЧ "Ценообразование":
Чтобы программно ее добавить, пишем нечто следующее:
// таблица значений
Реквизиты.Добавить(Новый РеквизитФормы("ЦеныВыбранные", Новый ОписаниеТипов("ТаблицаЗначений"), "", "ЦеныВыбранные"));
// и ее реквизиты
Реквизиты.Добавить(Новый РеквизитФормы("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"), "ЦеныВыбранные", "Номенклатура"));
Реквизиты.Добавить(Новый РеквизитФормы("Количество", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "Количество"));
Реквизиты.Добавить(Новый РеквизитФормы("Упаковка", Новый ОписаниеТипов("СправочникСсылка.УпаковкиНоменклатуры"), "ЦеныВыбранные", "Упаковка"));
Реквизиты.Добавить(Новый РеквизитФормы("Цена", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "Цена"));
Реквизиты.Добавить(Новый РеквизитФормы("ЦенаПС", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "ЦенаПС"));
Реквизиты.Добавить(Новый РеквизитФормы("ЦенаБП", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "ЦенаБП"));
Реквизиты.Добавить(Новый РеквизитФормы("ПроцентЦП", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "ПроцентЦП"));
Реквизиты.Добавить(Новый РеквизитФормы("ПроцентЦПТранспорт",Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "ПроцентЦПТранспорт"));
Реквизиты.Добавить(Новый РеквизитФормы("ПроцентДоп", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "ПроцентДоп"));
Реквизиты.Добавить(Новый РеквизитФормы("ЦенаПСТекущая", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "ЦенаПСТекущая"));
Реквизиты.Добавить(Новый РеквизитФормы("Отменить", Новый ОписаниеТипов("Булево"), "ЦеныВыбранные", "Отменить"));
Реквизиты.Добавить(Новый РеквизитФормы("Остаток", Новый ОписаниеТипов("Число"), "ЦеныВыбранные", "Остаток"));
Форма.ИзменитьРеквизиты(Реквизиты);
т.е. сначала создаем реквизит формы типа "ТаблицаЗначений", создаем колонки с нужным типом, затем отображаем эту таблицу значений на форме:
Элемент = Форма.Элементы.Вставить("ГруппаЦенообразование", Тип("ПолеГруппы"), Форма.Элементы.ГруппаСтраницы);
Элемент.Вид = ВидГруппыФормы.Страница;
Элемент.Видимость = Истина;
Элемент.Доступность = Истина;
Элемент.Группировка = ГруппировкаПодчиненныхЭлементовФормы.Вертикальная;
Элемент.ОтображатьЗаголовок = Истина;
Элемент.Заголовок = "Ценообразование";
7. Добавление реквизитов в форму списка (справочников, документов...) на основе динамического списка
В форму списка РКО у меня добавлены новые реквизиты. Поскольку список документов "собирается" динамическим списком (произвольный запрос), необходимо изменить его текст:
Форма.РасходныеКассовыеОрдера.ТекстЗапроса =
"ВЫБРАТЬ
| Документ.Ссылка,
| Документ.ПометкаУдаления,
| Документ.Номер,
| Документ.Дата,
...
...
| Документ.СтатьяДвиженияДенежныхСредств,
| Документ.Комментарий,
| ВложенныйЗапрос.АналитикаРасходов,
| ВложенныйЗапрос.СтатьяРасходов
|ИЗ
| Документ.РасходныйКассовыйОрдер КАК Документ
| ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
| РасходныйКассовыйОрдерРасшифровкаПлатежа.Ссылка КАК Ссылка,
| РасходныйКассовыйОрдерРасшифровкаПлатежа.НомерСтроки КАК НомерСтроки,
| РасходныйКассовыйОрдерРасшифровкаПлатежа.АналитикаРасходов КАК АналитикаРасходов,
| РасходныйКассовыйОрдерРасшифровкаПлатежа.СтатьяРасходов КАК СтатьяРасходов
| ИЗ
| Документ.РасходныйКассовыйОрдер.РасшифровкаПлатежа КАК РасходныйКассовыйОрдерРасшифровкаПлатежа
| ГДЕ
| РасходныйКассовыйОрдерРасшифровкаПлатежа.НомерСтроки = 1) КАК ВложенныйЗапрос
| ПО Документ.Ссылка = ВложенныйЗапрос.Ссылка";
и добавить на форме новые поля, привязки к которым есть в нашем тексте:
Элемент = ДобавитьРеквизит(Форма, "Комментарий", "РасходныеКассовыеОрдера", "РасходныеКассовыеОрдераКасса", "Комментарий", "РасходныеКассовыеОрдера.Комментарий");
Элемент = ДобавитьРеквизит(Форма, "СтатьяРасходов", "РасходныеКассовыеОрдера", "РасходныеКассовыеОрдераОснование", "Статья расходов", "РасходныеКассовыеОрдера.СтатьяРасходов");
Элемент = ДобавитьРеквизит(Форма, "АналитикаРасходов", "РасходныеКассовыеОрдера", "РасходныеКассовыеОрдераОснование", "Аналитика расходов", "РасходныеКассовыеОрдера.АналитикаРасходов");
Элемент = ДобавитьРеквизит(Форма, "СтатьяДвиженияДенежныхСредств", "РасходныеКассовыеОрдера", "РасходныеКассовыеОрдераОснование", "Статья ДДС", "РасходныеКассовыеОрдера.СтатьяДвиженияДенежныхСредств");
8. Добавление условного оформления
ЭлементОформления = Форма.УсловноеОформление.Элементы.Добавить();
// отбор
ЭлементОтбора = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("НецелоеКоличествоКоробок"); // имя поля
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Истина;
ЭлементОтбора.Использование = Истина;
// оформление
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
// поля
ПолеОформления = ЭлементОформления.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("КоличествоКоробок");
ПолеОформления.Использование = Истина;
Здесь я в форме обработки подбора товара подсвечиваю красным мое новое поле "Количество коробок", если он нецелое. Детали опускаю.
Напоминаю, что весь приведенный текст (за исключением п.5) пишется в общем "переопределяемом" модуле, а потому является максимально независимым от обновления.
Итак, я попытался описать все возможные ситуации доработок форм объектов, с которыми мне пришлось столкнуться в процессе перенесения моих доработок из одного релиза в другой. Надеюсь, что данная статья поможет сэкономить время тем, кто займется похожими задачами.