Каталог решений - Программное создание и расчет документов начисления зарплаты в конфигурации ЗУП 3.1 и ERP (по подразделениям)

Программное создание и расчет документов начисления зарплаты в конфигурации ЗУП 3.1 и ERP (по подразделениям)

Программное создание и расчет документов начисления зарплаты в конфигурации ЗУП 3.1 и ERP (по подразделениям)

В наличии

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

Категория:

Описание

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

Сначала кратко приведу как работает типовой механизм, потом опишу как я это реализовал в обработке. 

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

Как работает типовая логика.

При создании формы документа  для табличных частей, использующих показатели, создаются дополнительные колонки Показатель1…ПоказательN и Значение1…ЗначениеN, в которые переносятся данные из табличной части Показали, связанные по реквизиту ИдентификаторСтрокиВидаРасчета.

По команде "Заполнить" запускается расчет в фоновом задании, используется метод "ПодготовитьДанныеДляЗаполнения" из модуля менеджера. Думаю тут не нужно напоминать про существование параметра запуска "РежимОтладки", который позволяет не запускать выполнение в фоне, для удобства отладки.

  Результат = ДлительныеОперации.ЗапуститьВыполнениеВФоне(
  	УникальныйИдентификатор,
  	"Документы.НачислениеЗарплаты.ПодготовитьДанныеДляЗаполнения",
  	СтруктураПараметров,
  	НаименованиеЗадания);
  

Но в модуле менеджера основной логики нет, тут вызывается функция из общего модуля "Расчет зарплаты расширенный".

  ДанныеЗаполнения = РасчетЗарплатыРасширенный.ДанныеДляЗаполненияТаблицДокумента(ОписаниеДокумента, Организация, МесяцНачисления, ДополнительныеПараметры);
  

И уже в функции "ДанныеДляЗаполненияТаблицДокумента" происходит логика заполнения менеджера расчета, для расчета зарплаты. Ранее я уже описывал данный механизм в статье 

Использование менеджера расчета для расчета зарплаты в ЗУП 3.

Если кратко по этой функции, то мы получаем сотрудников для расчета

  Сотрудники = СотрудникиДляНачисленияЗарплаты(Организация, Подразделение, МесяцНачисления, ПараметрыСотрудников);
  

Инициализируем менеджер расчета

  МенеджерРасчета = СоздатьМенеджерРасчета(МесяцНачисления, Организация);
  

Рассчитываем начисления и переносим результаты расчета в данные заполнения, для возврата в документ.

  МенеджерРасчета.РассчитатьЗарплату();
  
  ДанныеЗаполнения.Начисления = МенеджерРасчета.Зарплата.Начисления;
  

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

  Если Результат.ЗаданиеВыполнено Тогда
  	ЗаполнениеПослеВыполненияДлительнойОперации();
  КонецЕсли;
  

Таблицы из формата менеджера расчета преобразуются в формат формы документа

  ДанныеДляЗаполненияВДанныеФормы(ДанныеДляЗаполнения, Объект);
  

Все обработчики переноса находятся общем модуле РасчетЗарплатыРасширенныйФормы

  РасчетЗарплатыРасширенныйФормы.РасчетЗарплатыНачисленияВДанныеФормы(ТаблицыНачислений, ДанныеЗаполнения.Начисления, Объект.Организация, Объект.МесяцНачисления, ПозицииВставки, Объект.РежимДоначисления);
  

В событии "ПередЗаписьюНаСервере" происходит перенос показателей из табличных частей в табличную часть "Показатели"

  ЗарплатаКадрыРасширенный.ВводНачисленийРеквизитВДанные(ЭтаФорма, ТекущийОбъект, ОписанияТаблиц, 2);
  

Вроде бы все просто, бери, копируй код в обработку, пиши свое решение. Сложность в том, что все механизмы переноса данных менеджера расчета в данные формы рассчитаны на форму. Все методы находятся в общем модуле РасчетЗарплатыРасширенныйФормы. В коде есть такие методы как Строка.Свойство(), что мы можем применить только к ДанныеФормыКоллекция.  

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

Собственно интерфейс обработки

 

Я максимально упростил условия. Т.е. в шапке у нас только месяц расчета организация и ответственный.

