В практике работы с зарплатными конфигурациями, такими как «1С:Зарплата и Управление Персоналом» (ЗУП 3.1), периодически возникает задача контроля полноты расчетов. Бухгалтеры и расчетчики могут столкнуться с ситуацией, когда сотруднику забыли начислить зарплату за месяц, либо начислили, но забыли сформировать ведомость на выплату. Рассмотрим детально, как решить эту задачу двумя путями: используя стандартные механизмы программы (для пользователей) и написав правильный запрос к базе данных (для программистов).
Нам необходимо получить два списка сотрудников:
Прежде чем писать код, проанализируем возможности самой программы. Зачастую найти «потерянных» сотрудников можно с помощью хитростей при работе с документами или стандартных отчетов.
Лайфхак с документом начисления
Один из самых быстрых способов найти тех, кого «забыли» при расчете зарплаты, — использовать механизм автоматического заполнения документа Начисление зарплаты и взносов. Алгоритм действий следующий:
Начисление зарплаты и взносов.Система автоматически подтянет всех сотрудников, по которым в данном месяце еще не был произведен окончательный расчет. Если вы уже рассчитали основную массу людей, в этот документ попадут только те, кого пропустили. Это отличный способ для экспресс-проверки перед закрытием месяца.
Использование отчетов
Для анализа ситуации используем типовые отчеты:
НачисленияУдержанияПоСотрудникам, чтобы увидеть факты начислений, и сравнить этот список со списком работающих.Если нам необходимо получить эти данные программно (например, для создания собственной обработки контроля или рассылки уведомлений), нам потребуется написать запрос. Разберем основную сложность, с которой сталкиваются разработчики при решении этой задачи — правильное использование ЛЕВОЕ СОЕДИНЕНИЕ.
Чтобы найти сотрудников без начислений, мы должны соединить две таблицы:
Условие отбора целевых записей: Таблица2.Сотрудник ЕСТЬ NULL.
Рассмотрим типичную ошибку при написании такого запроса. Часто разработчики накладывают условия на период регистрации или вид расчета в секции ГДЕ. Посмотрим на неправильный пример:
ВЫБРАТЬ
Сотр.Сотрудник
ИЗ
ВТ_ВсеСотрудники КАК Сотр
ЛЕВОЕ СОЕДИНЕНИЕ РегистрРасчета.Начисления КАК Нач
ПО Сотр.Сотрудник = Нач.Сотрудник
ГДЕ
Нач.ПериодРегистрации МЕЖДУ &НачалоПериода И &КонецПериода
И Нач.Сотрудник ЕСТЬ NULL
Почему это не сработает?
Как только мы добавляем условие ГДЕ Нач.ПериодРегистрации ..., мы отсекаем все строки, где присоединилась пустота (NULL), потому что NULL не может быть «между датами». В итоге ЛЕВОЕ СОЕДИНЕНИЕ фактически превращается во ВНУТРЕННЕЕ СОЕДИНЕНИЕ, и мы получаем только тех, у кого начисления были, а не наоборот.
Чтобы запрос отработал корректно, все условия фильтрации правой таблицы (период, вид расчета) необходимо перенести внутрь условия соединения (в секцию ПО). Тогда СУБД сначала отфильтрует правую таблицу, а затем попытается присоединить её к левой. Если записей не найдется, вернется NULL, который мы и ищем.
Разберем правильный код запроса по шагам:
Получаем список работающих сотрудников.
Для чистоты эксперимента лучше брать не справочник Сотрудники, а регистр сведений КадроваяИсторияСотрудников.СрезПоследних. Это исключит уволенных ранее сотрудников.
ВЫБРАТЬ
КадроваяИстория.Сотрудник КАК Сотрудник
ПОМЕСТИТЬ ВТ_Работающие
ИЗ
РегистрСведений.КадроваяИсторияСотрудников.СрезПоследних(&КонецПериода, ) КАК КадроваяИстория
ГДЕ
КадроваяИстория.ВидСобытия <> ЗНАЧЕНИЕ(Перечисление.ВидыКадровыхСобытий.Увольнение)
;
Соединяем с начислениями.
Используем ЛЕВОЕ СОЕДИНЕНИЕ и переносим отборы в ПО.
ВЫБРАТЬ
ВТ_Работающие.Сотрудник,
ЕСТЬNULL(Начисления.Результат, 0) КАК СуммаНачисления
ИЗ
ВТ_Работающие КАК ВТ_Работающие
ЛЕВОЕ СОЕДИНЕНИЕ РегистрРасчета.Начисления КАК Начисления
ПО ВТ_Работающие.Сотрудник = Начисления.Сотрудник
// ВАЖНО: Условия на правую таблицу находятся здесь!
И (Начисления.ПериодРегистрации МЕЖДУ &НачалоПериода И &КонецПериода)
// Если нужно исключить конкретные виды расчета, добавляем и их сюда
ГДЕ
// Оставляем только тех, кто не присоединился
Начисления.Сотрудник ЕСТЬ NULL
Важное примечание по оптимизации:
Вместо РегистрРасчета.Начисления часто эффективнее использовать оборотный регистр накопления НачисленияУдержанияПоСотрудникам. Он содержит уже итоговые суммы и работает быстрее в сложных запросах. Логика соединения остается той же: условия на период оборотов (или виртуальной таблицы оборотов) должны применяться до соединения или внутри условия соединения.
Вторая часть задачи — найти тех, кому начислили, но не выплатили. Здесь логика немного иная. Нам не нужно искать NULL, нам нужно сравнить приход и расход или посмотреть остаток.
Для этого обратимся к регистру накопления ВзаиморасчетыССотрудниками (или ЗарплатаКВыплате в зависимости от конфигурации и настроек). Самый надежный способ — получить остатки на конец периода.
ВЫБРАТЬ
ВзаиморасчетыОстатки.Сотрудник,
ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток КАК ДолгЗаОрганизацией
ИЗ
РегистрНакопления.ВзаиморасчетыССотрудниками.Остатки(&КонецПериода, ) КАК ВзаиморасчетыОстатки
ГДЕ
ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток > 0
Этот запрос покажет всех, кому организация должна деньги на указанную дату. Обратите внимание, что в ЗУП 3.1 структура долга может быть детализирована (по месяцам, статьям финансирования), поэтому при анализе остатков убедитесь, что вы не сворачиваете разрезы, которые могут скрывать пересортицу (например, плюс по одной статье и минус по другой).