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