Как программно создать несколько документов на основании счетов в 1С 7.7

Программист 1С v7.7 Управленческий учет
← На главную

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

Анализируем причину проблемы: почему цикл работает некорректно

Рассмотрим типичный код, который часто приводит к ошибке:


Пока док.ПолучитьДокумент() = 1 Цикл    
    ОткрытьФорму("Документ.Расходные_накладные", "СписатьСчёт", док.ТекущийДокумент());
КонецЦикла;

Проблема заключается в том, что функция ОткрытьФорму в 1С 7.7 работает асинхронно. Когда интерпретатор встречает эту команду, он посылает системе сигнал на открытие окна, но сам не дожидается завершения этого процесса (и тем более не ждет записи документа пользователем) и сразу переходит к следующей итерации цикла. В результате система пытается открыть множество форм практически одновременно. Так как контекст создания документа на основании требует инициализации интерфейсных ресурсов и переменных модуля формы, возникает конфликт, и в лучшем случае отрабатывает только первая команда.

Как отмечают участники обсуждения, цикл «пробегает быстрее», чем происходит реальное создание и инициализация объекта в памяти. Чтобы решить эту задачу, нам нужно заставить систему дождаться окончания обработки одного документа, прежде чем переходить к следующему.

Решение 1: Использование модального режима

Самый быстрый и простой способ исправить ситуацию — использовать метод ОткрытьФормуМодально. Проанализируем, как это меняет работу алгоритма:


Пока док.ПолучитьДокумент() = 1 Цикл    
    ОткрытьФормуМодально("Документ.Расходные_накладные", "СписатьСчёт", док.ТекущийДокумент());
КонецЦикла;

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

Решение 2: Полностью автоматическое («тихое») списание

Если задача стоит в том, чтобы списать счета «по одной кнопке» без участия пользователя, модальное открытие форм заставит человека нажимать кнопку «ОК» для каждого документа. Чтобы этого избежать, мы можем модифицировать модуль формы целевого документа (в нашем примере — Расходной накладной).

Рассмотрим алгоритм реализации «тихого» режима:

  1. В процедуре ПриОткрытии документа анализируем входящий параметр.
  2. Если передан специальный признак (например, текстовая строка "АвтоСписание"), выполняем стандартную логику ВводНаОсновании.
  3. Сразу после заполнения вызываем метод Записать().
  4. Устанавливаем СтатусВозврата(0), чтобы форма закрылась сама, не успев отрисоваться на экране.

Таким образом, для пользователя процесс будет выглядеть как быстрое мелькание окон (или вовсе незаметно), а документы будут созданы и проведены программно.

Решение 3: Вынос логики в Глобальный модуль (архитектурный подход)

Опытные разработчики указывают на то, что хранить логику заполнения документа исключительно в модуле формы — не самая лучшая практика. В 1С 7.7 процедура ВводНаОсновании жестко привязана к форме. Если вы попытаетесь создать документ через СоздатьОбъект, эта процедура просто не вызовется.

Выясним, как сделать код более универсальным. Мы можем вынести алгоритм заполнения Расходной накладной из Счета в отдельную экспортную процедуру в Глобальном модуле. Рассмотрим пример такой структуры:

В Глобальном модуле:


Процедура глЗаполнитьРасходнуюНаОснованииСчета(КонтекстРасходной, ДокОснование) Экспорт
    // Здесь описываем весь перенос данных: 
    // контрагент, склад, перебор табличной части
    КонтекстРасходной.Контрагент = ДокОснование.Контрагент;
    ДокОснование.ВыбратьСтроки();
    Пока ДокОснование.ПолучитьСтроку() = 1 Цикл
        КонтекстРасходной.НоваяСтрока();
        КонтекстРасходной.Товар = ДокОснование.Товар;
        КонтекстРасходной.Количество = ДокОснование.Количество;
    КонецЦикла;
КонецПроцедуры

В вашей обработке списания:


ДокРН = СоздатьОбъект("Документ.Расходные_накладные");
Пока докСчет.ПолучитьДокумент() = 1 Цикл
    ДокРН.Новый();
    глЗаполнитьРасходнуюНаОснованииСчета(ДокРН, докСчет.ТекущийДокумент());
    ДокРН.Записать();
    Если ДокРН.Провести() = 0 Тогда
        Сообщить("Не удалось провести накладную по счету " + докСчет.НомерДок);
    КонецЕсли;
КонецЦикла;

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

Важные замечания по реализации

Разберем несколько важных технических нюансов, которые помогут избежать ошибок при реализации любого из выбранных методов:

  1. Проверка остатков: При массовом списании высока вероятность, что на каком-то из документов товар на складе закончится. Обязательно проверяйте результат выполнения метода Провести().
  2. Транзакции: Если вам важно, чтобы либо списались все счета сразу, либо ни одного (в случае ошибки), оберните цикл в конструкцию НачатьТранзакцию() ... ЗафиксироватьТранзакцию(). Однако помните, что внутри транзакции нельзя использовать модальные окна и визуальное открытие форм.
  3. Контекст 1С 7.7: Помните, что в «семерке» передача контекста формы через параметры требует аккуратности. При использовании СоздатьОбъект вы работаете с объектом напрямую, а при ОткрытьФорму — через интерфейсную оболочку.

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

← На главную