Несколько шаблонов для доработки типовых конфигураций

Предлагаю несколько каркасов для создания новых объектов в типовых конфигурациях. Это выжимка из кода нескольких конфигураций, которая позволит быстро и красиво создавать и дорабатывать объекты метаданных с соблюдением идеологии исходной системы

1. Шаблон областей для модулей объектов, менеджеров, форм

Как многие знают, в типовых конфигурациях код модулей разделен на секции. Это помогает проще ориентироваться в коде. Я проанализировал различные объекты типовых конфигураций и создал заготовки для : 
    модуля формы
    модуля объекта
    модуля менеджера объекта
    общего модуля 
Замечание: конструкция #Область ИмяОбласти доступна под платформой 8.3, начиная с режима совместимости 8.2

Модуль формы
#Область ОбработчикиСобытийФормы



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

#Область ОбработчикиСобытийЭлементовШапкиФормы



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

#Область ОбработчикиСобытийЭлементовТаблицыФормы_ИмяТаблицы



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

#Область ОбработчикиКомандФормы



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

#Область СлужебныеПроцедурыИФункции



#КонецОбласти
Модуль объекта
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ПрограммныйИнтерфейс



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

#Область ОбработчикиСобытий



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

#Область СлужебныеПроцедурыИФункции



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

#КонецЕсли
Модуль менеджера
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ПрограммныйИнтерфейс



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

#Область СлужебныйПрограммныйИнтерфейс



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

#Область СлужебныеПроцедурыИФункции



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

#КонецЕсли
Общий модуль
#Область ПрограммныйИнтерфейс



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

#Область СлужебныйПрограммныйИнтерфейс



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

#Область СлужебныеПроцедурыИФункции



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

2. Шаблон проведения

Данная схема проверена на практике множество раз. Она понятная и расширяемая. Применяется практически для всех типовых документов. 

В модуле объекта заполняем два обработчика

Модуль объекта документа
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)

ДополнительныеСвойства.Вставить("ЭтоНовый", ЭтоНовый());
ДополнительныеСвойства.Вставить("РежимЗаписи", РежимЗаписи);

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

Процедура ОбработкаПроведения(Отказ, РежимПроведения)

ПроведениеСервер.ИнициализироватьДополнительныеСвойстваДляПроведения(Ссылка, ДополнительныеСвойства, РежимПроведения);

Документы.<НашДокумент>.ИнициализироватьДанныеДокумента(Ссылка, ДополнительныеСвойства);

ПроведениеСервер.ПодготовитьНаборыЗаписейКРегистрацииДвижений(ЭтотОбъект);

<ОбщийМодуль1>Сервер.Отразить<Регистр1>(ДополнительныеСвойства, Движения, Отказ);
<ОбщийМодуль1>Сервер.Отразить<Регистр2>(ДополнительныеСвойства, Движения, Отказ);


ПроведениеСервер.ЗаписатьНаборыЗаписей(ЭтотОбъект);

ПроведениеСервер.ОчиститьДополнительныеСвойстваДляПроведения(ДополнительныеСвойства);

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

 

В модуле менеджера выполняем заполнение движений следующим образом

Модуль менеджера документа
Процедура ИнициализироватьДанныеДокумента(ДокументСсылка, ДополнительныеСвойства, Регистры = Неопределено) Экспорт

////////////////////////////////////////////////////////////////////////////
// Создадим запрос инициализации движений

Запрос = Новый Запрос;
ЗаполнитьПараметрыИнициализации(Запрос, ДокументСсылка);

////////////////////////////////////////////////////////////////////////////
// Сформируем текст запроса

ТекстыЗапроса = Новый СписокЗначений;
ТекстЗапросаТаблица<Регистр1>(Запрос, ТекстыЗапроса, Регистры);
ТекстЗапросаТаблица<Регистр2>(Запрос, ТекстыЗапроса, Регистры);


ПроведениеСервер.ИницализироватьТаблицыДляДвижений(Запрос, ТекстыЗапроса, ДополнительныеСвойства.ТаблицыДляДвижений, Истина);

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

