Как исправить ошибки с префиксом номера и получателем во внешней печатной форме для документа «Оказание услуг» в 1С

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

Разработка внешних печатных форм (ВПФ) — одна из наиболее частых задач, с которой сталкиваются как начинающие программисты, так и бухгалтеры, решившие освоить азы конфигурирования в среде «1С:Предприятие 8». На первый взгляд задача кажется простой: скопировать макет, написать пару строк кода для заполнения параметров и вывести результат на экран. Однако при работе с типовыми конфигурациями (например, «1С:Бухгалтерия предприятия 3.0») разработчики часто сталкиваются с неочевидным поведением системы.

В этой статье мы подробно разберем две типичные проблемы, возникающие при создании внешней печатной формы для документа «Оказание услуг»:

  1. Вывод номера документа в «сыром» виде (с лидирующими нулями и префиксами организации/базы).
  2. Ошибка обращения к реквизиту Получатель или Контрагент напрямую через ссылку на документ.

Давайте вместе разберемся в причинах этих ошибок и найдем изящные способы их решения.

Выясняем причину №1: Откуда берутся префиксы в номере документа?

В платформе «1С:Предприятие» номера документов хранятся в полнотекстовом формате, чтобы обеспечить уникальность нумерации в рамках всей базы данных и различных организаций. Полный номер документа может выглядеть как 00БП-000001 или ОФ00-000025. Здесь присутствуют:

Если мы пишем код напрямую, обращаясь к реквизиту номера:

ОбластьШапка.Параметры.НомерДокумента = СсылкаНаОбъект.Номер;

Система послушно выведет весь этот технический мусор на печать. Клиентам и бухгалтерам это категорически не нравится — им нужен красивый, «чистый» номер (например, просто «1» или «25»).

Решение проблемы с префиксами номеров

Для приведения номера к человекочитаемому виду в современных конфигурациях, использующих Библиотеку стандартных подсистем (БСП), разработан специальный общий модуль. Нам не нужно самостоятельно писать сложные алгоритмы по обрезке строк с помощью функций Сред() или Лев(). Достаточно использовать готовую функцию общего модуля.

Рассмотрим, как правильно преобразовать номер для вывода в печатную форму:


// Очищаем номер от префиксов и лидирующих нулей
ОчищенныйНомер = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(СсылкаНаОбъект.Номер, Истина, Истина);
ОбластьШапка.Параметры.НомерДокумента = ОчищенныйНомер;

Проанализируем параметры функции НомерНаПечать:

Применение этого метода гарантирует, что на печать уйдет только чистый порядковый номер.

Выясняем причину №2: Почему ругается на реквизит «Получатель»?

Перейдем ко второй, более серьезной ошибке. Когда разработчик пытается получить контрагента кодом:

ОбластьШапка.Параметры.ПредставлениеПолучателя = СсылкаНаОбъект.Получатель;

1С аварийно завершает работу с ошибкой: «Поле объекта не обнаружено (Получатель)». Почему так происходит?

Всё дело в специфике самого документа «Оказание услуг». В отличие от стандартного документа «Реализация товаров и услуг», который оформляется на одного конкретного покупателя, документ «Оказание услуг» предназначен для группового ввода операций. Он используется, например, когда коммунальное предприятие или интернет-провайдер в конце месяца выставляет одинаковые услуги (акт и счет) сразу сотням своих абонентов.

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

Попытка обратиться к получателю напрямую через СсылкаНаОбъект — это грубейшая логическая ошибка, так как система просто не знает, какого именно контрагента из табличной части вы хотите напечатать в данный момент.

Решение проблемы с получателем: Обход в цикле

Чтобы наша внешняя печатная форма работала корректно, мы должны построить алгоритм так, чтобы программа обходила всю табличную часть Контрагенты нашего документа, получала данные по каждой строке и формировала отдельный лист акта (или счета) для каждого клиента.

Посмотрим на пример реализации такого обхода:


Для Каждого СтрокаТаблицы Из СсылкаНаОбъект.Contragenty Цикл // Обращаемся к табличной части "Контрагенты"
    
    // Получаем ссылку на текущего контрагента из строки
    ТекущийКонтрагент = СтрокаТаблицы.Контрагент;
    
    // Дальнейший код обработки данных по конкретному контрагенту...
    
КонецЦикла;

Теперь разберем, как получить полные сведения о контрагенте (его ИНН, КПП, адрес, банковские реквизиты) для вывода красивого представления в печатную форму. Для этого воспользуемся встроенными механизмами конфигурации «1С:Бухгалтерия предприятия».

Пошаговый пример построения функции печати во внешней обработке

