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

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

При работе в конфигурациях 1С, таких как "1С:Комплексная автоматизация 2.4" (а также в "1С:ERP Управление предприятием" или "1С:Управление торговлей 11"), мы сталкиваемся с необходимостью программного создания и проведения документов "Заказ клиента". Однако, если в системе включены автоматические скидки, обычная запись и попытка проведения такого документа из кода часто приводят к ошибке. Система сообщает о необходимости ручного пересчета скидок, что, естественно, не позволяет автоматизировать процесс.

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

Почему система требует ручного пересчета скидок при программном проведении?

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

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

Основной принцип решения: имитация интерактивного расчета

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

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

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

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

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

    
    // Предполагается, что 'ДокументСсылка' - это переменная, содержащая ссылку на ваш Заказ клиента
    // Например: ДокументСсылка = Документы.ЗаказКлиента.НайтиПоНомер("000000001", Дата("20230101"));
    
    // Получаем объект документа из его ссылки для дальнейшей работы
    ОбъектДок = ДокументСсылка.ПолучитьОбъект();
    
  2. Подготовка документа к расчету скидок

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

    • Очищаем табличную часть СкидкиНаценки. Эта табличная часть хранит детальную информацию о примененных скидках и наценках.
    • Устанавливаем реквизит СкидкиРассчитаны в значение Ложь. Этот флаг служит для системы индикатором того, что скидки еще не были корректно рассчитаны или требуют перерасчета. Сброс этого флага гарантирует, что система будет готова к новому расчету.
    
    // Очищаем предыдущие рассчитанные скидки для корректного пересчета.
    // Это критически важно, чтобы гарантировать применение актуальных скидок
    // и избежать ошибок из-за устаревших данных.
    ОбъектДок.СкидкиНаценки.Очистить();
    ОбъектДок.СкидкиРассчитаны = Ложь; // Сбрасываем флаг, сигнализируя системе о необходимости пересчета
    

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

  3. Формирование структуры параметров для процедуры расчета

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

    
    // Формируем структуру параметров для вызова серверной процедуры расчета скидок.
    // Эти параметры контролируют поведение процедуры.
    СтруктураПараметры = Новый Структура;
    СтруктураПараметры.Вставить("ПрименятьКОбъекту", Истина);
    СтруктураПараметры.Вставить("ТолькоПредварительныйРасчет", Ложь);
    СтруктураПараметры.Вставить("ВосстанавливатьУправляемыеСкидки", Ложь);
    СтруктураПараметры.Вставить("УправляемыеСкидки", Неопределено);
    

    Разберем значение каждого параметра:

    • ПрименятьКОбъекту (Истина): Установка этого свойства в Истина указывает процедуре, что результат расчета скидок должен быть сразу же применен к передаваемому объекту документа (в нашем случае, к ОбъектДок). Это означает, что процедура не только рассчитает скидки, но и внесет соответствующие изменения в табличные части документа.
    • ТолькоПредварительныйРасчет (Ложь): Значение Ложь для этого параметра означает, что мы выполняем окончательный, а не предварительный расчет. Если бы мы установили Истина, расчет произошел бы, но его результаты не были бы сохранены в документе, что не решило бы нашу задачу.
    • ВосстанавливатьУправляемыеСкидки (Ложь): Этот параметр контролирует восстановление ручных (управляемых пользователем) скидок, которые могли быть ранее назначены в документе. Установка в Ложь предотвращает их восстановление, гарантируя, что применятся только автоматические скидки согласно текущим условиям.
    • УправляемыеСкидки (Неопределено): Данный параметр используется, когда необходимо передать информацию о специфических ручных скидках для восстановления или применения. Поскольку наша задача — расчет автоматических скидок, мы можем оставить его в значении Неопределено.
  4. Вызов серверной процедуры расчета скидок

    После подготовки объекта документа и структуры параметров мы приступаем к ключевому действию — вызову серверной процедуры расчета скидок. В конфигурациях на платформе 1С:Предприятие 8, таких как КА 2.4, за расчет скидок по документу "Заказ клиента" отвечает специализированная функция СкидкиНаценкиСервер.РассчитатьПоЗаказуКлиента. Эту функцию необходимо вызывать на сервере, поэтому убедитесь, что ваш код выполняется в серверном контексте, либо используйте директивы компиляции, если вызов происходит из клиентского кода.

    
    // Вызываем серверную процедуру расчета скидок, передавая объект документа и параметры.
    // Эта процедура выполнит всю логику по поиску и применению автоматических скидок.
    // Если ваш код выполняется на клиенте, используйте #НаСервере или экспортную функцию модуля.
    СкидкиНаценкиСервер.РассчитатьПоЗаказуКлиента(ОбъектДок, СтруктураПараметры);
    

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

  5. Запись и проведение документа

    Когда все предыдущие шаги выполнены и скидки успешно рассчитаны и применены к объекту документа, мы можем без опасений записывать и проводить его. Теперь документ содержит все необходимые данные, и система не должна требовать ручного вмешательства.

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

    Использование РежимЗаписиДокумента.Проведение позволяет записать и провести документ одной операцией. Если требуется только запись без проведения, используйте РежимЗаписиДокумента.Запись.