Процедура ЗаполнитьПараметрыИнициализации(Запрос, ДокументСсылка)

Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.УстановитьПараметр("Ссылка", ДокументСсылка);
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеДокумента.Дата как Период,
| ДанныеДокумента.Организация
|ИЗ
| Документ.<НашДокумент> КАК ДанныеДокумента
|ГДЕ
| ДанныеДокумента.Ссылка = &Ссылка";
Реквизиты = Запрос.Выполнить().Выбрать();
Реквизиты.Следующий();

Запрос.УстановитьПараметр("Период",   Реквизиты.Период);
Запрос.УстановитьПараметр("Организация", Реквизиты.Организация);

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

Функция ТекстЗапросаВременнаяТаблица<ТабличнаяЧасть1>(Запрос, ТекстыЗапроса)

ИмяРегистра = "ВременнаяТаблица<ТабличнаяЧасть1>";

ТекстЗапроса = "
|ВЫБРАТЬ
| тч.НомерСтроки            КАК НомерСтроки,
| тч.Поле1                  КАК Поле1,
| тч.Поле2            КАК Поле2
|ПОМЕСТИТЬ
| ВременнаяТаблица<ТабличнаяЧасть1>
|ИЗ
| Документ.<НашДокумент>.<ТабличнаяЧасть1> КАК тч
|ГДЕ
| тч.Ссылка = &Ссылка
|";

ТекстыЗапроса.Добавить(ТекстЗапроса, ИмяРегистра);
Возврат ТекстЗапроса;

КонецФункции

Функция ТекстЗапросаТаблица<Регистр1>(Запрос, ТекстыЗапроса, Регистры)

ИмяРегистра = "<Регистр1>";

Если НЕ ПроведениеСервер.ТребуетсяТаблицаДляДвижений(ИмяРегистра, Регистры) Тогда
Возврат "";
КонецЕсли;

Если НЕ ПроведениеСервер.ЕстьТаблицаЗапроса("ВременнаяТаблица<ТабличнаяЧасть1>", ТекстыЗапроса) Тогда
ТекстЗапросаВременнаяТаблица<ТабличнаяЧасть1>(Запрос, ТекстыЗапроса);
КонецЕсли;

ТекстЗапроса = "
|ВЫБРАТЬ
| &Период                                КАК Период,
| &Организация                           КАК Организация,
| ТаблицаТовары.НомерСтроки              КАК НомерСтроки,
| ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход) КАК ВидДвижения,
| ТаблицаТовары.Номенклатура             КАК Номенклатура,
| ТаблицаТовары.Количество               КАК Количество
|ИЗ
| ВременнаяТаблица<ТабличнаяЧасть1> КАК ТаблицаТовары
|
|УПОРЯДОЧИТЬ ПО
| НомерСтроки
|";

ТекстыЗапроса.Добавить(ТекстЗапроса, ИмяРегистра);
Возврат ТекстЗапроса;

КонецФункции

 

Хочу отметить, что в приведенной схеме сначала табличная часть считывается во временную таблицу, а затем при заполнении движений используется уже эта временная таблица. С одной стороны это исключает повторные обращения к постоянной таблице БД, с другой стороны предоставляет дополнительные удобства (например, можно добавить служебные поля, которые потом использовать). 
Кроме того, такая схема хорошо подходит при использовании автоматизированного тестирования (TDD).

 

3. Шаблон управляемой формы документа, в котором перед проведением требуется задавать вопросы пользователю с запретом модальных вызовов

В типовой конфигурации у формы такого документа есть два служебных реквизита: 

 

Также добавляется служебная команда


В 

Модуль формы документа (команды)
&НаКлиенте
Процедура ПровестиИЗакрыть(Команда)

ОбщегоНазначения.ПровестиИЗакрыть(ЭтаФорма);

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

 

Обработчики ПередЗаписью() и ПослеЗаписи() выглядит примерно так:

Модуль формы документа (обработчики событий)
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)

