Как правильно спроектировать регистры и справочники для расчета зарплаты по подразделениям в 1С:Специалист

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

При подготовке к аттестации «1С:Специалист по платформе» в разделе сложных периодических расчетов одной из наиболее коварных является задача учета начислений сотрудников с привязкой к подразделениям и их индивидуальным графикам работы. Особую сложность вызывает условие, при котором оклад сотрудника может неоднократно меняться внутри расчетного периода, а сам сотрудник может переводиться из одного подразделения в другое. При этом совместительство не допускается.

В этой статье мы подробно разберем, как правильно спроектировать объекты метаданных, настроить связь с графиком работы и организовать программное разбиение записей на «кусочки» (периоды действия окладов) для успешного прохождения экзамена.

Анализ архитектурных ошибок: где хранить «Подразделение»?

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

  1. Добавление реквизита «Подразделение» в справочник «Физические лица» (или «Сотрудники»). Это грубейшая ошибка. Если подразделение будет обычным реквизитом справочника, система не сможет хранить историю перемещений сотрудника. При получении данных за прошлые периоды (например, при расчете за прошлый месяц или при перерасчетах) система всегда будет видеть только текущее подразделение сотрудника. Это делает невозможным корректный расчет по графикам подразделений в прошлых периодах.
  2. Создание измерения «Подразделение» в периодическом регистре сведений «Сведения о сотрудниках». На первый взгляд, это кажется логичным решением, но оно нарушает бизнес-логику задачи. Измерение в регистре сведений определяет уникальность записи. Если сделать подразделение измерением, то в одну и ту же дату система позволит записать для одного сотрудника две строки с разными подразделениями. Это означает допущение совместительства, которое по условию задачи строго запрещено («совместительство не допускается»).
  3. Создание ресурса «Подразделение» в периодическом регистре сведений «Сведения о сотрудниках». Это единственно верное решение. Поскольку измерением остается только Сотрудник, система гарантирует, что на любую дату у сотрудника может быть только одно подразделение (и один оклад). Историчность изменений при этом сохраняется за счет периодичности самого регистра сведений.

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

Шаг 1. Проектирование структуры регистров сведений

Для решения задачи нам понадобятся два ключевых регистра сведений. Спроектируем их структуру по всем стандартам фирмы «1С».

Регистр сведений «Сведения о сотрудниках» (периодический, в пределах дня):

Регистр сведений «Графики работы» (непериодический):

Шаг 2. Настройка регистра расчета «Основные начисления»

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

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

Шаг 3. Реализация алгоритма проведения методом «кусочков»

По условию задачи, оклад сотрудника может меняться несколько раз внутри расчетного периода. Если мы просто запишем одну строку начисления оклада с 1 по 31 августа, а оклад поменялся 10 августа, стандартный механизм платформы не сможет правильно рассчитать пропорции. Нам необходимо программно разбить период начисления на интервалы («кусочки») в соответствии с периодами действия окладов.

