Как программно изменить договор контрагента в документе «Поступление на расчетный счет» и отразить эквайринг?

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

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

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

Изначальный подход и его особенности

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

Посмотрим на пример кода, предложенный автором:


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДоговорыКонтрагентов.Наименование КАК Наименование,
| ДоговорыКонтрагентов.х_Подразд КАК х_Подразд
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| НЕ ДоговорыКонтрагентов.ПометкаУдаления
| И ДоговорыКонтрагентов.ВидДоговора = &ВидДоговора
| И ДоговорыКонтрагентов.Владелец = &Владелец";

Запрос.УстановитьПараметр("ВидДоговора", Перечисления.ВидыДоговоровКонтрагентов.Прочее);
Запрос.УстановитьПараметр("Владелец", Контрагент);
ВыгрузкаТЗ = Запрос.Выполнить().Выгрузить();

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

Для Каждого ДоговорОП Из ВыгрузкаТЗ Цикл
    Если Не ПустаяСтрока(ДоговорОП.х_Подразд) Тогда
        хДоговорОП = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию(ДоговорОП.Наименование);

        // Если ДокОбъект уже получен извне цикла,
        // то нужно удостовериться, что он относится к текущему контексту
        // В рамках примера, допустим, что это так.

        Доб = ДокОбъект.РасшифровкаПлатежа.Добавить();
        Доб.ДоговорКонтрагента = хДоговорОП.Ссылка;
        ДокОбъект.Контрагент = Контрагент;
        ДокОбъект.Организация = Организация;
        ДокОбъект.ДоговорКонтрагента = хДоговорОП.Ссылка;
        //ДокОбъект.РасшифровкаПлатежа[0].ДоговорКонтрагента = хДоговорОП.Ссылка; // Этот вариант был закомментирован, но логика похожа
        ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
        ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
    КонецЕсли;
КонецЦикла;

Автор справедливо уточнил, что ДокОбъект он получает из выборки детальных записей документа «Поступление на расчетный счет».

Разбираем потенциальные причины проблемы и пути решения

Давайте систематизируем возможные причины, по которым изменения не сохранялись, и предложим пути их решения, основываясь на нашем опыте и подсказках из обсуждения.

Причина 1: Многократная перезапись документа в цикле

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

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


Заполнили = Ложь;
Для Каждого ДоговорОП Из ВыгрузкаТЗ Цикл
    Если Заполнили Тогда
        Прервать; // Если документ уже был изменен и записан, выходим из цикла
    КонецЕсли;

    Если Не ПустаяСтрока(ДоговорОП.х_Подразд) Тогда
        // ... Ваш код для поиска и изменения договора ...

        // Если изменения были успешно применены и документ записан,
        // устанавливаем флаг и готовимся к выходу из цикла
        // ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
        Заполнили = Истина;
    КонецЕсли;
КонецЦикла;

В случае автора, он отметил, что это не его случай, так как договор оставался неизменным, а не перезаписывался. Однако, это важный аспект при работе с циклами и документами.

Причина 2: Неполное или некорректное заполнение табличной части «РасшифровкаПлатежа»

Табличная часть документа в 1С — это не просто набор данных, это объект со своей логикой. При проведении документа система выполняет множество проверок. Если строка табличной части РасшифровкаПлатежа была добавлена или изменена программно, но при этом не все обязательные реквизиты были заполнены или заполнены некорректно (например, не указана статья движения денежных средств, счет учета, сумма и т.д.), то типовая логика проведения документа может посчитать такую строку невалидной и просто удалить её.

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


// ... код до получения ДокОбъект и хДоговорОП ...

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

Если ДокОбъект.РасшифровкаПлатежа.Количество() > 0 Тогда
    // Если есть существующие строки, работаем с первой
    Доб = ДокОбъект.РасшифровкаПлатежа[0];
Иначе
    // Иначе добавляем новую строку
    Доб = ДокОбъект.РасшифровкаПлатежа.Добавить();
КонецЕсли;