Если НеВыполнятьПроверкуПередЗаписью Тогда
НеВыполнятьПроверкуПередЗаписью = Ложь;
Возврат;
КонецЕсли;

Если ПараметрыЗаписи.РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда

Отказ = Истина;
ЗадатьВопрос1(ПараметрыЗаписи);

КонецЕсли;

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

&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)

ОбщегоНазначения.ВыполнитьДействияПослеЗаписи(ЭтаФорма, Объект, ПараметрыЗаписи);

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

&НаКлиенте
Процедура ПередЗакрытием(Отказ, СтандартнаяОбработка)

ПринудительноЗакрытьФорму = Истина;

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

 

Кроме того, добавляется  служебная процедура Подключаемый_ЗакрытьФорму(), т.к. закрытие формы в типовых выполняется через обработчик ожидания, который выставляется после записи.

Модуль формы документа
&НаКлиенте
Процедура Подключаемый_ЗакрытьФорму() Экспорт

Закрыть();

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

 

Дальше пишем наш код с проверками. Не забываем обработчики завершения делать экспортными. Я проверки написал так:

Модуль формы документа
&НаКлиенте
Процедура ЗадатьВопрос1(ПараметрыЗаписи)
Оповещение = Новый ОписаниеОповещения("ЗадатьВопрос1Завершение", ЭтаФорма, ПараметрыЗаписи);
ТекстВопроса = "Какой-то вопрос 1 ?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ЗадатьВопрос1Завершение(Результат, ПараметрыЗаписи) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
ЗадатьВопрос2(ПараметрыЗаписи);
КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ЗадатьВопрос2(ПараметрыЗаписи)
Оповещение = Новый ОписаниеОповещения("ЗадатьВопрос2Завершение", ЭтаФорма, ПараметрыЗаписи);
ТекстВопроса = "Какой-то вопрос 2 ?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
КонецПроцедуры

&НаКлиенте
Процедура ЗадатьВопрос2Завершение(Результат, ПараметрыЗаписи) Экспорт
Если Результат = КодВозвратаДиалога.Да Тогда
ПередЗаписьюЗавершение(ПараметрыЗаписи);
КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПередЗаписьюЗавершение(ПараметрыЗаписи)

Результат = ОбщегоНазначения.ОбработатьЗаписьОбъектаВФорме(ЭтаФорма, ПараметрыЗаписи);

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

Хочу обратить внимание, что процедура ПередЗаписью() на клиенте вызывается два раза. Причем первый раз в ней выставляется Отказ=Истина, и вызывается первая процедура из цепочки вопросов. Если пройдены все необходимые проверки, то ПередЗаписью() вызывается второй раз программно, при этом начинается запись. Все обработчики записи/проведения из модуля объекта и модуля формы вызываются только после второго попадания в процедуру ПередЗаписью()

Файлы к статье

Шаблон 1

 

Шаблон 2
Пустая конфигурация, которая выполняет единственное действие — проведение некоторого документа по описанной схеме.

 

Шаблон 3
Пустая конфигурация, которая только выполняет только одно действие — провести и закрыть.

 

Зачем я написал эту статью?

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

