Книга знаний

1С:Предприятие / Объекты конфигурации / Регистры

Волшебная последовательность документов: перепроведение без шума и пыли (статья)

Чтобы сделать процесс проведения документов в многопользовательском режиме работы 1С:Предприятие 7.7 более приятным и комфортным для пользователей, можно применить, как минимум, два способа – вставлять паузы между последовательными проведениями документов в цикле, и использовать «интеллектуальную» последовательность проведения, наподобие последовательности с измерениями в системе «1С:Предприятие» версии 8.0, чтобы минимизировать количество проводимых документов. Автор статьи: romix | Редакторы: Волшебник
Последняя редакция №6 от 16.02.06 | История
URL: http://kb.mista.ru/article.php?id=53

Ключевые слова: восстановление, последовательность, перепроведение, документ, пакетное, групповое


Замечание (16.02.2006, romix)


Более компактная и быстродействующая реализация механизма "интеллектуальных последовательностей" приведена по этой ссылке:

Книга знаний: Асинхронные события 1С: полезные алгоритмы

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

Книга знаний: Перепроведение документов в 1С:Предприятие без блокировки других пользователей

Выборочное перепроведение


Второй способ, который вы можете применить – заранее исключает из проведения те документы, движения по которым при перепроведении заведомо не будут изменяться. Легко ли выявить такие документы алгоритмически? Давайте это оценим.

Пример №1


Предположим, что пользователь исправил документ недельной давности, в котором изменил ошибочно введенную сумму авансового платежа по договору №121 с контрагентом ООО «Томагавк-сервис» со 100000 рублей на 10000 рублей (у оператора дрогнула рука, и вместо одного нуля сами собой ввелись два).

Что при этом изменится? А изменятся при этом счета-фактуры «на аванс» и пятая графа счета-фактуры, где указан платежно-расчетный документ списанного аванса (неправильное их заполнение может послужить поводом к отказу от налоговых вычетов по НДС).

Налоговые инспектора уже просекли фишку, что эти документы чувствительны к любым правкам «задним числом», и страшное налоговое преступление перед государством здесь с наибольшей вероятностью будет раскрыто.

Документы по другим контрагентам и договорам при этом перепроводить и перепечатывать нет необходимости, что уже само по себе – большое облегчение.

Пример №2


Рассмотрим другой пример – списание товара по средней себестоимости. Предположим, что на склад поступило 100 кг товара «редиска свежая» на сумму 5000 рублей. Средняя цена составит, в нашем примере, 50 рублей за килограмм редиски. Списание по средней себестоимости постепенно сводит к нулю товарный и стоимостной запас редиски. Но что будет, если пользователь (например, обнаружив ошибку) изменит документ прихода (например, поставит сумму 6000 рублей)? А произойдет при этом следующее: товарный запас при полном списании уйдет в 0, а по денежным суммам будет числиться положительное или отрицательное… гм… сальдо. Чтобы этого не случилось, документы списания необходимо перепровести, и тогда нехорошее сальдо тоже уйдет в 0. Перепроводить при этом нужно не все документы, а только те из них, где фигурирует данная редиска на данном складе. Другие документы перепроводить, очевидно, не требуется.

Реализация алгоритма


В системе 1С:Предприятие 8.0 появился механизм Последовательностей с измерениями, который и реализует такую возможность. К сожалению, переход на версию 8.0 затруднен – там «все несовместимо» с 1С версии 7.7. Поэтому, поюзать 8-ку придется только мысленно, реализовав похожий механизм в 7.7 посредством, как вы уже, наверное, догадались, головы, рук и хвоста.

Чтобы сэмулировать последовательность, нам потребуется справочник, где мы будем хранить ссылки на документы, требующие перепроведения. Я назвал его ЭмуляцияПоследовательности, и его реквизиты – это Наименование (куда я пишу позицию документа для правильной его выборки «сверху вниз») и ссылка на документ (поле с типом «документ неопределенного вида»).

Алгоритм работы из модуля проведения может быть следующим:

1)    В процессе проведения документа выявить, по каким движениям (измерениям последовательности) появились различия. Пример кода с достаточно простым алгоритмом этого действия приведен ниже.
2)    Просмотреть более поздние (NB!) движения в разрезе выявленных различий. (при помощи методов УстановитьЗначениеФильтра и ВыбратьДвижения).
3)    Ссылки на выявленные по этим движениям документы запомнить в справочнике ЭмуляцияПоследовательности.
4)    Исключить текущий документ из этого справочника, если он там уже был.

Фоновой обработке, которая восстанавливает последовательность, потребуется периодически «просыпаться» и просматривать справочник ЭмуляцияПоследовательности на предмет наличия в нем документов. И перепроводить их (желательно, с паузами) «сверху вниз» (т.е. в порядке расположения документов на временной оси, для чего я пишу в наименование элемента справочника текстовую строку из ПолучитьПозицию()).

