Команда формы 1С — объект формы, который описывает действие: имя, заголовок, картинку, сочетание клавиш и процедуру-обработчик в модуле формы. Кнопка, пункт меню или подменю только ссылаются на команду, поэтому одно действие можно вывести в нескольких местах, а код написать один раз. Ниже — как создать и добавить команду формы в 1С 8.3, вызвать из неё сервер, обработать выделенные строки списка, сделать общую команду с параметром и встроить команду в типовую форму расширением.
Примеры написаны для условного документа ЗаказКлиента (реквизиты Склад, Контрагент, Ответственный, табличная часть Товары) и регистра накопления ОстаткиТоваров с измерениями Склад и Номенклатура. В своей конфигурации подставьте реальные имена.
Команды управляемой формы собраны в редакторе на вкладке «Команды», в ней три закладки: «Команды формы» (их создаёт разработчик), «Стандартные команды» (их добавляет платформа) и «Глобальные команды» (общие команды и команды объектов конфигурации). Новая команда создаётся так:
ЗаполнитьПоОстаткам.ИмяКоманды указывает на команду.| Свойство | Что задаёт |
|---|---|
| Действие | имя клиентской процедуры-обработчика в модуле формы |
| Заголовок, Подсказка | текст кнопки и всплывающую подсказку |
| Картинка, Отображение | значок из библиотеки картинок и режим показа: картинка, текст или оба |
| Сочетание клавиш | горячую клавишу для вызова команды |
| Функциональные опции | скрывают команду, если опции выключены |
| Изменяет сохраняемые данные | делает команду недоступной, когда форма открыта только для просмотра |
Обработчик команды, которая заполняет табличную часть остатками выбранного склада:
&НаКлиенте
Процедура ЗаполнитьПоОстаткам(Команда)
Если Не ЗначениеЗаполнено(Объект.Склад) Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Укажите склад, с которого заполнять товары.";
Сообщение.Поле = "Объект.Склад";
Сообщение.Сообщить();
Возврат;
КонецЕсли;
ЗаполнитьПоОстаткамНаСервере();
Модифицированность = Истина;
ПоказатьОповещениеПользователя("Товары заполнены", ,
СтрШаблон("Строк в табличной части: %1", Объект.Товары.Количество()));
КонецПроцедуры
&НаСервере
Процедура ЗаполнитьПоОстаткамНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ОстаткиТоваров.Остатки(, Склад = &Склад) КАК Остатки
|ГДЕ
| Остатки.КоличествоОстаток > 0
|
|УПОРЯДОЧИТЬ ПО
| Остатки.Номенклатура.Наименование";
Запрос.УстановитьПараметр("Склад", Объект.Склад);
Объект.Товары.Очистить();
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
КонецЦикла;
КонецПроцедуры
Команда — сам объект КомандаФормы. Если одна процедура обслуживает несколько команд, ветвитесь по Команда.Имя.Сообщение.Поле привязывает текст к полю, и платформа подсвечивает его на форме.&НаСервере — контекстный вызов: данные формы уходят на сервер и возвращаются с новыми строками Объект.Товары.Модифицированность = Истина: программное изменение данных не помечает форму изменённой, и без этой строки она закроется без вопроса о записи.Контекстный вызов передаёт на сервер всю форму с табличными частями. Если команда только читает данные, используйте &НаСервереБезКонтекста и передавайте значения параметрами. Пример — команда в командной панели таблицы Товары, которая показывает остаток по текущей строке:
&НаКлиенте
Процедура ПоказатьОстаток(Команда)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
Если ТекущиеДанные = Неопределено Или Не ЗначениеЗаполнено(ТекущиеДанные.Номенклатура) Тогда
ПоказатьПредупреждение(, "Выберите строку с номенклатурой.");
Возврат;
КонецЕсли;
Остаток = ОстатокНаСкладе(ТекущиеДанные.Номенклатура, Объект.Склад);
ПоказатьПредупреждение(, СтрШаблон("Остаток на складе: %1", Остаток));
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОстатокНаСкладе(Номенклатура, Склад)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Остатки.КоличествоОстаток), 0) КАК Количество
|ИЗ
| РегистрНакопления.ОстаткиТоваров.Остатки(
| ,
| Номенклатура = &Номенклатура
| И Склад = &Склад) КАК Остатки";
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
Запрос.УстановитьПараметр("Склад", Склад);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Количество;
КонецЕсли;
Возврат 0;
КонецФункции
На сервер уходят две ссылки, а не документ с сотней строк. В функции без контекста нет Объект и Элементы, поэтому всё нужное приходит параметрами. Проверка ТекущиеДанные = Неопределено обязательна: в пустой таблице текущей строки нет. Эту же команду можно вывести и в контекстное меню таблицы — обработчик останется один.
Перезаполнение стирает ручные правки, поэтому перед ним стоит спросить пользователя. Модальный Вопрос() в конфигурациях без модальности не работает, нужен асинхронный ПоказатьВопрос. Новая версия обработчика из первого примера:
&НаКлиенте
Процедура ЗаполнитьПоОстаткам(Команда)
Если Не ЗначениеЗаполнено(Объект.Склад) Тогда
ПоказатьПредупреждение(, "Укажите склад.");
Возврат;
КонецЕсли;
Если Объект.Товары.Количество() = 0 Тогда
ВыполнитьЗаполнение();
Возврат;
КонецЕсли;
ДополнительныеПараметры = Новый Структура("БылоСтрок", Объект.Товары.Количество());
Оповещение = Новый ОписаниеОповещения("ЗаполнитьПоОстаткамЗавершение", ЭтотОбъект, ДополнительныеПараметры);
ТекстВопроса = СтрШаблон("В табличной части уже есть строки (%1). Очистить и заполнить заново?",
ДополнительныеПараметры.БылоСтрок);
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет, 60, КодВозвратаДиалога.Нет, "Перезаполнение");
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьПоОстаткамЗавершение(Результат, ДополнительныеПараметры) Экспорт
Если Результат <> КодВозвратаДиалога.Да Тогда
Возврат; // ответ «Нет» или истёк таймаут
КонецЕсли;
ВыполнитьЗаполнение();
ПоказатьОповещениеПользователя("Товары перезаполнены", ,
СтрШаблон("Было строк: %1, стало: %2", ДополнительныеПараметры.БылоСтрок, Объект.Товары.Количество()));
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьЗаполнение()
ЗаполнитьПоОстаткамНаСервере(); // серверная процедура из первого листинга
Модифицированность = Истина;
КонецПроцедуры
ПоказатьВопрос не останавливает код: ЗаполнитьПоОстаткам завершается сразу, а продолжение выполняется в ЗаполнитьПоОстаткамЗавершение после ответа.ОписаниеОповещения, здесь это форма (ЭтотОбъект). Третий параметр переносит состояние на момент вопроса.КодВозвратаДиалога.Таймаут, и условие Результат <> КодВозвратаДиалога.Да отменит действие.Свойство Элементы.Список.ВыделенныеСтроки возвращает массив ключей строк, для списка документов это ссылки. У таблицы должен стоять режим выделения «Множественный», иначе выделить можно только одну строку. Команда меняет ответственного у выделенных заказов:
// Модуль формы списка документов ЗаказКлиента.
// Команда СменитьОтветственного выведена в командную панель таблицы Список.
&НаКлиенте
Процедура СменитьОтветственного(Команда)
ВыбранныеДокументы = Новый Массив;
Для Каждого ВыделеннаяСтрока Из Элементы.Список.ВыделенныеСтроки Цикл
// в сгруппированном списке выделяются и строки группировок
Если ТипЗнч(ВыделеннаяСтрока) = Тип("СтрокаГруппировкиДинамическогоСписка") Тогда
Продолжить;
КонецЕсли;
ВыбранныеДокументы.Добавить(ВыделеннаяСтрока);
КонецЦикла;
Если ВыбранныеДокументы.Количество() = 0 Тогда
ПоказатьПредупреждение(, "Выделите один или несколько заказов.");
Возврат;
КонецЕсли;
Оповещение = Новый ОписаниеОповещения("СменитьОтветственногоЗавершение", ЭтотОбъект, ВыбранныеДокументы);
ПоказатьВводЗначения(Оповещение, Неопределено, "Новый ответственный", Тип("СправочникСсылка.Пользователи"));
КонецПроцедуры
&НаКлиенте
Процедура СменитьОтветственногоЗавершение(Ответственный, ВыбранныеДокументы) Экспорт
Если Не ЗначениеЗаполнено(Ответственный) Тогда
Возврат; // окно выбора закрыто без выбора
КонецЕсли;
Ошибки = СменитьОтветственногоНаСервере(ВыбранныеДокументы, Ответственный);
Элементы.Список.Обновить();
Для Каждого ТекстОшибки Из Ошибки Цикл
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = ТекстОшибки;
Сообщение.Сообщить();
КонецЦикла;
ПоказатьОповещениеПользователя("Ответственный изменён", ,
СтрШаблон("Изменено: %1, ошибок: %2",
ВыбранныеДокументы.Количество() - Ошибки.Количество(), Ошибки.Количество()));
КонецПроцедуры
&НаСервереБезКонтекста
Функция СменитьОтветственногоНаСервере(Знач Документы, Знач Ответственный)
Ошибки = Новый Массив;
Для Каждого ДокументСсылка Из Документы Цикл
НачатьТранзакцию();
Попытка
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.ЗаказКлиента");
ЭлементБлокировки.УстановитьЗначение("Ссылка", ДокументСсылка);
Блокировка.Заблокировать();
ДокументОбъект = ДокументСсылка.ПолучитьОбъект();
ДокументОбъект.Ответственный = Ответственный;
ДокументОбъект.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
Ошибки.Добавить(СтрШаблон("%1: %2", ДокументСсылка,
КраткоеПредставлениеОшибки(ИнформацияОбОшибке())));
КонецПопытки;
КонецЦикла;
Возврат Ошибки;
КонецФункции
СтрокаГруппировкиДинамическогоСписка, их пропускаем.ПоказатьВводЗначения со ссылочным типом открывает форму выбора справочника. Массив документов доходит до обработчика через третий параметр ОписаниеОповещения.Ошибки.Элементы.Список.Обновить() перечитывает список. Если он открыт и в других окнах, добавьте ОповеститьОбИзменении(Тип("ДокументСсылка.ЗаказКлиента")).Доступность и видимость задают у кнопки — элемента формы, связанного с командой. Если команда выведена в нескольких местах, меняйте каждую кнопку. Права проверяем один раз при создании формы, доступность пересчитываем при изменении данных:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// права не меняются, пока форма открыта, поэтому видимость задаём один раз
Элементы.ФормаЗаполнитьПоОстаткам.Видимость =
ПравоДоступа("Изменение", Метаданные.Документы.ЗаказКлиента);
УстановитьДоступностьКоманд(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура СкладПриИзменении(Элемент)
УстановитьДоступностьКоманд(ЭтотОбъект);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
УстановитьДоступностьКоманд(ЭтотОбъект);
КонецПроцедуры
&НаКлиентеНаСервереБезКонтекста
Процедура УстановитьДоступностьКоманд(Форма)
Объект = Форма.Объект;
Элементы = Форма.Элементы;
СкладЗаполнен = ЗначениеЗаполнено(Объект.Склад);
// заполнять можно только непроведённый документ с указанным складом
Элементы.ФормаЗаполнитьПоОстаткам.Доступность = СкладЗаполнен И Не Объект.Проведен;
Элементы.ТоварыПоказатьОстаток.Доступность = СкладЗаполнен;
КонецПроцедуры
Процедура с директивой &НаКлиентеНаСервереБезКонтекста вызывается и из ПриСозданииНаСервере, и из клиентских событий, поэтому форму получает параметром. СкладПриИзменении назначьте событием «При изменении» поля склада, а имена кнопок сверьте с деревом элементов. Скрытая кнопка не защищает данные, поэтому серверный код должен сам проверять права.
Платформа сама добавляет в форму стандартные команды: «Записать», «Провести», «Провести и закрыть», «Скопировать», «Перечитать», а у таблиц — «Добавить», «Удалить», «Переместить вверх». Обработчик стандартной команды переназначить нельзя. Поведение меняют через события формы (ПередЗаписью, ПередЗаписьюНаСервере, у таблиц — ПередНачаломДобавления и ПередУдалением с параметром Отказ) или заменяют стандартную кнопку своей командой, которая вызывает метод формы Записать():
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// срабатывает для «Записать», «Провести», «Провести и закрыть» и для Записать() из кода
Если ПараметрыЗаписи.РежимЗаписи = РежимЗаписиДокумента.Проведение
И Объект.Товары.Количество() = 0 Тогда
Отказ = Истина;
ПоказатьПредупреждение(, "Нельзя провести заказ без товаров.");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПровестиИСоздатьРеализацию(Команда)
ПараметрыЗаписи = Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение);
Если Не Записать(ПараметрыЗаписи) Тогда
Возврат; // отказ в ПередЗаписью или ошибка проведения
КонецЕсли;
ПараметрыФормы = Новый Структура("Основание", Объект.Ссылка);
ОткрытьФорму("Документ.РеализацияТоваровУслуг.ФормаОбъекта", ПараметрыФормы);
Закрыть();
КонецПроцедуры
ПередЗаписью срабатывает при любом способе записи, включая вызов Записать() из своей команды, поэтому проверку не обойти. Параметр формы Основание запускает стандартный ввод на основании: новый документ заполнится в ОбработкаЗаполнения. Стандартную кнопку «Провести и закрыть» скройте свойством «Видимость».
Общая команда — отдельный объект метаданных в ветке «Общие» → «Общие команды». Её не нужно добавлять в каждую форму: платформа размещает её сама по свойствам «Группа» и «Тип параметра команды». С типом ДокументСсылка.ЗаказКлиента и группой «Командная панель формы.Важное» кнопка появится в форме документа и в форме списка заказов. Обработчик — процедура ОбработкаКоманды в модуле команды:
// Модуль общей команды ПечатьЭтикеток.
// Группа: «Командная панель формы.Важное».
// Тип параметра команды: ДокументСсылка.ЗаказКлиента.
// Режим использования параметра: Множественный.
&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
Если ТипЗнч(ПараметрКоманды) = Тип("Массив") Тогда
Документы = ПараметрКоманды;
Иначе
Документы = Новый Массив;
Документы.Добавить(ПараметрКоманды);
КонецЕсли;
Если Документы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Если КоличествоСтрокТоваров(Документы) = 0 Тогда
ПоказатьПредупреждение(, "В выбранных заказах нет товаров.");
Возврат;
КонецЕсли;
ПараметрыФормы = Новый Структура("Документы", Документы);
ОткрытьФорму("Обработка.ПечатьЭтикеток.Форма", ПараметрыФормы,
ПараметрыВыполненияКоманды.Источник,
ПараметрыВыполненияКоманды.Уникальность,
ПараметрыВыполненияКоманды.Окно,
ПараметрыВыполненияКоманды.НавигационнаяСсылка);
КонецПроцедуры
&НаСервере
Функция КоличествоСтрокТоваров(Документы)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(*) КАК Количество
|ИЗ
| Документ.ЗаказКлиента.Товары КАК Товары
|ГДЕ
| Товары.Ссылка В (&Документы)";
Запрос.УстановитьПараметр("Документы", Документы);
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий();
Возврат Выборка.Количество;
КонецФункции
В режиме использования параметра «Множественный» в ПараметрКоманды приходит массив выделенных ссылок, в режиме «Одиночный» — одна ссылка, и код принимает оба варианта. ПараметрыВыполненияКоманды содержит Источник (форму, из которой вызвана команда), Уникальность, Окно и НавигационнаяСсылка. Их передают в ОткрытьФорму, чтобы форма открылась в нужном окне и знала владельца. В форме обработки объявите параметр Документы.
| Команда формы | Общая команда | |
|---|---|---|
| Где создаётся | в редакторе формы, вкладка «Команды» | в дереве метаданных, ветка «Общие команды» |
| Обработчик | процедура модуля формы с параметром Команда | ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды) |
| Данные | Объект, реквизиты и Элементы формы | параметр команды и форма-источник |
| Размещение | только там, куда добавлена кнопка | во всех формах, подходящих по типу параметра и группе |
| Ограничение доступа | код формы, функциональные опции | право «Просмотр» в ролях, функциональные опции |
Правка типовой формы в основной конфигурации требует снять её с поддержки. Расширение решает задачу без этого:
Расш1_ПроверитьКонтрагента, и перетащите её в командную панель.// Модуль заимствованной формы документа ЗаказКлиента в расширении (префикс Расш1_).
// Новая команда расширения Расш1_ПроверитьКонтрагента.
&НаКлиенте
Процедура Расш1_ПроверитьКонтрагента(Команда)
Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
ПоказатьПредупреждение(, "Контрагент не выбран.");
Возврат;
КонецЕсли;
ПоказатьПредупреждение(, Расш1_РезультатПроверки(Объект.Контрагент));
КонецПроцедуры
&НаСервереБезКонтекста
Функция Расш1_РезультатПроверки(Контрагент)
Если Контрагент.ПометкаУдаления Тогда
Возврат "Контрагент помечен на удаление.";
ИначеЕсли Не ЗначениеЗаполнено(Контрагент.ИНН) Тогда
Возврат "У контрагента не заполнен ИНН.";
КонецЕсли;
Возврат "Проверка пройдена.";
КонецФункции
// Доработка типовой команды ЗаполнитьПоОстаткам без правки её кода.
&НаКлиенте
&Вместо("ЗаполнитьПоОстаткам")
Процедура Расш1_ЗаполнитьПоОстаткам(Команда)
Если Объект.Проведен Тогда
ПоказатьПредупреждение(, "Документ проведён: отмените проведение и повторите.");
Возврат;
КонецЕсли;
ПродолжитьВызов(Команда); // выполнить типовой обработчик
КонецПроцедуры
Типовую команду дорабатывают аннотацией: &Вместо с ПродолжитьВызов проверяет условие и запускает типовой обработчик, &Перед и &После добавляют код до или после него. Другой вариант — задать заимствованной команде действие с типом вызова «Перед», «После» или «Вместо» в палитре свойств. После обновления типовой конфигурации проверьте, что перехватываемая процедура не переименована.
Если состав команд зависит от прав или настроек, команду создают в ПриСозданииНаСервере. Обработчик при этом пишут в модуле заранее: платформа найдёт его по имени из свойства Действие.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если ПравоДоступа("Изменение", Метаданные.Документы.ЗаказКлиента) Тогда
ДобавитьКомандуОчистки();
КонецЕсли;
КонецПроцедуры
&НаСервере
Процедура ДобавитьКомандуОчистки()
НоваяКоманда = Команды.Добавить("ОчиститьТовары");
НоваяКоманда.Заголовок = "Очистить товары";
НоваяКоманда.Действие = "ОчиститьТовары"; // клиентская процедура этого модуля
НоваяКоманда.ИзменяетСохраняемыеДанные = Истина;
Кнопка = Элементы.Добавить("ФормаОчиститьТовары", Тип("КнопкаФормы"), КоманднаяПанель);
Кнопка.ИмяКоманды = НоваяКоманда.Имя;
КонецПроцедуры
&НаКлиенте
Процедура ОчиститьТовары(Команда)
Объект.Товары.Очистить();
Модифицированность = Истина;
КонецПроцедуры
Сначала создаётся команда через Команды.Добавить, затем кнопка, которой назначают ИмяКоманды. Свойство формы КоманднаяПанель — её основная командная панель. Как добавлять кодом реквизиты, группы и поля, разобрано в статье программное изменение формы 1С.
&НаСервере или &НаСервереБезКонтекста.ТекущиеДанные равно Неопределено. Проверяйте его до обращения к полям.Вопрос() или Предупреждение(), а модальность в свойствах конфигурации отключена. Замените их на ПоказатьВопрос и ПоказатьПредупреждение.Экспорт или лежит не в том модуле, который передан в ОписаниеОповещения.У команды формы один параметр — сама Команда. Данные берите из реквизитов формы, ТекущиеДанные или ВыделенныеСтроки таблицы. Чтобы одна процедура обслуживала несколько команд, назначьте им одно действие и проверяйте Команда.Имя.
Кнопка — элемент формы, который ссылается на команду через свойство ИмяКоманды. Одну команду можно вывести кнопкой в командной панели и пунктом контекстного меню таблицы, обработчик у них будет общий.
Заполните свойство «Сочетание клавиш» в палитре свойств команды. Программно: Команды.ЗаполнитьПоОстаткам.СочетаниеКлавиш = Новый СочетаниеКлавиш(Клавиша.F7, Ложь, Истина, Ложь), параметры — клавиша, Alt, Ctrl, Shift. Не занимайте стандартные сочетания вроде Ctrl+S («Записать») и Ctrl+Enter («Провести и закрыть»).
Проверьте четыре вещи: у команды задана группа, тип параметра совпадает с типом данных формы, у роли пользователя есть право «Просмотр» на команду, функциональная опция включена. Ещё пользователь мог скрыть кнопку через «Ещё» → «Изменить форму».
Если команда должна массово менять документы в типовой конфигурации с учётом прав и обновлений, быстрее поручить её разработчику: услуги по доработке 1С. Опишите, что должна делать команда, на странице разместить задание, и исполнители оценят срок и стоимость.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.