По ночам должны работать роботы…
По ночам должны работать роботы…
Наверное каждый из вас сталкивался с тем, что нужно что-то сделать в базе в монопольном режиме. И многие сталкивались с тем, что, чтобы сделать это, нужно выгонять всех пользователей, у которых много работы, или сидеть и делать это по ночам.
Восстановление последовательности, удаление помеченных на удаление объектов да и просто какие-то операции, требующие того, чтобы вся работа была закончена.
Фирма 1С не позаботилась о бедных пользователях, так давайте предоставим эту возможность… роботу. Точнее автоматической обработке.
Итак, данная статья написана на примере конфигурации ТиС. В нее включены следующие разделы:
1. Функция восстановления последовательностей.
2. Функция удаления помеченных элементов справочников и документов.
3. Организация автоматического запуска нашей обработки. Функция восстановления последовательностей.
Итак, последовательность. В 1С последовательность – это цепочка документов проведенных по порядку, в зависимости от даты и времени. Граница последовательности – последний документ, проведенный без нарушения последовательности. Следовательно, для восстановления последовательности мы должны перепровести все документы, входящие в последовательность, по порядку, начиная с последнего в последовательности.
Кстати, в ТиС есть стандартная Функция глВосстановлениеПоследовательности(), можете использовать и ее, но своя рубашка ближе к телу, а доверия фирма 1С давно уже не оправдывает :)
Моя функция приведена ниже с комментариями, я думаю их будет достаточно для понимания.
Функция ВосстановлениеПоследовательностей() Экспорт
Перем СтараяПозицияТА;
ТекстОшибки = СоздатьОбъект("Текст");
//сохраним позицию ТА
СтараяПозицияТА = ПолучитьПозициюТА();
БылиОшибкиПроведения = 0;
Документ=СоздатьОбъект("Документ");
Объект=СоздатьОбъект("Документ");
//получим наименьшую дату границ последовательностей
ВыбДата = Последовательность.ПолучитьАтрибут(Метаданные.Последовательность(1)).ПолучитьДату();
Для nn=1 По Метаданные.Последовательность() Цикл
Дат = Последовательность.ПолучитьАтрибут(Метаданные.Последовательность(nn)).ПолучитьДату();
ВыбДата = Мин(Дат,ВыбДата);
КонецЦикла;
//выберем документы
Документ.ВыбратьДокументы(ВыбДата,);
Пока (Документ.ПолучитьДокумент()>0) и (БылиОшибкиПроведения = 0) Цикл
//пропускаем непроведенные
Если Документ.Проведен()=0 Тогда
Продолжить;
КонецЕсли;
ТекДок = Документ.ТекущийДокумент();
//проверим на принадлежность последовательностям
Принадлежит=0;
Для nn=1 По Метаданные.Последовательность() Цикл
//поправка от selenat
Если ТекДок.ПринадлежитПоследовательности(Метаданные.Последовательность(nn)) = 1 Тогда
Принадлежит = 1;
Прервать;
КонецЕсли;
КонецЦикла;
Если МонопольныйРежим()>0 Тогда
// если оперативный документ находится за ТА, то ТА надо передвинуть в любом случае
Если (ТекДок.СравнитьТА()>0) или (Принадлежит = 1) Тогда
БылаПозиция = ПолучитьПозициюТА();
УстановитьТАНа(ТекДок);
Если БылаПозиция = ПолучитьПозициюТА() Тогда
// не удалось поменять (например, были открытые документы)
ТекстОшибки.ДобавитьСтроку(" Не удалось переместить ТА!");
БылиОшибкиПроведения = 1;
Продолжить;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если Принадлежит=1 Тогда
Объект.НайтиДокумент(ТекДок);
//пытаемся перепровести
Попытка
Если Объект.Провести() = 0 Тогда
ТекстОшибки.ДобавитьСтроку(" Не удалось провести документ "+Строка(Объект));
БылиОшибкиПроведения = 1;
КонецЕсли;
Исключение
ТекстОшибки.ДобавитьСтроку(" Не удалось провести документ "+Строка(Объект));
ТекстОшибки.ДобавитьСтроку(" Ошибка: "+ОписаниеОшибки());
БылиОшибкиПроведения = 1;
Прервать;
КонецПопытки;
КонецЕсли;
//сохраним последний проведенный документ, для позиционирования на нем последовательностей
ПослДок = Объект.ТекущийДокумент();
КонецЦикла;
//позиционируем последовательности на последнем проведенном документе
Для nn=1 По Метаданные.Последовательность() Цикл
Последовательность.ПолучитьАтрибут(Метаданные.Последовательность(nn)).Установить(ПослДок);
КонецЦикла;
//возвращаем старую ТА
Если (МонопольныйРежим()>0) и (СтараяПозицияТА <> ПолучитьПозициюТА()) Тогда
УстановитьТАПо(СтараяПозицияТА);
КонецЕсли;
Возврат ?(БылиОшибкиПроведения = 0,1,ТекстОшибки);
КонецФункции // ВосстановлениеПоследовательностей()
За некоторые замечания по оптимизации большое спасибо selenat.
Функция удаления помеченных элементов справочников и документов.
Далее на повестке дня у нас – удаление помеченных элементов. Есть такие, кто не будет читать эту статью, а просто переберет элементы и напишет Элемент.Удалить(1). Но наверное есть и те, кто так уже сделал и кому интересно, а как же все сделать правильно и избежать тех проблем, которые у них возникли :)
А правильный способ – это удалять только те элементы, на которые после удаления не останется ссылок в базе. То есть если ссылок совсем нет, или ссылающиеся элементы будут также удалены.
Функция ИнфаОбЭлементе(Эл)
Стр = "";
Если ТипЗначенияСтр(Эл) = "Справочник" Тогда
Стр = "Справочник." + Эл.Вид() + " " + Эл.Код + " " + Эл.Наименование;
ИначеЕсли ТипЗначенияСтр(Эл) = "Документ" Тогда
//вместо глНазваниеДокументаВжурнале(Эл) можно просто написать Эл
Стр = "Документ Номер: " + Эл.НомерДок + " " + глНазваниеДокументаВжурнале(Эл);
КонецЕсли;
Возврат(Стр);
КонецФункции //ИнфаОбЭлементе()
//*******************************************
Функция УдалениеПомеченных()
Спис = СоздатьОбъект("СписокЗначений");
ТабСсыл = СоздатьОбъект("ТаблицаЗначений");
ОтчТекст = СоздатьОбъект("Текст");
//составляем список помеченных на удаление
НайтиПомеченныеНаУдаление(Спис);
Если Спис.РазмерСписка() > 0 Тогда
//если есть помеченные - удаляем
УдалитьОбъекты(Спис,1,ТабСсыл);
//если остались неудаленные
Если ТабСсыл.КоличествоСтрок() > 0 Тогда
//сообщаем только об удаленных
Для nn = 1 По Спис.РазмерСписка() Цикл
Элем = Спис.ПолучитьЗначение(nn);
Если ТабСсыл.НайтиЗначение(Элем,,"Объект") = 0 Тогда
ОтчТекст.ДобавитьСтроку("Удалили "+ИнфаОбЭлементе(Элем));
КонецЕсли;
КонецЦикла;
//сообщаем только о неудаленных
ТабСсыл.ВыбратьСтроки();
Элем = "";
Пока ТабСсыл.ПолучитьСтроку() = 1 Цикл
Если Элем <> ТабСсыл.Объект Тогда
Элем = ТабСсыл.Объект;
ОтчТекст.ДобавитьСтроку("Не удалось удалить: " + ИнфаОбЭлементе(ТабСсыл.Объект));
ОтчТекст.ДобавитьСтроку(" Ссылки и комментарии:");
КонецЕсли;
ОтчТекст.ДобавитьСтроку(" " + ТабСсыл.Описание);
КонецЦикла;
Иначе
//если удалились все, то выведем их список
Для nn = 1 По Спис.РазмерСписка() Цикл
ОтчТекст.ДобавитьСтроку("Удалили "+ИнфаОбЭлементе(Спис.ПолучитьЗначение(nn)));
КонецЦикла;
КонецЕсли;
КонецЕсли;
Возврат(ОтчТекст);
КонецФункции //УдалениеПомеченных()
Функция вернет текстовый отчет об удаленных и неудаляемых элементах. Кстати, функция по скорости нисколько не уступает стандартной, так что можно на ее основе сделать копию стандартной обработки удаления. Функция была очень сильно сокращена и упрощена благодаря советам Соратника. Большое ему спасибо!
Организация автоматического запуска нашей обработки.
Как же теперь добиться автоматического выполнения этих функция?
Пошагово расспишу, как это сделал я, а вы уж сами решайте, как делать вам.
1. Завел нового пользователя Service. Имя лучше на латинице задавать, ниже опишу почему. Пароль лучше не самый простой, любопытных пользователей много и им будет очень интересно, зачем это тут.
2. В глобальном модуле в процедуре ПриНачалеРаботыСистемы открытие нового периода сделал с условием «Если ИмяПользователя() <> "Service" Тогда», ибо решил доверить его нашему боту.
3. В конце процедуры ПриНачалеРаботыСистемы прописал:
Если ИмяПользователя() = "Service" Тогда
Если Константа. АвтоматическиеРаботы = 1 Тогда
ОткрытьФорму("Отчет",,""+КаталогИБ()+"Автомат.ert");
КонецЕсли;
ЗавершитьРаботуСистемы(0);
КонецЕсли;
4. Добавил константу АвтоматическиеРаботы со значением 1 или 0, и дал права на ее редактирование нужным людям (себе и еще раз себе :] )
5. С установленной в 0 константой зашел в базу под новой учеткой и позакрывал всякую ерунду (Советы дня и пр.), а также проверил, чтобы не выскакивала какая-нибудь незапланированная лабуда.
6. Также в каталог базы кинул bat файл, с таким текстом:
del *.cdx
"C:\Program Files\1Cv77\BIN\1CV7s.exe" ENTERPRISE /M /DD:\1C\DB /NService /P159
Замечу, сначала удаляются все файлы cdx, чтобы при монопольном запуске не выдавалось противное окошко с вопросом о переиндексации (если отсутствуют индексные файлы, то переиндексация пройдет принудительно, без лишних вопросов), затем запускается 1С в нужной базе с нужным юзером (если бы имя юзера было на кириллице, вас бы поджидал большой облом :-} ) .
7. Проверил работоспособность вручную, затем создал новое задание в планировщике, поставил на 1 ночи, ибо работают у нас и до полуночи некоторые. Текст обработки Автомат.ert (соответственно к нему еще надо приписать приведенные выше функции)
//*******************************************
Функция ТекВремя(Сп=0) Экспорт
Часов = 0; Минут = 0; Секунд = 0;
ТекущееВремя(Часов,Минут,Секунд);
Возврат(?(Сп=0,"","" + ТекущаяДата() + " ") + Формат(Часов,"Ч(0)2") + ":"
+ Формат(Минут,"Ч(0)2") + ":" + Формат(Секунд,"Ч(0)2"));
КонецФункции
//*******************************************
Процедура ПриОткрытии()
Если Константа.АвтоматическиеРаботы = 1 Тогда
Текст = СоздатьОбъект("Текст");
path = ""+КаталогИБ()+"avtomat.txt";
Если ФС.СуществуетФайл(path) = 1 Тогда
Текст.Открыть(path);
КонецЕсли;
Текст.ДобавитьСтроку("");
Текст.ДобавитьСтроку("");
Текст.ДобавитьСтроку("- - -- - - - - - - - - - - - - - - - - - - - - - ");
Текст.ДобавитьСтроку("Начало - "+ТекВремя(1));
Текст.Записать(path);
Текст.ДобавитьСтроку("");
Если МонопольныйРежим()=0 Тогда
Текст.ДобавитьСтроку("Был немонопольный режим - ТА не перенесли, последовательности не востановили!");
Иначе
Текст.ДобавитьСтроку("Начинаем переносить ТА "+ТекВремя());
Текст.Записать(path);
//переносим ТА
Если РабочаяДата() > ПолучитьДатуТА() Тогда
УстановитьТАНа(РабочаяДата());
Текст.ДобавитьСтроку("Перенесли ТА "+ТекВремя());
Иначе
Текст.ДобавитьСтроку("Переносить ТА не потребовалось "+ТекВремя());
КонецЕсли;
//восстанавливаем последовательности
Текст.ДобавитьСтроку("");
Текст.ДобавитьСтроку("Начали восстановление последовательностей "+ТекВремя());
Текст.Записать(path);
Рез = ВосстановлениеПоследовательностей();
Если Рез = 1 Тогда
Текст.ДобавитьСтроку("Закончили успешно "+глТекВремя());
Иначе
Текст.ДобавитьСтроку(глТекВремя() + "Произошла слудующая ошибка при
восстановлении:");
Для nn = 1 По Рез.КоличествоСтрок() Цикл
Текст.ДобавитьСтроку(Рез.ПолучитьСтроку(nn));
КонецЦикла;
КонецЕсли;
Текст.Записать(path);
//удаляем помеченные
ВремТекст = УдалениеПомеченных();
Если ВремТекст.КоличествоСтрок() > 0 Тогда
Текст.ДобавитьСтроку("");
Текст.ДобавитьСтроку("Провели удаление помеченных элементов "+ТекВремя());
Для nn = 1 По ВремТекст.КоличествоСтрок() Цикл
Текст.ДобавитьСтроку(ВремТекст.ПолучитьСтроку(nn));
КонецЦикла;
КонецЕсли;
КонецЕсли;
Текст.Записать(path);
КонецЕсли;
//при установленной программе TheBat можно отправить письмо с текстом отчета
ЗапуститьПриложение("C:\TheBat\TheBat.exe /NOLOGO /MAILU=Мой ящик;TO=admin@company.com;"
+ "S=Проверка;TEXT=" + path + " /EXIT");
СтатусВозврата(0);
Возврат;
КонецПроцедуры
|