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