24 Comments

  1. Светлый ум

    (1) TODD22, чтобы ИТС почитать к нему доступ нужен

    Reply
  2. json

    (1) TODD22,

    хотелось бы узнать с какой страницей ИТС вы сравниваете и насколько материал представленный в данной статье совпадает с описанным там. Приведите ссылку на конкретный текст.

    Пока предполагаю, что вы путаете шаблон с примером. На ИТС приводятся примеры и методические рекомендации, то есть теория. Я привожу шаблоны, то есть кусочки кода из практики, которые можно вставить при разработке копипастом, изменив несколько деталей. Разница между шаблоном и примером в том, что в примере нужно думать, а в шаблоне вы берете код уже продуманный кем-то другим

    Reply
  3. TODD22

    (3)

    хотелось бы узнать с какой страницей ИТС вы сравниваете и насколько материал представленный в данной статье совпадает с описанным там. Приведите ссылку на конкретный текст.

    Про структуру модулей

    https://its.1c.ru/db/v8std#content:2149184104:hdoc

    Reply
  4. json

    (4) TODD22, на текущий момент у меня нет доступа к указанному вами разделу.

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

    Reply
  5. TODD22

    (5)

    о заменю первый раздел в текущей статье на что-нибудь другое

    Лучше тогда не заменить, а дополнить описанием какие разделы в модулях и для чего. Для тех у кого доступа к ИТС нет.

    Reply
  6. pbazeliuk

    (0) В «модуле менеджера объекта» забыли про директивы компиляции, это очень важно для работы конструктора запросов в «Управляемом приложении (Толстый клиент)», а так же для ЦУП.

    Reply
  7. json

    (7) pbazeliuk, замечание справедливо. Исправил в статье

    Reply
  8. klinval

    Вопрос про «3. Шаблон управляемой формы документа, в котором перед проведением требуется задавать вопросы пользователю с запретом модальных вызовов»:

    1. Для какой конфигурации у вас примеры? Например ОбщегоНазначения.ОбработатьЗаписьОбъектаВФорме например в БП 3.0.44.124 нет.

    Замечания:

    1. Не обработана ситуация, когда пользователь нажимает крестик. Он нажимает крестик задаётся 3 вопроса (1 стандартный — платформенный), пользователь ожидает, что документ закроется, но он остаётся. У меня есть статься по данной теме Как использовать ПоказатьВопрос в обработчике формы ПередЗаписью там эта ситуация разбирается.

    2. Лучше как нибудь по-другому назвать переменную НеВыполнятьПроверкуПередЗаписью. Тоже когда-то называли переменные с НЕ в начале. Потом появлялись конструкции вида:

    Если Не НеВыполнятьПроверкуПередЗаписью Тогда

    , которые ломали мозг при прочтении… Долго приходится соображать что делать если НеВыполнятьПроверкуПередЗаписью = Ложь. Плюс где-то на ИТС видел рекомендации 1С, что так называть не стоит.

    Думаю заменить НеВыполнятьПроверкуПередЗаписью на ОтключитьПроверкуПередЗаписью будет правильней. Логику менять не придётся, только название переменной.

    Reply
  9. TODD22

    (9) klinval,

    НеВыполнятьПроверкуПередЗаписью

    Да есть такая рекомендация на ИТС.

    Мне тут то же досталась самописная конфигурация там таких «Не НеЗагружено» и тд по 10 штук в одном модуле… сиди мозг ломай….

    Reply
  10. json

    (9) klinval, данный пример из ерпи. Модуль в оригинале называется ОбщегоНазначенияУТКлиент.

    Мое мнение такое: если я дорабатываю типовую конфигурацию, то стараюсь максимально следовать принципам, которые в ней используются. Если переменная называется так, и я с этим немного не согласен, то все равно использую подход принятый в системе.

    Это совершенно нормально, что не все согласны абсолютно со всеми именами переменных, процедур и т.д.

    Если каждый начнет применять собственный подходы (как обычно и происходит), то начинается каша.

    По поводу БП — я посмотрю чуть позже, как там реализовано, может быть имеет смысл дополнить статью, подумаю

    Reply
  11. json

    (9) klinval, не согласен с высказанным замечанием, что покрываются не все случаи. Ваше предположение не соответствует действительности. В моем примере кнопка «Х» не доступна.

    Reply
  12. klinval

    (11)

    Если переменная называется так, и я с этим немного не согласен, то все равно использую подход принятый в системе.

    Согласен, что делать то. Но я же не знал, что это вынужденное название из типовой конфы. Вот только получается 1С-ники не следуют своим же правилам…

    Reply
  13. TODD22

    (13) klinval,

    Вот только получается 1С-ники не следуют своим же правилам…

    Когда разбирался в стандартах разработки, я сразу смотрел в типовых(УТ 11 и БП 3). часто встречалось отличие от соглашения по написанию кода.

    Там то же люди пишут 🙂

    Reply
  14. klinval

    (12) я скачал ваш CF-ник. Развернул на пустую базу и крестик доступен (см. приложенный файл).

    Но допустим туда куда вы разворачивали крестик недоступен почему-то стал. Вы предлагаете другим тоже делать недоступным «Х» на всех стандартных или новых документах, чтобы предложенный функционал покрывал все случаи?

    Reply
  15. json

    (15) klinval, я невнимательно прочитал замечание. Ваша правда.

    Чуть позже посмотрю как это обходят в типовых. Исправлю в статье также.

    Reply
  16. klinval

    (16) мне Сергей Ожерельев (Поручик) сказал, что

    В форме контрагента в бухгалтерии 3.0 последних релизов тоже используется вопрос перед записью примерно по описанной методике.

    На тот момент (03.07.2015) так и было, сейчас же не смотрел. Я оттуда код и взял, т.к. сам до этого не мог обойти эту ситуацию. У меня способ изложен в п.5 статьи.

    По поводу покрытия на 100% когда «перед проведением требуется задавать вопросы «: к сожалению это невозможно. В той-же типовой БП вот с чем я столкнулся:

    В обработчик формы «ПередЗаписью» программа не заходит, если: 1) пользователь нажал на кнопку «Пометить на удаление / снять пометку»; 2) если пользователь нажал на не проведённом документе кнопку «ДТ/КТ». И это не всё: если вы на форме документа создали всё, как я написал, и пользователь из формы списка перепроведёт документ – то никаких вопросов программа ему не задаст. Необходимо все интересующие вас кнопки на форме списка заменять на свои и отслеживать действия пользователя. Ещё у документа может быть не одна форма документа, а несколько (например, документ ПоступлениеТоваровУслуг в БП 3.0, где 3 формы: общая, товары и услуги). В каждой форме документа надо написать много кода…

    + Можно провести документ с формы обработки (внешней или внутренней)… Некоторые моменты ещё можно обойти, но слишком трудозатратно. Да на 100% покрытия обычно и не требуется.

    Reply
  17. json

    (17) klinval, спасибо за подробное описание. Замечание абсолютно справедливо. Посмотрел решение в типовой.

    Добавил вот такой код:

    &НаКлиенте
    Процедура ПередЗакрытием(Отказ, СтандартнаяОбработка)
    
    ПринудительноЗакрытьФорму = Истина;
    
    КонецПроцедуры
    

    Теперь поведение корректное. Обновил в тексте публикации и в примере

    Reply
  18. vandalsvq
    Reply
  19. unichkin

    (1)(2)В ИТС уже несколько лет работает тест-драйв.

    Хорошая статья, такие нужны — и как можно больше. Чтобы наконец донести свет в массы:) По моему опыту — 6 из 10 программистов о ИТС тупо не знают. А из оставшихся трое — кладут на все это со словами «мне некогда», и тратят в 10 раз больше времени в попытках разобраться со своим-же кодом…

    Reply
  20. unichkin

    (14)

    Когда разбирался в стандартах разработки, я сразу смотрел в типовых(УТ 11 и БП 3). часто встречалось отличие от соглашения по написанию кода.

    Смотреть надо в БСП. Я думаю эти огрехи в типовых из-за старого кода — который писался еще в те времена, когда этот момент не был проработан.

    (19) По идее, все подобные области должны быть отнесены к разделу «СлужебныеПроцедурыИФункции». Основные разделы модуля регламентированы — именно к ним что-то добавлять не надо. Вот внутри разделов уже можно помещать свое. Скажем, если сложная форма и есть куча обработчиков элементов формы, их можно как-то логически разнести — по страницам например

    Reply
  21. webester

    (5)Вот вам то, что написано(смотрите вложение) если хотите иметь доступ к ИТС легально спросите меня как

    Reply
  22. rpgshnik

    Полезная информация.

    Reply
  23. Hans

    (5) не нужен доступ на эту статью.

    https://its.1c.ru/db/v8std#content:455:hdoc

    Reply
  24. Hans

    (20) Тест драйв ни о чем. Просто посмотреть что там есть вообще, или искать решение конкретной задачи. Толк от этого только сиюминутный.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *