При интеграции «1С:Зарплата и управление персоналом 3.1» (а также «1С:ERP» и «1С:Комплексная автоматизация») со сторонними системами часто возникает задача программного создания документов. Один из наиболее востребованных документов для переноса разовых начислений или показателей — это ДанныеДляРасчетаЗарплаты.
Пытаясь заполнить этот документ программно «в лоб», разработчики часто сталкиваются с ошибкой отсутствия реквизита: обращение вида ДокументОбъект.ДанныеСводно или ДокументОбъект.ДанныеДетально вызывает системное исключение. Выясним причину такого поведения платформы и разберем по шагам правильный алгоритм решения этой задачи.
Табличные части «ДанныеСводно» (используемая для консолидированного ввода показателей) и «ДанныеДетально» (используемая при вводе в разрезе сотрудников) — это динамические элементы управляемой формы. На уровне платформы они имеют тип данных ДанныеФормыКоллекция. Физически в структуре метаданных документа ДанныеДляРасчетаЗарплаты таких табличных частей не существует.
Такой подход разработчиков фирмы «1С» обусловлен гибкостью механизма настройки шаблонов ввода. В зависимости от выбранного шаблона (реквизит документа ВидДокумента, ссылающийся на справочник ВидыДокументовВводДанныхДляРасчетаЗарплаты), состав колонок и строк формы генерируется динамически при открытии документа в интерфейсе пользователя.
Интерактивный процесс работает следующим образом:
Соответственно, если мы пытаемся заполнить документ программно в фоновом задании или во внешней обработке без открытия формы (без создания контекста ЭтаФорма), нам **категорически не нужно** пытаться заполнять коллекцию ДанныеСводно. Вместо этого мы должны напрямую записать данные в физические таблицы документа.
Проанализируем структуру физических табличных частей документа ДанныеДляРасчетаЗарплаты. Чтобы документ успешно записался, провелся и впоследствии корректно открывался пользователями в интерфейсе, нам необходимо заполнить две ключевые табличные части:
ЗначенияПоказателей. Это основное хранилище числовых и качественных показателей расчета. Здесь содержатся следующие реквизиты:
Показатель — ссылка на справочник ПоказателиРасчетаЗарплаты.Значение — числовое значение вводимого показателя.Объект — ключевой реквизит связи. Если данные вводятся по сотрудникам, в это поле записывается ссылка на справочник Сотрудники. Если данные вводятся в целом по подразделению или организации (сводный ввод), это поле может оставаться пустым или содержать ссылку на подразделение в зависимости от настроек показателя.Период — дата действия (дата начала периода или конкретный день, за который вводится показатель).ФизическиеЛица. Она хранит список физических лиц, по которым регистрируются данные.
ФизическиеЛица.Важное предупреждение о синхронизации данных: Состав физических лиц в табличной части ФизическиеЛица должен строго соответствовать списку физлиц тех сотрудников, которые указаны в реквизите Объект табличной части ЗначенияПоказателей. Если данные будут рассинхронизированы (например, показатель по сотруднику добавлен, а его физлицо в ТЧ ФизическиеЛица отсутствует), то документ проведется без ошибок, но при попытке пользователя открыть его интерактивно система выдаст критическую ошибку «Индекс находится за границами массива».
Рассмотрим практический пример. Предположим, нам необходимо программно создать документ по шаблону детального ввода («По сотрудникам») и заполнить его данными, полученными из внешней системы.
// Шаг 1. Создаем новый документ на сервере
НовыйДокумент = Документы.ДанныеДляРасчетаЗарплаты.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = Справочники.Организации.НайтиПоРеквизиту("ИНН", "7701020304");
НовыйДокумент.ПериодРегистрации = НачалоМесяца(ТекущаяДата());
// Шаг 2. Указываем вид документа (настройку шаблона ввода)
// Важно выбирать предопределенный элемент или находить по наименованию/коду конкретный шаблон
ШаблонВвода = Справочники.ВидыДокументовВводДанныхДляРасчетаЗарплаты.НайтиПоНаименованию("Сдельный наряд");
Если ШаблонВвода.Пустая() Тогда
ВызватьИсключение "Не найден шаблон ввода данных для расчета зарплаты!";
КонецЕсли;
НовыйДокумент.ВидДокумента = ШаблонВвода;
// Заполняем другие обязательные реквизиты шапки (например, Подразделение, если требует шаблон)
НовыйДокумент.Подразделение = Справочники.ПодразделенияОрганизаций.НайтиПоНаименованию("Основной цех");
// Шаг 3. Заполняем табличные части данными
// Определим показатель, который мы будем вводить (например, "Сдельная оплата")
ПоказательСдельнойОплаты = Справочники.ПоказателиРасчетаЗарплаты.НайтиПоНаименованию("Сдельный заработок");
// Предположим, у нас есть массив структур с данными сотрудников и суммами
ДанныеДляЗагрузки = Новый Массив;
ДанныеСотрудника1 = Новый Структура;
ДанныеСотрудника1.Вставить("Сотрудник", Справочники.Сотрудники.НайтиПоНаименованию("Иванов Иван Иванович"));
ДанныеСотрудника1.Вставить("Значение", 45000);
ДанныеДляЗагрузки.Добавить(ДанныеСотрудника1);
ДанныеСотрудника2 = Новый Структура;
ДанныеСотрудника2.Вставить("Сотрудник", Справочники.Сотрудники.НайтиПоНаименованию("Петров Петр Петрович"));
ДанныеСотрудника2.Вставить("Значение", 52000);
ДанныеДляЗагрузки.Добавить(ДанныеСотрудника2);
// Обходим полученные данные и наполняем физические ТЧ документа
Для Каждого СтрокаДанных Из ДанныеДляЗагрузки Цикл
СотрудникСсылка = СтрокаДанных.Сотрудник;
Если СотрудникСсылка.Пустая() Тогда
Продолжить;
КонецЕсли;
// 3.1. Заполняем физическую таблицу показателей
НоваяСтрокаПоказателей = НовыйДокумент.ЗначенияПоказателей.Добавить();
НоваяСтрокаПоказателей.Показатель = ПоказательСдельнойОплаты;
НоваяСтрокаПоказателей.Значение = СтрокаДанных.Значение;
НоваяСтрокаПоказателей.Объект = СотрудникСсылка;
НоваяСтрокаПоказателей.Период = НовыйДокумент.ПериодРегистрации; // Чаще всего период совпадает с месяцем регистрации
// 3.2. Синхронизируем физическую таблицу ФизическиеЛица
// Получаем физлицо непосредственно из реквизита сотрудника
ФизЛицоСсылка = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СотрудникСсылка, "ФизическоеЛицо");
// Проверим, нет ли уже этого физлица в табличной части (чтобы избежать дублирования строк в ТЧ ФизическиеЛица)
ПараметрыПоиска = Новый Структура("ФизическоеЛицо", ФизЛицоСсылка);
НайденныеСтроки = НовыйДокумент.ФизическиеЛица.НайтиСтроки(ПараметрыПоиска);
Если НайденныеСтроки.Количество() = 0 Тогда
НоваяСтрокаФизЛиц = НовыйДокумент.ФизическиеЛица.Добавить();
НоваяСтрокаФизЛиц.ФизическоеЛицо = ФизЛицоСсылка;
КонецЕсли;
КонецЦикла;
// Шаг 4. Записываем и проводим документ
Попытка
// Режим проведения автоматически сформирует все нужные движения в регистрах
НовыйДокумент.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ успешно создан и проведен: " + НовыйДокумент.Ссылка);
Исключение
Сообщить("Ошибка при записи документа: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
После успешного выполнения кода и проведения транзакции, мы можем открыть информационную базу под учетной записью пользователя и найти созданный нами документ. Проанализируем результат:
ЗначенияПоказателей и ФизическиеЛица.ДанныеСводно (или ДанныеДетально).Таким образом, понимание архитектуры управляемых форм «1С» и разделения визуального представления от хранения в СУБД избавляет от необходимости эмулировать работу клиентской формы и позволяет писать чистый, производительный код на стороне сервера.