РежимНачисления — окончательный расчет, порядок выплаты — зарплата.

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

Расчет идентичный тому, что если вы создадите документ, укажите необходимы реквизиты и нажмете кнопку "Заполнить".

Расчет реализован через длительные операции, т.е. через фоновые задания. Естественно, т.к. это внешняя обработка, она должна быть подключена в дополнительные отчеты и обработки. При открытии напрямую, будет работать без фонового задания.

Выполнение команды через длительные операции отображением прогресса работы.

  Возврат ДлительныеОперации.ВыполнитьПроцедуру(
  	ПараметрыВыполнения, 
  	"ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки", 
  	ПараметрыЗадания,
  	АдресХранилища);
  

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

В модуле мы обходим в цикле выбранные подразделения и для каждого делаем расчет, в процессе сообщая прогресс выполнения

  Процедура РасчитатьЗарплату(Параметры, АдресРезультата) Экспорт
  
  	Подразделения = Параметры.Подразделения;
  	
  	ПорядковыйНомер = 1;	                            
  	КоличествоПодразделений = Подразделения.Количество();
  	
  	Для Каждого ПараметрыРасчета Из Подразделения Цикл
  		
  		Процент = Окр(ПорядковыйНомер / КоличествоПодразделений * 100, 0);
  		
  		ДлительныеОперации.СообщитьПрогресс(Процент, 
  			СтрШаблон(
  				НСтр("ru = 'Расчет подразделения %1 (%2 из %3)'"),
  				ПараметрыРасчета.Подразделение,
  				ПорядковыйНомер,
  				КоличествоПодразделений));
  			
  		РасчитатьДокумент(Параметры, ПараметрыРасчета);
  
  		ПорядковыйНомер = ПорядковыйНомер + 1;
  
  	КонецЦикла;	
  
  КонецПроцедуры
  

В процедуре РасчитатьДокумент создаем или получаем объект документа

  Если ЗначениеЗаполнено(ПараметрыРасчета.Документ) Тогда
  	ДокументОбъект = ПараметрыРасчета.Документ.ПолучитьОбъект();
  Иначе
      ДокументОбъект = Документы.НачислениеЗарплаты.СоздатьДокумент();
  КонецЕсли;
  

Как уже писал, использую максимально типовые механизмы, поэтому расчет проходит так же через типовой метод

  Документы.НачислениеЗарплаты.ПодготовитьДанныеДляЗаполнения(ПараметрыВыполнения, АдресХранилища);	
  
  СтруктураДанных = ПолучитьИзВременногоХранилища(АдресХранилища);
  

Ну и заполнение документа

  	Если СтруктураДанных.Свойство("ДанныеДляЗаполненияТаблицДокумента", ДанныеДляЗаполнения) Тогда
  
  		// Заполнение табличных частей и вторичных данных коллекций, которые с ней связаны.
  		ОчиститьТаблицыДокументаНаСервере(ДокументОбъект);
  		
  		//Эмуляция формы для удобства работы с типовыми механизмами 
  		//Для использования максимально типового функциола смоделируем в объекте данные формы документа
  		ЭтаФорма = ОбщегоНазначенияКлиентСервер.СкопироватьСтруктуру(ПараметрыВыполнения);
  		ЭтаФорма.Вставить("ВременныйОбъект", ДанныеДокументаВОбъект(ДокументОбъект));
  		
  		//Данные процедура полностью идентичная аналогичной процедуре формы документа НачислениеЗарплаты
  		ДанныеДляЗаполненияВДанныеОбъекта(ДанныеДляЗаполнения, ЭтаФорма.ВременныйОбъект);
  		
  		//Окончательно перенесем данные объекта в данные документа
  		ДанныеОбъектаВДанныеДокумента(ЭтаФорма, ДокументОбъект, ПараметрыВыполнения);
  

С учетом того, что мы в том числе перезаполняем документ, то чистим все таблицы документа

  Для Каждого ТабличнаяЧасть Из Метаданные.Документы.НачислениеЗарплаты.ТабличныеЧасти Цикл 
  	ДокументОбъект[ТабличнаяЧасть.Имя].Очистить();
  КонецЦикла;
  

Будем считать что в ТЧ ДополнительныеРеквизиты у нас пусто 🙂

