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