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