Для переноса данных менеджера расчета в данные "формы", использую копию процедуры формы документа "ДанныеДляЗаполненияВДанныеОбъекта".

Так же в модуль перенесены и адаптированы функции модуля формы по описанию документа и таблиц начислений (льготы, взносы, налоги, удержания в зависимости от настроек программы).

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

  Если СтрокаКоллекции.Свойство("ДокументОснование") Тогда
  	СтруктураДокументОснование.ДокументОснование = СтрокаКоллекции.ДокументОснование;
  	ЗаполнитьЗначенияСвойств(НоваяСтрокаПоказателя, СтруктураДокументОснование);
  КонецЕсли;
  

Для этого вместо таблиц значений использовал массивы структур

  Форма.Объект.Вставить(Элемент.Ключ, ОбщегоНазначения.ТаблицаЗначенийВМассив(Элемент.Значение));
  

Тут форма — это структура. Сделано для удобства и "эмуляции" формы.

Показатели в таблицы добавляю програмно

  	МаксимальноеКоличествоПоказателей = ЗарплатаКадрыРасширенныйПовтИсп.МаксимальноеКоличествоОтображаемыхПоказателей();
  	Для Каждого ТабличнаяЧасть Из Метаданные.Документы.НачислениеЗарплаты.ТабличныеЧасти Цикл
  		Объект.Вставить(ТабличнаяЧасть.Имя, ДокументОбъект[ТабличнаяЧасть.Имя].Выгрузить());
  		Для НомерПоказателя = 1 По МаксимальноеКоличествоПоказателей Цикл
  			Объект[ТабличнаяЧасть.Имя].Колонки.Добавить("Показатель" + НомерПоказателя, Новый ОписаниеТипов("СправочникСсылка.ПоказателиРасчетаЗарплаты"));
  			Объект[ТабличнаяЧасть.Имя].Колонки.Добавить("Значение" + НомерПоказателя, Метаданные.ОпределяемыеТипы.ЗначениеПоказателяРасчетаЗарплаты.Тип);
  		КонецЦикла;
  	КонецЦикла;                          
  

 

Записываем документ с чисткой перерасчетов.

  		ДокументОбъект.ДополнительныеСвойства.Вставить("УдалитьПерерасчетыТекущегоПериода", Истина);
  		Попытка
  			ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение); 
  		Исключение
  			ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
  			ОбщегоНазначения.СообщитьПользователю(СтрШаблон(НСтр("ru = 'Не удалось провести расчетный документ %1 по причине:
  				|%2'"), ДокументОбъект.Ссылка, КраткоеПредставлениеОшибки(ОписаниеОшибки())));
  		КонецПопытки;
  

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

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

 

Разработано в конфигурации 1С:Предприятие 8.3 (8.3.20.1613)

Код актуален для конфигурации Зарплата и управление персоналом КОРП, редакция 3.1 (3.1.20.71). Дополнительно протестировал  работу в 1С:ERP Управление предприятием 2 (2.5.7.201) (В демо версии подразделения для теста есть в организации "Деловой союз").

В конфигурации очень много своей специфики. Например по нумерации ИдентификаторСтрокиВидаРасчета в зависимости от документа и табличной части. Или например менеджер расчета вычеты возвращает в ТЧ НДФЛ, которые потом переносятся в отдельную табличную часть. Показатели, которые в менеджере расчета хранятся для каждой строки в отдельной таблице значений, а в документа одна табличная часть Показатели для всех ТЧ, связанных по идентификатору.

К чему это я. Результаты работы данный обработки я сравнивал через выгрузку документа через обработку "Выгрузка загрузка данных в XML" и затем сравнивая результаты расчета через обработку и вручную.

Весь код показать не могу, слишком много для статьи. Мог упустить что-нибудь важное в статье, отвечу в комментариях. Если что-то конкретное, то приведу пример кода. Обработка писалась с нуля исключительно для данной статьи. 

Для данной обработки функциональная опция "Выполнять расчет зарплаты по подразделениям" должна быть включена. Это можно сделать в настройках сняв галку на пункте "Расчет и выплата зарплаты выполняется по организации в целом".

 

has been added to your cart:
Оформление заказа