Как реализовать перемещение товаров между складами с контролем остатков в 1С?

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

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

Постановка задачи и структура метаданных

Для реализации перемещения нам понадобятся следующие объекты конфигурации:

  1. Справочник Номенклатура: содержит перечень товаров.
  2. Справочник Склады: список мест хранения.
  3. Регистр накопления "Товары на складах": вид регистра — Остатки. Измерения: Склад (тип СправочникСсылка.Склады), Номенклатура (тип СправочникСсылка.Номенклатура). Ресурс: Количество (тип Число).
  4. Документ "Перемещение товаров": реквизиты шапки СкладОтправитель и СкладПолучатель. Табличная часть Товары с колонками Номенклатура и Количество.

Важное замечание: Одной из критических ошибок, упомянутых в обсуждении, является использование типа Строка для измерений регистра. Всегда используйте ссылочные типы (СправочникСсылка), иначе контроль остатков по конкретному складу будет невозможен, а производительность системы упадет.

Шаг 1: Формирование движений (Приход и Расход)

Процесс перемещения состоит из двух движений в рамках одного регистра: списание со склада-отправителя и зачисление на склад-получатель. Рассмотрим, как это реализовать в процедуре ОбработкаПроведения.

Проанализируем программный код для записи движений:


Процедура ОбработкаПроведения(Отказ, Режим)
    
    // Очищаем движения перед записью, если документ перепроводится
    Движения.ТоварыНаСкладах.Записывать = Истина;
    Движения.ТоварыНаСкладах.Очистить();
    
    Для Каждого ТекСтрокаТовары Из Товары Цикл
        // Движение Расход (со склада-отправителя)
        Движение = Движения.ТоварыНаСкладах.ДобавитьРасход();
        Движение.Период = Дата;
        Движение.Склад = СкладОтправитель;
        Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
        Движение.Количество = ТекСтрокаТовары.Количество;
        
        // Движение Приход (на склад-получатель)
        Движение = Движения.ТоварыНаСкладах.ДобавитьПриход();
        Движение.Период = Дата;
        Движение.Склад = СкладПолучатель;
        Движение.Номенклатура = ТекСтрокаТовары.Номенклатура;
        Движение.Количество = ТекСтрокаТовары.Количество;
    КонецЦикла;
    
    // Записываем движения, чтобы проверить остатки с учетом текущего документа
    Движения.ТоварыНаСкладах.Записать();
    
    // Далее следует блок контроля остатков
КонецПроцедуры

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

Шаг 2: Реализация контроля остатков с помощью запроса

Рассмотрим ситуацию: если мы перемещаем 10 единиц товара, а на складе-отправителе всего 5, система должна выдать ошибку и отменить проведение. Для этого нам нужно проверить остатки после того, как наши движения попали в регистр.

Выясним причину, по которой контроль остатков может не работать. В обсуждении на форуме автор пытался сравнивать количество строк в таблице значений, вместо анализа самих данных. Правильный подход заключается в проверке отрицательных остатков на складе-отправителе.

Разберем по шагам создание запроса для контроля:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |	ТоварыНаСкладахОстатки.КоличествоОстаток КАК КоличествоОстаток
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(&МоментВремени, Склад = &СкладОтправитель) КАК ТоварыНаСкладахОстатки
    |ГДЕ
    |	ТоварыНаСкладахОстатки.КоличествоОстаток < 0";

Запрос.УстановитьПараметр("МоментВремени", Новый Граница(МоментВремени(), ГраницаТипа.Включая));
Запрос.УстановитьПараметр("СкладОтправитель", СкладОтправитель);

РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Недостаточно товара на складе отправителе! Номенклатура: " + ВыборкаДетальныеЗаписи.Номенклатура + 
                          ". Дефицит: " + Строка(-ВыборкаДетальныеЗаписи.КоличествоОстаток);
        Сообщение.Сообщить();
        Отказ = Истина;
    КонецЦикла;
КонецЕсли;

Шаг 3: Профессиональные тонкости и оптимизация

Рассмотрим подробнее важные нюансы, которые отличают качественный код от учебного примера:

  1. Использование Момента Времени: Чтобы запрос увидел движения самого документа, мы передаем в параметры Новый Граница(МоментВремени(), ГраницаТипа.Включая). Если использовать просто дату или момент времени без границы, результаты могут быть неточными при наличии других документов в ту же секунду.
  2. Группировка табличной части: Если в документе одна и та же номенклатура введена двумя строками, при записи движений они суммируются в регистре. Однако в запросе контроля остатков рекомендуется всегда накладывать фильтр по тем товарам, которые есть в документе. Это делается через конструкцию В (ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура ИЗ Документ... ГДЕ Ссылка = &Ссылка).
  3. Управляемые блокировки: В многопользовательской среде крайне важно использовать объект БлокировкаДанных. Без блокировки может возникнуть ситуация "Double Spending" (двойное списание), когда два пользователя одновременно проверяют остаток, видят, что товар есть, и оба его списывают, уводя остаток в минус.
  4. Метод СообщениеПользователю: Вместо устаревшего Сообщить() мы используем объект СообщениеПользователю. Это позволяет привязать текст ошибки к конкретному полю или строке табличной части, что значительно удобнее для конечного пользователя.

Проанализируем итоговый подход. Проблема автора сообщения №5 заключалась в том, что он проверял остатки на СкладПолучатель, где товар всегда прибавляется. Проверка должна выполняться строго по СкладОтправитель.

Резюме

Для успешной реализации перемещения необходимо:

Используя эти рекомендации, вы сможете создать надежный механизм перемещения товаров, который выдержит нагрузку в реальных условиях эксплуатации.

← На главную