Понадобилось вводить документ Начисление прочих доходов (или, чтобы никто не догадался, РегистрацияПрочихДоходов) программно. При этом основные данные известны, необходимо произвести расчет НДФЛ и тому подобного. Затруднение в том, что вопреки всяким законам логики и здравого смысла расчетные механизмы ЗУП3.1 завязаны на формы.
То есть, расчет производится на сервере, но форму на сервере получить нельзя, поэтому и рассчитать на сервере без клиента по сути нельзя.
В первой итерации расчет у меня производился обработкой, поэтому дело обошлось просто экспортной обёрткой поверх РассчитатьНДФЛИВзносыВсем() в расширении - пользуйтесь, если вам этого достаточно.
Во второй итерации, понятно, клиента 1С, пользовательского интерфейса и всего такого не стало.
Расчет выполняется в несколько вызовов, первый можно выполнить целиком на сервере:
РасчетНДФЛНарастающимИтогомСНачалаГода = УчетНДФЛФормыРасширенный.РасчетНДФЛНарастающимИтогомСНачалаГода(ДокументОбъект.ВидПрочегоДохода);
ПараметрыРасчета = УчетПрочихДоходов.ПараметрыРасчетаДокумента();
ПараметрыРасчета.СписокФизическихЛиц = СписокФизическихЛиц;
ПараметрыРасчета.РасчетНДФЛНарастающимИтогомСНачалаГода = РасчетНДФЛНарастающимИтогомСНачалаГода;
ДанныеРасчета = УчетПрочихДоходов.РассчитатьДокумент(ДокументОбъект, ПараметрыРасчета);
Следующий вызов, к сожалению, принимает в качестве аргумента Форму
УчетПрочихДоходов.РезультатРасчетаВДанныеФормы(Форма, ДанныеРасчета);
Проявив некоторую смекалку, я попытался решить проблему подменой формы:
Форма = Новый Структура;
Форма.Вставить("Объект", ДокументОбъект);
Форма.Вставить("ИтогВзносы", 0);
УчетПрочихДоходов.РезультатРасчетаВДанныеФормы(Форма, ДанныеРасчета);
Это помогло бы, если бы мы были на обычной форме. К сожалению, на управляемой это не совсем помогло, потому что на форме добавлены реквизиты и в сам объект. Эту проблему я решил, сконвертировав ДокументОбъект в структуру целиком, для чего написал функцию (наверное, полезная для чего-то ещё, нужно подумать):
Функция ОбъектВСтруктуру(Знач ДокументОбъект) Экспорт
Объект = Новый Структура;
Для Каждого Реквизит Из ДокументОбъект.Метаданные().СтандартныеРеквизиты Цикл
Объект.Вставить(Реквизит.Имя, ДокументОбъект[Реквизит.Имя]);
КонецЦикла;
Для Каждого Реквизит Из ДокументОбъект.Метаданные().Реквизиты Цикл
Объект.Вставить(Реквизит.Имя, ДокументОбъект[Реквизит.Имя]);
КонецЦикла;
Для Каждого МетаТЧ Из ДокументОбъект.Метаданные().ТабличныеЧасти Цикл
Объект.Вставить(МетаТЧ.Имя, Новый Массив);
// добавим строку-образец для добавления новых строк:
ПустаяСтрока = Новый Структура;
Для Каждого Реквизит Из МетаТЧ.СтандартныеРеквизиты Цикл
ПустаяСтрока.Вставить(Реквизит.Имя, Неопределено);
КонецЦикла;
Для Каждого Реквизит Из МетаТЧ.Реквизиты Цикл
ПустаяСтрока.Вставить(Реквизит.Имя, Неопределено);
КонецЦикла;
Объект.Вставить(МетаТЧ.Имя + "_ПустаяСтрока", ПустаяСтрока);
// заполним те строки, что есть:
ОбъектТЧ = Объект[МетаТЧ.Имя];
ДокументОбъектТЧ = ДокументОбъект[МетаТЧ.Имя];
Для Каждого СтрДокументОбъектТЧ Из ДокументОбъектТЧ Цикл
СтрОбъектТЧ = Новый Структура;
Для Каждого Реквизит Из МетаТЧ.СтандартныеРеквизиты Цикл
СтрОбъектТЧ.Вставить(Реквизит.Имя, СтрДокументОбъектТЧ[Реквизит.Имя]);
КонецЦикла;
Для Каждого Реквизит Из МетаТЧ.Реквизиты Цикл
СтрОбъектТЧ.Вставить(Реквизит.Имя, СтрДокументОбъектТЧ[Реквизит.Имя]);
КонецЦикла;
ОбъектТЧ.Добавить(СтрОбъектТЧ);
КонецЦикла;
КонецЦикла;
Возврат Объект;
КонецФункции
Обратите внимание на "_ПустаяСтрока" - оно появилось позже.
Теперь можно спокойно вставлять получившийся "Объект" в "Форму" и вызывать функцию (в процессе всплыло ещё два добавленных реквизита):
Процедура РассчитатьНачислениеПрочихДоходовНаСервере(Знач ДокументОбъект) Экспорт
СписокФизическихЛиц = Новый Массив;
Для Каждого Стр Из ДокументОбъект.НачисленияУдержанияВзносы Цикл
Если СписокФизическихЛиц.Найти(Стр.ФизическоеЛицо) = Неопределено Тогда
СписокФизическихЛиц.Добавить(Стр.ФизическоеЛицо);
КонецЕсли;
КонецЦикла;
// BSLLS:LineLength-off разбиение строки уменьшает читабельность
РасчетНДФЛНарастающимИтогомСНачалаГода = УчетНДФЛФормыРасширенный.РасчетНДФЛНарастающимИтогомСНачалаГода(ДокументОбъект.ВидПрочегоДохода);
// BSLLS:LineLength-on
ПараметрыРасчета = УчетПрочихДоходов.ПараметрыРасчетаДокумента();
ПараметрыРасчета.СписокФизическихЛиц = СписокФизическихЛиц;
ПараметрыРасчета.РасчетНДФЛНарастающимИтогомСНачалаГода = РасчетНДФЛНарастающимИтогомСНачалаГода;
ДанныеРасчета = УчетПрочихДоходов.РассчитатьДокумент(ДокументОбъект, ПараметрыРасчета);
// копируем документ в структуру:
Объект = ОбъектВСтруктуру(ДокументОбъект);
// это реквизиты, добавленные в оригинале на форму:
Для Каждого СтрОбъект Из Объект.НачисленияУдержанияВзносы Цикл
СтрОбъект.Вставить("ЗачтеноАвансовыхПлатежей", 0);
СтрОбъект.Вставить("ИтогВзносыСтрока", 0);
КонецЦикла;
Форма = Новый Структура;
Форма.Вставить("Объект", Объект);
Форма.Вставить("ИтогВзносы", 0);
УчетПрочихДоходов.РезультатРасчетаВДанныеФормы(Форма, ДанныеРасчета);
// теперь нужно все данные вернуть обратно в ДокументОбъект:
СтруктуруВОбъект(ДокументОбъект, Форма.Объект);
КонецПроцедуры
(ДокументОбъект заполняется физлицами, видом прочих доходов и суммами перед вызовом, проводится вызывающей стороной.)
К сожалению, приведённый код сам по себе не работает, потому что в УчетПрочихДоходов.РезультатРасчетаВДанныеФормы() добавляются и удаляются строки в табличных частях, а у меня вместо табличных частей массивы. Пришлось в итоге взять эту процедуру в расширение (ИзменениеИКонтроль) и добавить проверку на тип Формы, чего как раз хотелось избежать:
&ИзменениеИКонтроль("РезультатРасчетаВДанныеФормы")
Процедура РезультатРасчетаВДанныеФормы(Форма, ДанныеРасчета)
Объект = Форма.Объект;
#Вставка
ЭтоОбъектСтруктура = ТипЗнч(Объект) = Тип("Структура");
#КонецВставки
...
// Очищаем НДФЛ
Идентификаторы = Новый Соответствие;
КоличествоСтрок = Объект.НДФЛ.Количество();
Для Сч = 1 По КоличествоСтрок Цикл
СтрокаДокумента = Объект.НДФЛ[КоличествоСтрок - Сч];
Если СтрокиНачисленийФизЛиц[СтрокаДокумента.ФизическоеЛицо] <> Неопределено Тогда
Идентификаторы.Вставить(СтрокаДокумента.ИдентификаторСтрокиНДФЛ, Истина);
#Удаление
Объект.НДФЛ.Удалить(СтрокаДокумента);
#КонецУдаления
#Вставка
Если ЭтоОбъектСтруктура Тогда
Объект.НДФЛ.Удалить(КоличествоСтрок - Сч);
Иначе
Объект.НДФЛ.Удалить(СтрокаДокумента);
КонецЕсли;
#КонецВставки
...
// НДФЛ
ПараметрыПроверкиРаспределенияНДФЛ = ОтражениеЗарплатыВБухучетеРасширенный.ПараметрыДляПроверкиРезультатовРаспределенияУдержаний();
ПараметрыПроверкиРаспределенияНДФЛ.Сотрудник = Ложь;
Для Каждого СтрокаИсточник Из ДанныеРасчета.НДФЛ Цикл
#Удаление
СтрокаНДФЛ = Объект.НДФЛ.Добавить();
#КонецУдаления
#Вставка
Если ЭтоОбъектСтруктура Тогда
СтрокаНДФЛ = ОбщегоНазначения.СкопироватьРекурсивно(Объект.НДФЛ_ПустаяСтрока);
Объект.НДФЛ.Добавить(СтрокаНДФЛ);
Иначе
СтрокаНДФЛ = Объект.НДФЛ.Добавить();
КонецЕсли;
#КонецВставки
...
Итоговое решение мне кажется не очень изящным, не совсем устойчивым к обновлениям, но другого я не нашел.
Если видите ошибки, или знаете, как полностью рассчитать документ штатными средствами - пишите комментарии!
Расширение с тестовой обработкой приложено.
* Тестировалось на Зарплата и управление персоналом КОРП, редакция 3.1 (3.1.23.63), 1С:Предприятие 8.3 (8.3.19.1399)
** Заглавная картинка сгенерирована нейросетью midjourney по запросу "cat-like stupid server holds client window"