Описание задачи.
В конфигурации 1С:Бухгалтерия для Украины ред. 1.2. (БП) существует возможность ручной корректировки движений документов. Необходимо перенести данную возможность в конфигурацию Управление торговым предприятием для Украины (УТП).
Решение задачи.
- Минусы — конфигурацию нужно снимать с замка и дорабатывать.
- Плюсы — Доработки будут сделаны с минимальным влиянием на обновление.
1. Снимем конфигурацию с замка.
Для того чтобы вносить в конфигурацию свои доработки необходимо Включить возможность изменения.
Для этого идем в Конфигуратор, и в главном меню переходим в меню Конфигурация — Поддержка — Настройка поддержки. В новой форме нажимаем на кнопку Включить возможность изменения. Программа нам говорит, что в этом случае будет невозможно выполнять обновление в автоматическом режиме. Но мы настроены серьезно и с этим соглашаемся, нажав Да. Откроется новая форма Настройка правил поддержки в которой мы ничего не меняем и просто нажимаем ОК.
После того как программа "подумает", кнопка Включить возможность изменения исчезнет. Это означает что можно продолжать.
Теперь два раза щелкаем по самой верхней строке в дереве, где написано УправлениеТорговымПредприятием, и открывшемся новом окне отмечаем Объект поставщика редактируется с сохранением поддержки. Нажимаем ОК.
Теперь аналогичные манипуляции необходимо проделать с документом Платежное поручение исходящее.
Для этого раскрываем ветку Документы, находим в ней ПлатежноеПоручениеИсходящее и проделываем аналогичные манипуляции.
На этом подготовка конфигурации завершена.
2. Добавляем новые общие модули.
Для этого разворачиваем ветку Общие, находим ветку Общие модули, и добавляем три новых общих модуля:
- script_ОбщегоНазначения
- script_ПлатежноеПоручениеИсходящее
- script_ПодпискиНаСобытияФорм
Теперь необходимо настроить свойства каждого общего модуля. Для этого на каждом их них нужно нажать правой кнопкой мыши, и из выпадающего меню выбрать пункт Свойства. Далее нужно установить галочки как показано на картинке ниже.
Теперь нужно вставить в каждый общий модуль текст программного кода, который можно взять из спойлеров расположенных ниже. Для этого нужно два раза щелкнуть ЛКМ на общем модуле. После этого программа откроет окно редактора текста общего модуля, в который нужно скопировать и вставить текст из соответствующего спойлера.
Текст общего модуля — script_ОбщегоНазначения
////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ И ФУНКЦИИ РАБОТЫ С ДВИЖЕНИЯМИ ДОКУМЕНТОВ
// Удаляет движения документа по указанному регистру либо по всем регистрам.
//
// Параметры:
// Движения - коллекция движений документа.
// ИмяРегистра - имя регистра, движения по которому будут удалены;
// необязательный, если не указан - удаляются все движения.
//
Процедура УдалитьДвиженияДокумента(Движения,ИмяРегистра = "") Экспорт
Если ИмяРегистра = "" Тогда
Для Каждого ДвиженияРегистра Из Движения Цикл
ДвиженияРегистра.Очистить();
КонецЦикла;
Иначе
Движения[ИмяРегистра].Очистить();
КонецЕсли;
КонецПроцедуры // УдалитьДвиженияДокумента()
// Процедура удаления существующих движений документа при перепроведении (отмене проведения)
Процедура УдалитьДвиженияРегистратора(ДокументОбъект, Отказ, РучнаяКорректировка = Ложь) Экспорт
// получение списка регистров, по которым существуют движения
ТаблицаДвижений = ПолныеПрава.ОпределитьНаличиеДвиженийПоРегистратору(ДокументОбъект.Ссылка);
ТаблицаДвижений.Колонки.Добавить("НаборЗаписей");
Для Каждого СтрокаДвижения ИЗ ТаблицаДвижений Цикл
// имя регистра передается как значение, полученное с помощью
// функции ПолноеИмя() метаданных регистра
ПозицияТочки = Найти(СтрокаДвижения.Имя, ".");
ТипРегистра = Лев(СтрокаДвижения.Имя, ПозицияТочки - 1);
ИмяРегистра = СокрП(Сред(СтрокаДвижения.Имя, ПозицияТочки + 1));
Если РучнаяКорректировка Тогда
Набор = ДокументОбъект.Движения[ИмяРегистра];
Набор.Прочитать();
Набор.УстановитьАктивность(Ложь);
Иначе
ЕСли ТипРегистра = "РегистрНакопления" Тогда
МетаданныеНабора = Метаданные.РегистрыНакопления[ИмяРегистра];
Набор = РегистрыНакопления[ИмяРегистра].СоздатьНаборЗаписей();
ИначеЕсли ТипРегистра = "РегистрБухгалтерии" Тогда
МетаданныеНабора = Метаданные.РегистрыБухгалтерии[ИмяРегистра];
Набор = РегистрыБухгалтерии[ИмяРегистра].СоздатьНаборЗаписей();
ИначеЕсли ТипРегистра = "РегистрСведений" Тогда
МетаданныеНабора = Метаданные.РегистрыСведений[ИмяРегистра];
Набор = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
ИначеЕсли ТипРегистра = "РегистрРасчета" Тогда
МетаданныеНабора = Метаданные.РегистрыРасчета[ИмяРегистра];
Набор = РегистрыРасчета[ИмяРегистра].СоздатьНаборЗаписей();
КонецЕсли;
Если НЕ ПравоДоступа("Изменение", МетаданныеНабора) Тогда
// отсутствуют права на всю таблицу регистра
ОбщегоНазначения.СообщитьОбОшибке(НСтр("ru='Нарушение прав доступа';uk='Порушення прав доступу'"), Отказ, СтрокаДвижения.Имя);
Возврат;
КонецЕсли;
Набор.Отбор.Регистратор.Установить(ДокументОбъект.Ссылка);
// набор не записывается сразу, чтобы не откатывать транзакцию, если впоследствии
// выяснится, что на один из регистров не хватает прав.
КонецЕсли;
СтрокаДвижения.НаборЗаписей = Набор;
КонецЦикла;
Для Каждого СтрокаДвижения ИЗ ТаблицаДвижений Цикл
Попытка
СтрокаДвижения.НаборЗаписей.Записать();
Исключение
// возможно «сработал» RLS или механизм даты запрета изменения
ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки(), Отказ, СтрокаДвижения.Имя);
ВызватьИсключение НСтр("ru='Операция не выполнена';uk='Операція не виконана'");
КонецПопытки;
КонецЦикла;
Если (НЕ Отказ) И (НЕ РучнаяКорректировка) Тогда
Для Каждого Движение ИЗ ДокументОбъект.Движения Цикл
Если Движение.Количество() > 0 Тогда
Движение.Очистить();
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
// Процедура включения активности движений при проведении документа, движения которого
// заданы вручную
Процедура ВключитьАктивностьПоРегистратору(ДокументОбъект, Отказ) Экспорт
// получение списка регистров, по которым существуют движения
ТаблицаДвижений = ПолныеПрава.ОпределитьНаличиеДвиженийПоРегистратору(ДокументОбъект.Ссылка);
ТаблицаДвижений.Колонки.Добавить("НаборЗаписей");
Для Каждого СтрокаДвижения ИЗ ТаблицаДвижений Цикл
// имя регистра передается как значение, полученное с помощью
// функции ПолноеИмя() метаданных регистра
ПозицияТочки = Найти(СтрокаДвижения.Имя, ".");
ТипРегистра = Лев(СтрокаДвижения.Имя, ПозицияТочки - 1);
ИмяРегистра = СокрП(Сред(СтрокаДвижения.Имя, ПозицияТочки + 1));
Набор = ДокументОбъект.Движения[ИмяРегистра];
Набор.Прочитать();
Набор.УстановитьАктивность(Истина);
//Проверим дату в движениях
УстановитьПериод = Истина;
Если ТипРегистра = "РегистрСведений"
И Метаданные.РегистрыСведений[ИмяРегистра].ПериодичностьРегистраСведений = Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
УстановитьПериод = Ложь;
КонецЕсли;
Если УстановитьПериод Тогда
Для Каждого Запись Из Набор Цикл
Запись.Период = ДокументОбъект.Дата;
КонецЦикла;
КонецЕсли;
СтрокаДвижения.НаборЗаписей = Набор;
КонецЦикла;
Для Каждого СтрокаДвижения ИЗ ТаблицаДвижений Цикл
Попытка
СтрокаДвижения.НаборЗаписей.Записать();
Исключение
// возможно «сработал» RLS или механизм даты запрета изменения
ОбщегоНазначения.СообщитьОбОшибке(ОписаниеОшибки(), Отказ, СтрокаДвижения.Имя);
ВызватьИсключение НСтр("ru='Операция не выполнена';uk='Операція не виконана'");
КонецПопытки;
КонецЦикла;
КонецПроцедуры
// Выполняет движение по регистру.
//
// Параметры:
// НаборДвижений - набор движений регистра.
//
Процедура ВыполнитьДвижениеПоРегистру(НаборДвижений, ВидДвижения = Неопределено) Экспорт
ТаблицаДвижений = НаборДвижений.мТаблицаДвижений;
Если ТаблицаДвижений.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Если ТаблицаДвижений.Колонки.Найти("Период") = Неопределено Тогда
ТаблицаДвижений.Колонки.Добавить("Период", ОбщегоНазначения.ПолучитьОписаниеТиповДаты(ЧастиДаты.ДатаВремя))
КонецЕсли;
МетаРег = НаборДвижений.Метаданные();
ИзмеренияСостТипа = Новый Структура;
Для Каждого МетаИзм Из МетаРег.Измерения Цикл
Если МетаИзм.Тип.Типы().Количество() > 1 Тогда
ИзмеренияСостТипа.Вставить(МетаИзм.Имя);
КонецЕсли;
КонецЦикла;
Для Каждого МетаИзм Из МетаРег.Реквизиты Цикл
Если МетаИзм.Тип.Типы().Количество() > 1 Тогда
ИзмеренияСостТипа.Вставить(МетаИзм.Имя);
КонецЕсли;
КонецЦикла;
Для Каждого МетаРес Из МетаРег.Ресурсы Цикл
Если МетаРес.Тип.Типы().Количество() > 1 Тогда
ИзмеренияСостТипа.Вставить(МетаРес.Имя);
КонецЕсли;
КонецЦикла;
// Откопируем остальные колонки (структура таблиц совпадает).
ПерваяКолонка = Истина;
МассивСтрок = Новый Массив(ТаблицаДвижений.Количество());
Для каждого Колонка Из ТаблицаДвижений.Колонки Цикл
ИмяКолонки = Колонка.Имя;
Если ИмяКолонки <> "Период"
И ИмяКолонки <> "Активность"
И ИмяКолонки <> "НомерСтроки"
И ИмяКолонки <> ""
И ?(ИмяКолонки = "ВидДвижения", ВидДвижения = Неопределено, Истина)
И ИмяКолонки <> "МоментВремени" Тогда
ФлагКолонкиСостТипа = (ИзмеренияСостТипа.Свойство(ИмяКолонки));
Индекс = 0;
Для каждого СтрокаТаблицы Из ТаблицаДвижений Цикл
Если ПерваяКолонка Тогда
Если ВидДвижения = ВидДвиженияНакопления.Приход Тогда
СтрокаДвижения = НаборДвижений.ДобавитьПриход();
ИначеЕсли ВидДвижения = ВидДвиженияНакопления.Расход Тогда
СтрокаДвижения = НаборДвижений.ДобавитьРасход();
Иначе
СтрокаДвижения = НаборДвижений.Добавить(); // Для оборотных регистров
КонецЕсли;
МассивСтрок[Индекс] = СтрокаДвижения;
Если СтрокаТаблицы.Период = '00010101' Тогда
СтрокаДвижения.Период = НаборДвижений.мПериод;
Иначе
СтрокаДвижения.Период = СтрокаТаблицы.Период;
КонецЕсли;
Иначе
СтрокаДвижения = МассивСтрок[Индекс];
КонецЕсли;
Индекс = Индекс + 1;
ЗначКолонки = СтрокаТаблицы[ИмяКолонки];
Если ФлагКолонкиСостТипа Тогда
Если ЗначКолонки = Неопределено ИЛИ НЕ ЗначениеЗаполнено(ЗначКолонки) Тогда
СтрокаДвижения[ИмяКолонки] = Неопределено;
Иначе
СтрокаДвижения[ИмяКолонки] = ЗначКолонки;
КонецЕсли;
Иначе
СтрокаДвижения[ИмяКолонки] = ЗначКолонки;
КонецЕсли;
КонецЦикла;
ПерваяКолонка = Ложь;
КонецЕсли;
КонецЦикла;
КонецПроцедуры // ВыполнитьДвижениеПоРегистру()
// Подписка на событие для проверки на ручную корректировку
Процедура ОбработкаПроведения(ЭтотОбъект, Отказ, РежимПроведения) Экспорт
Если ОбщегоНазначения.ЕстьРеквизитДокумента("script_РучнаяКорректировка", ЭтотОбъект.Метаданные()) Тогда
Если ЭтотОбъект.script_РучнаяКорректировка Тогда
ОбщегоНазначения.СообщитьОбОшибке("Движения документа исправлены вручную!", Отказ);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
// Подписка на событие для проверки на ручную корректировку
Процедура ОбработкаУдаленияПроведения(ЭтотОбъект, Отказ, РежимПроведения) Экспорт
Если ОбщегоНазначения.ЕстьРеквизитДокумента("script_РучнаяКорректировка", ЭтотОбъект.Метаданные()) Тогда
Если ЭтотОбъект.script_РучнаяКорректировка Тогда
ЭтотОбъект.script_РучнаяКорректировка = Ложь;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
#Если Клиент Тогда
// Отображает картинку в ФормеСписка
//
Процедура РучнаяКорректировкаСписокПриПолученииДанных(ОформленияСтрок) Экспорт
Картинка = БиблиотекаКартинок.ИзменитьЭлектронноеПисьмо;
Для Каждого Строка Из ОформленияСтрок Цикл
Если Строка.ДанныеСтроки.script_РучнаяКорректировка Тогда
Строка.Ячейки.script_РучнаяКорректировка.УстановитьКартинку(Картинка);
Иначе
Строка.Ячейки.script_РучнаяКорректировка.Текст = "";
КонецЕсли;
КонецЦикла
КонецПроцедуры // РучнаяКорректировкаСписокПриПолученииДанных()
// Открывает ОсновнуюФорму ручной корректировки
//
Процедура РучнаяКорректировкаОсновнаяФорма(ЭтоНовый,Ссылка,ЭтотОбъект) Экспорт
Если НЕ ЭтоНовый Тогда
ФормаНастройки = Обработки.script_КорректировкаДвижений.ПолучитьФорму("ОсновнаяФорма", ЭтотОбъект, Строка(Ссылка));
ФормаНастройки.ДокументОбъект = ЭтотОбъект;
Если ФормаНастройки.Открыта() Тогда
Для Каждого СтрокаТаблицыРегистров из ФормаНастройки.Регистры Цикл
СтрокаТаблицыРегистров.Прочитан = Ложь;
//СтрокаТаблицыРегистров.Отрисован = Ложь;
КонецЦикла;
КолСтраниц = ФормаНастройки.ЭлементыФормы.ОсновнаяПанель.Страницы.количество()-1;
Для НомерСтраницы = 0 По КолСтраниц Цикл
Страница = ФормаНастройки.ЭлементыФормы.ОсновнаяПанель.Страницы[КолСтраниц-НомерСтраницы];
Если Страница.Видимость и Страница.Значение="" Тогда
ФормаНастройки.ЭлементыФормы.ОсновнаяПанель.Страницы.Удалить(ФормаНастройки.ЭлементыФормы.ОсновнаяПанель.Страницы.Индекс(Страница));
КонецЕсли;
КонецЦикла;
КонецЕсли;
ФормаНастройки.Открыть();
КонецЕсли;
КонецПроцедуры // РучнаяКорректировкаОсновнаяФорма()
// Открывает ОсновнуюФорму ручной корректировки после записи
//
Процедура РучнаяКорректировкаОсновнаяФормаПослеЗаписи(Проведен,ОткрыватьНастройкуДвижений,Ссылка,ЭтотОбъект) Экспорт
Если Проведен И ОткрыватьНастройкуДвижений Тогда
ФормаНастройки = Обработки.script_КорректировкаДвижений.ПолучитьФорму("ОсновнаяФорма", ЭтотОбъект, Строка(Ссылка));
Если ФормаНастройки.Открыта() Тогда
СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("Отображение", Истина);
НайденныеСтроки = ФормаНастройки.Регистры.НайтиСтроки(СтруктураОтбора);
Для Каждого Строка Из НайденныеСтроки Цикл
ЭтотОбъект.Движения[Строка.Имя].Прочитать();
КонецЦикла;
КонецЕсли;
Если ОткрыватьНастройкуДвижений Тогда
ФормаНастройки.ДокументОбъект = ЭтотОбъект;
ФормаНастройки.Открыть();
КОнецЕсли;
КОнецЕсли;
КонецПроцедуры // РучнаяКорректировкаОсновнаяФормаПослеЗаписи()
#КонецЕсли
// Проверка ручной корректировки
//
Функция РучнаяКорректировкаОбработкаПроведения(РучнаяКорректировка,Отказ,Заголовок,ЭтотОбъект) Экспорт
Если РучнаяКорректировка Тогда
ВключитьАктивностьПоРегистратору(ЭтотОбъект, Отказ);
ТекстСообщения = НСтр("ru='Движения документа отредактированы вручную и не могут быть автоматически актуализированы.';uk='Рухи документа відредаговані вручну й не можуть бути автоматично актуалізовані.'");
ОбщегоНазначения.СообщитьОбОшибке(ТекстСообщения, ,Заголовок);
Возврат Истина;
Иначе
УдалитьДвиженияРегистратора(ЭтотОбъект, Отказ, РучнаяКорректировка);
Возврат Ложь;
КонецЕсли;
КонецФункции // РучнаяКорректировкаОбработкаПроведения()
Текст общего модуля — script_ПлатежноеПоручениеИсходящее
////////////////////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ МОДУЛЯ ФОРМЫ СПИСКА
Процедура ФормаСпискаПередОткрытием(ЭтаФорма) Экспорт
НоваяКолонка = ЭтаФорма.ЭлементыФормы.ДокументСписок.Колонки.Добавить("script_РучнаяКорректировка");
НоваяКолонка.ТекстШапки = "";
НоваяКолонка.КартинкаШапки = БиблиотекаКартинок.ИзменитьЭлектронноеПисьмо;
НоваяКолонка.Данные = "script_РучнаяКорректировка";
НоваяКолонка.Ширина = 3;
НоваяКолонка.ИзменениеРазмера = ИзменениеРазмераКолонки.НеИзменять;
Колонки = ЭтаФорма.ЭлементыФормы.ДокументСписок.Колонки;
Колонка = ЭтаФорма.ЭлементыФормы.ДокументСписок.Колонки["script_РучнаяКорректировка"];
// сдвигаем колонку в самое начало табличного поля.
Колонки.Сдвинуть(Колонка, -1000); // больше 1000 колонок неверное не будет никогда
// теперь сдвигаем на нужную позицию
// нужно учитывать тот факт что форма еще не открылаль
// поэтому при расчете позиции нужно учитывать скрытые колонки
Колонки.Сдвинуть(НоваяКолонка, 1);
КонецПроцедуры
Процедура ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок) Экспорт
Если Элемент.Колонки.script_РучнаяКорректировка.Видимость Тогда
script_ОбщегоНазначения.РучнаяКорректировкаСписокПриПолученииДанных(ОформленияСтрок);
КонецЕсли;
КонецПроцедуры
// Процедура вызывается при нажатии кнопки "Изменить" командной панели
// табличного поля "Товары", вызывает сервисный механизм для
// группового изменения значений реквизитов табличной части "Товары".
//
Процедура ДокументСписокДействияФормыПроводкиДтКт(ЭтотОбъект, ЭлементыФормы, Кнопка) Экспорт
Если ЭлементыФормы.ДокументСписок.ТекущиеДанные = Неопределено тогда
Возврат
КонецЕсли;
script_ОбщегоНазначения.РучнаяКорректировкаОсновнаяФорма(Ложь,ЭлементыФормы.ДокументСписок.ТекущиеДанные.Ссылка,
ЭлементыФормы.ДокументСписок.ТекущиеДанные.Ссылка.ПолучитьОбъект());
КонецПроцедуры // ДействияФормыДействияФормыПроводкиДтКт()
////////////////////////////////////////////////////////////////////////////////////////////////
// ПРОЦЕДУРЫ МОДУЛЯ ФОРМЫ ДОКУМЕНТА
// Процедура вызывается при нажатии кнопки "Изменить" командной панели
// табличного поля "Товары", вызывает сервисный механизм для
// группового изменения значений реквизитов табличной части "Товары".
//
Процедура ДействияФормыДействияФормыПроводкиДтКт(ЭтотОбъект, ЭтаФорма, Кнопка) Экспорт
script_ОбщегоНазначения.РучнаяКорректировкаОсновнаяФорма(ЭтотОбъект.ЭтоНовый(), ЭтотОбъект.Ссылка, ЭтотОбъект);
КонецПроцедуры // ДействияФормыДействияФормыПроводкиДтКт()
Текст общего модуля — script_ПодпискиНаСобытияФорм
// Переопределяет обработчик события формы.
// Сохраняет штатный обработчик события внутри формы и устанавливает новый.
//
// Параметры:
// пФорма – Форма – форма;
// *пИмяСобытия – Строка – имя события;
// *пИмяЭлементаФормы – Строка – полное имя элемента формы.
// *пЛиИсключительное – Булево – сообщать о наличии старого обработчика;
// *пПсевдонимЭлементаФормы – Строка – псевдоним элемента формы, используется для назначения одного обработчика
// для нескольких однотипных событий формы.
//
Процедура script_УстановитьДействиеФормы(пФорма, пИмяСобытия = "", пИмяЭлементаФормы = "",
пЛиИсключительное = Ложь, пПсевдонимЭлементаФормы = "") Экспорт
Если пИмяЭлементаФормы <> "" Тогда
МассивФрагментов = script_ПолучитьМассивИзСтрокиСРазделителем(пИмяЭлементаФормы);
ИмяЭлементаФормы = МассивФрагментов[0];
Объект = пФорма.ЭлементыФормы[ИмяЭлементаФормы];
Если МассивФрагментов.Количество() > 1 Тогда
ИмяЭлементаФормы = ИмяЭлементаФормы + МассивФрагментов[1];
Если ТипЗнч(Объект) = Тип("ТабличноеПоле") Тогда
Объект = Объект.Колонки[МассивФрагментов[1]].ЭлементУправления;
ИначеЕсли ТипЗнч(Объект) = Тип("КоманднаяПанель") Тогда
Объект = Объект.Кнопки[МассивФрагментов[1]];
//{SCRIPT}
Если МассивФрагментов.Количество() > 2 Тогда
ИмяЭлементаФормы = ИмяЭлементаФормы + МассивФрагментов[2];
Если ТипЗнч(Объект) = Тип("КнопкаКоманднойПанели") Тогда
Объект = Объект.Кнопки[МассивФрагментов[2]];
КонецЕсли;
КонецЕсли;
//{SCRIPT}
КонецЕсли;
КонецЕсли;
Иначе
Объект = пФорма;
ИмяЭлементаФормы = "";
КонецЕсли;
ТекстМаркера = "рСтарыеОбработчики";
Если пФорма.ЭлементыФормы.Найти(ТекстМаркера) = Неопределено Тогда
пФорма.ЭлементыФормы.Добавить(Тип("ПолеВвода"), ТекстМаркера, Ложь);
КонецЕсли;
Если пПсевдонимЭлементаФормы <> "" Тогда
ПолноеИмяСобытия = пПсевдонимЭлементаФормы + пИмяСобытия;
Иначе
ПолноеИмяСобытия = ИмяЭлементаФормы + пИмяСобытия;
КонецЕсли;
Если ТипЗнч(Объект) = Тип("КнопкаКоманднойПанели") Тогда
СтароеДействие = Объект.Действие;
Иначе
СтароеДействие = Объект.ПолучитьДействие(пИмяСобытия);
КонецЕсли;
Если пЛиИсключительное и СтароеДействие <> Неопределено Тогда
Сообщить("Конфликт обработчиков события """ + пИмяСобытия + """ объекта """ + Строка(Объект) + """",
СтатусСообщения.Важное);
КонецЕсли;
НовоеДействие = Новый Действие("script_" + ПолноеИмяСобытия);
// Для отладки здесь следует отключать попытку. Она используется для назначения унифицированных обработчиков,
// наличие которых не является обязательным.
Попытка
Если ТипЗнч(Объект) = Тип("КнопкаКоманднойПанели") Тогда
Объект.Действие = НовоеДействие;
Иначе
Объект.УстановитьДействие(пИмяСобытия, НовоеДействие);
КонецЕсли;
Исключение
Возврат;
КонецПопытки;
СписокСоответствияОбработчиков = пФорма.ЭлементыФормы.рСтарыеОбработчики.СписокВыбора;
СписокСоответствияОбработчиков.Добавить(ИмяЭлементаФормы + "." + пИмяСобытия, СтароеДействие);
КонецПроцедуры // УстановитьДействиеФормы()
// Получаем стандартную строку аргументов для выполнения вызова обработчика события формы.
//
// Параметры:
// пОбъект – Форма, ЭлементФормы – объект события;
// *пИмяСобытия – Строка – имя события.
//
// Возвращаемое значение:
// – Строка – строка аргументов вида "([Аргумент1][, Аргумент2, ... , АргументN]);",
// где аргументы имеют свои стандартные названия для каждого события.
//
Функция script_ПолучитьАргументыДействияФормы(пОбъект, пИмяСобытия = "")
Аргументы = "";
Если ТипЗнч(пОбъект) = Тип("КнопкаКоманднойПанели") Тогда
// Это действие кнопки командной панели формы
Аргументы = "(Кнопка)";
ИначеЕсли ТипЗнч(пОбъект) = Тип("Форма") Тогда
// Это событие формы
Попытка
ОбъектОсновнойРеквизитФормы = пОбъект.ЭтотОбъект;
Исключение
ОбъектОсновнойРеквизитФормы = Неопределено;
КонецПопытки;
Если Аргументы = "" Тогда
Если пИмяСобытия = "ВнешнееСобытие" Тогда
Аргументы = "(Источник, Событие, Данные)";
ИначеЕсли пИмяСобытия = "ОбработкаАктивизацииОбъекта" Тогда
Аргументы = "(АктивныйОбъект, Источник)";
ИначеЕсли пИмяСобытия = "ОбработкаВыбора" Тогда
Аргументы = "(ЗначениеВыбора, Источник)";
ИначеЕсли пИмяСобытия = "ОбработкаЗаписиНовогоОбъекта" Тогда
Аргументы = "(Объект, Источник)";
ИначеЕсли пИмяСобытия = "ОбработкаОповещения" Тогда
Аргументы = "(ИмяСобытия, Параметр, Источник)";
ИначеЕсли пИмяСобытия = "ПередЗакрытием" Тогда
Аргументы = "(Отказ, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ПередЗаписью" Тогда
Если script_ПолучитьКорневойТипКонфигурации(ОбъектОсновнойРеквизитФормы) = "Документ" Тогда
Аргументы = "(Отказ, РежимЗаписи, РежимПроведения)";
Иначе
Аргументы = "(Отказ)";
КонецЕсли;
ИначеЕсли пИмяСобытия = "ПередОткрытием" Тогда
Аргументы = "(Отказ, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ПриЗаписи" Тогда
Аргументы = "(Отказ)";
ИначеЕсли пИмяСобытия = "ПриПовторномОткрытии" Тогда
Аргументы = "(СтандартнаяОбработка)";
Иначе
Аргументы = "()";
КонецЕсли;
КонецЕсли;
Иначе
// Это событие элемента формы
Если ТипЗнч(пОбъект) = Тип("ТабличноеПоле") Тогда
Если пИмяСобытия = "Выбор" Тогда
Аргументы = "(Элемент, ВыбраннаяСтрока, Колонка, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Перетаскивание" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка)";
ИначеЕсли пИмяСобытия = "ПроверкаПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Строка, Колонка)";
КонецЕсли;
ИначеЕсли ТипЗнч(пОбъект) = Тип("ТабличныйДокумент") Тогда
Если пИмяСобытия = "Выбор" Тогда
Аргументы = "(Элемент, Область, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Перетаскивание" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Область)";
ИначеЕсли пИмяСобытия = "ПроверкаПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка, Область)";
КонецЕсли;
КонецЕсли;
Если Аргументы = "" Тогда
Если пИмяСобытия = "АвтоПодборТекста" Тогда
Аргументы = "(Элемент, Текст, ТекстАвтоПодбора, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "НачалоВыбора" Тогда
Аргументы = "(Элемент, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "НачалоПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаВыбора" Тогда
Аргументы = "(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаЗаписиНовогоОбъекта" Тогда
Аргументы = "(Элемент, Объект, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОбработкаРасшифровки" Тогда
Аргументы = "(Элемент, Расшифровка, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОкончаниеВводаТекста" Тогда
Аргументы = "(Элемент, Текст, Значение, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ОкончаниеПеретаскивания" Тогда
Аргументы = "(Элемент, ПараметрыПеретаскивания, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Открытие" Тогда
Аргументы = "(Элемент, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "Очистка" Тогда
Аргументы = "(Элемент, СтандартнаяОбработка)";
ИначеЕсли пИмяСобытия = "ПередНачаломДобавления" Тогда
Аргументы = "(Элемент, Отказ, Копирование)";
ИначеЕсли пИмяСобытия = "ПередНачаломИзменения" Тогда
Аргументы = "(Элемент, Отказ)";
ИначеЕсли пИмяСобытия = "ПередОкончаниемРедактирования" Тогда
Аргументы = "(Элемент, НоваяСтрока, ОтменаРедактирования, Отказ)";
ИначеЕсли пИмяСобытия = "ПередУдалением" Тогда
Аргументы = "(Элемент, Отказ)";
ИначеЕсли пИмяСобытия = "ПриВыводеСтроки" Тогда
Аргументы = "(Элемент, ОформлениеСтроки, ДанныеСтроки)";
ИначеЕсли пИмяСобытия = "ПриИзмененииСодержимогоОбласти" Тогда
Аргументы = "(Элемент, Область)";
ИначеЕсли пИмяСобытия = "ПриНачалеРедактирования" Тогда
Аргументы = "(Элемент, НоваяСтрока, Копирование)";
ИначеЕсли пИмяСобытия = "ПриОкончанииРедактирования" Тогда
Аргументы = "(Элемент, НоваяСтрока, ОтменаРедактирования)";
ИначеЕсли пИмяСобытия = "ПриОкончанииРедактированияИнтервала" Тогда
Аргументы = "(Элемент, Интервал, Отмена)";
ИначеЕсли пИмяСобытия = "ПриПолученииДанных" Тогда
Аргументы = "(Элемент, ОформленияСтрок)";
ИначеЕсли пИмяСобытия = "ПриСменеСтраницы" Тогда
Аргументы = "(Элемент, ТекущаяСтраница)";
ИначеЕсли пИмяСобытия = "Регулирование" Тогда
Аргументы = "(Элемент, Направление, СтандартнаяОбработка)";
Иначе
Аргументы = "(Элемент)";
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат Аргументы + ";";
КонецФункции // ПолучитьАргументыДействияФормы()
// Получаем текст для выполнения вызова старого обработчика события формы или элемента формы.
//
// Параметры:
// пФорма – Форма – форма;
// *пИмяСобытия – Строка – имя события;
// *пИмяЭлементаФормы – Строка – полное имя элемента формы.
//
// Возвращаемое значение:
// – Строка – текст для выполнения.
//
Функция script_ПолучитьСтароеДействиеФормы(пФорма, пИмяСобытия = "", пИмяЭлементаФормы = "") Экспорт
Если пИмяЭлементаФормы <> "" Тогда
МассивФрагментов = script_ПолучитьМассивИзСтрокиСРазделителем(пИмяЭлементаФормы);
ИмяЭлементаФормы = МассивФрагментов[0];
Объект = пФорма.ЭлементыФормы[ИмяЭлементаФормы];
Если МассивФрагментов.Количество() > 1 Тогда
ИмяЭлементаФормы = ИмяЭлементаФормы + МассивФрагментов[1];
Если ТипЗнч(Объект) = Тип("ТабличноеПоле") Тогда
Объект = Объект.Колонки[МассивФрагментов[1]].ЭлементУправления;
ИначеЕсли ТипЗнч(Объект) = Тип("КоманднаяПанель") Тогда
Объект = Объект.Кнопки[МассивФрагментов[1]];
//{SCRIPT}
Если МассивФрагментов.Количество() > 2 Тогда
ИмяЭлементаФормы = ИмяЭлементаФормы + МассивФрагментов[2];
Если ТипЗнч(Объект) = Тип("КнопкаКоманднойПанели") Тогда
Объект = Объект.Кнопки[МассивФрагментов[2]];
КонецЕсли;
КонецЕсли;
//{SCRIPT}
КонецЕсли;
КонецЕсли;
Иначе
ИмяЭлементаФормы = "";
Объект = пФорма;
КонецЕсли;
СтарыйОбработчик = "";
СписокСоответствияОбработчиков = пФорма.ЭлементыФормы.рСтарыеОбработчики.СписокВыбора;
ЭлементСписка = СписокСоответствияОбработчиков.НайтиПоЗначению(ИмяЭлементаФормы + "." + пИмяСобытия);
Если ЭлементСписка <> Неопределено Тогда
СтарыйОбработчик = ЭлементСписка.Представление;
КонецЕсли;
Если СтарыйОбработчик <> "" Тогда
СтарыйОбработчик = СтарыйОбработчик + script_ПолучитьАргументыДействияФормы(Объект, пИмяСобытия);
КонецЕсли;
Возврат СтарыйОбработчик;
КонецФункции // ПолучитьСтароеДействиеФормы()
// Получаем текст для выполнения вызова обработчика события формы.
// Текст включает инициализацию параметра Элемент для событий элементов формы.
//
// Параметры:
// пФорма – Форма – форма;
// *пИмяСобытия – Строка – имя события;
// *пИмяЭлементаФормы – Строка – полное имя элемента формы.
//
// Возвращаемое значение:
// – Строка – текст для выполнения.
//
Функция script_ПолучитьДействиеФормы(пФорма, пИмяСобытия = "", пИмяЭлементаФормы = "") Экспорт
// Явное "определение" типа переменных.
Если Ложь Тогда
пФорма = ПолучитьОбщуюФорму(0);
КонецЕсли;
Если пИмяЭлементаФормы <> "" Тогда
МассивФрагментов = script_ПолучитьМассивИзСтрокиСРазделителем(пИмяЭлементаФормы);
Объект = пФорма.ЭлементыФормы[МассивФрагментов[0]];
Если МассивФрагментов.Количество() > 1 Тогда
ИмяСубэлемента = МассивФрагментов[1];
ИмяЭлементаФормы = ИмяЭлементаФормы + ИмяСубэлемента;
Если ТипЗнч(Объект) = Тип("ТабличноеПоле") Тогда
Объект = Объект.Колонки[ИмяСубэлемента].ЭлементУправления;
УстановкаПараметров = "Элемент = ЭлементыФормы." + ИмяЭлементаФормы + "Колонки["
+ ИмяСубэлемента + "].ЭлементУправления;" + Символы.ПС;
ИначеЕсли ТипЗнч(Объект) = Тип("КоманднаяПанель") Тогда
Объект = Объект.Кнопки[ИмяСубэлемента];
УстановкаПараметров = "Кнопка = ЭлементыФормы." + ИмяЭлементаФормы + "Кнопки["
+ ИмяСубэлемента + "];" + Символы.ПС;
Иначе
Сообщить("Неизвестный тип элемента """ + ТипЗнч(Объект) + """ с субэлементом """
+ ИмяСубэлемента + """ при получении действия формы");
КонецЕсли;
Иначе
УстановкаПараметров = "Элемент = ЭлементыФормы." + пИмяЭлементаФормы + ";" + Символы.ПС;
КонецЕсли;
Иначе
Объект = пФорма;
КонецЕсли;
Обработчик = Строка(Объект.ПолучитьДействие(пИмяСобытия));
Если Обработчик <> "" Тогда
Обработчик = УстановкаПараметров + Обработчик + script_ПолучитьАргументыДействияФормы(Объект, пИмяСобытия)
+ Символы.ПС;
КонецЕсли;
Возврат Обработчик;
КонецФункции // ПолучитьДействиеФормы()
// Функция разбивает строку разделителем.
//
// Параметры:
// пСтрока - Строка - которую разбиваем;
// *пРазделитель - Строка - символ-разделитель.
//
// Возвращаемое значение:
// - Массив - содержащий фрагменты, на которые разбивает строку разделитель.
//
Функция script_ПолучитьМассивИзСтрокиСРазделителем(пСтрока, пРазделитель = ".") Экспорт
Массив = Новый Массив;
лСтрока = СтрЗаменить(пСтрока, пРазделитель, Символы.ПС);
Для Счетчик = 1 По СтрЧислоСтрок(лСтрока) Цикл
Массив.Добавить(СтрПолучитьСтроку(лСтрока, Счетчик));
КонецЦикла;
Возврат Массив;
КонецФункции // ПолучитьМассивИзСтрокиСРазделителем()
// Определяет корневой тип конфигурации.
//
// Параметры:
// пЗначение – Произвольный – проверяемое значение.
//
// Возвращаемое значение:
// Строка – имя корневого типа конфигурации метаданных;
// Неопределено – значение не является объектом коневого типа конфигурации.
//
Функция script_ПолучитьКорневойТипКонфигурации(пЗначение) Экспорт
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(пЗначение));
Если ОбъектМетаданных <> Неопределено Тогда
МассивФрагментов = script_ПолучитьМассивИзСтрокиСРазделителем(ОбъектМетаданных.ПолноеИмя());
Если МассивФрагментов.Количество() = 2 Тогда
Возврат МассивФрагментов[0];
Иначе
// Ссылка на субобъект
КонецЕсли;
КонецЕсли;
Возврат Неопределено;
КонецФункции // ПолучитьКорневойТипКонфигурации()
С общими модулями тоже закончили. Идем дальше.
3. Добавление подписок на событие.
Теперь необходимо добавить две подписки на события:
- Обработка проведения
- Обработка отмены проведения
Данные обработчики перехватывают и обрабатывают попытку проведения или отмены проведения для тех документов, движения которых будут отредактированы пользователем вручную.
Для этого в дереве метаданных находим ветку Подписки на события и добавляем новые подписки:
- script_РучнаяКорректировка_ОбработкаПроведения
- script_РучнаяКорректировка_ОбработкаУдаленияПроведения
После этого каждую подписку нужно настроить так, как показано на картинке ниже.
С подписками тоже разобрались.
4. Добавление нового реквизита — Ручная корректировка.
Теперь в документ Платежное поручение исходящее необходимо добавить новый реквизит объекта — Ручная корректировка.
Примечание
В УТП установлен режим совместимости 8.2.13. Из-за этого мы не можем использовать объект метаданных — Общие реквизиты. Но разработчик всегда может самостоятельно поднять режим совместимости до версии 8.2.14. Это позволит не добавлять новый реквизит в каждый документ, но добавить один общий для всех реквизит — Ручная корректировка.
Но мы пойдем по пути добавления реквизита в документ.
Открываем окно дерева метаданных, разворачиваем ветку Документы, находим в ней документ ПлатежноеПоручениеИсходящее, и раскрываем его ветку. Далее раскрываем ветку Реквизиты и добавляем новый реквизит script_РучнаяКорректировка (Тип значения: Булево).
5. Изменения в форме документа.
После этого необходимо внести изменения в форму документа. Нам нужно чтобы при нажатии на кнопку ДтКт открывалась новая форма Корректировка движений.
Но мы не будем изменять родную форму — мы ее скопируем, назначим скопированную форму основной, и все доработки будем делать на скопированной форме.
Кроме того, нам нужно изменить форму программно, чтобы в случае обновления затрачивать минимум времени.
Для этого разворачиваем ветку Формы, находим в ней — ФормаДокумента и копируем ее. В результате появится еще одна форма — ФормаДокумента1. Переименовываем ее на scipt_ФормаДокумента.
Далее два раза щелкаем ЛКП на строке с именем документа — ПлатежноеПоручениеИсходящее. В результате откроется окно, в котором нужно перейти на закладку Формы. На этой закладке нам нужно заменить оригинальную форму документа на новую scipt_ФормаДокумента.
После этих манипуляций основной формой документа станет новая скопированная форма.
Теперь будем вносить изменения в модуль новой формы.
Для этого щелкаем на ней ЛКМ, и переходим на закладку Модуль. Опускаемся в самый конец текста и после всех строк вставляем кусок кода, расположенный ниже под спойлером:
///////////////////////////////////////////////////////////////////////
///////////////////////// SCRIPT ////////////////////////////////////
///////////////////////// НАЧАЛО ////////////////////////////////////
// Процедура открывате журнал проводок БУ с отбором по текущему регистратору
//
Процедура script_ДействияФормыПроводкиДтКтДействие(Кнопка)
script_ПлатежноеПоручениеИсходящее.ДействияФормыДействияФормыПроводкиДтКт(ЭтотОбъект, ЭтаФорма, Кнопка);
КонецПроцедуры // ДействияФормыПроводкиДтКтДействие()
// Переопределение обработчиков
script_УстановитьДействиеФормы(ЭтаФорма, "Действие", "ДействияФормы.ПроводкиДтКт");
///////////////////////// КОНЕЦ ////////////////////////////////////
///////////////////////// SCRIPT ////////////////////////////////////
///////////////////////////////////////////////////////////////////////
6. Изменения в форме списка (журнале) документов.
Выполняем те же манипуляции с формой списка, аналогичные — с формой документа. Т.е. копируем родную форму, переименовываем скопированную форму и устанавливаем ее основной вместо оригинальной. В результате получится должно так, как показано на картинке.
После этого открываем скопированную форму, переходим в конец модуля и вставляем следующий кусок кода:
///////////////////////////////////////////////////////////////////////
///////////////////////// SCRIPT ////////////////////////////////////
///////////////////////// НАЧАЛО ////////////////////////////////////
// Процедура открывате журнал проводок БУ с отбором по текущему регистратору
//
Процедура script_ПередОткрытием(Отказ, СтандартнаяОбработка)
script_ПлатежноеПоручениеИсходящее.ФормаСпискаПередОткрытием(ЭтаФорма);
КонецПроцедуры // ДействияФормыПроводкиДтКт()
// Процедура открывате журнал проводок БУ с отбором по текущему регистратору
//
Процедура script_ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок)
script_ПлатежноеПоручениеИсходящее.ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок);
КонецПроцедуры // ПриПолученииДанных()
// Процедура открывате журнал проводок БУ с отбором по текущему регистратору
//
Процедура script_ДействияФормыПроводкиДтКтДействие(ЭтотОбъект, ЭтаФорма, Кнопка)
script_ПлатежноеПоручениеИсходящее.ДокументСписокДействияФормыПроводкиДтКт(ЭтотОбъект, ЭлементыФормы, Кнопка);
КонецПроцедуры // ДействияФормыПроводкиДтКт()
////////////////////////////////////////////////////////////////////////////////
// ОПЕРАТОРЫ ОСНОВНОЙ ПРОГРАММЫ
script_УстановитьДействиеФормы(ЭтаФорма, "ПередОткрытием");
script_УстановитьДействиеФормы(ЭтаФорма, "ПриПолученииДанных", "ДокументСписок");
script_УстановитьДействиеФормы(ЭтаФорма, "Действие", "ДействияФормы.ПроводкиДтКт");
///////////////////////// КОНЕЦ ////////////////////////////////////
///////////////////////// SCRIPT ////////////////////////////////////
///////////////////////////////////////////////////////////////////////
7. Добавление обработки — Корректировка движений.
Главным элементом данного механизма является встроенная в БП обработка Корректировка движений. Необходимо ее сохранить из БП на диск, немного доработать и вставить в УТП в ветку Обработки.
Для этого находим в дереве метаданных ветку Обработки. Нажимаем на этой ветке ПКМ, и из выпадающего меню выбираем пункт Вставить внешнюю обработку.
После этого программа попросит выбрать файл внешней обработки с диска. После выбора файла, в конце списка должна появится наша новая обработка — script_КорректировкаДвижений.
Готовую обработку Корректировка движений можно скачать ниже.
Проверяем.
Сохраняем изменения. Для этого нажимаем на клавиатуре F7 и соглашаемся со всеми предложениями программы.
Запускаем программу в пользовательском режиме, переходим в список документов Платежное поручение исходящее.
Нажимаем в списке на кнопку ДтКт и … Вуаля.
Кроме возможности ручной корректировки движений, в списке появилась новая колонка, в которой отображается специальная пиктограмма, если движения документа были отредактированы вручную.
Откроем документ и тоже проверим работоспособность нового механизма.
После этого попытаемся провести документ. Если движения были отредактированы вручную, программа должна выдать сообщение об ошибке: Движения документа исправлены вручную!
При отмене проведения признак ручной корректировки будет снят, а все движения документа удалены.