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