Ниже приведем готовый к использованию, структурированный фрагмент кода для модуля вашей внешней печатной формы. Мы объединили решение обеих проблем в один рабочий алгоритм.


Функция Печать(МассивОбъектов, КоллекцияПечатныхФорм) Экспорт

    // Задаем параметры макета
    ИмяМакета = "ПФ_MXL_АктОказанияУслуг";
    ТабличныйДокумент = Новый ТабличныйДокумент;
    ТабличныйДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_ОказаниеУслуг";
    
    // Получаем макет обработки (он должен быть добавлен в дерево макетов вашей ВПФ)
    МакетОбработки = ПолучитьМакет(ИмяМакета);
    
    // Обходим массив документов, отправленных на печать (обычно там один документ)
    Для Каждого СсылкаНаОбъект Из МассивОбъектов Цикл
        
        // Очищаем номер от префиксов один раз для текущего документа
        НомерДокумента = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(СсылкаНаОбъект.Номер, Истина, Истина);
        
        // Запускаем цикл по всем получателям услуг из табличной части документа
        Для Каждого СтрокаКонтрагент Из СсылкаНаОбъект.Контрагенты Цикл
            
            // Получаем область "Шапка" из макета
            ОбластьШапка = МакетОбработки.ПолучитьОбласть("Шапка");
            
            // Заполняем параметры шапки
            ОбластьШапка.Параметры.НомерДокумента = НомерДокумента;
            ОбластьШапка.Параметры.ДатаДокумента  = Формат(СсылкаНаОбъект.Дата, "ДФ=дд.ММ.гггг");
            
            // Получаем представление организации (Поставщика)
            СведенияОПоставщике = БухгалтерскийУчетПереопределяемый.СведенияОЮрФизЛице(СсылкаНаОбъект.Организация, СсылкаНаОбъект.Дата);
            ПредставлениеПоставщика = ОбщегоНазначенияБПВызовСервера.ОписаниеОрганизации(
                СведенияОПоставщике, 
                "НаименованиеДляПечатныхФорм,ИНН,ЮридическийАдрес,Телефоны"
            );
            ОбластьШапка.Параметры.ПредставлениеПоставщика = ПредставлениеПоставщика;
            
            // Получаем представление текущего Получателя (Контрагента из строки)
            СведенияОПолучателе = БухгалтерскийУчетПереопределяемый.СведенияОЮрФизЛице(СтрокаКонтрагент.Контрагент, СсылкаНаОбъект.Дата);
            ПредставлениеПолучателя = ОбщегоНазначенияБПВызовСервера.ОписаниеОрганизации(
                СведенияОПолучателе, 
                "НаименованиеДляПечатныхФорм,ИНН,ЮридическийАдрес,Телефоны,НомерСчета,Банк,БИК,КоррСчет"
            );
            ОбластьШапка.Параметры.ПредставлениеПолучателя = ПредставлениеПолучателя;
            
            // Выводим заполненную шапку в итоговый табличный документ
            ТабличныйДокумент.Вывести(ОбластьШапка);
            
            // --- Здесь должен быть ваш код для вывода строк услуг и подвала ---
            // Например, вывод области "Строка" и области "Подвал"
            
            // Если в документе несколько контрагентов, каждый следующий акт печатаем с новой страницы
            Если СсылкаНаОбъект.Контрагенты.Индекс(СтрокаКонтрагент) < СсылкаНаОбъект.Контрагенты.Количество() - 1 Тогда
                ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЦикла;
    
    // Возвращаем сформированный табличный документ на экран или принтер
    Возврат ТабличныйДокумент;

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

Рекомендация по оптимизации производительности при работе с циклами

Представленный выше алгоритм отлично подходит для небольших документов или нечастого использования. Однако, если документ «Оказание услуг» содержит сотни или тысячи строк контрагентов, получение сведений о юрлицах (БухгалтерскийУчетПереопределяемый.СведенияОЮрФизЛице) внутри цикла может существенно замедлить работу системы. Программа будет выполнять обращение к базе данных на каждой итерации цикла.

Для оптимизации таких ресурсоемких операций рекомендуется:

  1. Заранее выгрузить список всех контрагентов из табличной части в массив;
  2. Одним запросом или вызовом функции получить сведения по всем контрагентам сразу (пакетный режим);
  3. Внутри цикла обращаться уже к подготовленному кэшу данных (соответствию или таблице значений в оперативной памяти).

Тем не менее, для первого опыта разработки внешней печатной формы базовый вариант с пошаговым циклом является наиболее понятным, наглядным и простым в отладке.

Используйте этот подход в своих разработках, и ваши печатные формы всегда будут работать без ошибок, радуя пользователей красивым и чистым оформлением!

← На главную