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