Предистория:
Запускал торговую систему в нашем филиале в Казахстане (до этого система уже работала в Украине и России). И во время переноса движений из старой базы в новую, постоянно сталкивался с проблемой несоответствия движений регистров из-за ошибок в старой базе. В конце концов, пришлось создать не только обработку, которая позволила произвольно редактировать содержимое регистров и реквизитов документа, но и механизм проведения документов по выбранным регистрам. Благо, что конфигурация создавалась с нуля и не нужно было втискиваться в рамки типовой конфигурации.
Проверялось на релизе:
1С 7.7.27, 1С++ 2.0.3.7, установлен MS Office с ActiveX компонентой MSComctlLib.TreeCtrl или наличие компоненты COMCTL.TreeCtrl (для навигации в виде дерева).
Что делает обработка и механизм:
Позволяет произвольно редактировать реквизиты шапки и табличной части документа, редактировать любые значения в регистрах документа. Также предлагается механизм для группового перепроведения документов по выбранным регистрам - очень полезная штука, когда нужно быстро перепровести документы за период и нет желания тратить время на выполнение расчетов по всем регистрам. Например, перепровести документы только по товарным регистрам, а взаиморасчеты не трогать.
Кому будет интересна:
Сразу запустить обработку не получится - в этой статье предлагается конструктор. Необходима модификация как кода обработки, так и кода конфигурации. Если Вы готовы пойти на этот шаг, тогда она для Вас. Учтите, что обработка создавалась для редактирования регистров оперативного учёта.
Что нужно будет поменять в конфигурации:
- Для всех документов конфигурации необходимо снять признак "Автоматическое удаление движений".
- Исправить во всех документах процедуру ОбработкаПроведения
- Ввести в систему понятие "участок": включает в себя набор логически связанных регистров, движения которых взаимосвязаны и раздельное проведение может вызвать логичские разногласия. Например, для ТиС это может быть регистр Остатки и Партии. Участки определяются в глобальной переменной.
- Модифицировать глобальный модуль для встраивания служебных процедур и функций, а также модифицировать существующие процедуры записи движений в регистры.
Подробности:
Чтобы запустить механизм прямого редактирования реквизитов документов, никаких изменений в конфигурации не нужно. Обработка просто считывает реквизиты в таблицы значений и после внесения изменений и подтверждения сохранения, записывает новые значения с помощью метода Записать().
А вот для редактирования движений регистров уже нужна модификация конфигурации. Нужно в режиме конфигуратора для документов снять признак "Автоматическое удаление движений" и модифицировать процедуру ОбработкаПроведения. Идея заключается в том, чтобы считать движения регистров в таблицу значений. После интерактивного редактирования таблицы, запустить процедуру ОбработкаПроведения документа с параметром типа ТаблицаЗначений, в котором будет передана таблица значений с исправленным содержимым регистров. Дальше исправленная процедура проведения будет записывать движения в регистр из переданной таблицы значений, а не выполнять расчет новых значений регистров, как это предусмотренно в обычном механизме проведения документа.
Для перепроведения документов по избранным участкам учёта, понадобится отдельная обработка, в которой можно указать участки для проведения. Обработка будет передавать в процедуру ОбработкаПроведения() параметр типа СписокЗначений. Этот параметр будет содержать список участков, по которым нужно перепроведение.
Необходимые изменения в конфигурации:
1. Добавить глобальную переменную для определения участков (групп регистров, которые проводятся всегда совместно) и инициализировать её
глСоставРегистровПоУчасткам = СоздатьОбъект("СписокЗначений");
глСоставРегистровПоУчасткам.ДобавитьЗначение("ПартииНаличие" , "Товары" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("РезервыТоваров" , "Товары" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("РезервыКонтрагентов" , "Товары" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("ПартииКорректировка" , "Товары" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("ОстаткиМОЛ" , "Товары" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("ОстаткиВКамерах" , "Товары" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("РасходыНаТовары" , "Расходы на товары");
глСоставРегистровПоУчасткам.ДобавитьЗначение("Покупатели" , "Взаиморасчеты" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("Поставщики" , "Взаиморасчеты" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("Касса" , "Деньги" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("Банк" , "Деньги" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("ОплатыДоговоров" , "Деньги" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("Факторинг" , "Деньги" );
глСоставРегистровПоУчасткам.ДобавитьЗначение("ИсполнениеПриказов" , "Приказы" );
2. Изменить во всех документах процедуру ОбработкаПроведения - добавить параметр ВидыДвижений
Процедура ОбработкаПроведения(ВидыДвижений="")
3. Предусмотреть в каждой процедуре ОбработкаПроведения механизм для проведения регистров по переданной таблице значений
// тар - при проведении из таблицы значений не должно выполняться никаких проверок на корректность данных
спУправлениеПроведением = СоздатьОбъект("СписокЗначений");
Если ТипЗначенияСтр(ВидыДвижений) = "ТаблицаЗначений" Тогда
Если глУправлениеПроведениемДокумента(Контекст, ВидыДвижений, спУправлениеПроведением) = 0 Тогда
Возврат;
КонецЕсли;
КонецЕсли;
Таблица значений с содержимым регистров содержит в себе названия регистров и вложенные таблицы с содержимым регистров
тбРегистры = СоздатьОбъект("ТаблицаЗначений");
тбРегистры.НоваяКолонка("Регистр", "Строка", 30);
тбРегистры.НоваяКолонка("ДвиженияРегистра");
тбРегистры.НоваяСтрока();
тбРегистры.Регистр = НазваниеРегистра;
тбРегистры.ДвиженияРегистра = СоздатьОбъект("ТаблицаЗначений");
4. Добавить в глобальный модуль процедуру глУправлениеПроведениемДокумента, которая определит по каким участкам нужно проводить регистры и очистит движения регистров если тип параметра ВидДвижений - СписокЗначений или есть ТаблицаЗначений - заменит движения регистров данными из таблицы значений.
// ====================================
// тар - вызывается из ОбработкиПроведения документа
// очищает содержимое регистров или если передан РежимПроведения как ТаблицаЗначений,
// то проводит документ по этим регистрам
// возвращает 1 - когда нужно в обработке проведения передать управление дальше и проводить регистры
// и 0 - когда дальше проводить не нужно
Функция глУправлениеПроведениемДокумента(Конт, ВидыДвижений, СписокПараметров) Экспорт
Перем Рез;
Рез = 1;
ПровестиТовары = 1;
ПровестиРасходыНаТовары = 1;
ПровестиВзаиморасчеты = 1;
ПровестиДеньги = 1;
ПровестиПриказы = 1;
Перепроведение = 0;
ТипРП = ТипЗначенияСтр(ВидыДвижений);
Если ТипРП = "СписокЗначений" Тогда
ПровестиТовары = 0;
ПровестиВзаиморасчеты = 0;
ПровестиДеньги = 0;
ПровестиРасходыНаТовары = 0;
ПровестиПриказы = 0;
Перепроведение = 1;
Для у = 1 По ВидыДвижений.РазмерСписка() Цикл
ТекУчасток = ВидыДвижений.ПолучитьЗначение(у);
Если ТекУчасток = "Товары" Тогда
ПровестиТовары = 1
ИначеЕсли ТекУчасток = "Взаиморасчеты" Тогда
ПровестиВзаиморасчеты = 1
ИначеЕсли ТекУчасток = "Деньги" Тогда
ПровестиДеньги = 1
ИначеЕсли ТекУчасток = "Расходы на товары" Тогда
ПровестиРасходыНаТовары = 1
ИначеЕсли ТекУчасток = "Приказы" Тогда
ПровестиПриказы = 1
КонецЕсли;
КонецЦикла;
ИначеЕсли ТипРП = "ТаблицаЗначений" Тогда
глПровестиПоТаблицеЗначений(Конт, ВидыДвижений);
Рез = 0;
Возврат Рез; // т.к. по переданным регистрам уже все проведено
ИначеЕсли ТипРП = "Строка" Тогда
Если НРег(ВидыДвижений) = "перепроведение" Тогда
Перепроведение = 1;
КонецЕсли;
КонецЕсли;
// тар - т.к. отменено автоматическое удаление движений
спОчищаемыеРегистры = СоздатьОбъект("СписокЗначений");
Для Номер = 1 По Метаданные.Регистр() Цикл
УдалитьДвиженияРегистра = 1;
ИдРегистра = Метаданные.Регистр(Номер).Идентификатор;
ТекУчасток = "";
поз = глСоставРегистровПоУчасткам.НайтиЗначение(ИдРегистра);
Если поз > 0 Тогда
глСоставРегистровПоУчасткам.ПолучитьЗначение(поз, ТекУчасток);
КонецЕсли;
Если (ПровестиТовары = 0) И (ТекУчасток = "Товары") Тогда
УдалитьДвиженияРегистра = 0;
КонецЕсли;
Если (ПровестиВзаиморасчеты = 0) И (ТекУчасток = "Взаиморасчеты") Тогда
УдалитьДвиженияРегистра = 0;
КонецЕсли;
Если (ПровестиДеньги = 0) И (ТекУчасток = "Деньги") Тогда
УдалитьДвиженияРегистра = 0;
КонецЕсли;
Если (ПровестиРасходыНаТовары = 0) И (ТекУчасток = "Расходы на товары") Тогда
УдалитьДвиженияРегистра = 0;
КонецЕсли;
Если (ПровестиПриказы = 0) И (ТекУчасток = "Приказы") Тогда
УдалитьДвиженияРегистра = 0;
КонецЕсли;
Если УдалитьДвиженияРегистра = 1 Тогда
Конт.ОчиститьДвижения("Регистр." + ИдРегистра);
спОчищаемыеРегистры.ДобавитьЗначение(ИдРегистра);
КонецЕсли;
КонецЦикла; // тар
// тар
Если (Перепроведение = 1) И (ТипЗначенияСтр(ВидыДвижений) = "СписокЗначений") Тогда
//глСообщить("Очищаются регистры:" + спОчищаемыеРегистры.ВСтрокуСРазделителями(), "i");
КонецЕсли; // тар
СписокПараметров.Установить("ПровестиТовары" , ПровестиТовары );
СписокПараметров.Установить("ПровестиРасходыНаТовары" , ПровестиРасходыНаТовары );
СписокПараметров.Установить("ПровестиВзаиморасчеты" , ПровестиВзаиморасчеты );
СписокПараметров.Установить("ПровестиДеньги" , ПровестиДеньги );
СписокПараметров.Установить("ПровестиПриказы" , ПровестиПриказы );
СписокПараметров.Установить("Перепроведение" , Перепроведение );
Возврат Рез;
КонецФункции
5. Если ВидДвижений - СписокЗначений, запустить в ОбработкеПроведения механизм очистки содержимого регистров, которые будут перепроводится и не трогать регистры, которые проводиться не будут
Если глУправлениеПроведениемДокумента(Контекст, ВидыДвижений, спУправлениеПроведением) = 0 Тогда
Возврат;
Иначе
ПровестиТовары = спУправлениеПроведением.Получить("ПровестиТовары");
ПровестиРасходыНаТовары = спУправлениеПроведением.Получить("ПровестиРасходыНаТовары");
ПровестиВзаиморасчеты = спУправлениеПроведением.Получить("ПровестиВзаиморасчеты");
ПровестиДеньги = спУправлениеПроведением.Получить("ПровестиДеньги");
Перепроведение = спУправлениеПроведением.Получить("Перепроведение");
КонецЕсли; // тар
6. В зависимости от перепроводимых участков, вызвать нужные процедуры расчета и заполнения регистров
Если ПровестиТовары = 1 Тогда // участок - товары
ДвиженияПартии();
ДвиженияРезерв();
КонецЕсли;
Если ПровестиВзаиморасчеты = 1 Тогда
ДвиженияВзаиморасчеты();
КонецЕсли;
7. Добавить в глобальный модуль процедуру заполнения движений регистра по таблице значений
Функция Присвоить(Чему,Что) Экспорт
Чему = Что;
Возврат "";
КонецФункции
Процедура глПровестиПоТаблицеЗначений(Конт, ВидыДвижений) Экспорт
ВидыДвижений.ВыбратьСтроки();
Пока ВидыДвижений.ПолучитьСтроку() = 1 Цикл
ВидРегистра = СокрЛП(ВидыДвижений.Регистр);
ОпределениеРегистра = "Регистр." + ВидРегистра;
ТекРегистр = ВидыДвижений.ДвиженияРегистра;
Если НЕ(ТипЗначенияСтр(ТекРегистр) = "ТаблицаЗначений") Тогда
Продолжить
КонецЕсли;
Конт.ОчиститьДвижения(ОпределениеРегистра);
Рег = 0;
СсылкаНаРегистр = "Конт." + ОпределениеРегистра;
Шаблон("[Присвоить(Рег, " + Шаблон("[СсылкаНаРегистр]") + ")]");
Для дв = 1 По ТекРегистр.КоличествоСтрок() Цикл
ТекРегистр.ПолучитьСтрокуПоНомеру(дв);
Для к = 1 По ТекРегистр.КоличествоКолонок() Цикл
ТекАтрибут = ТекРегистр.ПолучитьПараметрыКолонки(к);
ТекЗначениеАтрибута = ТекРегистр.ПолучитьЗначение(дв, к);
Если ТекАтрибут = "НомерСтрокиРегистра" Тогда
Если ТекЗначениеАтрибута > 0 Тогда
Рег.ПривязыватьСтроку(ТекЗначениеАтрибута);
КонецЕсли;
ИначеЕсли (ТекАтрибут = "НомерСтроки") ИЛИ (ТекАтрибут = "ФлагДвижения") Тогда
Продолжить
Иначе
Рег.УстановитьАтрибут(ТекАтрибут, ТекЗначениеАтрибута);
КонецЕсли;
КонецЦикла;
ФлагДвижения = ТекРегистр.ФлагДвижения;
Если ФлагДвижения = "+" Тогда
Рег.ДвижениеПриходВыполнить();
ИначеЕсли ФлагДвижения = "-" Тогда
Рег.ДвижениеРасходВыполнить();
Иначе
Рег.ДвижениеВыполнить();
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
8. Добавить в конфигурацию обработку "Управление содержимым документа" из этой статьи