В практике разработки на платформе 1С часто возникает ситуация, когда в рамках одного производственного или складского документа необходимо отразить цепочку действий: сначала оприходовать номенклатуру (например, полуфабрикат), а затем в этой же транзакции списать её на выпуск следующего изделия. Проблема заключается в том, что стандартные механизмы контроля остатков часто «не видят» приход, совершенный этим же документом, и выдают ошибку о нехватке товара. Рассмотрим подробнее, как правильно организовать логику проведения и архитектуру таких документов.
Разберем ситуацию, описанную в теме: пользователь переносит логику из старых систем (версии 7.7), где действия выполнялись последовательно. В современных версиях 1С (8.x) проведение документа — это единая транзакция. Если в коде ОбработкаПроведения сначала вызывается запрос на проверку остатков, а затем производится запись движений, то запрос не увидит новые поступления, так как они еще не зафиксированы в базе данных. Важно понимать: контроль остатков должен учитывать состояние регистра с учетом движений текущего документа.
Проанализируем наиболее эффективный способ, который используется во всех современных типовых конфигурациях (ERP, УТ 11, КА 2). Вместо того чтобы проверять наличие товара перед списанием, мы сначала записываем все движения документа, а затем проверяем — не ушли ли остатки в минус.
Рассмотрим алгоритм реализации этого метода:
ЗаписыватьВыбранныеИзменения или принудительно записываем движения в начале процедуры.При таком подходе, если документ приходует 10 единиц и тут же списывает 10 единиц, итоговое сальдо будет равно нулю. Система контроля не зафиксирует отрицательного остатка, и документ успешно проведется. Посмотрим на примерную структуру кода:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// 1. Очистка старых движений (если нужно)
Движения.ЗапасыНаСкладах.Очистить();
Движения.ЗапасыНаСкладах.Записывать = Истина;
// 2. Формируем движения Прихода
Для Каждого СтрокаПродукция Из Продукция Цикл
Движение = Движения.ЗапасыНаСкладах.ДобавитьПриход();
Движение.Номенклатура = СтрокаПродукция.Номенклатура;
Движение.Количество = СтрокаПродукция.Количество;
// ... заполнение остальных полей
КонецЦикла;
// 3. Формируем движения Расхода
Для Каждого СтрокаМатериалы Из Материалы Цикл
Движение = Движения.ЗапасыНаСкладах.ДобавитьРасход();
Движение.Номенклатура = СтрокаМатериалы.Номенклатура;
Движение.Количество = СтрокаМатериалы.Количество;
КонецЦикла;
// 4. Фиксируем движения в БД
Движения.ЗапасыНаСкладах.Записать();
// 5. Контроль остатков (после записи)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Запасы.Номенклатура,
| Запасы.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ЗапасыНаСкладах.Остатки(&МоментВремени, Номенклатура В (&СписокНоменклатуры)) КАК Запасы
|ГДЕ
| Запасы.КоличествоОстаток < 0";
Запрос.УстановитьПараметр("МоментВремени", Новый Граница(МоментВремени(), ВидГраницы.Включая));
Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатурыДокумента);
Результат = Запрос.Выполнить();
Если Не Результат.Пустой() Тогда
Отказ = Истина;
// Сообщение пользователю о нехватке
КонецЕсли;
КонецПроцедуры
Если вы вынуждены использовать «старую» методику контроля (проверка перед записью), необходимо правильно работать с временной меткой. При выполнении запроса к остаткам в параметре МоментВремени важно указывать объект Граница с включением самого документа. Однако выясним причину, почему это не всегда работает: если движения еще не записаны методом Записать(), никакой объект Граница не поможет их увидеть. Поэтому сначала — запись прихода, затем — проверка остатков для расхода.
Проанализируем ситуацию с точки зрения бизнес-логики. Часто промежуточный полуфабрикат, который создается и потребляется мгновенно, вообще не требует складского учета. В производственных системах 1С (таких как ERP) это называется «выпуск на затраты».
Рассмотрим альтернативный вариант:
При реализации схемы «приход и расход в одном флаконе» крайне важно использовать управляемые блокировки. Если ваша конфигурация работает в режиме управляемых блокировок, необходимо явно заблокировать те позиции номенклатуры, по которым производятся движения. Это гарантирует, что в момент между записью прихода и проверкой остатков никакой другой документ не «украдет» этот товар из базы данных.
Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ЗапасыНаСкладах");
ЭлементБлокировки.УстановитьПараметр("Номенклатура", СписокНоменклатуры);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
Пользователи часто просят видеть «текущий остаток» прямо в табличной части документа. Для самописного документа, где данные постоянно меняются, рекомендуется использовать Реквизит формы типа ТаблицаЗначений (а не динамический список, если важна оперативность расчетов).
Разберем шаги по реализации:
ТекущийОстатокНаСкладе + ПриходПоДокументу - РасходПоДокументу.Важный момент: колонка «Приход», которую пользователь хочет редактировать вручную, должна напрямую записываться в реквизит документа, чтобы изменения сохранялись при записи. Остальные колонки (например, «Остаток после проведения») должны быть только для чтения и служить информационным целям.
Для корректной работы документа с одновременным приходом и расходом необходимо отказаться от устаревшей последовательной проверки. Переход на новую методику проведения (контроль после записи движений) является «золотым стандартом» разработки на 1С 8.3. Это не только решает проблему нехватки остатков внутри одного документа, но и значительно повышает производительность системы за счет уменьшения времени ожидания блокировок.