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