Автоматическое создание документов в 1С для ордерного склада надёжнее всего строить через очередь задач: при проведении документа в регистр сведений ставится задача («создать или изменить расходный ордер по заказу»), а отдельный обработчик выбирает задачи по порядку, проверяет встречные изменения и создаёт или корректирует документы следующего уровня. Так заказ клиента, расходный ордер и отборы остаются согласованными, даже если клиент меняет заказ, а склад уже собирает товар.

Ниже — схема для УТ 11 / КА / ERP с ордерными складами: структура очереди, постановка задач из расширения, обработка с блокировками и правила разрешения конфликтов «с двух концов».

Цепочка документов ордерного склада и где ломается ручной ввод

Типовая цепочка отгрузки с ордерного склада:

  1. Заказ клиента — менеджер или импорт с сайта.
  2. Расходный ордер на товары — распоряжение складу.
  3. Отбор (размещение) товаров — задание кладовщику; по одному ордеру их может быть несколько.
  4. Реализация — после сборки.

Вручную через «Создать на основании» это работает, пока заказов немного. Когда заказы приходят из интернет-магазина сотнями, менеджеры не успевают создавать ордера, а склад простаивает. Первый шаг автоматизации очевиден — создавать следующий документ при проведении предыдущего, если выполнены условия: статус «К обеспечению», есть предоплата, подтверждение клиента.

Сложности начинаются с обратным потоком. Отбор может завершиться со статусом «выполнено с ошибками»: часть товара не нашли. Эти данные нужно поднять в расходный ордер, а оттуда — в заказ. А в это же время клиент звонит и уменьшает количество в той же позиции. Два изменения идут навстречу друг другу, и без правил одно перезапишет другое или они сложатся.

Автоматическое создание документов в 1С: данные идут по цепочке и вперёд, и обратно
Автоматическое создание документов в 1С: данные идут по цепочке и вперёд, и обратно

Архитектура: очередь задач вместо создания при проведении

Создавать подчинённые документы прямо в ОбработкаПроведения я не рекомендую:

  • транзакция проведения растёт, а с ней — время блокировок регистров;
  • ошибка при создании ордера откатывает проведение заказа;
  • нет места, где видно, что пошло не так.

Вместо этого проведение только ставит задачу в очередь, а обработку выполняет регламентное задание или дополнительная обработка БСП с расписанием.

КомпонентГде размещаюНазначение
Регистр сведений ОчередьКорректировкиДокументовРасширениеЗадачи и результаты их обработки
Перечисление ЗадачиКорректировкиРасширениеВиды задач
Обработчики &После("ОбработкаПроведения")РасширениеПостановка задач
Обработчики задачРасширение или дополнительная обработкаСоздание и изменение документов

Структура очереди

ПолеВидТип
ИдентификаторИзмерениеУникальныйИдентификатор
ОбъектРесурсДокументСсылка: ЗаказКлиента, РасходныйОрдерНаТовары, ОтборРазмещениеТоваров
ЗадачаРесурсПеречислениеСсылка.ЗадачиКорректировки
ДатаПостановкиРесурсДата и время
ОбработаноРесурсБулево
ДатаОбработкиРесурсДата и время
ОшибкаРесурсБулево
ОписаниеОшибкиРесурсСтрока неограниченная

Значения перечисления описывают направление движения данных:

ЗначениеСмысл
ОрдерПоЗаказуСоздать или изменить расходный ордер по заказу клиента
ОтборыПоОрдеруСоздать или изменить отборы по расходному ордеру
РазмещениеПоОрдеруСоздать размещение (возврат на полки) по ордеру
ОрдерПоОтборуИзменить расходный ордер по результату отбора
ЗаказПоОрдеруИзменить заказ клиента по расходному ордеру

Первые три — прямой поток, последние два — обратный.

Постановка задачи при проведении

В расширении добавляем обработчик после проведения заказа. Если запись в очередь выполняется в той же транзакции, задача появится только при успешном проведении — это то, что нужно.

1С
&После("ОбработкаПроведения")
Процедура Расш_ОбработкаПроведения(Отказ, РежимПроведения)

	Если Отказ Или ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;

	Если Статус = Перечисления.СтатусыЗаказовКлиентов.КОбеспечению Тогда
		ОчередьКорректировки.ДобавитьЗадачу(Ссылка, Перечисления.ЗадачиКорректировки.ОрдерПоЗаказу);
	КонецЕсли;

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

Условия постановки (статус, оплата, склад) — точка настройки под процессы конкретной компании. Сначала достаточно статуса, остальное добавляется по мере появления требований.

Процедура постановки не дублирует необработанные задачи: если задача по этому объекту уже ждёт, вторая не нужна — при обработке всё равно будут взяты актуальные данные документа.

1С
// Ставит задачу корректировки в очередь, если такой же необработанной задачи нет.
Процедура ДобавитьЗадачу(Объект, Задача) Экспорт

	УстановитьПривилегированныйРежим(Истина);

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	Очередь.Идентификатор КАК Идентификатор
	|ИЗ
	|	РегистрСведений.ОчередьКорректировкиДокументов КАК Очередь
	|ГДЕ
	|	Очередь.Объект = &Объект
	|	И Очередь.Задача = &Задача
	|	И НЕ Очередь.Обработано";
	Запрос.УстановитьПараметр("Объект", Объект);
	Запрос.УстановитьПараметр("Задача", Задача);

	Если Не Запрос.Выполнить().Пустой() Тогда
		Возврат;
	КонецЕсли;

	МенеджерЗаписи = РегистрыСведений.ОчередьКорректировкиДокументов.СоздатьМенеджерЗаписи();
	МенеджерЗаписи.Идентификатор = Новый УникальныйИдентификатор;
	МенеджерЗаписи.Объект = Объект;
	МенеджерЗаписи.Задача = Задача;
	МенеджерЗаписи.ДатаПостановки = ТекущаяДатаСеанса();
	МенеджерЗаписи.Записать();

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

