Как реализовать расчет себестоимости при перемещении товаров по регистру бухгалтерии?

Программист 1С v8.3 (Обычные формы) 1C:Бухгалтерия Бухгалтерский учет Торговля и дистрибуция
← На главную

При разработке механизмов складского учета в системе 1С программисты часто сталкиваются с задачей отражения перемещения товаров между складами. Казалось бы, это простая операция, требующая лишь формирования проводки по счету 41 (Товары на складах). Однако ключевая сложность заключается в правильном расчете суммы проводки — себестоимости перемещаемого товара. Поскольку в самом документе перемещения цена обычно не указывается пользователем (так как она определяется учетной политикой), система должна рассчитать ее автоматически в момент проведения на основании данных регистра бухгалтерии.

В этой статье мы подробно разберем, как реализовать этот алгоритм, на что обратить внимание при получении остатков и как избежать типичных ошибок, из-за которых в учете возникают «зависшие копейки».

Анализ структуры данных и настроек учета

Прежде чем приступать к написанию кода, проанализируем структуру нашего регистра. Для счета 41 (или его аналога в вашей конфигурации) обычно определены два основных субконто: Номенклатура и Склад. Важно понимать, как настроен учет по этим аналитикам в Плане счетов.

Рассмотрим два варианта настройки учета по складам:

  1. Только количественный учет: В этом случае система хранит суммы только в целом по организации. При перемещении между складами сумма налога и себестоимость не меняются в разрезе складов, так как остаток по сумме на складе-отправителе всегда равен нулю. В такой ситуации себестоимость считается как общая стоимость товара по организации, деленная на общее количество.
  2. Количественно-суммовой учет: Это наиболее распространенный вариант для детального учета. Здесь сумма хранится в разрезе каждого склада. Именно этот вариант мы будем рассматривать в нашем примере, так как он требует точного расчета стоимости списания со склада-отправителя.

Проанализируем ситуацию: если мы перемещаем товар со Склада 1 на Склад 2, нам необходимо получить остатки по счету 41 в разрезе Организации, Номенклатуры и Склада-отправителя.

Алгоритм расчета себестоимости по средней

Наиболее популярный метод оценки ТМЦ при выбытии — расчет по средней стоимости. Выясним причину, почему это стандарт индустрии: он прост в реализации и дает предсказуемый результат без привязки к конкретным партиям (если не используется ФИФО).

Математическая формула расчета выглядит следующим образом:

СуммаСписания = (СуммаОстатка / КоличествоОстатка) * КоличествоКПеремещению

Однако при программной реализации этой формулы возникают нюансы. Рассмотрим их подробнее.

Программная реализация: Получение остатков

Для получения данных мы должны обратиться к виртуальной таблице Остатки регистра бухгалтерии. Проанализируем пример кода для получения остатков перед формированием движений. Важно использовать Границу, чтобы не учитывать движения самого документа при его перепроведении.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ХозрасчетныйОстатки.СуммаОстаток КАК СуммаОстаток,
    |	ХозрасчетныйОстатки.КоличествоОстаток КАК КоличествоОстаток
    |ИЗ
    |	РегистрБухгалтерии.Хозрасчетный.Остатки(&МоментВремени, Счет = &Счет41, , 
    |		Организация = &Организация 
    |		И Субконто1 = &Номенклатура 
    |		И Субконто2 = &СкладОтправитель) КАК ХозрасчетныйОстатки";

Запрос.УстановитьПараметр("МоментВремени", Новый Граница(МоментВремени(), ВидГраницы.Исключая));
Запрос.УстановитьПараметр("Счет41", ПланыСчетов.Хозрасчетный.ТоварыНаСкладах);
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("Номенклатура", СтрокаТовары.Номенклатура);
Запрос.УстановитьПараметр("СкладОтправитель", СкладОтправитель);

Обратите внимание: мы используем Субконто1 и Субконто2. В реальной конфигурации лучше использовать обращение по имени вида субконто (ВидыСубконто.Номенклатура), чтобы код оставался рабочим при изменении порядка субконто в плане счетов.

Обработка проблемы «хвостов» и копеек

