Как программно провести документы из отчета на СКД по кнопке в 1С?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия Бухгалтерский учет Торговля и дистрибуция
← На главную

Мы рассмотрим подробно, как реализовать механизм массового проведения документов непосредственно из отчета, построенного на Системе Компоновки Данных (СКД) в 1С:Предприятии 8.3. Эта задача часто возникает, когда необходимо быстро обработать большой объем непроведенных документов, найденных по определенным критериям, например, счета-фактуры, у которых документ-основание уже проведен.

Для решения этой задачи нам потребуется выполнить несколько ключевых шагов:

  1. Программно получить ссылки на документы из результата отчета СКД.
  2. Программно провести каждый из полученных документов.
  3. Учесть особенности проведения конкретных документов (например, счетов-фактур).
  4. Обеспечить надежность и целостность данных при массовом проведении, используя транзакции.

Давайте разберем каждый шаг по порядку.

1. Получаем ссылки на документы из результата СКД

Первым и самым важным шагом является получение списка документов, которые мы собираемся провести. Отчет СКД по своей природе предназначен для отображения данных, но мы можем использовать его программные возможности для извлечения информации, в том числе и ссылок на объекты базы данных.

Если отчет СКД выводит поле ссылочного типа (например, поле "Документ" или "Ссылка"), то при формировании результата СКД автоматически заполняются специальные данные расшифровки для каждой такой ячейки. Именно эти данные мы и будем использовать.

Для программного формирования отчета и получения данных расшифровки нам потребуется несколько стандартных объектов 1С:

  1. КомпоновщикМакетаКомпоновкиДанных: Отвечает за создание макета компоновки данных на основе схемы СКД и настроек.
  2. ПроцессорКомпоновкиДанных: Обрабатывает макет компоновки данных и формирует результат компоновки данных.
  3. ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент: Выводит результат компоновки в табличный документ или позволяет итеративно обходить его.
  4. ДанныеРасшифровкиКомпоновкиДанных: Объект, который хранит информацию для расшифровки ячеек результата СКД.

Посмотрим на пример кода, как мы можем получить ссылки на документы из программно сформированного отчета СКД. Допустим, у нас есть схема компоновки данных, которая выводит поле "СсылкаНаДокумент" для каждой строки.

В этом примере мы будем работать с отчетом, который выводит непроведенные счета-фактуры. Предполагается, что в схеме СКД настроено поле, которое возвращает саму ссылку на документ, и для него разрешена расшифровка.


// Предполагается, что СхемаКомпоновкиДанных уже загружена или получена из макета
// НастройкиКомпоновкиДанных - это текущие настройки отчета

Перем мСписокДокументовДляПроведения; // Список собранных ссылок на документы

Процедура ПолучитьСписокДокументовДляПроведения()

    мСписокДокументовДляПроведения = Новый СписокЗначений;

    // 1. Создаем компоновщик макета
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, НастройкиКомпоновкиДанных, ДанныеРасшифровки);

    // 2. Создаем процессор компоновки данных
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);

    // 3. Создаем процессор вывода результата
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ТабличныйДокумент = Новый ТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);

    ПроцессорВывода.НачатьВывод();

    ЭлементРезультата = ПроцессорКомпоновки.Следующий();
    Пока ЭлементРезультата <> Неопределено Цикл

        Если ЭлементРезультата.Тип = ТипЭлементаРезультатаКомпоновкиДанных.Запись Тогда
            // Это строка с данными
            РасшифровкаЯчейки = ЭлементРезультата.Расшифровка;
            Если РасшифровкаЯчейки <> Неопределено Тогда
                // Получаем поля расшифровки для данной ячейки.
                // Предполагаем, что в СКД в поле "Документ" или "Ссылка"
                // мы выводим ссылку на документ.
                МассивПолейРасшифровки = ДанныеРасшифровки.ПолучитьПоляРасшифровки(РасшифровкаЯчейки);

                Для Каждого ПолеРасшифровки Из МассивПолейРасшифровки Цикл
                    Если ТипЗнч(ПолеРасшифровки.Значение) = Тип("ДокументСсылка.СчетФактураВыданный") Тогда
                        мСписокДокументовДляПроведения.Добавить(ПолеРасшифровки.Значение);
                    ИначеЕсли ТипЗнч(ПолеРасшифровки.Значение) = Тип("ДокументСсылка.СчетФактураПолученный") Тогда
                        мСписокДокументовДляПроведения.Добавить(ПолеРасшифровки.Значение);
                    КонецЕсли;
                КонецЦикла;
            КонецЕсли;
        КонецЕсли;

        ЭлементРезультата = ПроцессорКомпоновки.Следующий();
    КонецЦикла;

    ПроцессорВывода.ЗакончитьВывод();

    Сообщить("Найдено документов для проведения: " + мСписокДокументовДляПроведения.Количество());