Обработка очереди с блокировками

Обработчик выбирает необработанные задачи одного вида в порядке постановки. Каждая задача — в своей транзакции: ошибка по одному заказу не должна останавливать остальные.

1С
// Обрабатывает задачи «создать или изменить расходный ордер по заказу».
Процедура ОбработатьЗадачиОрдерПоЗаказу() Экспорт

	Задача = Перечисления.ЗадачиКорректировки.ОрдерПоЗаказу;
	Выборка = ОчередьКорректировки.НеобработанныеЗадачи(Задача).Выбрать();

	Пока Выборка.Следующий() Цикл

		// Встречная задача от склада ещё не обработана — ждём её.
		Если ОчередьКорректировки.ЕстьВстречнаяЗадача(Выборка.Объект, Задача) Тогда
			Продолжить;
		КонецЕсли;

		Результат = Новый Структура("Ошибка, ОписаниеОшибки", Ложь, "");

		НачатьТранзакцию();
		Попытка
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Документ.ЗаказКлиента");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Объект);
			ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
			Блокировка.Заблокировать();

			ПроверитьЗаказПередСозданиемОрдера(Выборка.Объект, Результат);
			Если Не Результат.Ошибка Тогда
				СоздатьИлиИзменитьРасходныйОрдер(Выборка.Объект);
			КонецЕсли;

			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			Результат.Ошибка = Истина;
			Результат.ОписаниеОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
		КонецПопытки;

		ОчередьКорректировки.ОтметитьОбработку(Выборка.Идентификатор, Результат);

	КонецЦикла;

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

Обратите внимание: отметка о результате пишется после транзакции. Если записать её внутри, при откате пропадёт и информация об ошибке.

ПроверитьЗаказПередСозданиемОрдера — место для бизнес-проверок: все строки имеют вариант обеспечения «Отгрузить» или «Отгрузить обособленно», товар отгружается с ордерного склада, заказ не закрыт. Нарушение — это не исключение, а осмысленная ошибка в очереди, которую увидит менеджер.

СоздатьИлиИзменитьРасходныйОрдер ищет существующий ордер по заказу. Нет — создаёт через Документы.РасходныйОрдерНаТовары.СоздатьДокумент() и Заполнить(Заказ), чтобы сработала типовая логика заполнения. Есть — сравнивает табличную часть с заказом и меняет только расхождения, после чего ставит задачу ОтборыПоОрдеру.

Встречные изменения: правила разрешения конфликтов

Очередь задач: встречная задача ждёт, пока обработается конфликтующая
Очередь задач: встречная задача ждёт, пока обработается конфликтующая

Самое важное в механизме — договориться о правилах, когда изменения приходят с двух сторон:

  • Факт важнее плана. Сначала применяется результат отбора (что реально собрано), затем изменения клиента. Поэтому задача ОрдерПоЗаказу ждёт, пока обработается встречная ОрдерПоОтбору по тому же ордеру.
  • Пересчёт, а не приращение. Ордер не «уменьшается на разницу», а пересчитывается из текущего заказа и фактов отборов. Тогда порядок применения не приводит к двойному вычитанию.
  • Отборы в работе не трогаем. Если по позиции отбор уже выполняется, изменение заказа порождает размещение (возврат на полки) после завершения отбора, а не правку отбора под руками кладовщика.
  • Повтор вместо ошибки. Если документ заблокирован пользователем, задача остаётся необработанной и берётся при следующем запуске.

Функция ЕстьВстречнаяЗадача проверяет по объекту и связанным документам, нет ли в очереди необработанных задач обратного направления. Карту «какая задача какой мешает» удобно держать в одном месте — в общем модуле очереди.

Запуск и мониторинг

  • Регламентное задание раз в одну–две минуты обрабатывает задачи всех видов в порядке: обратный поток, затем прямой.
  • В конфигурациях на БСП альтернативой служит дополнительная обработка с командой вида «ВызовСерверногоМетода» и расписанием — её проще поправить без обновления конфигурации.
  • Отчёт по регистру очереди с отбором «Ошибка = Истина» — ежедневный рабочий инструмент ответственного за склад.
  • Обработанные задачи старше месяца удаляются отдельным заданием.

Такие механизмы я делаю в расширениях, чтобы не снимать типовую с поддержки, — см. расширения 1С и доработку 1С под складские процессы.

Типичные ошибки

  • Создание подчинённых документов внутри проведения заказа — долгие блокировки и откат заказа при любой ошибке.
  • Запись результата задачи внутри транзакции — при откате теряется описание ошибки.
  • Приращение количества вместо пересчёта — двойное списание при встречных изменениях.
  • Отсутствие проверки ОбменДанными.Загрузка — задачи создаются при загрузке из обменов.
  • Одна транзакция на всю очередь — один проблемный заказ останавливает весь склад.

Итог

  • Проведение документа только ставит задачу в очередь, документы создаёт отдельный обработчик.
  • Каждая задача — в своей транзакции с управляемой блокировкой, результат пишется после неё.
  • Встречные задачи разрешаются правилами: факт важнее плана, пересчёт вместо приращения.
  • Очередь в регистре сведений одновременно служит журналом ошибок для пользователей.