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