КонецПроцедуры

Обратите внимание, что объект ДанныеРасшифровкиКомпоновкиДанных (в примере просто ДанныеРасшифровки) должен быть создан и передан в КомпоновщикМакетаКомпоновкиДанных и ПроцессорКомпоновкиДанных. Затем мы итеративно обходим результат компоновки, и для каждой записи (строки с данными) пытаемся получить ее расшифровку. Из расшифровки мы извлекаем значение поля, которое является ссылкой на документ. Мы проверяем тип ссылки, чтобы убедиться, что это действительно тот документ, который мы ищем (например, ДокументСсылка.СчетФактураВыданный).

2. Программно проводим полученные документы

После того как мы получили список ссылок на непроведенные документы, следующим шагом является их программное проведение. В 1С это достаточно стандартная операция. Для каждой ссылки нам необходимо получить объект документа и вызвать его метод записи с соответствующим режимом.

Процесс проведения документа включает следующие шаги:

  1. Получаем объект документа: Для каждой ссылки на документ мы вызываем метод ПолучитьОбъект(). Это "превращает" ссылку в полноценный объект, с которым можно работать, изменять его реквизиты и вызывать методы.
  2. Вызываем метод записи с проведением: Для объекта документа вызываем метод Записать(), передавая в него параметр РежимЗаписиДокумента.Проведение. Этот вызов приводит к выполнению процедуры ОбработкаПроведения в модуле объекта документа, которая, в свою очередь, формирует движения по регистрам (бухгалтерские проводки, записи в регистры накопления и сведений и т.д.).

Рассмотрим пример, как это можно сделать для одного документа:


Функция ПровестиДокумент(СсылкаНаДокумент)

    Попытка
        ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
        Если ДокОбъект.Проведен Тогда
            Сообщить("Документ " + СсылкаНаДокумент + " уже проведен.", СтатусСообщения.Информация);
            Возврат Истина; // Считаем, что все хорошо
        КонецЕсли;

        // Важно: если нужно изменить что-то перед проведением, это делается здесь
        // Например, ДокОбъект.Комментарий = "Проведен автоматически из отчета";
        // ДокОбъект.Записать(); // Если были изменения, сначала записываем без проведения, потом проводим
        // Или сразу записываем с проведением, если изменений не было или они были сделаны на лету

        ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
        Сообщить("Документ " + СсылкаНаДокумент + " успешно проведен.", СтатусСообщения.Информация);
        Возврат Истина;

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

КонецФункции

Обратите внимание на блок Попытка...Исключение. Он критически важен при массовых операциях, так как позволяет перехватывать ошибки, возникающие при проведении отдельных документов, записывать их в лог и продолжать обработку, не прерывая весь процесс.

3. Особенности проведения счетов-фактур

Счета-фактуры – это специфические документы, которые играют ключевую роль в учете НДС. При их программном проведении необходимо учитывать некоторые нюансы:

Если вы создаете счета-фактуры программно на основании других документов (например, "Реализация товаров и услуг"), то удобно использовать метод Заполнить():


// Пример создания и заполнения нового счета-фактуры (не относится к текущей задаче, но полезно для контекста)
СФ = Документы.СчетФактураВыданный.СоздатьДокумент();
СФ.Заполнить(ДокументРеализации); // ДокументРеализации - ссылка на документ-основание
// Затем можно установить дополнительные реквизиты, если нужно
СФ.Дата = ТекущаяДата();
// ...
СФ.Записать(РежимЗаписиДокумента.Проведение);

Однако, в нашей задаче мы работаем с уже существующими счетами-фактурами, которые просто не были проведены, поэтому основное внимание мы уделяем именно вызову метода Записать(РежимЗаписиДокумента.Проведение).

4. Организация пакетного проведения и использование транзакций

При проведении большого количества документов необходимо обеспечить целостность данных и высокую производительность. Для этого мы применяем механизм транзакций.

Транзакция – это последовательность операций, которая рассматривается как единое целое. Либо все операции в транзакции успешно завершаются (транзакция фиксируется), либо ни одна из них не применяется (транзакция отменяется). Это гарантирует, что база данных всегда будет находиться в согласованном состоянии.

Рассмотрим шаги по организации пакетного проведения с использованием транзакций:

  1. Начало транзакции: Используем метод НачатьТранзакцию() перед началом цикла проведения документов.
  2. Разбивка на порции: Проведение очень большого количества документов в одной транзакции может привести к длительным блокировкам базы данных и проблемам с производительностью, а также к превышению лимитов по времени транзакции. Поэтому рекомендуется разбивать список документов на порции (например, по 100, 500 или 1000 документов) и фиксировать транзакцию после каждой порции.
  3. Фиксация транзакции: Если все документы в текущей порции были успешно проведены, вызываем ЗафиксироватьТранзакцию().
  4. Отмена транзакции: Если при проведении какого-либо документа внутри порции произошла ошибка, мы можем вызвать ОтменитьТранзакцию(), чтобы отменить все изменения, сделанные в этой порции, и вернуть базу данных в исходное состояние до начала транзакции.
  5. Обработка ошибок: Для каждого документа важно предусмотреть механизм записи ошибок в лог или вывода сообщений пользователю, чтобы он мог понять, какие документы не удалось провести и по какой причине.

