Мы рассмотрим подробно, как реализовать механизм массового проведения документов непосредственно из отчета, построенного на Системе Компоновки Данных (СКД) в 1С:Предприятии 8.3. Эта задача часто возникает, когда необходимо быстро обработать большой объем непроведенных документов, найденных по определенным критериям, например, счета-фактуры, у которых документ-основание уже проведен.
Для решения этой задачи нам потребуется выполнить несколько ключевых шагов:
Давайте разберем каждый шаг по порядку.
Первым и самым важным шагом является получение списка документов, которые мы собираемся провести. Отчет СКД по своей природе предназначен для отображения данных, но мы можем использовать его программные возможности для извлечения информации, в том числе и ссылок на объекты базы данных.
Если отчет СКД выводит поле ссылочного типа (например, поле "Документ" или "Ссылка"), то при формировании результата СКД автоматически заполняются специальные данные расшифровки для каждой такой ячейки. Именно эти данные мы и будем использовать.
Для программного формирования отчета и получения данных расшифровки нам потребуется несколько стандартных объектов 1С:
КомпоновщикМакетаКомпоновкиДанных: Отвечает за создание макета компоновки данных на основе схемы СКД и настроек.ПроцессорКомпоновкиДанных: Обрабатывает макет компоновки данных и формирует результат компоновки данных.ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент: Выводит результат компоновки в табличный документ или позволяет итеративно обходить его.ДанныеРасшифровкиКомпоновкиДанных: Объект, который хранит информацию для расшифровки ячеек результата СКД.Посмотрим на пример кода, как мы можем получить ссылки на документы из программно сформированного отчета СКД. Допустим, у нас есть схема компоновки данных, которая выводит поле "СсылкаНаДокумент" для каждой строки.
В этом примере мы будем работать с отчетом, который выводит непроведенные счета-фактуры. Предполагается, что в схеме СКД настроено поле, которое возвращает саму ссылку на документ, и для него разрешена расшифровка.
// Предполагается, что СхемаКомпоновкиДанных уже загружена или получена из макета
// НастройкиКомпоновкиДанных - это текущие настройки отчета
Перем мСписокДокументовДляПроведения; // Список собранных ссылок на документы
Процедура ПолучитьСписокДокументовДляПроведения()
мСписокДокументовДляПроведения = Новый СписокЗначений;
// 1. Создаем компоновщик макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновкиДанных, ДанныеРасшифровки);
// 2. Создаем процессор компоновки данных
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);
// 3. Создаем процессор вывода результата
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ТабличныйДокумент = Новый ТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);
ПроцессорВывода.НачатьВывод();
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
Пока ЭлементРезультата <> Неопределено Цикл
Если ЭлементРезультата.Тип = ТипЭлементаРезультатаКомпоновкиДанных.Запись Тогда
// Это строка с данными
РасшифровкаЯчейки = ЭлементРезультата.Расшифровка;
Если РасшифровкаЯчейки <> Неопределено Тогда
// Получаем поля расшифровки для данной ячейки.
// Предполагаем, что в СКД в поле "Документ" или "Ссылка"
// мы выводим ссылку на документ.
МассивПолейРасшифровки = ДанныеРасшифровки.ПолучитьПоляРасшифровки(РасшифровкаЯчейки);
Для Каждого ПолеРасшифровки Из МассивПолейРасшифровки Цикл
Если ТипЗнч(ПолеРасшифровки.Значение) = Тип("ДокументСсылка.СчетФактураВыданный") Тогда
мСписокДокументовДляПроведения.Добавить(ПолеРасшифровки.Значение);
ИначеЕсли ТипЗнч(ПолеРасшифровки.Значение) = Тип("ДокументСсылка.СчетФактураПолученный") Тогда
мСписокДокументовДляПроведения.Добавить(ПолеРасшифровки.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
ЭлементРезультата = ПроцессорКомпоновки.Следующий();
КонецЦикла;
ПроцессорВывода.ЗакончитьВывод();
Сообщить("Найдено документов для проведения: " + мСписокДокументовДляПроведения.Количество());
КонецПроцедуры
Обратите внимание, что объект ДанныеРасшифровкиКомпоновкиДанных (в примере просто ДанныеРасшифровки) должен быть создан и передан в КомпоновщикМакетаКомпоновкиДанных и ПроцессорКомпоновкиДанных. Затем мы итеративно обходим результат компоновки, и для каждой записи (строки с данными) пытаемся получить ее расшифровку. Из расшифровки мы извлекаем значение поля, которое является ссылкой на документ. Мы проверяем тип ссылки, чтобы убедиться, что это действительно тот документ, который мы ищем (например, ДокументСсылка.СчетФактураВыданный).
После того как мы получили список ссылок на непроведенные документы, следующим шагом является их программное проведение. В 1С это достаточно стандартная операция. Для каждой ссылки нам необходимо получить объект документа и вызвать его метод записи с соответствующим режимом.
Процесс проведения документа включает следующие шаги:
ПолучитьОбъект(). Это "превращает" ссылку в полноценный объект, с которым можно работать, изменять его реквизиты и вызывать методы.Записать(), передавая в него параметр РежимЗаписиДокумента.Проведение. Этот вызов приводит к выполнению процедуры ОбработкаПроведения в модуле объекта документа, которая, в свою очередь, формирует движения по регистрам (бухгалтерские проводки, записи в регистры накопления и сведений и т.д.).Рассмотрим пример, как это можно сделать для одного документа:
Функция ПровестиДокумент(СсылкаНаДокумент)
Попытка
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокОбъект.Проведен Тогда
Сообщить("Документ " + СсылкаНаДокумент + " уже проведен.", СтатусСообщения.Информация);
Возврат Истина; // Считаем, что все хорошо
КонецЕсли;
// Важно: если нужно изменить что-то перед проведением, это делается здесь
// Например, ДокОбъект.Комментарий = "Проведен автоматически из отчета";
// ДокОбъект.Записать(); // Если были изменения, сначала записываем без проведения, потом проводим
// Или сразу записываем с проведением, если изменений не было или они были сделаны на лету
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ " + СсылкаНаДокумент + " успешно проведен.", СтатусСообщения.Информация);
Возврат Истина;
Исключение
Сообщить("Ошибка проведения документа " + СсылкаНаДокумент + ": " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
Возврат Ложь;
КонецПопытки;
КонецФункции
Обратите внимание на блок Попытка...Исключение. Он критически важен при массовых операциях, так как позволяет перехватывать ошибки, возникающие при проведении отдельных документов, записывать их в лог и продолжать обработку, не прерывая весь процесс.
Счета-фактуры – это специфические документы, которые играют ключевую роль в учете НДС. При их программном проведении необходимо учитывать некоторые нюансы:
Дата, Номер, Организация, Контрагент, ДокументОснование (если есть), ВидСчетаФактуры и табличные части с перечнем товаров/услуг, суммой НДС и т.д. Если мы проводим уже существующие, но непроведенные счета-фактуры, то предполагается, что эти реквизиты уже заполнены правильно. Однако, при возникновении ошибок проведения, одной из причин может быть некорректное или неполное заполнение данных.
РежимЗаписиДокумента.Проведение, который обычно соответствует оперативному проведению, но при этом проверяет корректность данных и вызывает ОбработкаПроведения.
Если вы создаете счета-фактуры программно на основании других документов (например, "Реализация товаров и услуг"), то удобно использовать метод Заполнить():
// Пример создания и заполнения нового счета-фактуры (не относится к текущей задаче, но полезно для контекста)
СФ = Документы.СчетФактураВыданный.СоздатьДокумент();
СФ.Заполнить(ДокументРеализации); // ДокументРеализации - ссылка на документ-основание
// Затем можно установить дополнительные реквизиты, если нужно
СФ.Дата = ТекущаяДата();
// ...
СФ.Записать(РежимЗаписиДокумента.Проведение);
Однако, в нашей задаче мы работаем с уже существующими счетами-фактурами, которые просто не были проведены, поэтому основное внимание мы уделяем именно вызову метода Записать(РежимЗаписиДокумента.Проведение).
При проведении большого количества документов необходимо обеспечить целостность данных и высокую производительность. Для этого мы применяем механизм транзакций.
Транзакция – это последовательность операций, которая рассматривается как единое целое. Либо все операции в транзакции успешно завершаются (транзакция фиксируется), либо ни одна из них не применяется (транзакция отменяется). Это гарантирует, что база данных всегда будет находиться в согласованном состоянии.
Рассмотрим шаги по организации пакетного проведения с использованием транзакций:
НачатьТранзакцию() перед началом цикла проведения документов.
ЗафиксироватьТранзакцию().
ОтменитьТранзакцию(), чтобы отменить все изменения, сделанные в этой порции, и вернуть базу данных в исходное состояние до начала транзакции.
Пример кода, демонстрирующий пакетное проведение документов с использованием транзакций:
Процедура ПровестиНайденныеДокументы()
Если мСписокДокументовДляПроведения.Количество() = 0 Тогда
Сообщить("Список документов для проведения пуст.");
Возврат;
КонецЕсли;
КоличествоДокументовВПорции = 100; // Определяем размер порции
КоличествоОбработанных = 0;
КоличествоУспешноПроведенных = 0;
КоличествоСОшибкаи = 0;
Сообщить("Начинаем пакетное проведение документов...");
Для Каждого СсылкаНаДокумент Из мСписокДокументовДляПроведения Цикл
Если КоличествоОбработанных % КоличествоДокументовВПорции = 0 Тогда
Если КоличествоОбработанных > 0 Тогда
// Фиксируем предыдущую транзакцию, если она была начата
Попытка
ЗафиксироватьТранзакцию();
Сообщить("Транзакция по " + КоличествоДокументовВПорции + " документам успешно зафиксирована.");
Исключение
Сообщить("Ошибка фиксации транзакции: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
// Здесь можно принять решение, что делать дальше, например, прервать весь процесс.
// Для примера просто продолжим.
КонецПопытки;
КонецЕсли;
// Начинаем новую транзакцию для следующей порции
НачатьТранзакцию();
КонецЕсли;
Попытка
ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
Если ДокОбъект.Проведен Тогда
Сообщить("Документ " + СсылкаНаДокумент + " уже проведен.", СтатусСообщения.Информация);
Иначе
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Документ " + СсылкаНаДокумент + " успешно проведен.", СтатусСообщения.Информация);
КоличествоУспешноПроведенных = КоличествоУспешноПроведенных + 1;
КонецЕсли;
Исключение
Сообщить("Ошибка проведения документа " + СсылкаНаДокумент + ": " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
КоличествоСОшибкаи = КоличествоСОшибкаи + 1;
// Ошибку в одной порции можно просто зафиксировать,
// а транзакцию для всей порции все равно попытаться зафиксировать,
// если остальные документы в ней прошли успешно.
// Если же ошибка критична и должна отменить всю порцию,
// то здесь нужно вызвать ОтменитьТранзакцию() и возможно выйти.
// В данном примере мы просто сообщаем об ошибке и продолжаем.
КонецПопытки;
КоличествоОбработанных = КоличествоОбработанных + 1;
КонецЦикла;
// Фиксируем последнюю транзакцию
Если КоличествоОбработанных > 0 И (КоличествоОбработанных % КоличествоДокументовВПорции <> 0 ИЛИ КоличествоОбработанных = мСписокДокументовДляПроведения.Количество()) Тогда
Попытка
ЗафиксироватьТранзакцию();
Сообщить("Последняя транзакция успешно зафиксирована.");
Исключение
Сообщить("Ошибка фиксации последней транзакции: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
КонецПопытки;
КонецЕсли;
Сообщить("Процесс пакетного проведения завершен.");
Сообщить("Всего обработано документов: " + КоличествоОбработанных);
Сообщить("Успешно проведено: " + КоличествоУспешноПроведенных);
Сообщить("С ошибками: " + КоличествоСОшибкаи);
КонецПроцедуры
В этом примере мы фиксируем транзакцию после каждой порции документов. Если внутри порции произошла ошибка, мы сообщаем о ней, но не отменяем всю порцию сразу, чтобы не потерять результаты проведения других документов в той же порции, которые могли пройти успешно. Однако, в зависимости от бизнес-логики, может быть оправданным и полное отменение транзакции для всей порции при первой же ошибке.
Критически важно: Перед реализацией подобного функционала на рабочей базе, всегда тестируйте его на тестовой копии, чтобы убедиться в корректности всех операций и отсутствии нежелательных побочных эффектов.
Таким образом, мы видим, что решение задачи по программному проведению документов из результата отчета СКД по кнопке в 1С требует комбинации знаний по работе с Системой Компоновки Данных для получения ссылок на объекты и стандартных механизмов 1С для работы с объектами базы данных и управления транзакциями. При правильном подходе этот функционал значительно повышает удобство и эффективность работы пользователей с системой.