При разработке в среде «1С:Предприятие 8» (особенно в современных конфигурациях, таких как «Управление нашей фирмой» — УНФ) программисты часто сталкиваются с задачей автоматизации ввода на основании. Использование механизма расширений позволяет реализовать эту логику без изменения типовой конфигурации, что существенно облегчает последующие обновления.
Однако довольно часто возникает следующая проблема: код написан без ошибок, транзакция записи проходит успешно (документ физически появляется в базе данных и его видно через консоль запросов), но пользователь в интерфейсе программы (в форме списка) созданный документ не видит. Разберем подробно, почему так происходит, как исправить эту ошибку и какие лучшие практики (Best Practices) существуют для решения подобных задач.
Для начала проанализируем код, который часто используют начинающие разработчики для создания документа на сервере:
&НаСервере
Процедура РасшПН_СоздатьПриходнуюНакладнуюПослеНаСервере()
ДанныеЗаполнения = Объект.Ссылка;
ДокПриходная = Документы.ПриходнаяНакладная.СоздатьДокумент();
ДокПриходная.Дата = ДанныеЗаполнения.Дата - 1; // Ошибка в расчете времени!
ДокПриходная.ЗаполнитьПоРасходнойНакладной(ДанныеЗаполнения);
ДокПриходная.Записать();
КонецПроцедуры
Данный алгоритм отрабатывает без ошибок, но документ «исчезает» из поля зрения пользователя. Рассмотрим детально четыре основные причины этого поведения.
Интерфейс управляемых форм 1С построен на работе с Динамическими списками. Платформа оптимизирует передачу данных между сервером и клиентом: список документов не перечитывает базу данных каждую секунду. Автоматическое обновление списка происходит только тогда, когда транзакция инициирована интерактивно из этой же формы (например, при нажатии на стандартную кнопку «Создать» или «Записать и закрыть» непосредственно в форме документа).
Если мы создаем и записываем документ «тихо» на сервере в фоновом режиме, клиентская форма списка просто «не знает» о том, что в базе данных появились новые записи. Для решения этой проблемы необходимо принудительно оповестить систему о создании нового объекта.
Решение: Модернизируем процедуру на клиенте, возвращая ссылку на созданный документ из серверного контекста и вызывая метод ОповеститьОбИзменении().
&НаКлиенте
Процедура РасшПН_СоздатьПриходнуюНакладнуюПосле(Команда)
// Получаем ссылку на созданный документ из серверной функции
НоваяСсылка = РасшПН_СоздатьПриходнуюНакладнуюПослеНаСервере();
Если ЗначениеЗаполнено(НоваяСсылка) Тогда
// Оповещаем систему об изменении типа данных (обновит все открытые списки приходных накладных)
ОповеститьОбИзменении(Тип("ДокументСсылка.ПриходнаяНакладная"));
// Либо оповещаем конкретную ссылку
ОповеститьОбИзменении(НоваяСсылка);
КонецЕсли;
КонецПроцедуры
В конфигурации «Управление нашей фирмой» (УНФ) и других прикладных решениях формы списков документов практически никогда не открываются «пустыми». В них по умолчанию установлены жесткие отборы по ключевым реквизитам: Организация, Подразделение, Склад или Состояние/Статус документа.
Если при программном создании документа мы не заполнили хотя бы один из этих реквизитов, документ запишется с пустым значением. В результате он будет создан в базе данных, но из-за несоответствия условиям отбора динамического списка (например, в списке стоит отбор по организации «Основная организация», а в созданном документе поле пустое) он мгновенно скроется от пользователя.
Выясним причину, почему реквизиты могут не заполниться. В исходном коде используется кастомная процедура ЗаполнитьПоРасходнойНакладной(). Если в ней не прописано явное заполнение организации, склада и контрагента, они останутся пустыми.
Чтобы избежать подобных ошибок, рекомендуется использовать штатный метод Заполнить(), который инициализирует системное событие ОбработкаЗаполнения в модуле объекта:
ДокПриходная = Документы.ПриходнаяНакладная.СоздатьДокумент();
// Инициализируем стандартный механизм заполнения на основании
ДокПриходная.Заполнить(ДанныеЗаполнения);
Обратите внимание на строку кода: ДокПриходная.Дата = ДанныеЗаполнения.Дата - 1;.
В платформе 1С тип данных Дата хранит значение с точностью до секунды. Математические операции сложения и вычитания производятся тоже в секундах!
Вычитая единицу, мы уменьшаем время документа ровно на одну секунду, а не на один день. Если исходный документ был создан, например, 01.11.2023 в 00:00:00, то вычитание одной секунды перенесет дату нового документа на 31.10.2023 в 23:59:59.
Если в форме списка у пользователя установлен период «Текущий месяц» (ноябрь), созданная накладная автоматически скроется, так как её дата попадет в предыдущий месяц.
Решение: Если требуется сместить дату на один день назад, необходимо вычитать 86 400 секунд (24 часа * 60 минут * 60 секунд):
// Уменьшаем дату ровно на 1 сутки
ДокПриходная.Дата = ДанныеЗаполнения.Дата - 86400;
Метод Записать() без параметров записывает документ в режиме «Запись», то есть как непроведенный черновик. Если в списке документов у пользователя включен быстрый фильтр «Только проведенные» или настроен вывод только согласованных документов, созданный черновик отображаться не будет.
---
Программное создание документов «вслепую» на сервере без вывода формы на экран пользователя оправдано только при групповой обработке данных или при работе регламентных заданий. Если действие инициируется нажатием кнопки в интерфейсе, такое поведение считается плохим тоном разработки (антипаттерном) по следующим причинам:
Записать() вызовет ошибку транзакции, и пользователь получит системный сбой.Рассмотрим два правильных варианта решения этой задачи.
Вместо непосредственной записи документа в базу данных на сервере, мы подготавливаем параметры и открываем пользователю форму новой приходной накладной на клиенте. Пользователь сможет визуально проверить заполненные данные, при необходимости дозаполнить специфические поля и самостоятельно нажать кнопку «Провести и закрыть».
Этот способ реализуется полностью на клиенте без написания сложных серверных вызовов:
&НаКлиенте
Процедура РасшПН_СоздатьПриходнуюНакладнуюПосле(Команда)
// Передаем ссылку на текущую расходную накладную в качестве основания
ПараметрыЗаполнения = Новый Структура("Основание", Объект.Ссылка);
ПараметрыФормы = Новый Структура("ЗначенияЗаполнения", ПараметрыЗаполнения);
// Открываем форму создания нового документа Приходная Накладная
ОткрытьФорму("Документ.ПриходнаяНакладная.ФормаОбъекта", ПараметрыФормы);
КонецПроцедуры
Чтобы переопределить стандартную логику заполнения на основании (если вас не устраивает типовой алгоритм УНФ), мы не копируем весь код конфигурации в расширение. Вместо этого мы расширяем модуль объекта Документ.ПриходнаяНакладная и перехватываем процедуру ОбработкаЗаполнения с помощью аннотации &После или &Вместо:
&После("ОбработкаЗаполнения")
Процедура РасшПН_ОбработкаЗаполнения(ДанныеЗаполнения, НаправлениеЗаполнения, СтандартнаяОбработка)
// Проверяем тип основания
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.РасходнаяНакладная") Тогда
// Корректируем дату документа под наши задачи (минус 1 день)
ЭтотОбъект.Дата = ДанныеЗаполнения.Дата - 86400;
// Здесь описываем свои точечные изменения логики заполнения табличных частей
// ...
КонецЕсли;
КонецПроцедуры
Современные версии конфигурации «1С:Управление нашей фирмой» построены на Библиотеке Стандартных Подсистем (БСП). В БСП интегрирован мощный механизм «Подключаемые команды».
Вместо ручного добавления кнопок на форму документа через расширение формы, правильнее зарегистрировать новую команду создания на основании в общей системе команд БСП. Для этого достаточно создать внешнюю обработку или добавить код интеграции в модуль создания команд.
Преимущества использования БСП:
Таким образом, комбинируя возможности расширений (перехват события ОбработкаЗаполнения) и правильные методы работы на клиенте (открытие предзаполненных форм через ОткрытьФорму с параметром ЗначенияЗаполнения), мы получаем надежное решение, устойчивое к обновлениям конфигурации и удобное для конечного пользователя.