Почему именно справочник?


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

Пример реализации


В качестве примера этого алгоритма, приведу модуль проведения тестового документа ПриходнаяНакладная, который содержит реквизиты Склад, Товар и Сумма. Аналогичный алгоритм  можно применить, с небольшими отличиями, для документов и регистров любого вида. Строки исходного кода, которые, вероятнее всего, придется при этом скорректировать, помечены комментарием с тремя звездочками: «//***». Количество потенциальных корректировок можно уменьшить, работая с регистром через метаданные, но это уменьшило бы понятность и читабельность примера (предлагаю читателю реализовать этот режим работы самостоятельно – ссылки на коммерческие решения опубликую). :-)

У документа должна быть сброшена галочка «Автоматическое удаление движений», чтобы мы смогли увидеть, а какие же движения были у документа до перепроведения.

/////////////////////////////////////////////////////////////////////////
перем г_тзДвижения; //Переменная хранит таблицу значений, в которой мы будем 
//записывать старые движения документа, потом вычитать из них новые движения и,
//таким образом, получать между ними разницу.

/////////////////////////////////////////////////////////////////////////
Процедура пЗапомнитьДокументДляПерепроведения(прм_док)
    //Процедура запоминает документ, переданный в качестве параметра прм_док,
    //в справочнике ЭмуляцияПоследовательности.
    
    спр=СоздатьОбъект("Справочник.ЭмуляцияПоследовательности");
    Если спр.НайтиПоРеквизиту("Документ",прм_док,1)=0 Тогда
        спр.Новый();
        спр.Документ = прм_док;
        стрПозиция=прм_док.ПолучитьПозицию(); //32-символьная позиция документа
        спр.Позиция = стрПозиция;
        спр.Записать();
    КонецЕсли;
КонецПроцедуры    // ЗапомнитьДокументДляПерепроведения

/////////////////////////////////////////////////////////////////////////
Процедура пУдалитьТекущийДокументИзПоследовательности()
    //Процедура удаляет текущий документ из справочника
    //ЭмуляцияПоследовательности, если он там есть.
    
    спр=СоздатьОбъект("Справочник.ЭмуляцияПоследовательности");
    Если спр.НайтиПоРеквизиту("Документ",ТекущийДокумент(),1)=1 Тогда
        спр.Удалить(1);
    КонецЕсли;
КонецПроцедуры    // УдалитьТекущийДокументИзПоследовательности

/////////////////////////////////////////////////////////////////////////
Процедура пИскатьДокументыВниз(прм_Товар, прм_Склад) //***
    //Процедура просматривает движения ниже текущего документа, у которых
    //измерения совпадают с параметрами процедуры.
    //Если такие движения есть, то запоминает соответствующие документы в 
    //справочнике при помощи пЗапомнитьДокументДляПерепроведения().
        
        рег=СоздатьОбъект("Регистр.ОстаткиТовара"); //***
        рег.УстановитьЗначениеФильтра("Товар",прм_Товар); //***
        рег.УстановитьЗначениеФильтра("Склад",прм_Склад); //***
        рег.ВыбратьДвижения(ТекущийДокумент(),);
        док1=ТекущийДокумент();
        Пока рег.ПолучитьДвижение()=1 Цикл
            док=рег.ТекущийДокумент();
            Если док=док1 Тогда
                Продолжить;
            КонецЕсли;
            Сообщить(" --- Выявлен документ для перепроведения: "+док,"!");
            пЗапомнитьДокументДляПерепроведения(док);
            док1=док;
        КонецЦикла; //по движениям регистра
    
КонецПроцедуры    // ИскатьДокументыВниз



/////////////////////////////////////////////////////////////////////////
Процедура пЗапомнитьСтарыеДвижения()
    //Процедура запоминает движения документа в глобальной таблице значений.
    тз=г_тзДвижения;
    
    рег=СоздатьОбъект("Регистр.ОстаткиТовара");//***
    
    тз.НоваяКолонка("Товар","Справочник.Товары");//***
    тз.НоваяКолонка("Склад","Справочник.Склады");//***
    тз.НоваяКолонка("СуммаРуб","Число", 14,2); //***
    
    рег.ВыбратьДвиженияДокумента(ТекущийДокумент());
    Пока рег.ПолучитьДвижение()=1 Цикл
        тз.НоваяСтрока();
        тз.Товар=рег.Товар; //***
        тз.Склад=рег.Склад; //***
        тз.СуммаРуб=рег.СуммаРуб; //***
    КонецЦикла;    
КонецПроцедуры    // ЗапомнитьДвижения


