Как реализовать простой алгоритм партионного учета ФИФО в 1С?

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

Реализация партионного учета по методу ФИФО (First In, First Out — «первым пришел, первым ушел») является одной из фундаментальных задач при разработке систем складского учета и расчета себестоимости. В среде 1С существует несколько подходов к решению этой задачи: от классических циклов, пришедших из времен версии 7.7, до высокопроизводительных запросов на языке SQL, использующих расчет накопленного итога. В этой статье мы подробно разберем, как построить цепочку партий и правильно списать товар, сохраняя корректность учета.

Основы логики ФИФО: что мы пытаемся достичь

Прежде чем приступать к написанию кода, давайте проанализируем саму суть задачи. Нам необходимо при каждой продаже или списании товара определять, из каких именно поступлений (партий) этот товар был взят. Основное правило: сначала списываются остатки самого старого поступления, затем — следующего за ним по времени, и так далее, пока не будет покрыто все требуемое количество.

Рассмотрим основные объекты, которые нам понадобятся:

Вариант 1. Классический алгоритм перебора остатков в цикле

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

Алгоритм действий следующий:

  1. Получаем из регистра накопления остатки по конкретной номенклатуре и складу, развернутые по измерениям Партия.
  2. Сортируем полученную таблицу по дате (моменту времени) партии по возрастанию.
  3. Организуем цикл по строкам остатков, в котором постепенно вычитаем требуемое количество из каждой партии.

Посмотрим на пример реализации такого кода:


// Предположим, нам нужно списать КоличествоСписания товара НоменклатураСсылка
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ПартииТоваровОстатки.Партия КАК Партия,
    |	ПартииТоваровОстатки.КоличествоОстаток КАК Количество
    |ИЗ
    |	РегистрНакопления.ПартииТоваров.Остатки(&МоментВремени, Номенклатура = &Номенклатура) КАК ПартииТоваровОстатки
    |УПОРЯДОЧИТЬ ПО
    |	Партия.МоментВремени";

Запрос.УстановитьПараметр("МоментВремени", МоментВремениСписания);
Запрос.УстановитьПараметр("Номенклатура", НоменклатураСсылка);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

ОсталосьСписать = КоличествоСписания;

Пока Выборка.Следующий() И ОсталосьСписать > 0 Цикл
    СписываемИзПартии = Мин(ОсталосьСписать, Выборка.Количество);
    
    // Здесь формируем движения или записи в таблицу
    НоваяСтрока = ТаблицаСписания.Добавить();
    НоваяСтрока.Партия = Выборка.Партия;
    НоваяСтрока.Количество = СписываемИзПартии;
    
    ОсталосьСписать = ОсталосьСписать - СписываемИзПартии;
КонецЦикла;

Если ОсталосьСписать > 0 Тогда
    Сообщить("Недостаточно товара на складе!");
КонецЕсли;

Вариант 2. Современный метод накопленного итога (Метод Чистова)

При работе с большими документами (сотни строк) выполнение отдельного запроса для каждой строки в цикле сильно замедляет систему. В современной практике 1С 8.3 мы используем один массивный запрос, который рассчитывает распределение партий для всех строк документа сразу. Этот подход часто называют «методом накопленного итога».

Проанализируем механику этого метода:

  1. Подготовка данных: Мы собираем все товары, которые нужно списать, во временную таблицу.
  2. Получение партий: Выбираем все доступные остатки по этим товарам в другую временную таблицу.
  3. Связь таблиц: Мы соединяем таблицы таким образом, чтобы для каждой партии рассчитать «Накопленный остаток» (сколько товара было в этой и всех предыдущих партиях).
  4. Условие пересечения: Партия должна быть списана, если ее накопленный остаток пересекается с интервалом требуемого количества.

Рассмотрим пример построения такого запроса. В нем ключевым моментом является соединение таблицы партий «самой с собой» для расчета суммы по порядку:


// ВТ_Партии - временная таблица с полями: Номенклатура, Партия, Количество
// ВТ_Требуется - временная таблица с полями: Номенклатура, НужноСписать

"ВЫБРАТЬ
|	Т1.Номенклатура,
|	Т1.Партия,
|	Т1.Количество КАК КоличествоПартии,
|	СУММА(Т2.Количество) КАК НакопленныйОстаток
|ПОМЕСТИТЬ ВТ_ПартииСИтогом
|ИЗ
|	ВТ_Партии КАК Т1
|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Партии КАК Т2
|		ПО Т1.Номенклатура = Т2.Номенклатура
|			И Т1.Партия.МоментВремени >= Т2.Партия.МоментВремени
|СГРУППИРОВАТЬ ПО
|	Т1.Номенклатура, Т1.Партия, Т1.Количество";

После этого мы соединяем ВТ_ПартииСИтогом с ВТ_Требуется. Условие списания выглядит так: партия попадает в выборку, если НакопленныйОстаток - КоличествоПартии < НужноСписать.

Технические нюансы и «подводные камни»

При реализации алгоритма ФИФО важно учитывать несколько критических моментов, которые могут привести к ошибкам в учете:

1. Момент времени vs Дата: Никогда не сортируйте партии только по дате. Если в одну секунду пришло два поступления, 1С не гарантирует их порядок в запросе, если не указана ссылка. Используйте УПОРЯДОЧИТЬ ПО Партия.МоментВремени.

2. Блокировки данных: Если вы пишете обработку, которая должна работать в многопользовательском режиме, обязательно устанавливайте БлокировкаДанных на регистр накопления перед чтением остатков. В противном случае две одновременно работающие обработки могут «увидеть» одни и те же остатки и дважды списать одну и ту же партию, уводя остаток в минус.

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

4. Учет в разрезе характеристик и складов: Помните, что ФИФО работает внутри конкретного разреза учета. Если у вас включен учет по складам или характеристикам, запрос должен содержать соответствующие фильтры в параметрах виртуальной таблицы Остатки.

Срок годности (FEFO) как развитие ФИФО

Если ваша задача связана со скоропортящимися товарами, классический ФИФО (по дате прихода) заменяется на FEFO (First Expired, First Out). В этом случае в запросе мы меняем поле сортировки. Вместо Партия.МоментВремени мы используем поле Партия.СрокГодности (или соответствующий реквизит серии). Это гарантирует, что система предложит списать в первую очередь тот товар, который быстрее испортится, даже если он пришел на склад позже других.

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

← На главную