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