Итоговый пример программного кода

Объединив все рассмотренные шаги, мы получаем следующий рабочий пример кода, который позволяет программно обрабатывать "Заказы клиентов" с автоматическими скидками, обеспечивая их корректное проведение без ручного вмешательства:


// Пример использования:
// Допустим, у нас есть ссылка на документ "Заказ клиента", который нужно провести.
// Например:
// ДокументСсылкаНаЗаказ = Документы.ЗаказКлиента.НайтиПоНомер("ТД00-000001", Дата("20230101"));
// Если ДокументСсылкаНаЗаказ = Неопределено Тогда
//     Сообщить("Заказ клиента не найден.");
//     Возврат;
// КонецЕсли;

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

// Шаг 1: Очищаем предыдущие рассчитанные скидки.
// Это критически важный шаг для обеспечения того, чтобы применились
// актуальные скидки и не было конфликтов со старыми данными.
// Очищаем табличную часть, где хранятся детали по скидкам.
ОбъектДок.СкидкиНаценки.Очистить();
// Сбрасываем флаг, который показывает, что скидки были рассчитаны.
// Это заставляет систему выполнить перерасчет.
ОбъектДок.СкидкиРассчитаны = Ложь;

// Шаг 2: Формируем структуру параметров для вызова серверной процедуры расчета скидок.
// Эти параметры определяют поведение процедуры расчета.
СтруктураПараметры = Новый Структура;

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

// "ТолькоПредварительныйРасчет" = Ложь: Выполняем окончательный расчет,
// а не просто оценку без сохранения результатов.
СтруктураПараметры.Вставить("ТолькоПредварительныйРасчет", Ложь);

// "ВосстанавливатьУправляемыеСкидки" = Ложь: Отключаем восстановление
// любых ручных скидок, если они были ранее применены.
// Это гарантирует применение именно автоматических скидок.
СтруктураПараметры.Вставить("ВосстанавливатьУправляемыеСкидки", Ложь);

// "УправляемыеСкидки" = Неопределено: Этот параметр используется для
// передачи данных о ручных скидках. В контексте автоматических скидок
// его можно оставить неопределенным.
СтруктураПараметры.Вставить("УправляемыеСкидки", Неопределено);

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

// Шаг 4: Запись и проведение документа после успешного расчета скидок.
// Теперь, когда все скидки рассчитаны и применены к объекту,
// документ готов к проведению.
Попытка
    // Используем РежимЗаписиДокумента.Проведение для одновременной записи и проведения.
    ОбъектДок.Записать(РежимЗаписиДокумента.Проведение);
    Сообщить("Заказ клиента №" + ОбъектДок.Номер + " от " + Формат(ОбъектДок.Дата, "ДФ=dd.MM.yyyy") + " успешно проведен со скидками.");
Исключение
    Сообщить("Ошибка при проведении заказа клиента №" + ОбъектДок.Номер + " от " + Формат(ОбъектДок.Дата, "ДФ=dd.MM.yyyy") + ": " + ОписаниеОшибки());
КонецПопытки;

// После успешного проведения можно закрыть форму объекта, если это необходимо.
// Например: Закрыть();

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

← На главную