В практике работы с программой 1С:Бухгалтерия 8 (редакция 3.0) разработчики и аналитики часто сталкиваются с ситуацией, когда в настройках параметров учета (в плане счетов) для материальных счетов (10, 41, 43 и др.) включен количественный учет по складам, но отключен суммовой. Это означает, что программа хранит информацию о суммах только в целом по организации, а на складах учитываются исключительно штуки, литры или килограммы.
Когда пользователям требуется отчет (например, ведомость по материалам или товарам на складах) с суммами, возникает закономерный вопрос: как получить эти данные, если физически в регистре бухгалтерии по субконто "Склады" суммы равны нулю? Попытки решить эту задачу «в лоб» часто приводят к грубым математическим ошибкам или к неработоспособности программы. В этой статье мы подробно разберем, почему простые решения не работают, детально проанализируем математические ловушки и предложим корректные способы решения проблемы с помощью запросов.
Иногда у начинающих разработчиков возникает мысль: написать во внешнем отчете код, который перед выполнением запроса временно изменит настройку программы на «Учет запасов по складам — по количеству и сумме», выполнит запрос, а затем вернет настройку обратно в режим «только по количеству». Давайте разберем, почему этот подход не только не решит задачу, но и опасен для базы данных:
Хозрасчетный. При ее изменении у субконто Склады на счетах учета ТМЦ динамически устанавливается или снимается признак «Суммовой». Это транзакционная операция, требующая монопольного доступа к базе данных. Программа инициирует полную реструктуризацию таблиц регистра бухгалтерии. Выполнить такое действие быстро в коде отчета «на лету» технически невозможно — система просто заблокирует базу для всех пользователей на длительное время.Поэтому идея программного переключения настроек абсолютно нежизнеспособна. Нам необходимо решать задачу исключительно математическими методами в самом запросе, не меняя глобальных настроек системы.
Рассмотрим стандартное предложение: рассчитывать сумму на складе по средней цене. На первый взгляд, логика проста: нужно взять общее количество и общую сумму по номенклатуре из регистра, рассчитать среднюю цену единицы товара и умножить ее на количество товара на конкретном складе.
Однако при попытке реализовать этот расчет через стандартные обороты по складам (например, используя виртуальную таблицу РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты) мы сталкиваемся с серьезной проблемой при наличии документов «Перемещение товаров».
Проанализируем проводки, которые делает документ перемещения между складами при выключенном суммовом учете по складам:
Теперь посмотрим, что происходит в виртуальной таблице ОстаткиИОбороты в разрезе складов, если мы пытаемся посчитать среднюю цену прихода за период по формуле: СуммаПрихода / КоличествоПрихода.
Представим ситуацию на примере:
Если мы снимем обороты по Складу №1, мы увидим:
Если мы попытаемся рассчитать среднюю цену прихода по Складу №1 напрямую по оборотам, мы получим: 1000 руб. / 20 шт. = 50 руб. за штуку! Наша средняя цена искусственно занизилась в два раза просто из-за того, что товар перемещался между складами. Количество оборотов прихода "раздулось" за счет внутренних перемещений без изменения суммовой стоимости. Результат отчета полностью разойдется с реальными данными оборотно-сальдовой ведомости (ОСВ) по организации.
Выяснив причину этой коллизии, мы можем перейти к разработке правильных алгоритмов расчета в запросах.
Поскольку в системе ведется учет по средней стоимости в целом по юридическому лицу (организации), наиболее корректным и методологически верным решением будет оценка остатков товаров на складах по средней цене организации на дату остатка.
Разберем по шагам, как построить такой запрос с использованием механизма временных таблиц:
Номенклатура (и, при необходимости, Счет). Сумму на складе рассчитываем путем умножения количества на складе на среднюю цену, вычисленную по данным организации.Посмотрим на пример практической реализации такого запроса:
ВЫБРАТЬ
ХозрасчетныйОстаткиИОбороты.Счет КАК Счет,
ХозрасчетныйОстаткиИОбороты.Субконто1 КАК Номенклатура,
СУММА(ХозрасчетныйОстаткиИОбороты.КоличествоНачальныйОстатокДт) КАК КоличествоНачОстВсего,
СУММА(ХозрасчетныйОстаткиИОбороты.СуммаНачальныйОстатокДт) КАК СуммаНачОстВсего,
СУММА(ХозрасчетныйОстаткиИОбороты.КоличествоКонечныйОстатокДт) КАК КоличествоКонОстВсего,
СУММА(ХозрасчетныйОстаткиИОбороты.СуммаКонечныйОстатокДт) КАК СуммаКонОстВсего
ПОМЕСТИТЬ ВТ_ИтогиПоОрганизации
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, , , Счет В ИЕРАРХИИ (&СчетУчета), , Организация = &Организация) КАК ХозрасчетныйОстаткиИОбороты
ГРУППИРОВАТЬ ПО
ХозрасчетныйОстаткиИОбороты.Счет,
ХозрасчетныйОстаткиИОбороты.Субконто1
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ХозрасчетныйОстаткиИОбороты.Счет КАК Счет,
ХозрасчетныйОстаткиИОбороты.Субконто1 КАК Номенклатура,
ВЫБОР
КОГДА ТИПЗНАЧЕНИЯ(ХозрасчетныйОстаткиИОбороты.Субконто2) = ТИП(Справочник.Склады)
ТОГДА ХозрасчетныйОстаткиИОбороты.Субконто2
КОГДА ТИПЗНАЧЕНИЯ(ХозрасчетныйОстаткиИОбороты.Субконто3) = ТИП(Справочник.Склады)
ТОГДА ХозрасчетныйОстаткиИОбороты.Субконто3
ИНАЧЕ НЕОПРЕДЕЛЕНО
КОНЕЦ КАК Склад,
СУММА(ХозрасчетныйОстаткиИОбороты.КоличествоНачальныйОстатокДт) КАК КоличествоНачОстСклад,
СУММА(ХозрасчетныйОстаткиИОбороты.КоличествоКонечныйОстатокДт) КАК КоличествоКонОстСклад
ПОМЕСТИТЬ ВТ_ДанныеПоСкладам
ИЗ
РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, , , Счет В ИЕРАРХИИ (&СчетУчета), , Организация = &Организация) КАК ХозрасчетныйОстаткиИОбороты
ГРУППИРОВАТЬ ПО
ХозрасчетныйОстаткиИОбороты.Счет,
ХозрасчетныйОстаткиИОбороты.Субконто1,
ВЫБОР
КОГДА ТИПЗНАЧЕНИЯ(ХозрасчетныйОстаткиИОбороты.Субконто2) = ТИП(Справочник.Склады)
ТОГДА ХозрасчетныйОстаткиИОбороты.Субконто2
КОГДА ТИПЗНАЧЕНИЯ(ХозрасчетныйОстаткиИОбороты.Субконто3) = ТИП(Справочник.Склады)
ТОГДА ХозрасчетныйОстаткиИОбороты.Субконто3
ИНАЧЕ НЕОПРЕДЕЛЕНО
КОНЕЦ
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ДанныеПоСкладам.Счет КАК Счет,
ВТ_ДанныеПоСкладам.Номенклатура КАК Номенклатура,
ВТ_ДанныеПоСкладам.Склад КАК Склад,
// Начальный остаток
ВТ_ДанныеПоСкладам.КоличествоНачОстСклад КАК КоличествоНачальныйОстаток,
ВЫБОР
КОГДА ЕСТЬNULL(ВТ_ИтогиПоОрганизации.КоличествоНачОстВсего, 0) <> 0
ТОГДА ВТ_ДанныеПоСкладам.КоличествоНачОстСклад * (ЕСТЬNULL(ВТ_ИтогиПоОрганизации.СуммаНачОстВсего, 0) / ВТ_ИтогиПоОрганизации.КоличествоНачОстВсего)
ИНАЧЕ 0
КОНЕЦ КАК СуммаНачальныйОстаток,
// Конечный остаток
ВТ_ДанныеПоСкладам.КоличествоКонОстСклад КАК КоличествоКонечныйОстаток,
ВЫБОР
КОГДА ЕСТЬNULL(ВТ_ИтогиПоОрганизации.КоличествоКонОстВсего, 0) <> 0
ТОГДА ВТ_ДанныеПоСкладам.КоличествоКонОстСклад * (ЕСТЬNULL(ВТ_ИтогиПоОрганизации.СуммаКонОстВсего, 0) / ВТ_ИтогиПоОрганизации.КоличествоКонОстВсего)
ИНАЧЕ 0
КОНЕЦ КАК СуммаКонечныйОстаток
ИЗ
ВТ_ДанныеПоСкладам КАК ВТ_ДанныеПоСкладам
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ИтогиПоОрганизации КАК ВТ_ИтогиПоОрганизации
ПО ВТ_ДанныеПоСкладам.Счет = ВТ_ИтогиПоОрганизации.Счет
И ВТ_ДанныеПоСкладам.Номенклатура = ВТ_ИтогиПоОрганизации.Номенклатура
Проанализируем этот запрос. Мы разделили получение данных на два независимых потока. В итоговой выборке мы используем классическое деление общей суммы на общее количество (расчет средней цены) и умножаем полученный коэффициент на складской остаток. Конструкция ВЫБОР КОГДА ... <> 0 страхует нас от потенциальной ошибки деления на ноль, которая может возникнуть, если по какой-то причине в системе образовался суммовой остаток без количественного (или наоборот).
Если вашей задачей является построение отчета, в котором должны корректно отображаться именно обороты (приход и расход ТМЦ по складам с суммами), то использование средних цен на начало и конец периода может не подойти. В этом случае требуется исключить внутренний оборот (перемещения) из расчета средней цены оборотов.
Для этого мы можем применить два подхода:
При перемещении товаров проводка происходит внутри одного и того же счета (например, с 41.01 на 41.01). Мы можем исключить такие обороты в запросе, обратившись к оборотам с фильтром по корреспонденции. Нам нужно отсечь записи, в которых корреспондирующий счет равен счету учета.
Рассмотрим пример настройки параметров виртуальной таблицы:
ВЫБРАТЬ
ХозрасчетныйОбороты.Счет КАК Счет,
ХозрасчетныйОбороты.Субконто1 КАК Номенклатура,
ХозрасчетныйОбороты.КоличествоОборотДт КАК КоличествоПриходЧистый,
ХозрасчетныйОбороты.СуммаОборотДт КАК СуммаПриходЧистый
ИЗ
РегистрБухгалтерии.Хозрасчетный.Обороты(
&НачалоПериода,
&КонецПериода,
,
Счет В ИЕРАРХИИ (&СчетУчета),
,
Организация = &Организация
И НЕ КорСчет В ИЕРАРХИИ (&СчетУчета), // Исключаем внутренние обороты
,
) КАК ХозрасчетныйОбороты
Такой фильтр гарантирует, что в расчете средней цены прихода будут участвовать только внешние поступления (от поставщиков, из производства и т.д.), имеющие реальное суммовое выражение. Обороты по перемещениям будут отсечены.
Если фильтрации по счетам недостаточно (например, при сложных схемах учета), можно использовать детализацию до регистратора и отсекать документы перемещения явным образом в условиях запроса:
ГДЕ
НЕ ХозрасчетныйОстаткиИОбороты.Регистратор ССЫЛКА Документ.ПеремещениеТоваров
Важное предупреждение: Использование детализации до регистратора в виртуальных таблицах остатков и оборотов может существенно снизить производительность запроса на больших базах данных. Рекомендуется отдавать предпочтение первому способу с анализом корреспондирующих счетов.
Если расчетные методы в отчетах кажутся слишком сложными или не закрывают всех потребностей бизнеса, руководство компании может принять решение о переходе на полноценный количественно-суммовой складской учет. Прежде чем сделать этот шаг, необходимо детально проанализировать последствия:
Таким образом, переход на количественно-суммовой учет по складам — это серьезный методологический шаг, требующий взвешенного решения руководства и главного бухгалтера, а также готовности к трудоемкому процессу исправления исторических данных.
Мы проанализировали проблему вывода стоимостных показателей в складских отчетах при ведении только количественного учета в 1С. Выяснили, что попытки программного переключения настроек не имеют практического смысла и опасны для работоспособности системы. Наиболее надежным, быстрым и методологически верным способом решения задачи является разработка внешнего отчета с запросом, который рассчитывает среднюю цену в целом по организации (без учета перемещений) и транслирует ее на количественные остатки конкретных складов.