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

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

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

В чем заключается главная проблема неоперативного контроля?

Проанализируем типовую ошибку разработчиков: попытку проверить остаток только на дату проводимого задним числом документа или только на текущий момент времени. Ни один из этих вариантов не гарантирует отсутствие отрицательных остатков:

  1. Проверка только на дату документа: если на дату документа списания остаток был положительным (например, 10 шт.), но через три дня весь товар был списан в ноль другим документом, то вставка нового расхода задним числом приведет к тому, что в будущей точке времени остаток станет отрицательным.
  2. Проверка только на текущий момент времени: на текущий момент на складе может быть достаточно товара из-за более поздних поступлений, однако в промежутке между датой документа и текущим моментом возникнет временной разрыв с отрицательным остатком.
  3. Отмена проведения и уменьшение прихода: отрицательный остаток может возникнуть не только при вводе нового документа расхода, но и при отмене проведения документа поступления, перемещения или уменьшении количества в табличной части приходной накладной задним числом.

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

Алгоритм проверки минимального нарастающего остатка на интервале

Рассмотрим, как правильно организовать вычисление остатка по временной шкале без создания избыточной нагрузки на СУБД. Вычислять нарастающий итог с помощью соединения таблицы движений самой с собой в запросе не рекомендуется из-за квадратичной сложности. Оптимальный по производительности алгоритм строится в два шага:

  1. Запросом получаем остаток по регистру накопления на момент времени проводимого документа (с учетом движения самого документа).
  2. Выбираем все движения по контролируемым позициям номенклатуры и складам за период от момента времени документа до текущей даты, упорядоченные по моменту времени.
  3. В цикле выборки последовательно пересчитываем нарастающий остаток. Если на каком-либо шаге остаток становится меньше нуля, прерываем проверку и выставляем флаг отказа.

Посмотрим на пример реализации проверки в коде:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВремТаблица.Номенклатура КАК Номенклатура,
    |	ВремТаблица.Склад КАК Склад,
    |	ВремТаблица.Количество КАК Количество
    |ПОМЕСТИТЬ ВТ_Товары
    |ИЗ
    |	&ТаблицаТоваров КАК ВремТаблица
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	Остатки.Номенклатура КАК Номенклатура,
    |	Остатки.Склад КАК Склад,
    |	Остатки.КоличествоОстаток КАК КоличествоОстаток
    |ПОМЕСТИТЬ ВТ_НачальныеОстатки
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах.Остатки(&МоментВремени, (Номенклатура, Склад) В
    |		(ВЫБРАТЬ Номенклатура, Склад ИЗ ВТ_Товары)) КАК Остатки
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	Движения.Период КАК Период,
    |	Движения.Регистратор КАК Регистратор,
    |	Движения.Номенклатура КАК Номенклатура,
    |	Движения.Склад КАК Склад,
    |	ВЫБОР
    |		КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
    |			ТОГДА Движения.Количество
    |		ИНАЧЕ -Движения.Количество
    |	КОНЕЦ КАК ИзменениеКоличества
    |ИЗ
    |	РегистрНакопления.ТоварыНаСкладах КАК Движения
    |ГДЕ
    |	Движения.Период > &ПериодДокумента
    |	И (Движения.Номенклатура, Движения.Склад) В
    |		(ВЫБРАТЬ Номенклатура, Склад ИЗ ВТ_Товары)
    |
    |УПОРЯДОЧИТЬ ПО
    |	Движения.Период,
    |	Движения.МоментВремени";

После выполнения запроса пересчитываем нарастающий остаток во встроенном языке:


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

// Проверяем движения в будущем
ВыборкаДвижения = РезультатЗапросаДвижения.Выбрать();
Пока ВыборкаДвижения.Следующий() Цикл
    Ключ = Новый Структура("Номенклатура,Склад", ВыборкаДвижения.Номенклатура, ВыборкаДвижения.Склад);
    ТекОстаток = ТекущиеОстатки[Ключ];
    Если ТекОстаток = Неопределено Тогда
        ТекОстаток = 0;
    КонецЕсли;
    
    ТекОстаток = ТекОстаток + ВыборкаДвижения.ИзменениеКоличества;
    Если ТекОстаток < 0 Тогда
        Отказ = Истина;
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = СтрШаблон("Документ нельзя провести задним числом: позиция %1 на складе %2 уйдет в минус на дату %3 по документу %4", 
            ВыборкаДвижения.Номенклатура, ВыборкаДвижения.Склад, ВыборкаДвижения.Период, ВыборкаДвижения.Регистратор);
        Сообщение.Сообщить();
        Прервать;
    КонецЕсли;
    
    ТекущиеОстатки.Вставить(Ключ, ТекОстаток);
КонецЦикла;

Точки встраивания проверки: модули проведения или подписки на события

Разберем варианты реализации контроля в архитектуре конфигурации:

  1. Встраивание в модули документов (процедура ОбработкаПроведения): позволяет иметь полный контекст документа, понимать, оперативное это проведение или неоперативное, а также управлять логикой в зависимости от прав пользователя. Минус подхода — необходимость изменения множества типовых модулей.
  2. Подписка на событие ПриЗаписи набора записей регистра накопления: централизованный способ, охватывающий все документы. Однако в обработчике набора записей отсутствует контекст самого документа (проводится ли он вручную, пересчитывается ли фоновым заданием или отменяется проведение). Параметры приходится передавать через свойство ДополнительныеСвойства набора записей.
  3. Подписка на событие ОбработкаУдаленияПроведения документов: обязательна для контроля действий, связанных с отменой проведения приходных накладных и перемещений, так как отмена прихода задним числом точно так же способна сделать будущие остатки отрицательными.

Ограничения производительности и блокировки данных

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

  1. Управляемые блокировки на интервал: при проверке остатков на временном отрезке объект БлокировкаДанных должен накладывать исключительную блокировку на регистр по номенклатуре и складу не в одной точке, а на весь период от даты документа до текущего момента.
  2. Падение параллельности: если один пользователь проводит документ задним числом, он блокирует движения по указанным товарам для всех остальных пользователей, проводящих оперативные документы в текущем времени. Это приводит к постоянным тайм-аутам блокировок и взаимным блокировкам (deadlocks).
  3. Особенности учета в 1С:УПП (режим РАУЗ): при включенной расширенной аналитике учета затрат внутримесячный партионный контроль отключается штатно. Корректность суммовых и количественных остатков обеспечивается регламентной процедурой закрытия месяца (документ Расчет себестоимости выпуска). Попытка включить жесткий интервальный контроль на уровне складских регистров может нарушить регламентную работу учетной системы.

Альтернативные архитектурные решения

Если блокировать проведение документов задним числом в режиме реального времени нецелесообразно из-за производительности, применяют альтернативные подходы:

  1. Использование последовательностей документов: проведение задним числом разрешается без тяжелых онлайн-проверок, но граница последовательности сдвигается назад. Корректность движений и списаний контролируется пакетно при регламентном восстановлении последовательности.
  2. Асинхронный (отложенный) контроль: при проведении документа задним числом факт проведения фиксируется в служебном регистре сведений. Регламентное фоновое задание в фоновом режиме анализирует интервальные остатки и формирует отчет для главного бухгалтера или ответственных лиц с перечнем документов, приведших к возникновению кассовых или складских разрывов.
  3. Административное ограничение: запрет на проведение и отмену проведения документов задним числом через настройку прав доступа и даты запрета изменения данных.
← На главную