Рассмотрим, как реализовать этот алгоритм в модуле проведения документа НачислениеЗарплаты. Проанализируем логику формирования движений по шагам:


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
	
	Движения.ОсновныеНачисления.Записывать = Истина;
	Движения.ОсновныеНачисления.Очистить();
	
	// Получаем данные из табличной части документа
	ТаблицаНачислений = ТабличнаяЧастьНачисления.Выгрузить();
	
	Для Каждого СтрокаТЧ Из ТаблицаНачислений Цикл
		
		// Нам нужно определить все изменения оклада и подразделения внутри периода строки ТЧ
		Запрос = Новый Запрос;
		Запрос.Текст = 
		"ВЫБРАТЬ
		|	&НачалоПериода КАК Период,
		|	СведенияСрез.Оклад КАК Оклад,
		|	СведенияСрез.Подразделение КАК Подразделение
		|ИЗ
		|	РегистрСведений.СведенияОСотрудниках.СрезПоследних(&НачалоПериода, Сотрудник = &Сотрудник) КАК СведенияСрез
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	СведенияИзменения.Период,
		|	СведенияИзменения.Оклад,
		|	СведенияИзменения.Подразделение
		|ИЗ
		|	РегистрСведений.СведенияОСотрудниках КАК СведенияИзменения
		|ГДЕ
		|	СведенияИзменения.Сотрудник = &Сотрудник
		|	И СведенияИзменения.Период > &НачалоПериода
		|	И СведенияИзменения.Период <= &КонецПериода
		|
		|УПОРЯДОЧИТЬ ПО
		|	Период";
		
		Запрос.УстановитьПараметр("Сотрудник", СтрокаТЧ.Сотрудник);
		Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(СтрокаТЧ.ДатаНачала));
		Запрос.УстановитьПараметр("КонецПериода", КонецДня(СтрокаТЧ.ДатаОкончания));
		
		РезультатЗапроса = Запрос.Выполнить();
		Если РезультатЗапроса.Пустой() Тогда
			// Если данных в регистре сведений нет, это ошибка заполнения кадровых данных
			Продолжить;
		КонецЕсли;
		
		Выборка = РезультатЗапроса.Выбрать();
		ТаблицаИнтервалов = Новый ТаблицаЗначений;
		ТаблицаИнтервалов.Колонки.Добавить("ДатаНачала", Новый ОписаниеТипов("Дата"));
		ТаблицаИнтервалов.Колонки.Добавить("ДатаОкончания", Новый ОписаниеТипов("Дата"));
		ТаблицаИнтервалов.Колонки.Добавить("Оклад", Новый ОписаниеТипов("Число"));
		ТаблицаИнтервалов.Колонки.Добавить("Подразделение", Новый ОписаниеТипов("СправочникСсылка.Подразделения"));
		
		Пока Выборка.Следующий() Цикл
			НоваяСтрока = ТаблицаИнтервалов.Добавить();
			НоваяСтрока.ДатаНачала = Выборка.Период;
			НоваяСтрока.Оклад = Выборка.Оклад;
			НоваяСтрока.Подразделение = Выборка.Подразделение;
		КонецЦикла;
		
		// Заполняем даты окончания для каждого сформированного интервала
		КоличествоИнтервалов = ТаблицаИнтервалов.Количество();
		Для Индекс = 0 По КоличествоИнтервалов - 1 Цикл
			Если Индекс < КоличествоИнтервалов - 1 Тогда
				ТаблицаИнтервалов[Индекс].ДатаОкончания = ТаблицаИнтервалов[Индекс + 1].ДатаНачала - 86400; // Минус один день
			Иначе
				ТаблицаИнтервалов[Индекс].ДатаОкончания = КонецДня(СтрокаТЧ.ДатаОкончания);
			КонецЕсли;
		КонецЦикла;
		
		// Записываем «кусочки» в регистр расчета
		Для Каждого Интервал Из ТаблицаИнтервалов Цикл
			Движение = Движения.ОсновныеНачисления.Добавить();
			Движение.Сотрудник = СтрокаТЧ.Sотрудник;
			Движение.Подразделение = Интервал.Подразделение;
			Движение.ВидРасчета = СтрокаТЧ.ВидРасчета;
			
			// Периоды действия и регистрации
			Движение.ПериодРегистрации = СтрокаТЧ.ПериодРегистрации;
			Движение.ПериодДействияНачало = Интервал.ДатаНачала;
			Движение.ПериодДействияКонец = Интервал.ДатаОкончания;
			
			// Дополнительные параметры для расчета
			Движение.Параметр = Интервал.Оклад; 
		КонецЦикла;
		
	КонецЦикла;
	
КонецПроцедуры

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

Шаг 4. Настройка механизма перерасчетов

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

Выполним следующие настройки в конфигураторе:

  1. Создадим объект метаданных Перерасчет.ПерерасчетЗарплаты, подчиненный нашему регистру расчета. В измерениях перерасчета укажем Сотрудник и Подразделение.
  2. В плане видов расчета для вида расчета Премия перейдем на вкладку «Использование» и добавим Оклад в список «Ведущие виды расчета». Теперь при изменении записей оклада платформа автоматически зарегистрирует необходимость перерасчета премии.

Программная регистрация перерасчетов при изменении окладов в истории:

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


Процедура ПриЗаписи(Отказ, Замещение)
	
	// Для каждого измененного сотрудника определяем период изменения
	// и регистрируем запись в таблице перерасчета
	Для Каждого Запись Из ЭтотОбъект Цикл
		
		// Формируем запись перерасчета для регистра расчета ОсновныеНачисления
		НаборПерерасчета = РегистрыРасчета.ОсновныеНачисления.Перерасчеты.ПерерасчетЗарплаты.СоздатьНаборЗаписей();
		
		НоваяЗапись = НаборПерерасчета.Добавить();
		НоваяЗапись.Сотрудник = Запись.Сотрудник;
		НоваяЗапись.Подразделение = Запись.Подразделение;
		НоваяЗапись.ОбъектПерерасчета = Документы.НачислениеЗарплаты.ПустаяСсылка(); // или ссылка на исходный документ при наличии связи
		
		// Записываем без замещения, чтобы не затереть другие актуальные перерасчеты
		НаборПерерасчета.Записать(Ложь);
		
	КонецЦикла;
	
КонецПроцедуры

Подведем итоги

Правильное проектирование объектов метаданных — это фундамент успешной сдачи экзамена «1С:Специалист по платформе». Мы выяснили, что:

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

← На главную