В процессе автоматизации торговой деятельности часто возникает задача жесткого контроля взаиморасчетов. Типичный пример: заказчик требует запретить создание документов оплаты — Приходного кассового ордера (ПКО) или Поступления безналичных денежных средств (ПДС), если сумма по уже оформленным платежам покрывает или превышает общую сумму заказа-основания. Если заказ оформлен на 14 000 рублей, а пользователь уже создал ПКО на эту сумму, система не должна разрешать создание еще одного платежного документа.
Реализация такого контроля имеет множество подводных камней, начиная от открытия «пустых» незаписанных форм документов и заканчивая распределением авансов и изменением сумм в самих заказах. В этой статье мы подробно рассмотрим, как правильно спроектировать и реализовать данный механизм контроля в конфигурациях 1С, находящихся на поддержке, используя механизмы подписок на события и расширения.
Мы разберем эту задачу по шагам: от логики проверки остатка лимита до конкретной программной реализации в различных точках жизненного цикла документа.
---
Начинающие разработчики часто совершают классическую ошибку: при создании нового ПКО они пытаются найти все ранее созданные ПКО и ПДС по данному заказу прямым запросом к таблицам этих документов. Рассмотрим, почему такой подход неэффективен и опасен в реальных бизнес-сценариях:
Правильное решение: Для получения актуальной информации о состоянии расчетов по заказу необходимо обращаться к остаткам специализированных регистров накопления взаиморасчетов (например, РасчетыСКлиентами.Остатки или аналогичным в вашей конфигурации). Если по регистру остаток долга клиента по заказу равен нулю (или меньше нуля), то создавать новый документ оплаты запрещено.
---
Чтобы обеспечить максимальное удобство для пользователя и гарантировать надежность данных, контроль необходимо разделить на два независимых этапа:
ОбработкаЗаполнения. Его цель — не дать пользователю даже открыть форму нового документа оплаты, если заказ уже полностью оплачен.ПередЗаписью или ОбработкаПроведения. Этот этап критически важен, так как он защищает систему от проведения документов, которые были изменены вручную, от проведения ранее сохраненных «черновиков», а также от программного создания документов сторонними обработками или обменами.Давайте подробно разберем реализацию каждого из этих этапов.
---
Если пользователь пытается создать ПКО на основании заказа, который уже оплачен, система должна выдать предупреждение и прервать процесс. При этом пустая форма нового документа открываться не должна.
Согласно стандартам разработки фирмы «1С», для предотвращения открытия формы документа при вводе на основании в обработчике ОбработкаЗаполнения необходимо вызывать исключение с помощью оператора ВызватьИсключение.
Поскольку наша конфигурация находится на поддержке, мы не будем менять типовой код модулей документов. Вместо этого мы создадим Расширение конфигурации или воспользуемся Подпиской на событие ОбработкаЗаполнения для интересующих нас документов (ПКО и Поступление ДС).
Рассмотрим пример кода для подписки на событие ОбработкаЗаполнения:
Процедура КонтрольЗаполненияОплатыНаОсновании(Источник, ДанныеЗаполнения, СтандартнаяОбработка) Экспорт
// Проверяем, что заполнение происходит на основании Заказа клиента
ТипЗаказа = Тип("ДокументСсылка.ЗаказКлиента");
ЗаказСсылка = Неопределено;
Если ТипЗнч(ДанныеЗаполнения) = ТипЗаказа Тогда
ЗаказСсылка = ДанныеЗаполнения;
ИначеЕсли ТипЗнч(ДанныеЗаполнения) = Тип("Структура") Тогда
// Для документов Поступление ДС данные часто передаются структурой
ДанныеЗаполнения.Свойство("ДокументОснование", ЗаказСсылка);
Если Не ЗначениеЗаполнено(ЗаказСсылка) Тогда
ДанныеЗаполнения.Свойство("ЗаказКлиента", ЗаказСсылка);
КонецЕсли;
КонецЕсли;
Если Не ЗначениеЗаполнено(ЗаказСсылка) Или ТипЗнч(ЗаказСсылка) <> ТипЗаказа Тогда
Возврат; // Нас интересует только ввод на основании Заказа клиента
КонецЕсли;
// Вычисляем остаток долга по заказу
ОстатокКОплате = ПолучитьОстатокДолгаПоЗаказу(ЗаказСсылка);
Если ОстатокКОплате <= 0 Тогда
// Формируем понятное сообщение пользователю и отменяем создание документа
ТекстИсключения = НСтр("ru = 'Невозможно создать документ оплаты! Заказ %Заказ% уже полностью оплачен.'");
ТекстИсключения = СтрЗаменить(ТекстИсключения, "%Заказ%", Строка(ЗаказСсылка));
ВызватьИсключение ТекстИсключения;
КонецЕсли;
КонецПроцедуры
Важный нюанс для безналичных платежей (ПДС): В отличие от ПКО, документы безналичной оплаты часто загружаются через банк-клиент или создаются через специальное рабочее место. В этом случае в качестве ДанныеЗаполнения передается не прямая ссылка на заказ, а сложная структура или даже массив. Код выше учитывает это и пытается извлечь основание платежа из структуры данных.
---
Для того чтобы функция ПолучитьОстатокДолгаПоЗаказу возвращала корректные данные, нам необходимо написать оптимальный запрос к регистру остатков взаиморасчетов. Ниже приведен пример такой функции для типовой конфигурации (на примере регистра РасчетыСКлиентами):
Функция ПолучитьОстатокДолгаПоЗаказу(ЗаказСсылка) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(ВзаиморасчетыОстатки.ДолгОстаток, 0) КАК ДолгОстаток,
| ЕСТЬNULL(ВзаиморасчетыОстатки.КОплатеОстаток, 0) КАК КОплатеОстаток
|ИЗ
| РегистрНакопления.РасчетыСКлиентами.Остатки(
| ,
| АналитикаУчетаПоПартнерам.ОбъектРасчетов = &ЗаказСсылка
| ИЛИ ОбъектРасчетов = &ЗаказСсылка
| ) КАК ВзаиморасчетыОстатки";
Запрос.УстановитьПараметр("ЗаказСсылка", ЗаказСсылка);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
// Возвращаем сумму, которую клиенту осталось оплатить по данному заказу
Возврат Выборка.КОплатеОстаток;
КонецЕсли;
Возврат 0;
КонецФункции
Примечание: Названия измерений регистра и полей структуры могут незначительно отличаться в зависимости от версии вашей конфигурации (например, в УТ 11.4/11.5 используется аналитика через объект расчетов КлючАналитикиУчетаНоменклатуры или справочник ОбъектыРасчетов). Обязательно адаптируйте данный запрос под вашу схему метаданных.
---
Интерактивная блокировка в ОбработкаЗаполнения защищает только в момент нажатия кнопки «Создать на основании». Но хитрый пользователь может обойти это ограничение:
Чтобы исключить эти ситуации, мы обязаны добавить проверку в подписку на событие ОбработкаПроведения для документов ПКО и Поступление ДС. Проанализируем алгоритм проверки при проведении:
Процедура КонтрольСуммыОплатыПриПроведении(Источник, Отказ, РежимПроведения) Экспорт
// Проверяем, есть ли в документе привязка к Заказу клиента.
// Так как реквизиты могут находиться в табличной части (например, "РасшифровкаПлатежа"),
// мы должны собрать все заказы из ТЧ документа.
ТаблицаЗаказов = ПолучитьЗаказыИзДокументаОплаты(Источник);
Для Каждого СтрокаЗаказа Из ТаблицаЗаказов Цикл
// Получаем текущий остаток лимита по регистру БЕЗ учета движений текущего документа
ОстатокДолга = ПолучитьОстатокДолгаПоЗаказуИсключаяТекущийДокумент(СтрокаЗаказа.ЗаказКлиента, Источник);
Если СтрокаЗаказа.СуммаПлатежа > ОстатокДолга Тогда
// Сообщаем об ошибке проведения
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(
СтрШаблон(НСтр("ru = 'Превышена сумма оплаты по заказу %1! Доступный лимит для оплаты: %2 руб. Пытаются провести: %3 руб.'"),
СтрокаЗаказа.ЗаказКлиента,
ОстатокДолга,
СтрокаЗаказа.СуммаПлатежа),
Источник,
,
,
Отказ
);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
При расчете остатка в проведении крайне важно использовать метод исключения движений текущего документа. Если мы этого не сделаем, то проведение документа будет блокироваться его же собственной суммой, которая уже записана в регистре взаиморасчетов.
---
Мы спроектировали надежную и отказоустойчивую систему контроля оплат. При внедрении механизма обязательно убедитесь, что вы учли все пункты из следующего списка:
Используя данный подход, вы сможете гарантировать чистоту данных в системе взаиморасчетов и исключить человеческий фактор при приеме платежей от клиентов.