Разработка и отладка отчетов или обработок выгрузки данных в среде 1С:Предприятие 7.7 требует от разработчика глубокого понимания принципов работы механизма бухгалтерских итогов. Особенно часто трудности возникают при взаимодействии с забалансовыми счетами, такими как счет 002.1 («Материалы, принятые на ответственное хранение»). Разберем по шагам типичные и скрытые ошибки, из-за которых обработка выгрузки остатков может не работать или выгружать некорректные данные.
В этой статье мы подробно проанализируем исходный код автора, выявим архитектурные и логические неточности платформы 1С 7.7 и напишем полностью рабочее и оптимизированное решение для экспорта остатков в формат DBF.
Для того чтобы понять, почему программа не заходила в цикл выборки субконто, детально проанализируем структуру забалансового счета 002.1 и особенности работы объекта БухгалтерскиеИтоги.
Ошибка 1. Несоответствие состава субконто на счете 002.1
В типовой конфигурации «1С:Бухгалтерия 7.7» забалансовый счет 002.1 имеет строго определенный набор субконто. Как правило, их всего три:
В исходном коде обработки мы видим попытку инициализировать сразу пять видов субконто:
Ит.ИспользоватьСубконто(ВидыСубконто.Материалы); // 1
Ит.ИспользоватьСубконто(ВидыСубконто.Контрагенты); // 2
Ит.ИспользоватьСубконто(ВидыСубконто.Договоры); // 3 - отсутствует на счете 002.1!
Ит.ИспользоватьСубконто(ВидыСубконто.ОбъектыСтроительства); // 4 - отсутствует на счете 002.1!
Ит.ИспользоватьСубконто(ВидыСубконто.МестаХранения); // 5
Выясним причину, почему это ломает весь алгоритм. Когда мы строим запрос с помощью метода ВыполнитьЗапрос() и указываем субконто, которых физически нет на выбранном счете, система 1С 7.7 на этих уровнях выборки возвращает пустое множество. Вложенные циклы Пока Ит.ПолучитьСубконто(3) = 1 и Пока Ит.ПолучитьСубконто(4) = 1 просто не выполняются, из-за чего выполнение программы прерывается, и код никогда не доходит до записи данных в DBF.
Ошибка 2. Путаница с индексами субконто при обращении к данным
В платформе 1С 7.7 метод Ит.Субконто(N) возвращает объект субконто по порядковому номеру его объявления в методах ИспользоватьСубконто(), а не по его физическому смыслу или настройкам в плане счетов.
Рассмотрим порядок инициализации в коде автора:
ВидыСубконто.МатериалыВидыСубконто.КонтрагентыПри этом далее по тексту программы автор пытается получить значения следующим образом:
Контрагент = Ит.Subkonto(1); // Здесь фактически будет получен Материал!
Номенклатура = Ит.Subkonto(4); // Здесь произойдет ошибка, так как 4-го уровня не существует
Такой подход неизбежно приводит к искажению структуры файла выгрузки, когда наименования материалов записываются в поля контрагентов, а реквизиты номенклатуры вообще остаются пустыми.
Ошибка 3. Игнорирование забалансового учета (метод Опции)
По умолчанию объект БухгалтерскиеИтоги рассчитывает остатки и обороты только по балансовым счетам. Чтобы включить забалансовые счета (к которым относится 002.1) в расчеты, необходимо явно использовать метод Опции(). Посмотрим на синтаксис этого метода:
Опции(ПризнакЗабаланса, ПризнакОборотныхСубконто)
Где параметр ПризнакЗабаланса должен быть равен 1 (включать суммы по забалансовым счетам).
Важное правило: Метод Ит.Опции(1) должен вызываться строго ДО вызова метода Ит.ВыполнитьЗапрос(). Если вызвать его позже, итоги будут сформированы без забалансового учета, и выборка останется пустой.
Ошибка 4. Преждевременная фильтрация нулевых остатков
В исходном коде на каждом уровне вложенности циклов по субконто встречается условие:
Если (Ит.СКД("С") = 0) и (Ит.СКК("С") = 0) Тогда
Продолжить;
КонецЕсли;
Проанализируем эту ситуацию. В 1С 7.7 на верхних уровнях группировки (например, по Контрагенту) остатки суммируются по всем подчиненным элементам. Если у одного контрагента числится один материал с остатком +1000 рублей, а второй — с остатком -1000 рублей (например, из-за пересортицы), то на уровне Контрагента общий итог будет равен нулю. Программа выполнит команду Продолжить на верхнем цикле и полностью пропустит этого контрагента, хотя детальные остатки по материалам у него есть. Проверку остатков нужно выполнять только на самом глубоком уровне детализации!
Ошибка 5. Избыточный обход по счетам
Если в параметрах метода ВыполнитьЗапрос() мы жестко передаем конкретный счет ("002.1"), то использование конструкции:
Ит.ВыбратьСчета();
Пока Ит.ПолучитьСчет() = 1 Цикл
становится избыточным. Запрос уже ограничен одним счетом, поэтому мы можем сразу приступать к выборке субконто первого уровня.
Теперь, когда все ошибки проанализированы, разработаем правильный алгоритм. Мы настроим корректную последовательность вызовов методов, ограничим выборку тремя реальными субконто счета 002.1 и оптимизируем логику фильтрации остатков.
Сначала подготовим структуру нашей таблицы. Нам необходимо правильно задать типы данных и длину полей для исключения обрезки строк.
ДБФ = СоздатьОбъект("XBase");
ДБФ.ДобавитьПоле("Shcet", "S", 15, );
ДБФ.ДобавитьПоле("StKod", "S", 9, );
ДБФ.ДобавитьПоле("KONTR", "S", 150, );
ДБФ.ДобавитьПоле("KontrIdent", "S", 150, );
ДБФ.ДобавитьПоле("MatKod", "S", 9, );
ДБФ.ДобавитьПоле("MatName", "S", 150, );
ДБФ.ДобавитьПоле("MatIdent", "S", 150, );
ДБФ.ДобавитьПоле("MatED", "S", 50, );
ДБФ.ДобавитьПоле("Sklad", "S", 100, );
ДБФ.ДобавитьПоле("SkladKOD", "S", 45, );
ДБФ.ДобавитьПоле("MOL", "S", 100, );
ДБФ.ДобавитьПоле("Summa", "N", 15, 2);
ДБФ.ДобавитьПоле("Kol", "N", 15, 5);
ДБФ.СоздатьФайл("D:\1cBases\temp\Обработки\002_1.DBF");
Создадим объект бухгалтерских итогов, установим опции забалансового учета и укажем верные субконто в строгом порядке: Контрагенты -> Материалы -> МестаХранения.
Ит = СоздатьОбъект("БухгалтерскиеИтоги");
// Включаем забалансовый учет ДО выполнения запроса
Ит.Опции(1, 0);
// Инициализируем только существующие субконто счета 002.1
Ит.ИспользоватьСубконто(ВидыСубконто.Контрагенты); // Будет соответствовать индексу 1
Ит.ИспользоватьСубконто(ВидыСубконто.Материалы); // Будет соответствовать индексу 2
Ит.ИспользоватьСубконто(ВидыСубконто.МестаХранения); // Будет соответствовать индексу 3
// Проверяем заполнение даты выгрузки
Если ПустоеЗначение(Дата1) = 1 Тогда
Дата1 = РабочаяДата();
КонецЕсли;
СтрокаУслСчета = "002.1";
// Выполняем запрос по счету за указанный день
Если Ит.ВыполнитьЗапрос(Дата1, Дата1, СтрокаУслСчета) = 0 Тогда
Сообщить("Ошибка выполнения запроса по счету " + СтрокаУслСчета, "!");
Возврат;
КонецЕсли;
Организуем трехступенчатый цикл обхода. Обратите внимание, что проверку на нулевые остатки мы перенесли в самый конец — непосредственно перед добавлением записи в DBF. Также учтем, что реквизит МОЛ (Материально ответственное лицо) у справочника Склады может быть периодическим, поэтому получать его значение нужно на конкретную дату.
// Начинаем выборку с 1-го уровня (Контрагенты)
Ит.ВыбратьСубконто(1);
Пока Ит.ПолучитьСубконто(1) = 1 Цикл
Контрагент = Ит.Субконто(1);
// Переходим на 2-й уровень (Материалы)
Ит.ВыбратьСубконто(2);
Пока Ит.ПолучитьСубконто(2) = 1 Цикл
Номенклатура = Ит.Субконто(2);
// Переходим на 3-й уровень (Места хранения / Склады)
Ит.ВыбратьСубконто(3);
Пока Ит.ПолучитьСубконто(3) = 1 Цикл
Склад = Ит.Субконто(3);
// Получаем детальные остатки по текущему разрезу аналитики
ОстатокСумма = Ит.СКД("С") - Ит.СКК("С");
ОстатокКол = Ит.СКД("К") - Ит.СКК("К");
// Исключаем пустые остатки во избежание захламления файла
Если (ОстатокСумма = 0) И (ОстатокКол = 0) Тогда
Продолжить;
КонецЕсли;
// Добавляем запись в DBF-файл
ДБФ.Добавить();
ДБФ.Shcet = СтрокаУслСчета;
ДБФ.StKod = Контрагент.Код;
ДБФ.KONTR = Контрагент.Наименование;
ДБФ.KontrIdent = ЗначениеВСтрокуВнутр(Контрагент);
ДБФ.Summa = ОстатокСумма;
ДБФ.Kol = ОстатокКол;
ДБФ.MatKod = Номенклатура.Код;
ДБФ.MatName = Номенклатура.Наименование;
ДБФ.MatIdent = ЗначениеВСтрокуВнутр(Номенклатура);
// Обработка базовой единицы измерения
Если ПустоеЗначение(Номенклатура.БазоваяЕдиница) = 0 Тогда
ДБФ.MatED = Номенклатура.БазоваяЕдиница.Наименование;
ДБФ.MatEDKOD = Номенклатура.БазоваяЕдиница.Код;
КонецЕсли;
ДБФ.Sklad = Склад.Наименование;
ДБФ.SkladKOD = Склад.Код;
// Получаем периодическое значение МОЛ на дату выгрузки
Если ПустоеЗначение(Склад.МОЛ) = 0 Тогда
ДБФ.MOL = Склад.МОЛ.Получить(Дата1).Наименование;
КонецЕсли;
ДБФ.Записать();
КонецЦикла; // Конец цикла по складам
КонецЦикла; // Конец цикла по материалам
КонецЦикла; // Конец цикла по контрагентам
ДБФ.ЗакрытьФайл();
Сообщить("Выгрузка остатков успешно завершена!", "i");
Мы полностью переработали алгоритм выгрузки остатков по забалансовому счету 002.1. Исключение неактуальных для данного счета уровней субконто («Договоры» и «Объекты строительства») позволило восстановить корректную работу вложенных циклов. Включение метода Опции(1) обеспечило попадание данных по забалансовым счетам в выборку бухгалтерских итогов, а перенос фильтрации остатков на самый глубокий уровень аналитики гарантирует точность выгружаемых сумм и количеств.