Как многим известно, в 1с 7.7 процедура проведения документа отделена процедуры записи. Это порождает неприятные ситуации, когда можно записать документ без его перепроведения. Поэтому во многих документах красуется волшебная директива “ПриЗаписиПерепроводить(1)”, заставляющая платформу автоматически запускать перепроведение документа при записи, и если проведение не удалось, то откатывать транзакцию записи. Если разрешить запись без перепроведения, может возникнуть следующая ситуация:
Оператор добавил в проведенную расходную накладную товар, которого нет на остатке и попытался перепровести её. Документ сначала записался, но обработка проведения завершилась с ошибкой: недостаточно товара на складе и движения документа не изменились. Таким образом в документе мы видим большее количество товара, сумма документа увеличилась, мы даже можем напечатать его в таком состоянии. Но по регистрам проведено меньшее количество товара, меньшая сумма по взаиморасчетам. Еще веселее становится, когда мы захотим перепровести все документы. Мы отключаем контроль остатков, чтобы гарантированно перепровести все документы. Злополучная накладная успешно проводится, и в конце мы обнаруживаем что простое перепроведение документов изменило текущие остатки: по складу, по взаиморасчетам, возможно даже по кассе. Мне кажется именно поэтому в 1с 8 процедуру записи и проведения документа объединили в одну. Этим я показал ситуацию, когда изменения в документе сохранились, но движения его при этом не поменялись. Она происходит довольно часто и уже не вызывает шока.
А теперь я хочу продемонстрировать обратную ситуацию: есть некая обработка, она меняет реквизиты документа, из-за смены реквизитов изменяются движения документа в момент проведения, но после этой обработки реквизиты документа остаются в исходном состоянии. А движения изменены. Мистика? Глючная или битая база? Косяк платформы? Такие вопросы приходили в мою голову. Но тот факт, что другая обработка, в которой нет ничего кроме изменения реквизита, записи и проведения отрабатывала на этом документе как положено, заставляла упорно искать ошибку в логике кода. И я нашел ее. Проблемы возникали только с существующими и проведенными документами. Новые документы, которые эта обработка создавала успешно сохранялись, а существующие как будто были для нее неприкасаемы. Притом отладчик во всех местах твердил, что реквизитам присваиваются значения и нигде потом они не очищаются. Потом я даже вручную устанавливал значения тем реквизитам, которые обработкой очищались и после обработки эти значения оставались на месте. Очень похоже на откат транзакции, но ведь движения документа сохраняются. Да и как можно откатить транзакцию записи документа после проведения?
Виновником оказался метод
СделатьНепроведенным()
А теперь по порядку:
Для теста создаем простейшую конфигурацию:
-
Справочник_1
-
Документ_1 без дополнительных реквизитов
-
Документ_2 реквизит шапки: Документ_1 - ссылка на Документ_1, Реквизиты таб части Справочник_1 - тип Справочник_1; Количество - тип число.
-
Оборотный регистр: Оброт_1: Измерение: Справочник_1 - тип Справочник_1; Ресурс: Количество - тип число.
- Обработка ТестПерепроведения: с таким кодом:
Процедура Выполнить()
док2 = СоздатьОбъект("Документ.Документ_2");
док2.НайтиДокумент(ВыбДокумент_2);
док2.Документ_1 = ВыбДокумент_1;
Сообщить("1. Перед распроведением Документ 1: "+док2.Документ_1);
док2.СделатьНепроведенным();
Сообщить("2. После распроведения Документ 1: "+док2.Документ_1);
док2.Записать();
Сообщить("3. После записи Документ 1: "+док2.Документ_1);
док2.Провести();
Сообщить("4. После проведения Документ 1: "+док2.Документ_1);
КонецПроцедуры
Модуль проведения документа_2:
Процедура ОбработкаПроведения()
Сообщить("Из модуля документа. В процессе проведения Документ 1: "+Документ_1);
Если Документ_1.Выбран() = 1 Тогда
ВыбратьСтроки();
Пока ПолучитьСтроку() = 1 Цикл
Регистр.Оборот_1.Движение(Справочник_1,Количество);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Заходим в режим Предприятия и создаем один документ типа Документ_1 и один документ типа Документ_2. Поле Документ_1 в шапке оставляем пустым, а табличную часть заполняем произвольными данными, например как на рисунке:
Проводим. Согласно прописанной логике, после проведения документа в нем нет никаких движений поскольку Документ_1 не выбран.
Затем запускаем обработку и заполняем соотвествующие поля нашими созданными документами и нажимаем кнопку “Выполнить”. В окне сообщений мы видим что на каждом этапе, что в обработке, что в модуле документа реквизит Документ_1 был заполнен Документом_1 с номером 1.
Но когда мы откроем Документ_2, то увидим прежнюю картину, как на первом рисунке. Реквизит Документ_1 пустой. Откроем теперь движения Документа_2:
Удивительно! Движения есть, хотя они должны быть только при заполненном реквизите Документ_1. В SQL базе я наблюдал еще и запись документа_2 в журнал подчиненных документу 1, в файловом варианте это не происходит. Когда же этот реквизит был очищен? А очистился он в момент вызова метода СделатьНепроведенным() в обработке. Точнее, как я себе представляю, очистился не реквизит, а признак в объекте документа, что реквизиты были изменены. То есть было так:
-
Создаем программный объект документа.
-
Находим по ссылке в базе данных существующую запись и заполняем поля объекта из базы данных.
-
Редактируем реквизит “Документ_1” в объекте документа, при этом создается отметка что этот реквизит надо будет обновить базе данных.
-
Снимаем проведение с документа. При этом все отметки о модифицированности документа снимаются (наверно такая особенность платформы).
-
Записываем документ. База данных при этом не модифицируется.
-
Проводим документ. Поскольку в программном объекте поле Документ_1 заполнено, код в модуле объекта создает движения в регистре накопления и успешно их прописывает в базу, ведь у проведения отдельная транзакция.
Лечится такая ситуация очень просто. После шага 3 надо было Записать() документ, чтобы изменения сохранились в базе данных. Надеюсь кому-нибудь эта статья поможет сэкономить приличный моток нервов. В приложенных файлах архив с описанной тестовой конфигурацией.