Как рассчитать количество готовой номенклатуры по результатам инвентаризации её составных частей (коробок) в 1С?

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

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

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

В чем сложность задачи: проблема пересекающихся компонентов

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

Проанализируем классический пример:

Предположим, в ходе инвентаризации на складе фактически насчитали следующее количество коробок:

В зависимости от того, по какому принципу мы начнем собирать готовые комплекты, мы получим разные результаты учета:

Вариант 1 (приоритет сборки Номенклатуры 1):

Вариант 2 (приоритет сборки Номенклатуры 2):

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

Решение 1: Использование штатного механизма «Товарных мест» в 1С:ERP

Если вы используете современные конфигурации (1С:ERP, 1С:Комплексная автоматизация или 1С:Управление торговлей ред. 11), в системе уже есть готовый инструмент для решения этой задачи. Он называется механизмом товарных мест.

Рассмотрим, как настроить этот процесс без программирования:

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

Решение 2: Разработка собственного алгоритма автоматической сборки

Если штатный функционал по каким-то причинам не подходит (например, учет ведется в более простой конфигурации, или требуется кастомный документ инвентаризации, где на одной вкладке отображается готовая номенклатура, а на другой — составные коробки), необходимо реализовать собственный алгоритм автоматического расчета.

Проанализируем структуру документа инвентаризации, предложенную в обсуждении:

Наша задача — после заполнения фактического количества коробок рассчитать фактическое количество готовой номенклатуры и обновить первую вкладку.

Рассмотрим последовательный (жадный) алгоритм сборки с приоритетом по порядку строк. Этот алгоритм прост в реализации, работает быстро и закрывает 95% потребностей бизнеса.

Пошаговое описание алгоритма:

  1. Создаем временную таблицу (или структуру в памяти) с фактическими остатками коробок, полученными в результате пересчета.
  2. Сортируем список номенклатуры на вкладке «Номенклатура» в соответствии с бизнес-приоритетами (например, сначала дорогие товары, затем более дешевые, или по алфавиту/артикулу).
  3. Организуем цикл по строкам таблицы номенклатуры.
  4. Для текущей номенклатуры запускаем внутренний цикл «поштучной» сборки:
    • Получаем состав (спецификацию) для сборки ровно 1 единицы этой номенклатуры.
    • Проверяем, есть ли на нашем «виртуальном складе» (в таблице остатков коробок) все необходимые коробки в нужном количестве.
    • Если все коробки в наличии: уменьшаем количество этих коробок в таблице остатков на величину состава сборки, увеличиваем счетчик собранной готовой номенклатуры на 1 и переходим к следующей итерации поштучной сборки этой же номенклатуры.
    • Если хотя бы одной коробки не хватает: сборка данной номенклатуры прекращается. Фиксируем полученное количество готового товара в колонке «Факт» текущей строки и переходим к следующей номенклатуре в списке.
  5. Все коробки, которые остались в таблице фактических остатков после прохождения всего цикла сборки, признаются некомплектом. Их можно вывести отдельным списком или отразить как излишки запчастей.

Пример программного кода на встроенном языке 1С

Посмотрим, как выглядит программная реализация данного алгоритма. Этот код можно разместить в обработчике команды «Рассчитать факт номенклатуры» в форме документа инвентаризации.


&НаСервере
Процедура РассчитатьФактНоменклатурыНаСервере()
    
    // Шаг 1. Создаем таблицу фактических остатков коробок для оперативной работы в памяти
    ТаблицаКоробок = Новый ТаблицаЗначений;
    ТаблицаКоробок.Колонки.Добавить("Коробка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
    ТаблицаКоробок.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
    
    // Заполняем таблицу данными из вкладки "Состав" документа
    Для Каждого СтрокаСостава Из Объект.Состав Цикл
        НоваяСтрока = ТаблицаКоробок.Добавить();
        НоваяСтрока.Коробка = СтрокаСостава.Коробка;
        НоваяСтрока.Количество = СтрокаСостава.КоличествоФакт;
    КонецЦикла;
    
    // Индексируем таблицу для быстрого поиска коробок
    ТаблицаКоробок.Индексы.Добавить("Коробка");
    
    // Шаг 2. Обходим номенклатуру в порядке приоритета (как они расположены на вкладке Номенклатура)
    Для Каждого СтрокаНом Из Объект.Номенклатура Цикл
        
        ИмяНоменклатуры = СтрокаНом.Номенклатура;
        СобраноШтук = 0;
        
        // Получаем спецификацию (состав коробок) для 1 единицы товара
        // Предположим, функция ВозвратитьСоставНоменклатуры(Номенклатура) возвращает Таблицу Значений с колонками "Коробка", "Количество"
        ТаблицаСостава = ПолучитьСоставОднойЕдиницы(ИмяНоменклатуры);
        Если ТаблицаСостава.Количество() = 0 Тогда
            Продолжить; // Если состава нет, собрать невозможно
        КонецЕсли;
        
        // Цикл поштучной сборки текущей номенклатуры
        Пока Истина Цикл
            
            ВозможноСобратьЕдиницу = Истина;
            
            // Проверяем наличие всех компонентов для сборки 1 штуки
            Для Каждого СтрокаКомпонента Из ТаблицаСостава Цикл
                НайденнаяКоробка = ТаблицаКоробок.Найти(СтрокаКомпонента.Коробка, "Коробка");
                
                Если НайденнаяКоробка = Неопределено Или НайденнаяКоробка.Количество < СтрокаКомпонента.Количество Тогда
                    ВозможноСобратьЕдиницу = Ложь;
                    Прервать; // Не хватает компонентов, прерываем проверку сборки этой единицы
                КонецЕсли;
            КонецЦикла;
            
            // Если все компоненты на месте, производим "списание" коробок со склада и добавляем готовый товар
            Если ВозможноСобратьЕдиницу Тогда
                Для Каждого СтрокаКомпонента Из ТаблицаСостава Цикл
                    НайденнаяКоробка = ТаблицаКоробок.Найти(СтрокаКомпонента.Коробка, "Коробка");
                    НайденнаяКоробка.Количество = НайденнаяКоробка.Количество - СтрокаКомпонента.Количество;
                КонецЦикла;
                СобраноШтук = СобраноШтук + 1;
            Иначе
                Прервать; // Сборка данной номенклатуры завершена, переходим к следующему товару в списке
            КонецЕсли;
            
        КонецЦикла;
        
        // Записываем результат расчета в табличную часть номенклатуры
        СтрокаНом.КоличествоФакт = СобраноШтук;
        
    КонецЦикла;
    
    // Шаг 3. Оставшиеся в таблице "ТаблицаКоробок" записи с количеством > 0 — это некомплектные излишки.
    // Их можно вывести пользователю или записать в отдельную таблицу некомплекта.
    ОтразитьНекомплектныеОстатки(ТаблицаКоробок);

КонецПроцедуры

Как оптимизировать распределение (для продвинутых задач)

Рассмотренный выше жадный алгоритм отлично работает, если у вас четко заданы приоритеты в списке. Однако, если перед вами стоит задача автоматической оптимизации (например, собрать товары так, чтобы получить максимальную суммарную стоимость или минимизировать остаток неликвидных деталей), обычный последовательный перебор не поможет.

Для решения задач глобальной оптимизации распределения применяется метод линейного программирования (в частности, целочисленного программирования):

Отражение результатов в учете

Важно помнить, что коробки, оставшиеся после расчета в некомплекте, нельзя просто проигнорировать. С точки зрения регламентированного и управленческого учета необходимо корректно отразить их судьбу:

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

← На главную