Как программно заполнить вид оплаты в документе Чек ККМ без открытия формы?

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

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

Рассмотрим подробнее архитектуру хранения таких данных, выясним причину возникновения подобной проблемы на примере типовых конфигураций на обычных формах (например, УТП, УТ 10.3) и разберем правильный способ программного заполнения документа ЧекККМ.

В чем заключается архитектурная причина проблемы

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

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

  1. При открытии формы код считывает данные из первой строки табличной части Оплата и помещает значение в реквизит формы.
  2. При изменении значения пользователем срабатывает процедура-обработчик ПриИзмененииВидаОплаты(), которая программно создает или перезаписывает строку в табличной части Оплата:

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

    СтрокаОплаты.ВидОплаты = ВидОплаты;
    ПересчитатьАвтоматическиеСкидки();
КонецПроцедуры

Важно понимать: реквизиты формы — это временные данные пользовательского интерфейса, существующие только в оперативной памяти клиентского приложения на время жизни экранной формы. Они не сохраняются в базе данных при прямом вызове метода Записать() объекта.

Почему обращение через форму документа ошибочно

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


Ч = Документы.ЧекККМ.СоздатьДокумент();
// Заполнение реквизитов шапки...
фДокОбъ = Ч.ПолучитьФорму("ФормаДокумента");
фДокОбъ["ВидОплаты"] = Справочники.ВидыОплатЧекаККМ.НайтиПоРеквизиту("ТипОплаты", Перечисления.ТипыОплатЧекаККМ.Наличные);
фДокОбъ.Открыть();
Ч.Записать();

Такой подход влечет за собой критические ограничения:

  1. Необходимость интерактивного открытия: реквизиты формы живут только пока открыта форма. Если не вызвать Открыть(), форма не инициализируется корректно, а данные не попадут в объект. Если же открывать форму в цикле для десятков создаваемых документов, экран пользователя будет перегружен постоянно всплывающими окнами.
  2. Ограничения контекста исполнения: вызовы методов ПолучитьФорму() и Открыть() принципиально недопустимы на сервере, в регламентных и фоновых заданиях, а также в веб-сервисах и HTTP-сервисах. Код с обращением к элементам формы завершится аварийной ошибкой.
  3. Пропуск логики модуля формы: даже если форма была получена, изменение значения ее реквизита из внешнего кода не вызывает автоматически обработчик события ПриИзменении, из-за чего табличная часть объекта все равно может остаться пустой.

Правильное решение: прямое заполнение табличной части объекта

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

  1. Создаем новый экземпляр документа через метод СоздатьДокумент() менеджера документов.
  2. Заполняем обязательные реквизиты шапки (организацию, склад, кассу ККМ, контрагента, вид операции).
  3. Находим необходимый элемент справочника видов оплат (например, по типу оплаты через перечисление ТипыОплатЧекаККМ).
  4. Добавляем новую строку в табличную часть Оплата создаваемого объекта и заполняем ее реквизиты (вид оплаты и сумму).
  5. Вызываем метод Записать() в нужном режиме (запись или проведение).

Посмотрим на итоговый пример кода обработки:


// Создаем новый документ
ТвойЧек = Документы.ЧекККМ.СоздатьДокумент();
ТвойЧек.Дата = ТекущаяДата();
ТвойЧек.ВидОперации = Перечисления.ВидыОперацийЧекККМ.Продажа;
ТвойЧек.Организация = Справочники.Организации.НайтиПоКоду("2");
ТвойЧек.Склад = Справочники.Склады.НайтиПоКоду("000000001");
ТвойЧек.КассаККМ = Справочники.КассыККМ.НайтиПоКоду("23");

// Находим нужный вид оплаты по типу
ВидОплаты = Справочники.ВидыОплатЧекаККМ.НайтиПоРеквизиту("ТипОплаты", Перечисления.ТипыОплатЧекаККМ.Наличные);

// Заполняем табличную часть Оплата напрямую в объекте
СтрокаОплаты = ТвойЧек.Оплата.Добавить();
СтрокаОплаты.ВидОплаты = ВидОплаты;
СтрокаОплаты.Сумма = 1000; // Указываем итоговую сумму платежа

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

Рекомендации по пакетной обработке и сопутствующим расчетам

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

  1. Вызов логики пересчетов: при ручном изменении вида оплаты в интерфейсе конфигурация нередко вызывает процедуры пересчета автоматических скидок или заполнения эквайринговых параметров. Если в вашем случае требуются подобные действия, их следует вызывать напрямую через экспортные методы модуля объекта документа или модулей общих процедур перед вызовом Записать().
  2. Оптимизация производительности через транзакции: если в цикле создается большое количество чеков (от нескольких десятков до сотен), запись каждого документа следует оборачивать в явную транзакцию с помощью операторов НачатьТранзакцию() и ЗафиксироватьТранзакцию(). Это существенно ускорит выполнение за счет пакетной фиксации изменений в базе данных.
  3. Использование метода Заполнить(): для минимизации ручного кода заполнения реквизитов шапки можно использовать типовой механизм ТвойЧек.Заполнить(<ДанныеЗаполнения>), если в модуле объекта определен обработчик события ОбработкаЗаполнения.

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

← На главную