Доб.ДоговорКонтрагента = хДоговорОП.Ссылка;
// !!! КРИТИЧНО: Заполнить все остальные обязательные реквизиты строки !!!
// Примерные реквизиты, которые могут быть обязательными:
// Доб.ВидДенежныхСредств = Справочники.ВидыДенежныхСредств.Наличные; // Или безналичные
// Доб.Сумма = ДокОбъект.СуммаДокумента;
// Доб.СчетУчета = ПланыСчетов.Хозрасчетный.ЭквайринговыеОперации; // Например, 57.03
// Доб.СтатьяДвиженияДенежныхСредств = Справочники.СтатьиДвиженияДенежныхСредств.ОплатаОтПокупателей; // Или другая подходящая статья
// Доб.Контрагент = ДокОбъект.Контрагент;
// Доб.Организация = ДокОбъект.Организация;
// Доб.Подразделение = ДоговорОП.х_Подразд; // Если это поле нужно
// ... другие необходимые поля ...

ДокОбъект.ДоговорКонтрагента = хДоговорОП.Ссылка; // Изменяем договор и в шапке документа
ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);

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

Причина 3: Особенности записи и проведения документа

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

Автор использовал две команды подряд:


ДокОбъект.Записать(РежимЗаписиДокумента.Запись);
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);

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

Причина 4: Использование механизма обмена данными

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


ДокОбъект.ОбменДанными.Загрузка = Истина;
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
ДокОбъект.ОбменДанными.Загрузка = Ложь; // Важно сбросить флаг после операции

Установка ОбменДанными.Загрузка = Истина сигнализирует системе, что данные импортируются или изменяются специальной обработкой, и она может пропустить некоторые ручные проверки или логику, которая в обычном режиме мешала бы изменению. Однако используйте этот метод с осторожностью, поскольку он может обойти важные проверки целостности данных.

Причина 5: Вид договора и его влияние

В коде автора запрос ищет договоры с ВидДоговора = Перечисления.ВидыДоговоровКонтрагентов.Прочее. Мы должны убедиться, что выбранный нами договор эквайринга действительно имеет такой вид, и что этот вид договора корректно обрабатывается системой для счета 57.03. Некоторые виды договоров могут иметь специфическую логику в конфигурации, которая влияет на их использование и проведение документов.

Если по договору уже есть проведенные документы, 1С может не дать изменить его вид. В таких случаях часто приходится создавать новый договор с нужным видом, чтобы избежать конфликтов и некорректного учета.

Причина 6: Документ уже проведен

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

Комплексный подход к решению и диагностике с помощью отладчика

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

Пошаговая инструкция по отладке

  1. Запуск отладчика: Запустите 1С:Предприятие в режиме отладки (через конфигуратор или специальную кнопку запуска).
  2. Установка точек останова: Установите точки останова в ключевых местах вашего кода:
    • Перед получением объекта документа: ДокОбъект = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект;
    • После присвоения ссылки на договор в табличной части: Доб.ДоговорКонтрагента = хДоговорОП.Ссылка;
    • После присвоения ссылки на договор в шапке документа: ДокОбъект.ДоговорКонтрагента = хДоговорОП.Ссылка;
    • Непосредственно перед вызовом ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
    • Сразу после вызова ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
  3. Просмотр значений переменных: В процессе пошагового выполнения кода (клавиша F10), внимательно отслеживайте значения переменных:
    • Убедитесь, что ДокОбъект действительно является нужным документом.
    • Проверьте, что хДоговорОП содержит корректную ссылку на ваш договор эквайринга.
    • После присвоений, убедитесь, что значения реквизитов ДокОбъект.ДоговорКонтрагента и Доб.ДоговорКонтрагента (в строке табличной части) действительно изменились на ожидаемые ссылки. Вы можете использовать панель «Табло» или «Значения» в отладчике.
  4. Анализ поведения при записи/проведении:
    • Остановитесь перед ДокОбъект.Записать(РежимЗаписиДокумента.Проведение); и проверьте состояние документа (все ли поля заполнены, особенно в табличной части).
    • Выполните команду Записать() (F10) и сразу же после этого проверьте значения реквизитов документа и строк табличной части. Они должны быть такими, какими вы их установили.
    • Выполните команду Провести() (F10). Сразу после этого снова проверьте значения реквизитов. Если они изменились на старые или стали пустыми, это указывает на то, что типовая логика проведения документа перезаписала ваши изменения.
    • Если документ не проводится или выдает ошибку, отладчик поможет вам увидеть, на каком этапе и по какой причине это происходит.

