Как сохранить список выбранных подразделений в документе 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

При доработке типовых документов часто возникает необходимость дать пользователю возможность выбрать не одно значение ссылочного типа, а целый список — например, сразу несколько подразделений для формирования табеля учета рабочего времени или расчетных ведомостей. Начинающие разработчики нередко пытаются добавить на форму реквизит с типом СписокЗначений и сталкиваются с тем, что после закрытия и повторного открытия документа выбранные данные бесследно исчезают. Проанализируем, почему это происходит, какие типичные ошибки допускаются при попытке обойти проблему и как правильно реализовать постоянное хранение списка значений в объекте конфигурации.

Почему данные в реквизите формы не сохраняются

Разберем фундаментальное отличие между реквизитами объекта и реквизитами формы в платформе 1С:Предприятие 8.3:

  1. Реквизит объекта физически связан с таблицами базы данных в СУБД. При вызове стандартных процедур записи и проведения значения реквизитов объекта гарантированно записываются в постоянную память.
  2. Реквизит формы существует исключительно в оперативной памяти во время открытого сеанса работы с конкретным окном. Как только форма закрывается, контекст клиентского приложения уничтожается, а все данные реквизитов формы обнуляются.

Попытка включить свойство Автоматическое сохранение данных формы в настройках для решения этой задачи также является ошибочной. Данный механизм платформы сохраняет пользовательские настройки формы в системном хранилище и привязывается к текущему пользователю и его рабочему месту, а не к конкретному экземпляру документа в информационной базе. В результате получается неприятный побочный эффект: пользователь открывает совершенно другой документ, а в нем отображается тот же самый список подразделений, который был выбран в прошлый раз.

Типичные ошибки при реализации через ХранилищеЗначения и строку

В попытках быстро решить задачу разработчики иногда выбирают обходные пути, которые порождают трудноуловимые ошибки в коде и архитектурные проблемы.

Первая распространенная ошибка — попытка сериализовать список значений в реквизит с типом ХранилищеЗначения непосредственно из клиентского контекста:


&НаСервере
Функция ПолучитьЗначениеХЗ()
    Возврат ХранилищеПодразделений.Получить();
КонецФункции

&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    ХранилищеПодразделений = Новый ХранилищеЗначения(СписокЗначенийПодразделений);
КонецПроцедуры

Выясним причину ошибок в этом фрагменте:

  1. Тип ХранилищеЗначения доступен только на сервере. При попытке вызвать обращение к методам или свойствам хранилища на тонком клиенте компилятор 1С выдаст исключение: Переменная не определена.
  2. В обработчике ПриЗаписиНаСервере контекст формы не содержит прямой переменной объекта. Присваивание должно выполняться строго через параметр ТекущийОбъект: ТекущийОбъект.ХранилищеПодразделений = ....

Вторая опасная альтернатива — сохранение списка ссылок в обычную строку через метод ЗначениеВСтрокуВнутр(). Такой способ категорически не рекомендуется использовать для ссылочных данных. Он полностью разрушает ссылочную целостность базы данных: если подразделение будет помечено на удаление и удалено стандартной обработкой, платформа не сможет отследить ссылку внутри текстовой строки. В результате в документе останется битая внутренняя ссылка (битый GUID). Кроме того, по значениям из строки невозможно построить запрос, выполнить соединение таблиц или настроить быстрый отбор в журнале документов.

Оптимальное решение: использование табличной части

Наиболее надежный, производительный и архитектурно правильный способ хранения списка ссылок в документе — добавление специализированной табличной части. В типовых конфигурациях, таких как ЗУП 3.1, добавление табличной части к стандартному документу ТабельУчетаРабочегоВремени следует выполнять через расширение конфигурации с отключенным безопасным режимом, чтобы не снимать документ с поддержки.

Рассмотрим по шагам реализацию табличной части Подразделения с реквизитом Подразделение (тип СправочникСсылка.ПодразделенияОрганизаций).

Реализация через пользовательский интерфейс с динамическим добавлением

Если по условиям задачи требуется сохранить удобный для пользователя интерфейс с отдельным полем выбора и списком, проанализируем рабочий код обработчиков формы.

Посмотрим на процедуру добавления подразделения по кнопке формы:


&НаКлиенте
Процедура ДобавитьПодразделение(Команда)
    
    Если ЗначениеЗаполнено(Объект.Подразделение) Тогда
        
        Если СписокЗначенийПодразделений.НайтиПоЗначению(Объект.Подразделение) = Неопределено Тогда
            
            СписокЗначенийПодразделений.Добавить(Объект.Подразделение);
            СтрокаТЧПодразделения = Объект.Подразделения.Добавить();
            СтрокаТЧПодразделения.Подразделение = Объект.Подразделение;
            
        Иначе
            
            Сообщить("Данное подразделение уже добавлено в список");
            
        КонецЕсли;
        
        Объект.Подразделение = ПредопределенноеЗначение("Справочник.ПодразделенияОрганизаций.ПустаяСсылка");
        
    КонецЕсли;
    
КонецПроцедуры

Обратим внимание на важную деталь: для очистки поля ввода ссылочного реквизита на клиенте необходимо использовать метод ПредопределенноеЗначение или присваивать Неопределено. Конструкция вида Объект.Подразделение.Пустая() недопустима, так как метод Пустая() возвращает логический тип (булево), что приведет к ошибке несоответствия типов данных.

Синхронизация данных при открытии и перечитывании формы

Для корректного отображения ранее сохраненных строк табличной части при открытии документа перенесем инициализацию на сторону сервера. Выполнять заполнение в обработчике ПриОткрытии на клиенте не рекомендуется, так как это порождает лишние серверные вызовы и не срабатывает при стандартном перечитывании объекта. Оптимальное место — серверный обработчик ПриЧтенииНаСервере:


&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
    
    СписокЗначенийПодразделений.Очистить();
    
    Для Каждого СтрокаТЧ Из Объект.Подразделения Цикл
        СписокЗначенийПодразделений.Добавить(СтрокаТЧ.Подразделение);
    КонецЦикла;
    
КонецПроцедуры

Прямое отображение табличной части на форме (Best Practice)

Рассмотрим наиболее лаконичный подход, рекомендуемый стандартами разработки 1С. В большинстве случаев дублирующий реквизит формы с типом СписокЗначений вовсе не требуется. Достаточно перетащить саму табличную часть Объект.Подразделения в элементы формы в виде элемента управления Таблица.

В этом случае платформа автоматически решает все сопутствующие задачи:

  1. Обеспечивает ввод, удаление и редактирование строк без написания программного кода.
  2. Корректно устанавливает признак Модифицированность = Истина при любых манипуляциях пользователя со списком.
  3. Снимает риск рассинхронизации между экранным списком и реальными строками табличной части.
  4. Обеспечивает корректную работу типовых механизмов контроля ссылочной целостности и быстродействие при проведении документа.
← На главную