В процессе автоматизации работы в 1С:Управление торговлей 11.4 программисты часто сталкиваются с ситуацией, когда документ ПеремещениеТоваров загружается программно, его статус меняется на Принято, метод Записать(РежимЗаписиДокумента.Проведение) отрабатывает без ошибок, но движения по регистру ТоварыНаСкладах на склад-получатель не формируются. При этом расход со склада-отправителя зафиксирован корректно. Самое странное, что проблема может возникать спорадически: например, в одном случае из ста.
В данной статье мы подробно разберем причины такого поведения системы, проанализируем архитектурные особенности конфигураций на базе ERP и УТ 11, а также выработаем надежные алгоритмы программного проведения документов.
Прежде всего, нам необходимо проанализировать настройки склада-получателя. В УТ 11 логика работы документа ПеремещениеТоваров напрямую зависит от того, используется ли на складе ордерная схема при поступлении товаров.
Рассмотрим два варианта развития событий:
ТоварыНаСкладах. Если движений нет — это техническая ошибка проведения.ПеремещениеТоваров в статусе Принято лишь завершает финансовый цикл перемещения. Он не делает прихода на остатки в физический регистр накопления. Физический приход на остатки в этом случае формируется документом ПриходныйОрдерНаТовары.Выясним причину, почему при программной загрузке это может стать проблемой. Иногда данные о складе кэшируются или функциональные опции считываются неверно в момент транзакции. Если ваша система «думает», что склад ордерный, движения перемещения будут выглядеть обрезанными. Всегда проверяйте программно настройки склада перед записью.
Одной из наиболее распространенных ошибок при программном формировании движений является использование флага ОбменДанными.Загрузка = Истина. Рассмотрим, почему это критично.
Когда мы устанавливаем этот флаг, система отключает большинство проверок в модуле объекта и подписках на события. В типовых решениях 1С (УТ 11, ERP, КА) логика формирования движений часто вынесена в модуль менеджера или специальные обработчики, которые могут игнорироваться при режиме загрузки. В результате документ получает статус Проведен, но таблицы движений остаются пустыми.
Проанализируем правильный подход к записи объекта:
// Не рекомендуется использовать ОбменДанными.Загрузка = Истина,
// если вы хотите, чтобы система сформировала движения по регистрам.
ДокОбъект = СсылкаНаПеремещение.ПолучитьОбъект();
ДокОбъект.Статус = Перечисления.СтатусыПеремещенийТоваров.Принято;
// Перед проведением важно убедиться, что документ заполнен корректно
Если ДокОбъект.ПроверитьЗаполнение() Тогда
Попытка
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
ЗаписатьЛог("Ошибка проведения: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Важный нюанс: В некоторых релизах УТ 11.4 наблюдалась особенность — если менять статус одновременно с вызовом метода Записать(РежимЗаписиДокумента.Проведение), система могла отработать по старой логике (как для статуса Отгружено). Чтобы этого избежать, попробуйте применить двухэтапную схему: сначала записать документ со статусом в режиме Запись, а затем вызвать процедуру проведения.
Если проблема возникает редко (1 док из 100) и в логах нет ошибок, стоит рассмотреть теорию скрытых конфликтов блокировок. На платформе 8.3.11 и выше в кластере 1С возможна ситуация, когда транзакция завершается на уровне встроенного языка, но из-за таймаута или специфики работы СУБД (MS SQL или PostgreSQL) изменения в регистрах не фиксируются.
Рассмотрим, как это происходит. В УТ 11 используется управляемый режим блокировок. Если одновременно с вашей загрузкой выполняется тяжелое фоновое задание (например, расчет себестоимости или распределение запасов), система может не успеть записать данные в таблицы итогов. Хотя метод Записать() не выкинет исключение Исключение, данные физически не попадут в базу из-за особенностей кэширования сервера приложений.
Для минимизации таких рисков рекомендуется:
Попытка...Исключение.Разберем вариант, предложенный опытными разработчиками: создание регламентного задания, которое ищет документы со статусом Принято, у которых отсутствуют движения по регистру ТоварыНаСкладах (или ТоварыОрганизаций).
Посмотрим на пример запроса для поиска таких «пустых» документов:
ВЫБРАТЬ
ПеремещениеТоваров.Ссылка КАК Ссылка
ИЗ
Документ.ПеремещениеТоваров КАК ПеремещениеТоваров
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах
ПО ПеремещениеТоваров.Ссылка = ТоварыНаСкладах.Регистратор
ГДЕ
ПеремещениеТоваров.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыПеремещенийТоваров.Принято)
И ПеремещениеТоваров.Проведен = ИСТИНА
И ТоварыНаСкладах.Регистратор ЕСТЬ NULL
Найденные таким образом документы следует перепроводить в фоновом режиме. Это гарантирует, что даже если при первичной загрузке произошел программный сбой, система в конечном итоге придет в консистентное состояние.
Начиная с версий 11.4, в системе появился регистр сведений РаспределениеЗапасов. Именно на его данных строятся многие отчеты по остаткам. Иногда движения в регистре накопления ТоварыНаСкладах физически существуют, но отчеты показывают пустоту. Это происходит из-за того, что запись в регистр сведений по каким-то причинам не обновилась.
Проанализируем ситуацию: если в консоли запросов вы видите приход по складу, а в отчете «Остатки и доступность товаров» — нет, значит, проблема в регистре распределения. В типовых конфигурациях есть обработка «Исправление ошибок в регистре сведений "Распределение запасов"». Рекомендуется запускать её периодически при массовых программных загрузках.
Для того чтобы решить проблему гарантированно, мы рекомендуем придерживаться следующего алгоритма:
ОбменДанными.Загрузка = Ложь.Записать(РежимЗаписиДокумента.Проведение) добавьте проверку наличия записей в наборе движений.Пример кода с проверкой движений:
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
ДвиженияСформированы = Ложь;
Для Каждого Набор Из ДокОбъект.Движения Цикл
Если Набор.Метаданные().Имя = "ТоварыНаСкладах" И Набор.Количество() > 0 Тогда
ДвиженияСформированы = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не ДвиженияСформированы И ДокОбъект.Статус = Перечисления.СтатусыПеремещенийТоваров.Принято Тогда
// Здесь можно вызвать повторное проведение или записать критическую ошибку в лог
ДокОбъект.Записать(РежимЗаписиДокумента.ОтменаПроведения);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЕсли;
Такой подход позволит вам оперативно выявлять «недопроведенные» документы и минимизировать расхождения в складском учете. Помните, что архитектура УТ 11 сложна и многогранна, поэтому программная работа с документами требует соблюдения всех типовых механизмов конфигурации.