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