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