Часто разработчики 1С:Предприятие 7.7 сталкиваются с неприятной ситуацией: при попытке записи документа с неуникальным номером платформа выдает собственное модальное окно с предупреждением "Номер не уникальный". Окно блокирует работу, и стандартная конструкция перехвата ошибок кажется бессильной. Давайте проанализируем эту ситуацию и разберем эффективные способы решения проблемы.
Для начала выясним причину, почему перехват ошибки работает не во всех случаях. Конструкция Попытка ... Исключение в версии 7.7 перехватывает только программные ошибки, которые возникают непосредственно в контексте выполнения вашего программного модуля.
Разберем поведение платформы при интерактивном сохранении (когда пользователь нажимает кнопку сохранения в форме документа). В этом случае платформа сначала выполняет код предопределенной процедуры ПриЗаписи(). Если вызов не отменен, платформа выходит из программного контекста и начинает транзакцию записи на низком уровне ядра (движка). Именно там осуществляется жесткая проверка уникальности. Поскольку это происходит уже вне вашего кода, генерируется системное модальное окно, которое невозможно обернуть в Попытку.
При программном вызове метода Записать() транзакция инициируется внутри вашего кода, поэтому исключение успешно генерируется и может быть обработано.
Рассмотрим, как правильно обрабатывать ошибку, если мы записываем документ программно (например, при обработке массива данных). В этом случае конструкция перехвата отлично сработает, и мы сможем проанализировать ОписаниеОшибки().
Посмотрим на пример функции, которая безопасно записывает документ и, в случае сбоя нумерации, пытается исправить ситуацию:
Функция ЗаписатьДокБезопасно(Док)
Завершение = 0;
Результат = 1;
Пока Завершение = 0 Цикл
Попытка
Док.Записать();
Завершение = 1;
Исключение
Описание = "Номер не уникальный";
Ошибка = ОписаниеОшибки();
// Проверяем текст ошибки
Если Лев(Ошибка, СтрДлина(Описание)) = Описание Тогда
Завершение = 0;
// Пытаемся получить следующий номер
Док.НомерДок = Инкремент(Док.НомерДок);
Сообщить("Сбой нумератора, пробуем номер: " + Док.НомерДок);
Иначе
Завершение = 1;
Результат = 0;
Сообщить("Необработанная ошибка при записи: " + Ошибка);
КонецЕсли;
КонецПопытки;
КонецЦикла;
Возврат Результат;
КонецФункции
Проанализируем ситуацию с интерактивной записью. Так как ядро мы перехватить не можем, нам необходимо проверить уникальность номера до того, как управление перейдет к движку платформы. Идеальное место для этого — предопределенная процедура ПриЗаписи().
Самый простой подход — попытаться найти документ с таким же номером перед записью. Однако здесь есть критический нюанс, который часто упускают: если пользователь открыл уже существующий документ, изменил его и нажал "Сохранить", метод НайтиПоНомеру() найдет этот же самый документ! Из-за этого алгоритм выдаст ложную ошибку и заблокирует работу.
Рассмотрим правильный код с обязательной проверкой, не нашли ли мы самих себя:
Процедура ПриЗаписи()
Док = СоздатьОбъект("Документ." + Вид());
// Ищем документ с таким же номером за этот же период (год/месяц/день в зависимости от настроек)
Если Док.НайтиПоНомеру(НомерДок, ДатаДок) = 1 Тогда
// Важнейшая проверка: исключаем сам текущий документ
Если ТекущийДокумент() <> Док.ТекущийДокумент() Тогда
Сообщить("Внимание! Документ с таким номером уже существует в базе!");
// Отменяем стандартную запись платформы
СтатусВозврата(0);
Возврат;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Использование СтатусВозврата(0) гарантирует, что платформа отменит дальнейшие действия и не дойдет до системного окна ошибки.
Разберем по шагам, как можно улучшить алгоритм и какие еще технологии применяются для решения этой задачи:
ПриЗаписи() при обнаружении дубля можно вызвать стандартный метод УстановитьНовыйНомер(). Программа сама исправит номер, и запись пройдет успешно. Пользователю достаточно вывести информационное сообщение: "Номер был занят. Документу автоматически присвоен новый номер".НайтиПоНомеру() работает медленно из-за инициализации OLE-объектов, лучше использовать прямые SQL-запросы через компоненту 1cpp.dll. С помощью объекта ODBCRecordset можно мгновенно проверить таблицу журнала документов _1SJOURN на совпадение полей DOCNO и несовпадение IDDOC.1spy.dll или VKHook), которые позволяют устанавливать Hook на уровне операционной системы Windows для перехвата вызова функции MessageBox. Они могут программно нажать кнопку "ОК" в окне ядра 1С. Однако мы рекомендуем избегать таких методов: они считаются "грязными" хаками и могут приводить к внезапному аварийному завершению работы (крашу) клиента 1С. Программная проверка в ПриЗаписи() работает значительно стабильнее и безопаснее.Применяя описанный подход с проверкой уникальности в предопределенных процедурах, вы полностью избавите пользователей от раздражающих системных окон и обеспечите надежный контроль над нумерацией документов в вашей базе.