/////////////////////////////////////////////////////////////////////////
Процедура пВычестьНовыеДвижения()
    //Процедура вычитает из ранее запомненных "старых" движений документа 
    //новые движения, чтобы выявить различия.
    
    тз=г_тзДвижения;
    
    рег=СоздатьОбъект("Регистр.ОстаткиТовара"); //***
    
    рег.ВыбратьДвиженияДокумента(ТекущийДокумент());
    Пока рег.ПолучитьДвижение()=1 Цикл
        тз.НоваяСтрока();
        тз.Товар=рег.Товар; //***
        тз.Склад=рег.Склад; //***
        тз.СуммаРуб=-рег.СуммаРуб; //сумма - с обратным знаком//***
    КонецЦикла;    
    
    тз.Свернуть("Склад,Товар","СуммаРуб"); //***
    //теперь ненулевые значения говорят о различиях между тем, что было
    //и тем, что стало
КонецПроцедуры    // ЗапомнитьДвижения


    
/////////////////////////////////////////////////////////////////////////
Процедура пОбработкаРазличий()
    //Процедура просматривает таблицу значений, в которую мы ранее записали старые движения,
    //и вычли из них новые, на предмет ненулевых сумм. Если таковые есть, то вызывает
    //пИскатьДокументыВниз() для соответствующих измерений.
    тз=г_тзДвижения;
    
    тз.ВыбратьСтроки();
    Пока тз.ПолучитьСтроку() = 1 Цикл
        Если тз.СуммаРуб=0 Тогда//***
            Продолжить;
        КонецЕсли;    
        
        Сообщить("Зафиксировано различие по измерениям "+тз.Товар+" "+тз.Склад,"!"); //***
        пИскатьДокументыВниз(тз.Товар, тз.Склад); //***
        
    КонецЦикла;// по строкам тз, где различия
КонецПроцедуры    // пОбработкаРазличий


/////////////////////////////////////////////////////////////////////////
Процедура ОбработкаПроведения() //Предопределенная процедура 1С
    
    г_тзДвижения=СоздатьОбъект("ТаблицаЗначений");
    

        //Запомним старые движения регистра в таблицу значений
    пЗапомнитьСтарыеДвижения();
    
    //Очистим движения.
    ОчиститьДвижения(); //встроенный метод 1С
    
    //Выполним новые движения.
    
ВыбратьСтроки();
    Пока ПолучитьСтроку() = 1 Цикл

        Регистр.ОстаткиТовара.Склад = Склад;
        Регистр.ОстаткиТовара.Товар = Товар;
        Регистр.ОстаткиТовара.Количество = Количество;
        Регистр.ОстаткиТовара.СуммаРуб = СуммаРуб;
        Регистр.ОстаткиТовара.ДвижениеПриходВыполнить();
        
    КонецЦикла;
    
    //Вычтем новые движения из таблицы значений
    пВычестьНовыеДвижения();
    
    //Обработаем различия
    пОбработкаРазличий();
    
    //Текущий документ уже проведен, поэтому удаляем его из списка документов для проведения
    пУдалитьТекущийДокументИзПоследовательности();

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

///////////////////////////////////////////////////////////////////////
Процедура ОбработкаУдаленияПроведения() //Предопределенная процедура 1С
    Сообщить("-----------");
    Сообщить("Удаление движений документа: "+ТекущийДокумент(),"!");
    спр=СоздатьОбъект("Справочник.ЭмуляцияПоследовательности");
    ВыбратьСтроки();
    Сообщить("Удаление движений по измерениям "+Товар+" "+Склад,"!"); //***
    Пока ПолучитьСтроку() = 1 Цикл
        пИскатьДокументыВниз(Товар,Склад); //***
    КонецЦикла;
    пУдалитьТекущийДокументИзПоследовательности();
КонецПроцедуры


Альтернатива


Заметим, что этот документ несколько сложнее «простого человеческого» документа с модулем проведения наподобие

Процедура ОбработкаПроведения()
  ВыбратьСтроки();
  Пока ПолучитьСтроку() = 1 Цикл
    Регистр.ОстаткиТовара.Склад = Склад;
    Регистр.ОстаткиТовара.Товар = Товар;
    Регистр.ОстаткиТовара.Количество = Количество;
    Регистр.ОстаткиТовара.СуммаРуб = СуммаРуб;
    Регистр.ОстаткиТовара.ДвижениеПриходВыполнить();
  КонецЦикла;
КонецПроцедуры


но такова се ля ви. Искусство требует жертв, и мы можем предположить, что похожий (если не в точности такой же) алгоритм применяют разработчики движка 1С:Предприятие 8.0.  Разница может быть только в том, что там это зашито в движок, а в 7.7 – нет.

Ссылка на пример конфигурации из этой статьи для скачивания:
http://x-romix.narod.ru/consecution.rar (17К), качать *левой* кнопкой мыши.

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2024 | Mista.ru

Яндекс.Метрика