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