Разберем ситуацию, описанную в сообщениях форума: что будет, если на складе осталось 3 единицы товара общей стоимостью 10 рублей, и мы перемещаем все 3 единицы? Если мы просто разделим 10 на 3, получим 3.3333... При округлении до 2 знаков (3.33) и умножении на 3, мы получим сумму 9.99. В итоге на складе останется 0 единиц товара, но «зависнет» копейка в 0.01 рубля.

Чтобы этого избежать, мы должны использовать проверку: если количество к перемещению равно остатку на складе, мы должны списать всю сумму остатка полностью без вычислений.

Посмотрим на пример правильной логики расчета в коде:


Если Выборка.КоличествоОстаток > 0 Тогда
    Если Выборка.КоличествоОстаток = КоличествоКПеремещению Тогда
        // Списываем всё под ноль, включая копейки
        СуммаСписания = Выборка.СуммаОстаток;
    Иначе
        // Считаем пропорционально
        СуммаСписания = Выборка.СуммаОстаток / Выборка.КоличествоОстаток * КоличествоКПеремещению;
    КонецЕсли;
Иначе
    // Обработка ситуации отрицательного остатка (если разрешено учетной политикой)
    СуммаСписания = 0; 
КонецЕсли;

Формирование проводок по регистру бухгалтерии

Теперь соберем все воедино и сформируем движение. В перемещении товаров проводка будет иметь вид Дт 41 (Склад-получатель) — Кт 41 (Склад-отправитель). Поскольку это внутреннее перемещение в рамках одной организации, сумма по дебету и кредиту будет одинаковой, а количество отражается только по тем счетам/субконто, где ведется количественный учет.


Движение = Движения.Хозрасчетный.Добавить();
Движение.Период = Дата;
Движение.Организация = Организация;

// Настройка дебета (Куда пришел товар)
Движение.СчетДт = ПланыСчетов.Хозрасчетный.ТоварыНаСкладах;
Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = СтрокаТовары.Номенклатура;
Движение.СубконтоДт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = СкладПолучатель;
Движение.КоличествоДт = КоличествоКПеремещению;

// Настройка кредита (Откуда ушел товар)
Движение.СчетКт = ПланыСчетов.Хозрасчетный.ТоварыНаСкладах;
Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Номенклатура] = СтрокаТовары.Номенклатура;
Движение.СубконтоКт[ПланыВидовХарактеристик.ВидыСубконто.Склады] = СкладОтправитель;
Движение.КоличествоКт = КоличествоКПеремещению;

// Сумма перемещения (рассчитанная выше себестоимость)
Движение.Сумма = СуммаСписания;
Движение.Содержание = "Перемещение товаров между складами";

Дополнительные рекомендации

Проанализируем еще несколько важных моментов, которые помогут сделать ваше решение профессиональным:

  1. Блокировка данных: При проведении документа обязательно устанавливайте исключительную блокировку на комбинацию Номенклатура + Склад. Это предотвратит ситуацию, когда два одновременных документа «прочитают» один и тот же остаток и дважды спишут одну и ту же сумму, создав отрицательное сальдо. Используйте объект БлокировкаДанных.
  2. Контроль остатков: Выясним причину, почему автор в коде (сообщение 8) не получал результата. Если в системе не введено поступление товаров ДО момента перемещения, КоличествоОстаток будет равно нулю, и сумма также будет нулевой. Всегда добавляйте проверку: если остатка недостаточно, документ не должен проводиться (или выводить предупреждение).
  3. Налоговый учет: В типовых решениях 1С (например, БП 3.0) расчет ведется параллельно для бухгалтерского и налогового учета. Не забудьте заполнить ресурсы СуммаНУ, если ваша организация ведет учет по налогу на прибыль.
  4. Транзакционность: Всю работу с запросом и записью движений выполняйте в обработчике ОбработкаПроведения. Не пытайтесь рассчитывать себестоимость «на лету» в форме документа, так как данные в базе могут измениться к моменту нажатия кнопки «Провести».

Подведем итог: расчет себестоимости при перемещении — это не просто деление суммы на количество. Это комплексная задача, включающая контроль момента времени, работу с остатками и обязательную проверку на полное списание остатка для предотвращения накопления копеек. Следуя приведенным шагам, вы сможете реализовать надежный механизм складского учета в любой конфигурации на платформе 1С.

← На главную