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