Как в запросе 1С правильно объединить оплаты с начислениями и пересчитать суммы по курсу валют на дату документа?

Программист 1С v8.3 (Обычные формы) Управленческий учет
← На главную

При разработке управленческих отчетов программисты часто сталкиваются с задачей сопоставления данных из разных источников: например, фактических оплат из регистра накопления ВзаиморасчетыСКонтрагентами и плановых начислений (ставок) из документов, которые не делают движений по регистрам (например, ЗаказыНаТранспорт). Типичная ошибка в таких случаях — использование соединений по периоду и контрагенту, что приводит к "расползанию" сумм или их дублированию. Рассмотрим подробнее, как решить эту проблему с помощью объединения таблиц и корректного получения курсов валют.

Почему суммы попадают не в тот период или дублируются?

Проанализируем ситуацию: если мы используем ЛЕВОЕ СОЕДИНЕНИЕ таблицы оплат с таблицей заказов по полям Контрагент и ПериодМесяц, система пытается сопоставить каждую запись об оплате с каждой записью о заказе в рамках одного месяца. Если в одном месяце было три оплаты и два заказа, в результате соединения мы получим шесть строк (декартово произведение), и итоговые суммы будут неверными.

Более того, если в первом месяце была только оплата, а заказ появился во втором, при левом соединении по оплате данные второго месяца могут вообще не попасть в выборку или привязаться к первому месяцу, если условие соединения составлено неверно. Выясним причину: для корректного сопоставления разнородных данных в 1С общепринятым стандартом является использование конструкции ОБЪЕДИНИТЬ ВСЕ.

Решение №1: Использование ОБЪЕДИНИТЬ ВСЕ вместо соединений

Рассмотрим правильную структуру запроса. Мы должны собрать данные из всех источников в одну временную таблицу, создав в каждой части объединения "поля-заглушки" (пустые поля) для показателей из другой таблицы. Посмотрим на пример реализации:


// Часть 1: Получаем оплаты
ВЫБРАТЬ
    Взаиморасчеты.ПериодМесяц КАК Период,
    Взаиморасчеты.Контрагент КАК Контрагент,
    Взаиморасчеты.СуммаВзаиморасчетовПриход КАК СуммаОплаты,
    0 КАК СуммаФрахта
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    РегистрНакопления.ВзаиморасчетыСКонтрагентами.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Авто, , ) КАК Взаиморасчеты

ОБЪЕДИНИТЬ ВСЕ

// Часть 2: Получаем заказы (начисления)
ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Заказы.Дата, МЕСЯЦ),
    Заказы.ПрямойВладелецТранспорта,
    0,
    Заказы.ФрахтСтавка
ИЗ
    Документ.ЗаказыНаТранспорт КАК Заказы
ГДЕ
    Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода
;

////////////////////////////////////////////////////////////////////////////////
// Часть 3: Финальная группировка
ВЫБРАТЬ
    ВТ_Данные.Период,
    ВТ_Данные.Контрагент,
    СУММА(ВТ_Данные.СуммаОплаты) КАК СуммаОплаты,
    СУММА(ВТ_Данные.СуммаФрахта) КАК СуммаФрахта
ИЗ
    ВТ_Данные КАК ВТ_Данные
СГРУППИРОВАТЬ ПО
    ВТ_Данные.Период,
    ВТ_Данные.Контрагент

Такой подход гарантирует, что каждая сумма попадет ровно в свой период (месяц), а финальная группировка "схлопнет" записи по контрагенту, показав и оплату, и фрахт в одной строке, если они относятся к одному месяцу.

Решение №2: Получение курса валюты на дату документа

Если ставка фрахта в документе указана в валюте (например, EUR), а отчет должен быть в рублях, необходимо пересчитать сумму по курсу на дату документа. Использовать виртуальную таблицу СрезПоследних в соединении с физической таблицей документов внутри запроса нельзя, так как СрезПоследних без параметров возвращает курс на текущий момент, а с параметром-датой — на одну конкретную дату для всей выборки.

Разберем по шагам алгоритм получения курса для каждой строки документов:

  1. Подготовка уникальных дат: Сначала выберем все уникальные даты документов и валюты во временную таблицу.
  2. Поиск точки среза: Соединим эти даты с физической таблицей регистра КурсыВалют, где период курса меньше или равен дате документа.
  3. Выбор максимального периода: Сгруппируем результат по документу и найдем МАКСИМУМ(Период) регистра. Это и будет дата последнего установленного курса перед созданием документа.
  4. Получение значения курса: Полученную таблицу с найденными "точками курса" соединим с регистром КурсыВалют еще раз для получения числовых значений.

Проанализируем пример кода для получения итоговой суммы с учетом курса и кратности:


// 1. Получаем данные документов
ВЫБРАТЬ
    Заказы.Дата КАК ДатаДок,
    Заказы.ВалютаРасчета КАК Валюта,
    Заказы.ФрахтСтавка КАК СуммаВалюта,
    Заказы.ПрямойВладелецТранспорта КАК Контрагент
ПОМЕСТИТЬ ВТ_Логиста
ИЗ
    Документ.ЗаказыНаТранспорт КАК Заказы;

////////////////////////////////////////////////////////////////////////////////
// 2. Находим ближайшую дату курса для каждого документа
ВЫБРАТЬ
    ВТ_Логиста.ДатаДок,
    ВТ_Логиста.Валюта,
    МАКСИМУМ(КурсыВалют.Период) КАК ПериодКурса
ПОМЕСТИТЬ ВТ_ТочкиКурса
ИЗ
    ВТ_Логиста КАК ВТ_Логиста
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
        ПО ВТ_Логиста.Валюта = КурсыВалют.Валюта
            И ВТ_Логиста.ДатаДок >= КурсыВалют.Период
СГРУППИРОВАТЬ ПО
    ВТ_Логиста.ДатаДок,
    ВТ_Логиста.Валюта;

////////////////////////////////////////////////////////////////////////////////
// 3. Получаем значения курса и пересчитываем сумму
ВЫБРАТЬ
    ВТ_Логиста.ДатаДок,
    ВТ_Логиста.Контрагент,
    ВТ_Логиста.СуммаВалюта * ЕСТЬNULL(Курсы.Курс, 1) / ЕСТЬNULL(Курсы.Кратность, 1) КАК СуммаРуб
ИЗ
    ВТ_Логиста КАК ВТ_Логиста
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ТочкиКурса КАК ТК
        ПО ВТ_Логиста.ДатаДок = ТК.ДатаДок И ВТ_Логиста.Валюта = ТК.Валюта
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК Курсы
        ПО ТК.Валюта = Курсы.Валюта И ТК.ПериодКурса = Курсы.Период

Важные нюансы и рекомендации

При реализации подобных запросов следует обратить внимание на несколько критических моментов:

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

← На главную