1С-Предприятие 8.0. Практическое пособие разработчика

Создание процедуры расчета записей регистра Начисления


До сих пор мы с вами просто заносили в регистр расчета "Начисления" записи о том, что необходимо выполнить какой-либо вид расчета. Но каким именно образом получать эти результаты мы не говорили. Теперь настало время описать алгоритмы формирования различных видов расчетов.

Поскольку эти алгоритмы нам понадобится использовать не только в документе "Начисление сотрудникам", удобнее всего будет разместить их в отдельном общем модуле.

Откроем в конфигураторе текст обработчика проведения документа "НачислениеСотрудникам" и добавим в него вызов процедуры из общего модуля:

Процедура ОбработкаПроведения(Отказ, Режим)

//{{__КОНСТРУКТОР_ДВИЖЕНИЙ_РЕГИСТРОВ

   // Данный фрагмент построен конструктором.

   // При повторном использовании конструктора, внесенные вручную изменения будут утеряны!!!

   Для Каждого ТекСтрокаНачисления Из Начисления Цикл



       // регистр Начисления

       Движение = Движения.Начисления.Добавить();

       Движение.Сторно = Ложь;

       Движение.ВидРасчета = ТекСтрокаНачисления.ВидРасчета;

       Движение.ПериодДействияНачало = ТекСтрокаНачисления.ДатаНачала;

       Движение.ПериодДействияКонец = КонецДня(ТекСтрокаНачисления.ДатаОкончания);

       Движение.ПериодРегистрации = ТекСтрокаНачисления.ДатаНачала;

       Движение.БазовыйПериодНачало = ТекСтрокаНачисления.ДатаНачала;

       Движение.БазовыйПериодКонец = КонецДня(ТекСтрокаНачисления.ДатаОкончания);

       Движение.Сотрудник = ТекСтрокаНачисления.Сотрудник;

       Движение.ГрафикРаботы = ТекСтрокаНачисления.ГрафикРаботы;

       Движение.ИсходныеДанные = ТекСтрокаНачисления.Результат;




   КонецЦикла;

   // записываем движения регистров

   Движения.Начисления.Записать();

   // получим список всех сотрудников, содержащихся в документе

   Запрос = Новый Запрос(

      "ВЫБРАТЬ РАЗЛИЧНЫЕ

      |    НачисленияСотрудникамНачисления.Сотрудник [349]

      |ИЗ

      |    Документ.НачисленияСотрудникам.Начисления КАК НачисленияСотрудникамНачисления

      |ГДЕ

      |    НачисленияСотрудникамНачисления.Ссылка = &ТекущийДокумент");

   Запрос.УстановитьПараметр("ТекущийДокумент", Ссылка);

   //сформируем список сотрудников

   ТаблЗнач = Запрос.Выполнить().Выгрузить();

   МассивСотрудников = ТаблЗнач.ВыгрузитьКолонку("Сотрудник");

   СписокСотрудников = Новый СписокЗначений;

   СписокСотрудников.ЗагрузитьЗначения(МассивСотрудников);

   РасчитатьНачисления(Движения.Начисления, ПланыВидовРасчета.ОсновныеНачисления.Оклад, СписокСотрудников);

   Движения.Начисления.Записать(,Истина);

   РасчитатьНачисления(Движения.Начисления, ПланыВидовРасчета.ОсновныеНачисления.Премия, СписокСотрудников);

   Движения.Начисления.Записать(,Истина);

   //}}__КОНСТРУКТОР_ДВИЖЕНИЙ_РЕГИСТРОВ

КонецПроцедуры

Обратите внимание, что при проведении документа мы сначала записываем движения, сформированные документом, в регистр, а затем передаем этот набор записей регистра в процедуру расчета. Сначала для расчета первичных записей ("Оклад"), а затем для расчета вторичных ("Премия"). Процедура расчета, на основе описанных в ней алгоритмов и данных, содержащихся в записях регистра, должна сформировать значения ресурсов регистра. После того, как ресурсы будут рассчитаны, мы перезаписываем набор записей регистра без формирования записей перерасчета (второй параметр в методе Записать() – Истина).



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

Теперь создадим в ветке "Общие" новый общий модуль "ПроведениеРасчетов". Добавим в него заготовку процедуры "РасчитатьНачисления":

Процедура РасчитатьНачисления(НаборЗаписейРегистра, ТребуемыйВидРасчета, СписокСотрудников) Экспорт

   //Рассчитать первичные записи

   Если ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Оклад Тогда

   //Рассчитать вторичные записи

   ИначеЕсли ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Премия Тогда

   КонецЕсли;

КонецПроцедуры

Алгоритм расчета начислений будет различным при расчете первичных и вторичных записей, и каждая из его частей будет находиться в своей ветке условия Если...

При расчете первичных записей нам понадобятся данные графика из регистра расчета, поэтому добавим в первую ветку условия запрос по виртуальной таблице регистра расчета "РегистрРасчета.Начисления.ДанныеГрафика":

Процедура РасчитатьНачисления(НаборЗаписейРегистра, ТребуемыйВидРасчета, СписокСотрудников) Экспорт

   //Рассчитать первичные записи

   Если ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Оклад Тогда

       Запрос = Новый Запрос;

       Запрос.Текст =

       "ВЫБРАТЬ

       |    НачисленияДанныеГрафика.ЗначениеПериодДействия КАК Норма,

       |    НачисленияДанныеГрафика.ЗначениеФактическийПериодДействия КАК Факт,

       |    НачисленияДанныеГрафика.НомерСтроки КАК НомерСтроки

       |ИЗ

       |    РегистрРасчета.Начисления.ДанныеГрафика(



       |        Регистратор = &Регистратор

       |            И ВидРасчета = &ВидРасчета

       |            И Сотрудник В (&СписокСотрудников)) КАК НачисленияДанныеГрафика";

       Запрос.УстановитьПараметр("Регистратор", НаборЗаписейРегистра.Отбор.Регистратор.Значение);[351]

       Запрос.УстановитьПараметр("ВидРасчета", ТребуемыйВидРасчета);

       Запрос.УстановитьПараметр("СписокСотрудников",СписокСотрудников);

       ВыборкаРезультата = Запрос.Выполнить().Выбрать();

   //Рассчитать вторичные записи

   ИначеЕсли ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Премия Тогда

   КонецЕсли;

КонецПроцедуры

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

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

Процедура РасчитатьНачисления(НаборЗаписейРегистра, ТребуемыйВидРасчета, СписокСотрудников) Экспорт

   //Рассчитать первичные записи

   Если ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Оклад Тогда

      Запрос = Новый Запрос;

      Запрос.Текст =

      "ВЫБРАТЬ

      |    НачисленияДанныеГрафика.ЗначениеПериодДействия КАК Норма,



      |    НачисленияДанныеГрафика.ЗначениеФактическийПериодДействия КАК Факт,

      |    НачисленияДанныеГрафика.НомерСтроки КАК НомерСтроки

      |ИЗ

      |    РегистрРасчета.Начисления.ДанныеГрафика(

      |        Регистратор = &Регистратор

      |            И ВидРасчета = &ВидРасчета

      |            И Сотрудник В (&СписокСотрудников)) КАК НачисленияДанныеГрафика";

      Запрос.УстановитьПараметр("Регистратор", НаборЗаписейРегистра.Отбор.Регистратор.Значение);

      Запрос.УстановитьПараметр("ВидРасчета", ТребуемыйВидРасчета);

      Запрос.УстановитьПараметр("СписокСотрудников",СписокСотрудников);[352]

      ВыборкаРезультата = Запрос.Выполнить().Выбрать();

      Для Каждого ЗаписьРегистра Из НаборЗаписейРегистра Цикл

          СтруктураНомер = Новый Структура("НомерСтроки");

          СтруктураНомер.НомерСтроки = ЗаписьРегистра.НомерСтроки;

          ВыборкаРезультата.Сбросить();

          Если ВыборкаРезультата.НайтиСледующий(СтруктураНомер) Тогда

              Если ВыборкаРезультата.Норма = 0 Тогда

                  Сообщить("Вид расчета: Оклад - Нет рабочих дней в заданном периоде",);



                  ЗаписьРегистра.Результат = 0;

              Иначе

                  // рассчитать оклад по фактическому периоду и исходным данным

                  ЗаписьРегистра.Результат = (ЗаписьРегистра.ИсходныеДанные / ВыборкаРезультата.Норма) * ВыборкаРезультата.Факт;

                  Сообщить("Выполнен расчет "+ЗаписьРегистра.Регистратор+" - "+ЗаписьРегистра.ВидРасчета+" - "+ЗаписьРегистра.Сотрудник,);

              КонецЕсли;

          КонецЕсли;

      КонецЦикла;

   //Рассчитать вторичные записи

   ИначеЕсли ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Премия Тогда

   КонецЕсли;

КонецПроцедуры

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

Теперь добавим текст запроса во вторую ветку условия Если... с той лишь разницей, что теперь мы будем получать значения базы, используя виртуальную таблицу регистра расчета "РегистрРасчета.Начисления.БазаНачисления":

Процедура РасчитатьНачисления(НаборЗаписейРегистра, ТребуемыйВидРасчета, СписокСотрудников) Экспорт

   //Рассчитать первичные записи

   Если ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Оклад Тогда

...



   // Рассчитать вторичные записи [353]

   ИначеЕсли ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Премия Тогда

       Запрос = Новый Запрос;

       Запрос.Текст =

           "ВЫБРАТЬ

           |    НачисленияБазаНачисления.РезультатБаза КАК База,

           |    НачисленияБазаНачисления.НомерСтроки КАК НомерСтроки

           |ИЗ

           |    РегистрРасчета.Начисления.БазаНачисления(

           |        &ИзмеренияОсновного,

           |        &ИзмеренияБазового,

           |        ,

           |        Регистратор = &Регистратор

           |           И ВидРасчета = &ВидРасчета

           |           И Сотрудник В (&СписокСотрудников)) КАК НачисленияБазаНачисления";

           Измер = Новый Массив(1);

           Измер[0] = "Сотрудник";

           Запрос.УстановитьПараметр("ИзмеренияОсновного", Измер);

           Запрос.УстановитьПараметр("ИзмеренияБазового", Измер);



           Запрос.УстановитьПараметр("Регистратор", НаборЗаписейРегистра.Отбор.Регистратор.Значение);

           Запрос.УстановитьПараметр("ВидРасчета",ТребуемыйВидРасчета);

           Запрос.УстановитьПараметр("СписокСотрудников", СписокСотрудников);

           ВыборкаРезультата = Запрос.Выполнить().Выбрать();

   КонецЕсли;

КонецПроцедуры

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

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

Процедура РасчитатьНачисления(НаборЗаписейРегистра, ТребуемыйВидРасчета, СписокСотрудников) Экспорт

   //Рассчитать первичные записи

   Если ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Оклад Тогда

... [354]

   //Рассчитать вторичные записи

   ИначеЕсли ТребуемыйВидРасчета = ПланыВидовРасчета.ОсновныеНачисления.Премия Тогда

       Запрос = Новый Запрос;

       Запрос.Текст =

           "ВЫБРАТЬ

           |    НачисленияБазаНачисления.РезультатБаза КАК База,

           |    НачисленияБазаНачисления.НомерСтроки КАК НомерСтроки

           |ИЗ

           |    РегистрРасчета.Начисления.БазаНачисления(



           |        &ИзмеренияОсновного,

           |        &ИзмеренияБазового,

           |        ,

           |        Регистратор = &Регистратор

           |           И ВидРасчета = &ВидРасчета

           |           И Сотрудник В (&СписокСотрудников)) КАК НачисленияБазаНачисления";

           Измер = Новый Массив(1);

           Измер[0] = "Сотрудник";

           Запрос.УстановитьПараметр("ИзмеренияОсновного", Измер);

           Запрос.УстановитьПараметр("ИзмеренияБазового", Измер);

           Запрос.УстановитьПараметр("Регистратор", НаборЗаписейРегистра.Отбор.Регистратор.Значение);

           Запрос.УстановитьПараметр("ВидРасчета",ТребуемыйВидРасчета);

           Запрос.УстановитьПараметр("СписокСотрудников", СписокСотрудников);

           ВыборкаРезультата = Запрос.Выполнить().Выбрать();

           Для Каждого ЗаписьРегистра Из НаборЗаписейРегистра Цикл

               СтруктураНомер = Новый Структура("НомерСтроки");



               СтруктураНомер.НомерСтроки = ЗаписьРегистра.НомерСтроки;

               ВыборкаРезультата.Сбросить();

               Если ВыборкаРезультата.НайтиСледующий(СтруктураНомер) Тогда

                   ЗаписьРегистра.Результат = ВыборкаРезультата.База * (10/100);

                   Сообщить("Выполнен расчет "+ЗаписьРегистра.Регистратор+" - "+ЗаписьРегистра.ВидРасчета+" - "+ЗаписьРегистра.Сотрудник,);

               КонецЕсли;

           КонецЦикла;

   КонецЕсли;

КонецПроцедуры

Сумму начисленной премии мы рассчитываем как 10% от рассчитанной оплаты по окладу.[355]

Запустим 1С:Предприятие в режиме отладки и проверим правильность работы процедуры расчета.

Отменим проведение документа Начисление сотрудникам №3 и перепроведем документы Начисление сотрудникам №1 и №2. Регистр расчета Начисления должен выглядеть следующим образом:



Гусакову и Деловому начислена премия в размере 10% от суммы начисления по окладу.

Проведем документ Начисление сотрудникам №3, а затем №1 и№2. Состояние регистра изменится следующим образом:



В результате невыхода Гусакова на работу, сумма его оплаты по окладу будет уменьшена и соответствующим образом уменьшится начисленная ему премия. [356]


Содержание раздела