При разработке отчетов по материальным счетам (например, 10.3 «Топливо») в платформе 1С:Предприятие 7.7 программисты часто сталкиваются с задачей: необходимо одновременно увидеть начальный остаток материалов на складе, общий приход от поставщиков, конечный остаток и детальную расшифровку — на какие именно склады и в каком количестве было оформлено внутреннее перемещение. Попытка решить эту задачу с помощью одного объекта БухгалтерскиеИтоги с подключением метода ИспользоватьКорСубконто() нередко приводит к неожиданным результатам: дебетовый оборот пропадает или становится нулевым, а конечное сальдо уходит в глубокий минус.
Разберем детально, почему так происходит на уровне платформы 1С 7.7, и рассмотрим проверенные практические способы решения этой проблемы.
Проанализируем механику работы объекта БухгалтерскиеИтоги. Когда в коде объявляется фильтрация по корреспондирующей аналитике:
Ит.ИспользоватьКорСубконто(ВидыСубконто.МестаХранения, , 1);
Платформа накладывает жесткое условие на выборку проводок: в расчет оборотов включаются только те проводки, у корреспондирующего счета которых в плане счетов присутствует субконто типа МестаХранения. Выясним, как это отражается на типовых операциях:
Дт 10.3 - Кт 10.3. И у дебетового, и у кредитового счета субконто «Места хранения» присутствует. Такие проводки попадают в кредитовый оборот корректно.Дт 10.3 - Кт 60.1. У счета 60.1 субконто «Места хранения» отсутствует (там используются виды субконто «Контрагенты» и «Договоры»). В итоге платформа полностью исключает эти проводки из оборотов запроса.В результате дебетовый оборот склада (ДО()) становится равным нулю. Конечное сальдо (СКД()), рассчитываемое как СНД + ДО - КО, вычисляется без учета поступлений, что искажает итоговый результат и делает остатки отрицательными. Кроме того, само понятие остатка в разрезе корреспондирующего субконто в бухгалтерском учете отсутствует — остатки существуют только по конкретному счету и его собственной аналитике.
Рассмотрим самое надежное и быстрое в реализации решение: разделение задач получения общих остатков и детализации корреспонденций между двумя независимыми экземплярами БухгалтерскиеИтоги.
ИспользоватьКорСубконто().Посмотрим на практическую реализацию этого метода:
Процедура Сформировать()
Перем ИтОстатки, ИтКорр;
Склад = ЗначениеИзСтроки("{""Справочник"",""МестаХранения"",""00001""}");
// 1. Запрос общих остатков и оборотов (тип итогов 3)
ИтОстатки = СоздатьОбъект("БухгалтерскиеИтоги");
ИтОстатки.ИспользоватьСубконто(ВидыСубконто.МестаХранения, Склад, 2);
ИтОстатки.ИспользоватьСубконто(ВидыСубконто.Материалы, , 1);
ИтОстатки.ВыполнитьЗапрос(НачДата, КонДата, "10.3", , , 3, , "К");
// 2. Запрос корреспонденции по перемещениям (тип итогов 2 - только обороты)
ИтКорр = СоздатьОбъект("БухгалтерскиеИтоги");
ИтКорр.ИспользоватьСубконто(ВидыСубконто.МестаХранения, Склад, 2);
ИтКорр.ИспользоватьСубконто(ВидыСубконто.Материалы, , 1);
ИтКорр.ИспользоватьКорСубконто(ВидыСубконто.МестаХранения, , 1);
// Явно ограничиваем корр-счет счетом "10.3" для ускорения выборки
ИтКорр.ВыполнитьЗапрос(НачДата, КонДата, "10.3", "10.3", , 2, , "К");
// Обход данных по счетам и материалам
ИтОстатки.ВыбратьСчета();
Пока ИтОстатки.ПолучитьСчет() = 1 Цикл
// Позиционируем второй объект на этот же счет
ИтКорр.ПолучитьСчет(ИтОстатки.Счет);
ИтОстатки.ВыбратьСубконто(2, , , , , "Наименование");
Пока ИтОстатки.ПолучитьСубконто(2) = 1 Цикл
Сообщить("Материал: " + ИтОстатки.Субконто(2) +
" | Нач. ост: " + ИтОстатки.СНД(3) +
" | Приход: " + ИтОстатки.ДО(3) +
" | Кон. ост: " + ИтОстатки.СКД(3));
// Позиционируем объект корреспонденций на текущий материал
Если ИтКорр.ПолучитьСубконто(2, , ИтОстатки.Субконто(2)) = 1 Тогда
ИтКорр.ВыбратьКорСубконто(1);
Пока ИтКорр.ПолучитьКорСубконто(1) = 1 Цикл
Сообщить(" Перемещено на склад: " + ИтКорр.КорСубконто(1) +
" - Количество: " + ИтКорр.КорКО(3));
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Обратим внимание на метод точечного позиционирования ИтКорр.ПолучитьСубконто(2, , ИтОстатки.Субконто(2)). Вместо медленного вложенного цикла перебора мы сразу переходим к нужной позиции номенклатуры во втором объекте, что существенно ускоряет формирование отчета.
Если требуется объединить расчет в рамках одного источника данных без создания двух объектов бухгалтерских итогов, разберем использование встроенного языка запросов 1С 7.7. Объект Запрос позволяет гибко разделить расчет общего движения и специфической корреспонденции при помощи условий в переменных:
Процедура СформироватьЧерезЗапрос()
Перем ТекстЗапроса, Запрос;
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(ОтчетПоТопливу)
|Период с НачДата по КонДата;
|Склад = БухгалтерскиеИтоги.Субконто(1);
|Материал = БухгалтерскиеИтоги.Субконто(2);
|КорСклад = БухгалтерскиеИтоги.КорСубконто(1);
|КоррСчет = БухгалтерскиеИтоги.КорСчет;
|
|НачОст = БухгалтерскиеИтоги.СНД(, 3);
|ПриходВсего = БухгалтерскиеИтоги.ДО(, 3);
|КонОст = БухгалтерскиеИтоги.СКД(, 3);
|РасходПеремещение = БухгалтерскиеИтоги.КорКО(, 3);
|
|Функция ОстНач = Сумма(НачОст);
|Функция ВсегоПриход = Сумма(ПриходВсего);
|Функция ОстКон = Сумма(КонОст);
|Функция Перемещение = Сумма(РасходПеремещение) когда (КоррСчет = СчетПоКоду(""10.3""));
|
|Группировка Материал без групп упорядочить по Материал.Наименование;
|Группировка КорСклад без групп;
|
|Условие(Склад = ВыбСклад);
|//}}ЗАПРОС
";
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
Пока Запрос.Группировка(1) = 1 Цикл
Сообщить("Материал: " + Запрос.Материал +
" | Нач: " + Запрос.ОстНач +
" | Приход: " + Запрос.ВсегоПриход +
" | Кон: " + Запрос.ОстКон);
Пока Запрос.Группировка(2) = 1 Цикл
Если ПустоеЗначение(Запрос.КорСклад) = 0 Тогда
Сообщить(" На склад: " + Запрос.КорСклад + " Передано: " + Запрос.Перемещение);
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Разберем достоинства такого подхода: благодаря условию когда (КоррСчет = СчетПоКоду("10.3")) в агрегатной функции Перемещение, внутренние перемещения рассчитываются изолированно, при этом переменные ПриходВсего и КонОст содержат достоверные данные по всему счету с учетом проводок с поставщиками.