Столкнулись с проблемой, когда при распределении затрат и расчете себестоимости при закрытии месяца (1С:ERP Управление предприятием, редакция 2.4.13.123) получили ошибку "Переполнение стека встроенного языка на сервере". Мы воспользовались встроенным ограничителем (в настройках формы закрытия месяца) для рекурсивных функции и экспериментально выяснили, что для избежания этой ошибки значение по умолчанию 650 надо уменьшить до 430, тогда ошибка уходит, но и затраты полностью не распределяются. Проблема не решена.
Анализ работы алгоритма и данных, показал, что проблема не в циклических ссылках (в штатном алгоритме заложена проверка на циклические ссылки), а просто очень большой список документов для распределения, где-то порядка 1300+, а штатный алгоритм на каждый шаг для распределения вызывает рекурсивно сам себя и чтобы полностью распределить все затраты, понадобилась бы глубина рекурсии 1300+ соответственно.
Т.к. проблема была не в данных, а в самом алгоритме, было принято решение переписать рекурсию в итерацию, а переменные контекста хранить в массиве, размер которого уже ограничивается только доступной оперативной памятью компьютера, а не размером callstack.
Привожу получившийся результат:
Функция ИнициализироватьКонтекст(ПараметрыРасчета, Данные, ИндексРасхода)
Контекст = Новый Структура("Результат", Неопределено);
Контекст.Вставить("ИндексРасхода", ИндексРасхода);
СтатистикаРасчета = ПараметрыРасчета.РаспределениеПартий.СтатистикаРасчета[ПараметрыРасчета.РаспределениеПартий.СтатистикаРасчета.Количество() - 1];
СтатистикаРасчета.КоличествоДанных = СтатистикаРасчета.КоличествоДанных + 1;
Если НЕ НачалоЗамераРасчетаЦепочек(ПараметрыРасчета, Данные, ИндексРасхода) Тогда
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "ПереполнениеСтека");
Возврат Контекст;
КонецЕсли;
ОписаниеДвижений = ПараметрыРасчета.РаспределениеПартий.ОписаниеДвижений;
Контекст.Вставить("ОписаниеДвижений", ОписаниеДвижений);
Контекст.Вставить("ЦепочкаРасхода", Данные.ЦепочкиДвижений[ИндексРасхода]);
Если Контекст.ЦепочкаРасхода = Неопределено Тогда
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "НеТребуется");
Возврат Контекст;
КонецЕсли;
Контекст.Вставить("Источники", Контекст.ЦепочкаРасхода.Источники);
Если Контекст.Источники.Количество() = 0 Тогда
Если ОписаниеДвижений.Свойство("УдалятьСтрокиБезИсточников") Тогда
Контекст.Расход = Данные.Расходы[ИндексРасхода];
Данные.Расходы.Удалить(ИндексРасхода);
Данные.ЦепочкиДвижений.Удалить(ИндексРасхода);
Данные.СтрокиРасходов.Удалить(Контекст.Расход);
Данные.СтрокиЦепочек.Удалить(Контекст.ЦепочкаРасхода);
КонецЕсли;
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "НетИсточников");
Возврат Контекст;
КонецЕсли;
Если ИндексРасхода = Данные.ПройденныйПуть[ИндексРасхода] Тогда
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "Зацикливание");
Возврат Контекст;
КонецЕсли;
ИндексСтроки = Данные.ИндексСтроки;
Для Каждого ИндексПрихода Из Контекст.Источники Цикл
Если ИндексПрихода > ИндексСтроки Тогда
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "НетДанных"); // еще нет всех строк для распределения
Возврат Контекст;
КонецЕсли;
КонецЦикла;
Данные.ПройденныйПуть.Вставить(ИндексРасхода, ИндексРасхода);
Контекст.Вставить("НовыеПриходы", Неопределено);
Расход = Данные.Расходы[ИндексРасхода];
Контекст.Вставить("Расход", Расход);
Контекст.Вставить("ЕстьСторно", ОписаниеДвижений.Свойство("ЕстьСторно"));
Контекст.Вставить("ЭтоСторно", (Расход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Сторно
ИЛИ Расход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.СторноВозвратНаДругойСклад
ИЛИ Контекст.ЕстьСторно И Расход.Сторно));
ТребуетсяСортировка = Ложь;
Если Контекст.Источники.Количество() > 1 И ЗначениеЗаполнено(ОписаниеДвижений.ПоляСортировки) Тогда
ЗначенияПолей = Новый Структура(ОписаниеДвижений.ПоляСортировки);
ЗаполнитьЗначенияСвойств(ЗначенияПолей, Контекст.Расход);
Для Каждого ПолеСортировки Из ЗначенияПолей Цикл
Если ЗначениеЗаполнено(ПолеСортировки.Значение) Тогда
ТребуетсяСортировка = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Если ТребуетсяСортировка Тогда
СортироватьИсточникиПоФИФО_ЛИФО(Контекст.Источники, ОписаниеДвижений.ПолеПорядка, Данные.Приходы, Данные.Расходы, Контекст.ЭтоСторно);
СортироватьИсточникиПоЗначениямПолей(Контекст.Источники, ЗначенияПолей, Данные.Приходы, Данные.Расходы, Контекст.Расход.Регистратор, Контекст.ЭтоСторно);
КонецЕсли;
Контекст.Вставить("Счетчик", 0);
Контекст.Вставить("ВГраница", Контекст.Источники.ВГраница());
ИнвертироватьПоказатели(Расход, ОписаниеДвижений.Показатели, Контекст.ЭтоСторно);
Контекст.Вставить("УменьшатьБазисРасхода", ОписаниеДвижений.Свойство("УменьшатьБазис"));
Контекст.Вставить("БазисРасходаДоРасчета", Расход[ОписаниеДвижений.БазисРасхода]);
Контекст.Вставить("ТекущийБазисРасхода", Контекст.БазисРасходаДоРасчета);
Контекст.Вставить("ИсточникиКУдалению", Новый Массив);
Контекст.Вставить("ИндексИсточника", Неопределено);
// 0 - начало цикла до рекурсивного вызова,
// 1 - сразу после завершения рекурсии
// 2 - оставшаяся часть цикла
Контекст.Вставить("Шаг", 0);
Возврат Контекст;
КонецФункции
Функция ЗавершитьКонтекст(ПараметрыРасчета, Данные, Контекст)
Расход = Контекст.Расход;
УменьшатьБазисРасхода = Контекст.УменьшатьБазисРасхода;
ОписаниеДвижений = Контекст.ОписаниеДвижений;
БазисРасходаДоРасчета = Контекст.БазисРасходаДоРасчета;
ТекущийБазисРасхода = Контекст.ТекущийБазисРасхода;
НовыеПриходы = Контекст.НовыеПриходы;
ИсточникиКУдалению = Контекст.ИсточникиКУдалению;
Источники = Контекст.Источники;
ЦепочкаРасхода = Контекст.ЦепочкаРасхода;
ЭтоСторно = Контекст.ЭтоСторно;
ИндексРасхода = Контекст.ИндексРасхода;
Если УменьшатьБазисРасхода Тогда
Расход[ОписаниеДвижений.БазисРасхода] = ТекущийБазисРасхода;
КонецЕсли;
Если ОписаниеДвижений.Свойство("ПоляГруппировки")
И НовыеПриходы <> Неопределено
И НовыеПриходы.Количество() > 0 Тогда
НовыеПриходы.Свернуть(ОписаниеДвижений.ПоляГруппировки, ОписаниеДвижений.ПоляСуммирования);
Если Данные.Приходы[ИндексРасхода] = Неопределено Тогда
Данные.Приходы.Вставить(ИндексРасхода, Новый Массив);
КонецЕсли;
Для Каждого Строка Из НовыеПриходы Цикл
НовыйПриход = Данные.СтрокиПриходов.Добавить();
ЗаполнитьЗначенияСвойств(НовыйПриход, Строка);
Если НовыйПриход[ОписаниеДвижений.БазисРасхода] = 0 Тогда
НовыйПриход[ОписаниеДвижений.БазисРасхода] = БазисРасходаДоРасчета;
КонецЕсли;
Данные.Приходы[ИндексРасхода].Добавить(НовыйПриход);
КонецЦикла;
Контекст.НовыеПриходы = Неопределено;
КонецЕсли;
Если ИсточникиКУдалению.Количество() > 0 Тогда
Для Каждого ИндексИсточника Из ИсточникиКУдалению Цикл
ИндексВМассиве = Источники.Найти(ИндексИсточника);
Если ИндексВМассиве <> Неопределено Тогда
Источники.Удалить(ИндексВМассиве);
КонецЕсли;
КонецЦикла;
Данные.ЦепочкиДвижений[ИндексРасхода].Источники = Источники;
КонецЕсли;
Если (Расход[ОписаниеДвижений.БазисРасхода] > 0
ИЛИ Расход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыБезПартии И Расход.Знаменатель > 0)
И (Источники.Количество() > 0 ИЛИ НЕ ОписаниеДвижений.Свойство("УдалятьСтрокиБезИсточников")) Тогда
ИнвертироватьПоказатели(Расход, ОписаниеДвижений.Показатели, ЭтоСторно);
Иначе
Данные.Расходы.Удалить(ИндексРасхода);
Данные.ЦепочкиДвижений.Удалить(ИндексРасхода);
Данные.СтрокиРасходов.Удалить(Расход);
Данные.СтрокиЦепочек.Удалить(ЦепочкаРасхода);
КонецЕсли;
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "Выполнено");
Возврат Контекст;
КонецФункции
Функция РассчитатьПартиюРекурсивно(ПараметрыРасчета, Данные, ИндексРасходаНачальный)
Стэк = Новый Массив();
НовыйКонтекст = ИнициализироватьКонтекст(ПараметрыРасчета, Данные, ИндексРасходаНачальный);
Если НовыйКонтекст.Результат <> Неопределено Тогда
Возврат НовыйКонтекст.Результат;
КонецЕсли;
Стэк.Добавить(НовыйКонтекст);
РезультатРасчета = НовыйКонтекст;
Пока Стэк.Количество() > 0 Цикл
Контекст = Стэк[Стэк.ВГраница()];
ИндексРасхода = Контекст.ИндексРасхода;
Если Контекст.Шаг = 0 Тогда
Если Контекст.Счетчик > Контекст.ВГраница Тогда
РезультатРасчета = ЗавершитьКонтекст(ПараметрыРасчета, Данные, Контекст);
Стэк.Удалить(Стэк.ВГраница());
Продолжить;
КонецЕсли;
Контекст.ИндексИсточника = Контекст.Источники[?(Контекст.ЭтоСторно, Контекст.ВГраница - Контекст.Счетчик, Контекст.Счетчик)];
Контекст.Счетчик = Контекст.Счетчик + 1;
Контекст.Вставить("МассивПриходов", Данные.Приходы[Контекст.ИндексИсточника]);
Если Контекст.МассивПриходов = Неопределено Тогда
Если Данные.Расходы[Контекст.ИндексИсточника] = Неопределено Тогда
Контекст.ИсточникиКУдалению.Добавить(Контекст.ИндексИсточника);
Продолжить;
КонецЕсли;
Источник = Данные.Расходы[Контекст.ИндексИсточника];
Если НЕ Источник.РасчетЗавершен Тогда
Контекст.Шаг = 1;
НовыйКонтекст = ИнициализироватьКонтекст(ПараметрыРасчета, Данные, Контекст.ИндексИсточника);
Если НовыйКонтекст.Результат = Неопределено Тогда
Стэк.Добавить(НовыйКонтекст);
Продолжить;
КонецЕсли;
Иначе
Контекст.Вставить("МассивПриходов", Данные.Приходы[Контекст.ИндексИсточника]);
Если Контекст.МассивПриходов = Неопределено Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если Контекст.Шаг = 1 Тогда
Контекст.ВГраница = Контекст.Источники.ВГраница();
Если Данные.Расходы[ИндексРасхода] = Неопределено Тогда
Контекст.Результат = ОкончаниеЗамераРасчетаЦепочек(ПараметрыРасчета, "НеТребуется");
РезультатРасчета = Контекст;
Стэк.Удалить(Стэк.ВГраница());
Продолжить;
КонецЕсли;
Контекст.МассивПриходов = Данные.Приходы[Контекст.ИндексИсточника];
Если Контекст.МассивПриходов = Неопределено Тогда
Контекст.Шаг = 0;
Продолжить;
КонецЕсли;
КонецЕсли;
Контекст.Шаг = 0;
ЭтоСторно = Контекст.ЭтоСторно;
ЕстьСторно = Контекст.ЕстьСторно;
ОписаниеДвижений = Контекст.ОписаниеДвижений;
БазисРасходаДоРасчета = Контекст.БазисРасходаДоРасчета;
ТекущийБазисРасхода = Контекст.ТекущийБазисРасхода;
УменьшатьБазисРасхода = Контекст.УменьшатьБазисРасхода;
Расход = Контекст.Расход;
ИндексИсточника = Контекст.ИндексИсточника;
МассивПриходов = Контекст.МассивПриходов;
ЦепочкаРасхода = Контекст.ЦепочкаРасхода;
Если ЭтоСторно Тогда
СортироватьПриходыПоЛИФО(ОписаниеДвижений.ПолеПорядка, Контекст.МассивПриходов);
КонецЕсли;
ПриходыКУдалению = Новый Массив;
КоличествоДвижений = ПараметрыРасчета.РаспределениеПартий.РасчетныеПартии.Количество();
УменьшениеБазисаРасхода = 0;
Для Каждого Приход Из МассивПриходов Цикл
Если УменьшатьБазисРасхода Тогда
Если Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходы
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыБезПартии
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыСПартией
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыРегл
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыУпр
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.КорректировкаПриобретенияПрошлогоПериода
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ОтклонениеВСтоимости
ИЛИ (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Прошлое
И ЗначениеЗаполнено(Приход.Номенклатура))
ИЛИ (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Сторно
И ЗначениеЗаполнено(Приход.Номенклатура)
И ОписаниеДвижений.Контекст <> "ПодготовкаДанныхДляУчетаНДСиУСН")
ИЛИ (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ОстатокСгруппированный
И ЗначениеЗаполнено(Приход.Номенклатура))
ИЛИ (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ПартияСгруппированная
И ЗначениеЗаполнено(Приход.Номенклатура))
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Партия
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Остаток Тогда
Если ОписаниеДвижений.Контекст = "ПодготовкаДанныхДляУчетаНДСиУСН"
И (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ОстатокСгруппированный
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ПартияСгруппированная) Тогда
Расход[ОписаниеДвижений.БазисРасхода] = ТекущийБазисРасхода;
Если Приход.Количество <> 0 Тогда // данные о партии товара (не доп. расходы)
УменьшениеБазисаРасхода = Мин(ТекущийБазисРасхода, Приход[ОписаниеДвижений.БазисРасхода]);
// Могут быть только доп расходы по товарам. У таких записей нет количества.
ИначеЕсли УменьшениеБазисаРасхода = 0 Тогда
УменьшениеБазисаРасхода = ТекущийБазисРасхода;
КонецЕсли;
Иначе
Расход[ОписаниеДвижений.БазисРасхода] = БазисРасходаДоРасчета;
КонецЕсли;
ИначеЕсли ОписаниеДвижений.Контекст = "ПодготовкаДанныхДляУчетаНДСиУСН"
И (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Партия
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Выпуск
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.СписанияНаВыпуск
ИЛИ (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Потребление
И Расход.ТипЗаписи <> Перечисления.ТипыЗаписейПартий.Сторно
И Расход.ТипЗаписи <> Перечисления.ТипыЗаписейПартий.СторноВозвратНаДругойСклад)
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ПотреблениеАвто
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ПотреблениеПроизводство) Тогда
Расход[ОписаниеДвижений.БазисРасхода] = БазисРасходаДоРасчета;
ИначеЕсли ОписаниеДвижений.Контекст <> "ПодготовкаДанныхДляУчетаНДСиУСН"
И (Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.СписанияНаВыпуск
ИЛИ Приход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.СписанияНаВыпускПостатейные) Тогда
Расход[ОписаниеДвижений.БазисРасхода] = БазисРасходаДоРасчета;
Иначе
УменьшениеБазисаРасхода = Мин(ТекущийБазисРасхода, Приход[ОписаниеДвижений.БазисРасхода]);
Расход[ОписаниеДвижений.БазисРасхода] = ТекущийБазисРасхода;
КонецЕсли;
Если Расход[ОписаниеДвижений.БазисРасхода] <= 0 Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
РасчетнаяПартия = ДобавитьИЗаполнитьРасчетнуюПартию(ПараметрыРасчета, Расход, Приход, ЭтоСторно);
Если Приход[ОписаниеДвижений.БазисПрихода] <= 0 Тогда
ПриходыКУдалению.Добавить(Приход);
КонецЕсли;
Если РасчетнаяПартия.РасчетЗавершен Тогда
Если ЦепочкаРасхода.Приемники.Количество() > 0 Тогда
Если ОписаниеДвижений.Свойство("ПоляГруппировки") Тогда
Если Контекст.НовыеПриходы = Неопределено Тогда
Контекст.НовыеПриходы = Данные.СтрокиПриходов.СкопироватьКолонки();
КонецЕсли;
НовыйПриход = Контекст.НовыеПриходы.Добавить();
ЗаполнитьЗначенияСвойств(НовыйПриход, РасчетнаяПартия);
ИнвертироватьПоказатели(НовыйПриход, ОписаниеДвижений.Показатели,
НовыйПриход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Сторно
ИЛИ НовыйПриход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.СторноВозвратНаДругойСклад
ИЛИ ЕстьСторно И НовыйПриход.Сторно);
Иначе
НовыйПриход = Данные.СтрокиПриходов.Добавить();
ЗаполнитьЗначенияСвойств(НовыйПриход, РасчетнаяПартия);
ИнвертироватьПоказатели(НовыйПриход, ОписаниеДвижений.Показатели,
НовыйПриход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.Сторно
ИЛИ НовыйПриход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.СторноВозвратНаДругойСклад
ИЛИ ЕстьСторно И НовыйПриход.Сторно);
Если Данные.Приходы[ИндексРасхода] = Неопределено Тогда
Данные.Приходы.Вставить(ИндексРасхода, Новый Массив);
КонецЕсли;
Данные.Приходы[ИндексРасхода].Добавить(НовыйПриход);
КонецЕсли;
КонецЕсли;
Иначе
УдалитьРасчетнуюПартию(ПараметрыРасчета, РасчетнаяПартия);
КонецЕсли;
РасчетнаяПартия = Неопределено;
Если НЕ УменьшатьБазисРасхода И Расход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыБезПартии Тогда
Если Расход.Знаменатель <= 0 Тогда
Прервать;
КонецЕсли;
ИначеЕсли НЕ УменьшатьБазисРасхода И Расход[ОписаниеДвижений.БазисРасхода] <= 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Если УменьшатьБазисРасхода
И КоличествоДвижений <> ПараметрыРасчета.РаспределениеПартий.РасчетныеПартии.Количество()
И УменьшениеБазисаРасхода <> 0 Тогда
ТекущийБазисРасхода = ТекущийБазисРасхода - УменьшениеБазисаРасхода;
Контекст.ТекущийБазисРасхода = ТекущийБазисРасхода;
КонецЕсли;
Для Каждого Приход Из ПриходыКУдалению Цикл
Индекс = МассивПриходов.Найти(Приход);
Если Индекс <> Неопределено Тогда
МассивПриходов.Удалить(Индекс);
КонецЕсли;
Данные.СтрокиПриходов.Удалить(Приход);
КонецЦикла;
Если МассивПриходов.Количество() = 0 Тогда
Данные.Приходы.Удалить(ИндексИсточника);
Если Данные.Расходы[ИндексИсточника] = Неопределено Тогда
ЦепочкаПрихода = Данные.ЦепочкиДвижений[ИндексИсточника];
Если ЦепочкаПрихода <> Неопределено Тогда
Данные.ЦепочкиДвижений.Удалить(ИндексИсточника);
Данные.СтрокиЦепочек.Удалить(ЦепочкаПрихода);
КонецЕсли;
КонецЕсли;
ИначеЕсли ПриходыКУдалению.Количество() > 0 Тогда
Данные.Приходы.Вставить(ИндексИсточника, МассивПриходов);
КонецЕсли;
Если НЕ УменьшатьБазисРасхода И Расход.ТипЗаписи = Перечисления.ТипыЗаписейПартий.ДопРасходыБезПартии Тогда
Если Расход.Знаменатель <= 0 Тогда
РезультатРасчета = ЗавершитьКонтекст(ПараметрыРасчета, Данные, Контекст);
Стэк.Удалить(Стэк.ВГраница());
Продолжить;
КонецЕсли;
ИначеЕсли НЕ УменьшатьБазисРасхода И Расход[ОписаниеДвижений.БазисРасхода] <= 0 Тогда
РезультатРасчета = ЗавершитьКонтекст(ПараметрыРасчета, Данные, Контекст);
Стэк.Удалить(Стэк.ВГраница());
Продолжить;
КонецЕсли;
КонецЦикла;
Возврат РезультатРасчета.Результат;
КонецФункции
После применения измененного алгоритма затраты распределились и себестоимость рассчиталась. Ограничение на количество шагов из алгоритма удалять не стал, поэтому ограничение в настройках выставили в 2000.
Для использования этого алгоритма, надо просто заменить исходную процедуру РасчетСебестоимостиПрикладныеАлгоритмы.РассчитатьПартиюРекурсивно на указанный выше код и увеличить ограничение на количество вызовов в настройках закрытия месяца.
На этом все. Надеюсь, это будет полезно еще кому-нибудь.