en un clic
form-patterns
For managed form module code and server calls
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Menu
For managed form module code and server calls
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Basé sur la classification professionnelle SOC
When writing or reviewing BSL, apply 1C standards
При написании или ревью BSL применять стандарты 1С
Orchestrator: routing work and agent phases
Оркестратор: маршрутизация работы и фаз агентов
BSL LSP navigation: definitions, refs, call graph
Rules for using RLM tools for project search and navigation in 1C/BSL
| name | form-patterns |
| description | For managed form module code and server calls |
| alwaysApply | false |
Key principle: Minimize the number and size of server calls. Every &НаСервере call is a network round-trip plus serialization of the entire form context.
1. &НаКлиентеНаСервереБезКонтекста — чистые вычисления, обе среды
2. &НаСервереБезКонтекста — обращение к БД, без контекста формы
3. &НаСервере — нужен доступ к реквизитам формы
4. &НаКлиенте — интерактивная логика (диалоги, навигация)
Do you need access to the database?
├── No → &НаКлиенте or &НаКлиентеНаСервереБезКонтекста
└── Yes →
Do you need access to form attributes?
├── No → &НаСервереБезКонтекста (only parameters are passed)
└── Yes → &НаСервере (the entire context is passed)
// Чистое вычисление - обе среды
&НаКлиентеНаСервереБезКонтекста
Функция РассчитатьСумму(Количество, Цена, СтавкаНДС)
СуммаБезНДС = Количество * Цена;
СуммаНДС = СуммаБезНДС * СтавкаНДС / 100;
Возврат СуммаБезНДС + СуммаНДС;
КонецФункции
// Нужны данные из базы, но реквизиты формы НЕ нужны
&НаСервереБезКонтекста
Функция ПолучитьДанныеНоменклатуры(НоменклатураСсылка)
Возврат ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
НоменклатураСсылка,
"Наименование, ЕдиницаИзмерения, СтавкаНДС, Цена");
КонецФункции
// Нужен доступ к реквизитам формы
&НаСервере
Процедура ПересчитатьИтогиНаСервере()
Объект.СуммаДокумента = Объект.Товары.Итог("Сумма");
Объект.СуммаНДС = Объект.Товары.Итог("СуммаНДС");
КонецПроцедуры
Each server call is about 100 ms (serialization + round-trip + deserialization). Three calls = 300 ms of latency. One call that returns all data = 100 ms.
&НаКлиенте
Процедура КонтрагентПриИзменении(Элемент)
ДанныеЗаполнения = ПолучитьДанныеЗаполненияПоКонтрагенту(Объект.Контрагент, Объект.Дата);
Объект.Договор = ДанныеЗаполнения.Договор;
Объект.Организация = ДанныеЗаполнения.Организация;
Объект.Валюта = ДанныеЗаполнения.Валюта;
КурсВалюты = ДанныеЗаполнения.Курс;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеЗаполненияПоКонтрагенту(КонтрагентСсылка, ДатаДокумента)
Результат = Новый Структура;
РеквизитыКонтрагента = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(
КонтрагентСсылка, "ДоговорПоУмолчанию, ОсновнаяОрганизация");
Результат.Вставить("Договор", РеквизитыКонтрагента.ДоговорПоУмолчанию);
Результат.Вставить("Организация", РеквизитыКонтрагента.ОсновнаяОрганизация);
Если ЗначениеЗаполнено(РеквизитыКонтрагента.ДоговорПоУмолчанию) Тогда
ВалютаДоговора = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
РеквизитыКонтрагента.ДоговорПоУмолчанию, "ВалютаВзаиморасчетов");
Результат.Вставить("Валюта", ВалютаДоговора);
Результат.Вставить("Курс", РаботаСКурсамиВалют.ПолучитьКурсВалюты(ВалютаДоговора, ДатаДокумента));
Иначе
Результат.Вставить("Валюта", Неопределено);
Результат.Вставить("Курс", 1);
КонецЕсли;
Возврат Результат;
КонецФункции
On the server side of a form, object data is not the real object, but ДанныеФормы*. To call object module methods, conversion is required.
| Scenario | Is conversion needed? |
|---|---|
| Reading form attributes | No - Объект.Реквизит works directly |
| Calling object module methods | Yes |
| Passing the object to a common module | Yes - common modules work with real objects |
&НаСервере
Процедура ЗаполнитьПоУмолчаниюНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТоварыПоУмолчанию();
// ОБЯЗАТЕЛЬНО: преобразовать обратно, иначе изменения не отразятся на форме!
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
// ПЛОХО: изменения теряются!
&НаСервере
Процедура ЗаполнитьНаСервере()
ДокументОбъект = РеквизитФормыВЗначение("Объект");
ДокументОбъект.ЗаполнитьТаблицу();
// ЗАБЫЛИ: ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
1. ПриСозданииНаСервере - form is created on the server, data is loaded
2. ПриОткрытии - form is displayed on the client
1. ПередЗаписью - client: can be canceled (Отказ = Истина)
2. ПередЗаписьюНаСервере - server: final check
3. ПриЗаписиНаСервере - server: in the same transaction
4. ПослеЗаписиНаСервере - server: form update
5. ПослеЗаписи - client: notification
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если Параметры.Свойство("ЗначенияЗаполнения") Тогда
ЗаполнитьЗначенияСвойств(Объект, Параметры.ЗначенияЗаполнения);
КонецЕсли;
Элементы.ГруппаСклад.Видимость = (Объект.ВидОперации <>
Перечисления.ВидыОпераций.Услуга);
ОбновитьСписокВыбораДоговоров();
КонецПроцедуры
// ПередЗаписью (клиент) - проверки, требующие подтверждения пользователя
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если Объект.СуммаДокумента > 1000000 Тогда
Если Не ПараметрыЗаписи.Свойство("ПодтверждениеСуммы") Тогда
Отказ = Истина;
ПоказатьВопрос(
Новый ОписаниеОповещения("ПослеПодтвержденияСуммы", ЭтотОбъект, ПараметрыЗаписи),
НСтр("ru = 'Сумма документа превышает 1 000 000. Продолжить?'"),
РежимДиалогаВопрос.ДаНет);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// ПослеЗаписи - оповещения на клиенте
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Оповестить("Запись_РеализацияТоваровУслуг",
Новый Структура("Ссылка", Объект.Ссылка), ЭтотОбъект);
КонецПроцедуры
A dynamic list automatically implements pagination, search, and sorting.
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Отборы через КомпоновкаДанных (пользователь может их изменить)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
СписокДокументов,
"Организация",
Организация,
ВидСравненияКомпоновкиДанных.Равно,
,
ЗначениеЗаполнено(Организация));
КонецПроцедуры
СписокДокументов.ПроизвольныйЗапрос = Истина;
СписокДокументов.ТекстЗапроса =
"ВЫБРАТЬ
| Реализация.Ссылка,
| Реализация.Дата,
| Реализация.Номер,
| Реализация.Контрагент,
| Реализация.СуммаДокумента,
| ЕСТЬNULL(Задолженность.СуммаОстаток, 0) КАК ОстатокЗадолженности
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки КАК Задолженность
| ПО Реализация.Контрагент = Задолженность.Контрагент
| И Реализация.Договор = Задолженность.Договор
|{ГДЕ
| Реализация.Дата >= &ДатаНачала}";
Rules: do not load all data; use КомпоновкаДанных for filters, not WHERE; do not use ORDER BY in a custom query.
&НаСервере
Процедура УстановитьУсловноеОформление()
УсловноеОформление.Элементы.Очистить();
ЭлементУО = УсловноеОформление.Элементы.Добавить();
ЭлементОтбора = ЭлементУО.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ДатаОплаты");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Меньше;
ЭлементОтбора.ПравоеЗначение = ТекущаяДатаСеанса();
ЭлементОтбора.Использование = Истина;
ЭлементОтбора2 = ЭлементУО.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора2.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Оплачен");
ЭлементОтбора2.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора2.ПравоеЗначение = Ложь;
ЭлементОтбора2.Использование = Истина;
ЭлементУО.Оформление.УстановитьЗначениеПараметра("ЦветФона", WebЦвета.МисттиРоуз);
ПолеОформления = ЭлементУО.Поля.Элементы.Добавить();
ПолеОформления.Поле = Новый ПолеКомпоновкиДанных("СписокДокументов");
КонецПроцедуры
Modal calls (Предупреждение(), Вопрос()) are prohibited in the web client. Use ОписаниеОповещения.
ITS standard: "Restrictions on the use of modal methods".
&НаКлиенте
Процедура УдалитьСтрокуТоваров(Команда)
Если Элементы.Товары.ТекущаяСтрока = Неопределено Тогда
Возврат;
КонецЕсли;
ПоказатьВопрос(
Новый ОписаниеОповещения("ПослеПодтвержденияУдаления", ЭтотОбъект),
НСтр("ru = 'Удалить выбранную строку?'"),
РежимДиалогаВопрос.ДаНет,
,
КодВозвратаДиалога.Нет);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПодтвержденияУдаления(Результат, ДополнительныеПараметры) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
Объект.Товары.Удалить(Элементы.Товары.ТекущаяСтрока);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОткрытьФормуПодбораТоваров(Команда)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Организация", Объект.Организация);
ПараметрыФормы.Вставить("Склад", Объект.Склад);
ПараметрыФормы.Вставить("Дата", Объект.Дата);
ПараметрыФормы.Вставить("МножественныйВыбор", Истина);
ОткрытьФорму("Обработка.ПодборТоваров.Форма",
ПараметрыФормы,
ЭтотОбъект,
,,,
Новый ОписаниеОповещения("ПослеПодбораТоваров", ЭтотОбъект));
КонецПроцедуры
&НаКлиенте
Процедура ПослеПодбораТоваров(РезультатПодбора, ДополнительныеПараметры) Экспорт
Если РезультатПодбора = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого ДанныеТовара Из РезультатПодбора Цикл
НоваяСтрока = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ДанныеТовара);
КонецЦикла;
Модифицированность = Истина;
КонецПроцедуры
Collect all visibility/availability changes into a single server call to avoid UI "flicker".
&НаСервере
Процедура УправлениеВидимостью()
ЭтоУслуга = Объект.ВидОперации = Перечисления.ВидыОпераций.Услуга;
Элементы.Склад.Видимость = НЕ ЭтоУслуга;
Элементы.ТоварыКоличество.Видимость = НЕ ЭтоУслуга;
Элементы.ТоварыЕдиницаИзмерения.Видимость = НЕ ЭтоУслуга;
Элементы.ГруппаДоставка.Видимость = НЕ ЭтоУслуга;
Элементы.Контрагент.ТолькоПросмотр = Объект.Проведен;
КонецПроцедуры
&НаКлиенте
Процедура ВидОперацииПриИзменении(Элемент)
УправлениеВидимостью(); // Один серверный вызов на все изменения
КонецПроцедуры
// Изменение количества - пересчёт суммы на клиенте
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ТекущаяСтрока.Сумма = РассчитатьСумму(
ТекущаяСтрока.Количество, ТекущаяСтрока.Цена, ТекущаяСтрока.СтавкаНДС);
КонецПроцедуры
// Изменение номенклатуры - нужен сервер, чтобы получить данные
&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ДанныеНоменклатуры = ПолучитьДанныеНоменклатуры(ТекущаяСтрока.Номенклатура);
ТекущаяСтрока.ЕдиницаИзмерения = ДанныеНоменклатуры.ЕдиницаИзмерения;
ТекущаяСтрока.Цена = ДанныеНоменклатуры.Цена;
ТекущаяСтрока.СтавкаНДС = ДанныеНоменклатуры.СтавкаНДС;
ТекущаяСтрока.Сумма = РассчитатьСумму(
ТекущаяСтрока.Количество, ТекущаяСтрока.Цена, ТекущаяСтрока.СтавкаНДС);
КонецПроцедуры
// Форма документа - после записи
&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
Оповестить("Запись_РеализацияТоваровУслуг",
Новый Структура("Ссылка, Проведен", Объект.Ссылка, Объект.Проведен),
ЭтотОбъект);
КонецПроцедуры
// Форма списка - обработка оповещения
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия = "Запись_РеализацияТоваровУслуг" Тогда
Элементы.Список.Обновить();
КонецЕсли;
КонецПроцедуры
Primitives (String, Number, Date, Boolean), References, Enumerations, Structure, Map, Array, FixedCollections, ValueStorage.
| Type | Alternative |
|---|---|
| ValueTable | FormDataCollection (via form attributes) |
| ValueTree | FormDataTree (via form attributes) |
| MetadataObject | Pass MetadataName (a string) |
| Query, QueryResult | Pass the result (structure/array) |
Some aspects of form correctness are determined not by module code, but by properties of the form and its elements. When generating XML, reviewing the form, or modifying a standard form, check these invariants.
| Area | Check |
|---|---|
| Commands | A command that changes persisted data must have ИзменяетСохраняемыеДанные. Otherwise, the form may not become modified, and with ТолькоПросмотр the command will remain active. |
| Tabular sections | On the tabular section page, the data path for the header must be filled in, for example Объект.Товары.КоличествоСтрок, if the header should display the number of rows. |
| Required fields | Use the ПроверкаЗаполнения property where requiredness is a property of the attribute. Manual validation in code is acceptable for conditional business rules, but should not replace platform validation without reason. |
| List form | Add ГруппаПользовательскихНастроек if the list form should show user filters. |
| Standard forms | Implement standard form customizations programmatically. If tabs are present, place new attributes on a separate service tab; if there are no tabs, place them in a service group without a header or highlighting, unless the specification indicates an exact location. |
For validation in a form handler, use the standard call:
Если Не ПроверитьЗаполнение() Тогда
Возврат;
КонецЕсли;
Do not scatter assignments of Видимость, Доступность, and ТолькоПросмотр across different form handlers. Collect them in a single procedure and call it as the final step after initialization and data filling.
&НаКлиенте
Процедура ОбновитьВидимостьДоступностьЭлементовФормы()
ЭтоУслуга = Объект.ВидОперации = Перечисления.ВидыОпераций.Услуга;
Элементы.Склад.Видимость = Не ЭтоУслуга;
Элементы.Комментарий.ТолькоПросмотр = Объект.Проведен;
КонецПроцедуры
Prefer boolean algebra over Если ... Тогда ... Иначе ... branching when the property value is expressed directly by a condition.