Рекомендуемый алгоритм действий для программного изменения документа

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

  1. Получаем объект документа:

    Нам нужно получить модифицируемый объект документа. Предполагаем, что у нас есть ссылка на документ.

    
    СсылкаНаДокумент = ВыборкаДетальныеЗаписи.Ссылка; // Или другой способ получения ссылки
    ДокОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    
  2. Находим нужный договор эквайринга:

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

    
    // Предположим, что Контрагент и Организация уже определены.
    // x_Подразд - это реквизит договора, используемый для фильтрации.
    ЗапросДоговоров = Новый Запрос;
    ЗапросДоговоров.Текст = "ВЫБРАТЬ ПЕРВЫЕ 1
    |    ДоговорыКонтрагентов.Ссылка КАК Ссылка
    |ИЗ
    |    Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
    |ГДЕ
    |    НЕ ДоговорыКонтрагентов.ПометкаУдаления
    |    И ДоговорыКонтрагентов.Владелец = &Владелец
    |    И ДоговорыКонтрагентов.ВидДоговора = &ВидДоговора
    |    И ДоговорыКонтрагентов.Наименование ПОДОБНО &ШаблонНаименованияДоговора"; // Можно уточнить поиск, например, по наименованию или другим реквизитам
    
    ЗапросДоговоров.УстановитьПараметр("Владелец", ДокОбъект.Контрагент);
    ЗапросДоговоров.УстановитьПараметр("ВидДоговора", Перечисления.ВидыДоговоровКонтрагентов.Прочее); // Или другой специфичный вид, если есть
    ЗапросДоговоров.УстановитьПараметр("ШаблонНаименованияДоговора", "%эквайринг%"); // Уточнить, как искать нужный договор
    
    РезультатЗапросаДоговоров = ЗапросДоговоров.Выполнить().Выбрать();
    
    Если РезультатЗапросаДоговоров.Следующий() Тогда
        хДоговорЭквайринга = РезультатЗапросаДоговоров.Ссылка;
    Иначе
        Сообщить("Не найден договор эквайринга для контрагента " + ДокОбъект.Контрагент.Наименование);
        Возврат; // Выходим, если договор не найден
    КонецЕсли;
    
  3. Очищаем и заново заполняем табличную часть «РасшифровкаПлатежа»:

    Если нам нужно гарантировать, что в табличной части будет только одна корректная строка с новым договором, то лучше её очистить и добавить новую строку.

    
    ДокОбъект.РасшифровкаПлатежа.Очистить();
    Доб = ДокОбъект.РасшифровкаПлатежа.Добавить();
    
  4. Заполняем все необходимые реквизиты строки табличной части и шапки документа:

    Это самый критичный шаг. Убедитесь, что заполняете не только ДоговорКонтрагента, но и все остальные обязательные реквизиты строки и документа. Ориентируйтесь на то, как эти поля заполняются вручную или при корректной выгрузке из клиент-банка.

    
    // Заполняем реквизиты строки табличной части
    Доб.ДоговорКонтрагента = хДоговорЭквайринга;
    Доб.Контрагент = ДокОбъект.Контрагент; // Важно указать контрагента
    Доб.Организация = ДокОбъект.Организация; // Важно указать организацию
    Доб.Сумма = ДокОбъект.СуммаДокумента; // Сумма из шапки документа
    Доб.СтавкаНДС = Перечисления.СтавкиНДС.БезНДС; // Или ваша ставка НДС
    Доб.СчетУчета = ПланыСчетов.Хозрасчетный.НайтиПоКоду("57.03"); // Убедитесь, что счет есть
    Доб.СтатьяДвиженияДенежныхСредств = Справочники.СтатьиДвиженияДенежныхСредств.НайтиПоНаименованию("Поступления от продаж по платежным картам"); // Пример
    
    // Если есть аналитика по счету 57.03, тоже ее заполняем:
    // Доб.СубконтоКт1 = ...
    // Доб.СубконтоКт2 = ...
    
    // Заполняем реквизиты шапки документа
    ДокОбъект.ДоговорКонтрагента = хДоговорЭквайринга;
    // Возможно, нужно установить и другие поля, например, вид операции, если он влияет на проводки
    // ДокОбъект.ВидОперации = Перечисления.ВидыОперацийПоступлениеНаРасчетныйСчет.ОплатаОтПокупателя;
    
  5. Устанавливаем флаг ОбменДанными.Загрузка = Истина:

    Это может помочь обойти некоторые типовые проверки при записи.

    ДокОбъект.ОбменДанными.Загрузка = Истина;
  6. Записываем и проводим документ:

    Теперь, когда все данные подготовлены, мы можем записать и провести документ.

    
    ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
    ДокОбъект.ОбменДанными.Загрузка = Ложь; // Важно сбросить флаг!
    
  7. Проверяем результат:

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

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

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

← На главную