При работе в конфигурациях 1С, таких как "1С:Комплексная автоматизация 2.4" (а также в "1С:ERP Управление предприятием" или "1С:Управление торговлей 11"), мы сталкиваемся с необходимостью программного создания и проведения документов "Заказ клиента". Однако, если в системе включены автоматические скидки, обычная запись и попытка проведения такого документа из кода часто приводят к ошибке. Система сообщает о необходимости ручного пересчета скидок, что, естественно, не позволяет автоматизировать процесс.
Давайте выясним, почему возникает такая ситуация, и разберем по шагам, как можно программно отработать заказ с включенными автоматическими скидками, обеспечив его корректное проведение.
Механизм автоматических скидок в 1С разработан таким образом, чтобы расчет скидок происходил не при каждом изменении данных объекта из кода, а в строго определенные моменты. В интерактивном режиме (когда пользователь работает с формой документа) расчет скидок обычно запускается при записи документа или принудительно по нажатию специальной кнопки в форме, например, "Рассчитать скидки". Это действие инициирует вызов серверных процедур, которые выполняют комплексный анализ: они проверяют состав и сумму документа, действующие маркетинговые акции, условия соглашений с клиентом и, исходя из этого, заполняют табличные части документа рассчитанными скидками.
При программной работе, когда мы изменяем данные объекта документа напрямую в коде и затем пытаемся его записать и провести, эти серверные процедуры автоматического расчета скидок не вызываются по умолчанию. Документ записывается без обновленных данных о скидках, и система при попытке проведения обнаруживает несоответствие или отсутствие рассчитанных скидок, что приводит к требованию ручного вмешательства. Часто это выражается в том, что реквизит документа СкидкиРассчитаны остается в состоянии Ложь, сигнализируя системе, что скидки не были учтены.
Для того чтобы обойти это ограничение и успешно проводить документы "Заказ клиента" с автоматическими скидками в программном режиме, нам необходимо имитировать действия, которые выполняет система при интерактивном расчете. Это означает, что мы должны **принудительно вызвать соответствующую серверную процедуру расчета скидок** перед тем, как будем записывать и проводить документ.
Предлагаем рассмотреть подробный алгоритм действий, который включает в себя необходимые шаги для корректного расчета и применения автоматических скидок перед проведением документа.
Получение объекта документа
Первым шагом, как и в любой программной работе с документами, является получение объекта документа, с которым мы планируем работать. Это может быть как уже существующий документ, так и новый, только что созданный нами.
// Предполагается, что 'ДокументСсылка' - это переменная, содержащая ссылку на ваш Заказ клиента
// Например: ДокументСсылка = Документы.ЗаказКлиента.НайтиПоНомер("000000001", Дата("20230101"));
// Получаем объект документа из его ссылки для дальнейшей работы
ОбъектДок = ДокументСсылка.ПолучитьОбъект();
Подготовка документа к расчету скидок
Перед тем как вызвать процедуру расчета, крайне важно подготовить документ. Этот этап заключается в очистке информации о ранее рассчитанных скидках в документе. Такой подход позволяет избежать ситуаций, когда старые, возможно, уже неактуальные скидки, могут конфликтовать с новыми условиями или помешать их корректному применению. Для этого мы выполняем следующие действия:
СкидкиНаценки. Эта табличная часть хранит детальную информацию о примененных скидках и наценках.СкидкиРассчитаны в значение Ложь. Этот флаг служит для системы индикатором того, что скидки еще не были корректно рассчитаны или требуют перерасчета. Сброс этого флага гарантирует, что система будет готова к новому расчету.
// Очищаем предыдущие рассчитанные скидки для корректного пересчета.
// Это критически важно, чтобы гарантировать применение актуальных скидок
// и избежать ошибок из-за устаревших данных.
ОбъектДок.СкидкиНаценки.Очистить();
ОбъектДок.СкидкиРассчитаны = Ложь; // Сбрасываем флаг, сигнализируя системе о необходимости пересчета
Именно этот шаг (сброс флага СкидкиРассчитаны) был подмечен одним из участников обсуждения, как потенциальная причина проблемы, и является важным элементом правильного алгоритма.
Формирование структуры параметров для процедуры расчета
Серверная процедура расчета скидок требует передачи специальной структуры параметров. Эта структура управляет логикой выполнения процедуры и определяет, как именно будет производиться расчет и какие действия будут выполнены с объектом документа. Давайте рассмотрим ключевые параметры, которые мы должны включить:
// Формируем структуру параметров для вызова серверной процедуры расчета скидок.
// Эти параметры контролируют поведение процедуры.
СтруктураПараметры = Новый Структура;
СтруктураПараметры.Вставить("ПрименятьКОбъекту", Истина);
СтруктураПараметры.Вставить("ТолькоПредварительныйРасчет", Ложь);
СтруктураПараметры.Вставить("ВосстанавливатьУправляемыеСкидки", Ложь);
СтруктураПараметры.Вставить("УправляемыеСкидки", Неопределено);
Разберем значение каждого параметра:
ПрименятьКОбъекту (Истина): Установка этого свойства в Истина указывает процедуре, что результат расчета скидок должен быть сразу же применен к передаваемому объекту документа (в нашем случае, к ОбъектДок). Это означает, что процедура не только рассчитает скидки, но и внесет соответствующие изменения в табличные части документа.ТолькоПредварительныйРасчет (Ложь): Значение Ложь для этого параметра означает, что мы выполняем окончательный, а не предварительный расчет. Если бы мы установили Истина, расчет произошел бы, но его результаты не были бы сохранены в документе, что не решило бы нашу задачу.ВосстанавливатьУправляемыеСкидки (Ложь): Этот параметр контролирует восстановление ручных (управляемых пользователем) скидок, которые могли быть ранее назначены в документе. Установка в Ложь предотвращает их восстановление, гарантируя, что применятся только автоматические скидки согласно текущим условиям.УправляемыеСкидки (Неопределено): Данный параметр используется, когда необходимо передать информацию о специфических ручных скидках для восстановления или применения. Поскольку наша задача — расчет автоматических скидок, мы можем оставить его в значении Неопределено.Вызов серверной процедуры расчета скидок
После подготовки объекта документа и структуры параметров мы приступаем к ключевому действию — вызову серверной процедуры расчета скидок. В конфигурациях на платформе 1С:Предприятие 8, таких как КА 2.4, за расчет скидок по документу "Заказ клиента" отвечает специализированная функция СкидкиНаценкиСервер.РассчитатьПоЗаказуКлиента. Эту функцию необходимо вызывать на сервере, поэтому убедитесь, что ваш код выполняется в серверном контексте, либо используйте директивы компиляции, если вызов происходит из клиентского кода.
// Вызываем серверную процедуру расчета скидок, передавая объект документа и параметры.
// Эта процедура выполнит всю логику по поиску и применению автоматических скидок.
// Если ваш код выполняется на клиенте, используйте #НаСервере или экспортную функцию модуля.
СкидкиНаценкиСервер.РассчитатьПоЗаказуКлиента(ОбъектДок, СтруктураПараметры);
После успешного выполнения этой процедуры объект ОбъектДок будет содержать актуальные данные о рассчитанных скидках в своей табличной части СкидкиНаценки, а флаг СкидкиРассчитаны будет автоматически установлен в Истина.
Запись и проведение документа
Когда все предыдущие шаги выполнены и скидки успешно рассчитаны и применены к объекту документа, мы можем без опасений записывать и проводить его. Теперь документ содержит все необходимые данные, и система не должна требовать ручного вмешательства.
// После успешного расчета скидок, записываем и проводим документ.
// Теперь документ соответствует всем требованиям для проведения.
Попытка
ОбъектДок.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Заказ клиента " + ОбъектДок.Номер + " успешно проведен со скидками.");
Исключение
Сообщить("Ошибка при проведении заказа клиента " + ОбъектДок.Номер + ": " + ОписаниеОшибки());
КонецПопытки;
Использование РежимЗаписиДокумента.Проведение позволяет записать и провести документ одной операцией. Если требуется только запись без проведения, используйте РежимЗаписиДокумента.Запись.
Объединив все рассмотренные шаги, мы получаем следующий рабочий пример кода, который позволяет программно обрабатывать "Заказы клиентов" с автоматическими скидками, обеспечивая их корректное проведение без ручного вмешательства:
// Пример использования:
// Допустим, у нас есть ссылка на документ "Заказ клиента", который нужно провести.
// Например:
// ДокументСсылкаНаЗаказ = Документы.ЗаказКлиента.НайтиПоНомер("ТД00-000001", Дата("20230101"));
// Если ДокументСсылкаНаЗаказ = Неопределено Тогда
// Сообщить("Заказ клиента не найден.");
// Возврат;
// КонецЕсли;
// Получаем объект документа для изменения и обработки.
// Работа с объектом документа позволяет изменять его данные.
ОбъектДок = ДокументСсылкаНаЗаказ.ПолучитьОбъект();
// Шаг 1: Очищаем предыдущие рассчитанные скидки.
// Это критически важный шаг для обеспечения того, чтобы применились
// актуальные скидки и не было конфликтов со старыми данными.
// Очищаем табличную часть, где хранятся детали по скидкам.
ОбъектДок.СкидкиНаценки.Очистить();
// Сбрасываем флаг, который показывает, что скидки были рассчитаны.
// Это заставляет систему выполнить перерасчет.
ОбъектДок.СкидкиРассчитаны = Ложь;
// Шаг 2: Формируем структуру параметров для вызова серверной процедуры расчета скидок.
// Эти параметры определяют поведение процедуры расчета.
СтруктураПараметры = Новый Структура;
// "ПрименятьКОбъекту" = Истина: Результаты расчета будут применены
// непосредственно к объекту документа, который мы передаем.
СтруктураПараметры.Вставить("ПрименятьКОбъекту", Истина);
// "ТолькоПредварительныйРасчет" = Ложь: Выполняем окончательный расчет,
// а не просто оценку без сохранения результатов.
СтруктураПараметры.Вставить("ТолькоПредварительныйРасчет", Ложь);
// "ВосстанавливатьУправляемыеСкидки" = Ложь: Отключаем восстановление
// любых ручных скидок, если они были ранее применены.
// Это гарантирует применение именно автоматических скидок.
СтруктураПараметры.Вставить("ВосстанавливатьУправляемыеСкидки", Ложь);
// "УправляемыеСкидки" = Неопределено: Этот параметр используется для
// передачи данных о ручных скидках. В контексте автоматических скидок
// его можно оставить неопределенным.
СтруктураПараметры.Вставить("УправляемыеСкидки", Неопределено);
// Шаг 3: Вызываем серверную процедуру расчета скидок для Заказа клиента.
// Модуль "СкидкиНаценкиСервер" содержит логику расчета.
// Важно: эта процедура должна быть вызвана на сервере.
// Если ваш текущий код выполняется на клиенте, вам потребуется
// обернуть этот вызов в серверную экспортную функцию или использовать
// директиву компиляции &НаСервере.
СкидкиНаценкиСервер.РассчитатьПоЗаказуКлиента(ОбъектДок, СтруктураПараметры);
// Шаг 4: Запись и проведение документа после успешного расчета скидок.
// Теперь, когда все скидки рассчитаны и применены к объекту,
// документ готов к проведению.
Попытка
// Используем РежимЗаписиДокумента.Проведение для одновременной записи и проведения.
ОбъектДок.Записать(РежимЗаписиДокумента.Проведение);
Сообщить("Заказ клиента №" + ОбъектДок.Номер + " от " + Формат(ОбъектДок.Дата, "ДФ=dd.MM.yyyy") + " успешно проведен со скидками.");
Исключение
Сообщить("Ошибка при проведении заказа клиента №" + ОбъектДок.Номер + " от " + Формат(ОбъектДок.Дата, "ДФ=dd.MM.yyyy") + ": " + ОписаниеОшибки());
КонецПопытки;
// После успешного проведения можно закрыть форму объекта, если это необходимо.
// Например: Закрыть();
Этот комплексный подход обеспечивает полное соответствие логике работы автоматических скидок в конфигурациях 1С и позволяет нам интегрировать процессы создания и проведения заказов в автоматизированные сценарии. Теперь пользователи избавлены от необходимости ручного вмешательства, а программные задачи по обработке заказов выполняются корректно и эффективно.