Пример кода, демонстрирующий пакетное проведение документов с использованием транзакций:


Процедура ПровестиНайденныеДокументы()

    Если мСписокДокументовДляПроведения.Количество() = 0 Тогда
        Сообщить("Список документов для проведения пуст.");
        Возврат;
    КонецЕсли;

    КоличествоДокументовВПорции = 100; // Определяем размер порции
    КоличествоОбработанных = 0;
    КоличествоУспешноПроведенных = 0;
    КоличествоСОшибкаи = 0;

    Сообщить("Начинаем пакетное проведение документов...");

    Для Каждого СсылкаНаДокумент Из мСписокДокументовДляПроведения Цикл

        Если КоличествоОбработанных % КоличествоДокументовВПорции = 0 Тогда
            Если КоличествоОбработанных > 0 Тогда
                // Фиксируем предыдущую транзакцию, если она была начата
                Попытка
                    ЗафиксироватьТранзакцию();
                    Сообщить("Транзакция по " + КоличествоДокументовВПорции + " документам успешно зафиксирована.");
                Исключение
                    Сообщить("Ошибка фиксации транзакции: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
                    // Здесь можно принять решение, что делать дальше, например, прервать весь процесс.
                    // Для примера просто продолжим.
                КонецПопытки;
            КонецЕсли;
            // Начинаем новую транзакцию для следующей порции
            НачатьТранзакцию();
        КонецЕсли;

        Попытка
            ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
            Если ДокОбъект.Проведен Тогда
                Сообщить("Документ " + СсылкаНаДокумент + " уже проведен.", СтатусСообщения.Информация);
            Иначе
                ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
                Сообщить("Документ " + СсылкаНаДокумент + " успешно проведен.", СтатусСообщения.Информация);
                КоличествоУспешноПроведенных = КоличествоУспешноПроведенных + 1;
            КонецЕсли;

        Исключение
            Сообщить("Ошибка проведения документа " + СсылкаНаДокумент + ": " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
            КоличествоСОшибкаи = КоличествоСОшибкаи + 1;
            // Ошибку в одной порции можно просто зафиксировать,
            // а транзакцию для всей порции все равно попытаться зафиксировать,
            // если остальные документы в ней прошли успешно.
            // Если же ошибка критична и должна отменить всю порцию,
            // то здесь нужно вызвать ОтменитьТранзакцию() и возможно выйти.
            // В данном примере мы просто сообщаем об ошибке и продолжаем.
        КонецПопытки;

        КоличествоОбработанных = КоличествоОбработанных + 1;
    КонецЦикла;

    // Фиксируем последнюю транзакцию
    Если КоличествоОбработанных > 0 И (КоличествоОбработанных % КоличествоДокументовВПорции <> 0 ИЛИ КоличествоОбработанных = мСписокДокументовДляПроведения.Количество()) Тогда
        Попытка
            ЗафиксироватьТранзакцию();
            Сообщить("Последняя транзакция успешно зафиксирована.");
        Исключение
            Сообщить("Ошибка фиксации последней транзакции: " + ОписаниеОшибки(), СтатусСообщения.Ошибка);
        КонецПопытки;
    КонецЕсли;

    Сообщить("Процесс пакетного проведения завершен.");
    Сообщить("Всего обработано документов: " + КоличествоОбработанных);
    Сообщить("Успешно проведено: " + КоличествоУспешноПроведенных);
    Сообщить("С ошибками: " + КоличествоСОшибкаи);

КонецПроцедуры

В этом примере мы фиксируем транзакцию после каждой порции документов. Если внутри порции произошла ошибка, мы сообщаем о ней, но не отменяем всю порцию сразу, чтобы не потерять результаты проведения других документов в той же порции, которые могли пройти успешно. Однако, в зависимости от бизнес-логики, может быть оправданным и полное отменение транзакции для всей порции при первой же ошибке.

Критически важно: Перед реализацией подобного функционала на рабочей базе, всегда тестируйте его на тестовой копии, чтобы убедиться в корректности всех операций и отсутствии нежелательных побочных эффектов.

Таким образом, мы видим, что решение задачи по программному проведению документов из результата отчета СКД по кнопке в 1С требует комбинации знаний по работе с Системой Компоновки Данных для получения ссылок на объекты и стандартных механизмов 1С для работы с объектами базы данных и управления транзакциями. При правильном подходе этот функционал значительно повышает удобство и эффективность работы пользователей с системой.

← На главную