Дано:
Есть склад, на котором реализована адресная система хранения.
Каждое место хранения маркируется 3-х значным индексом вида:
<SS><NN><LL>
Где, SS номер ряда стеллажей, NN – номер стойки в ряду стеллажей, LL уровень стеллажа.
На складе ведется только количественный учет, без суммового.
Хранение товаров на складе определяется следующим правилам:
* На каждом месте хранения одновременно может находиться только товар одного вида.
* Каждому товару присуще свойство – коэффициент палетирования, указывающий сколько единиц хранения товара составляет палетту.
* На одном месте хранения может храниться не более одной полной палетты.
Задание:
Реализовать процессы склада, отвечающие следующим критериям: | |
---|---|
Процесс | Критерий |
Прием товара | Занять как можно меньше свободных мест хранения, соответственно оставить как можно больше свободных мест хранения |
Отпуск товара | Отпуск товара производится с минимальный перемещением персонала склада. Нужно отсортировать позиции в накладной в соответствии с порядком обхода склада. |
Уплотнение склада | Освободить как можно больше мест хранения. |
Инвентаризация | Частичная инвентаризация по товарным позициям или по местам хранения (ряд, стойка, уровень). |
Создать следующие отчеты: | |
Отчет | Описание |
Остатки товара | Остатки товара на складе по местам хранения |
Материальная ведомость | Все движения выбранных товарных позиций по складу за выбранный период |
Загруженность склада | Временная диаграмма загруженности склада за выбранный период |
Решение.
Создаем:
1. Справочник "Номенклатура", реквизит КП(Коэффициент палетирования)
2. Регистр сведений "МестаХранения", измерения: SS, NN, LL; ресурс: ПорядокОбхода
Будет хранить перечень всех мест хранения на складе
Будет хранить складские остатки в разрезе номенклатур и адресов
Прием товара
Создаем документ "ПриемТовара", табличные части:
- "Товары" (колонки: Номенклатура, Количество);
- "МестаХранения" (колонки: Номенклатура, SS, NN, LL, Количество)
Создаем форму, размещаем на ней элементы управления.
Документ будет позволять ручное размещение товара, поэтому необходима функция проверки правильности заполнения позиций (может запускаться перед записью документа и после редактирования строки):
// Проверить строки
//
&НаСервере
Функция МестаХраненияПроверитьСтроки(ПроверкаРаспределения = Истина, ВыводитьСообщения = Истина)
// Получить места по складу
тзМеста = СкладСервер.ПолучитьМеста(Объект.Дата);
тзМестаХранения = Объект.МестаХранения.Выгрузить();
тзМестаХранения.Свернуть("Номенклатура, SS, NN, LL", "Количество");
тзТовары = Объект.Товары.Выгрузить();
тзТовары.Свернуть("Номенклатура", "Количество");
// Проверка доступности мест хранения
Для каждого СтрокаМестаХранения Из тзМестаХранения Цикл
Отбор = Новый Структура();
Отбор.Вставить("SS", СтрокаМестаХранения.SS);
Отбор.Вставить("NN", СтрокаМестаХранения.NN);
Отбор.Вставить("LL", СтрокаМестаХранения.LL);
Строки = тзМеста.НайтиСтроки(Отбор);
Если Строки.Количество() = 0 Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не найдено!", Новый Структура("Парам1, Парам2, Парам3", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли Строки.Количество() > 1 ИЛИ НЕ (Строки[0].Номенклатура = СтрокаМестаХранения.Номенклатура ИЛИ Строки[0].Номенклатура = NULL) Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 содержит различные номенклатуры!", Новый Структура("Парам1, Парам2, Парам3", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли ?(Строки[0].Номенклатура = NULL,0,Строки[0].Количество) + СтрокаМестаХранения.Количество > СтрокаМестаХранения.Номенклатура.КП Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 переполнено! Доступно: Парам4", Новый Структура("Парам1, Парам2, Парам3, Парам4", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL, (?(Строки[0].Номенклатура = NULL, СтрокаМестаХранения.Номенклатура.КП,Строки[0].НоменклатураКП) - ?(Строки[0].Номенклатура = NULL,0,Строки[0].Количество))), ВыводитьСообщения);
Возврат Ложь;
КонецЕсли;
СтрокаПроверки = тзТовары.Добавить();
СтрокаПроверки.Номенклатура = СтрокаМестаХранения.Номенклатура;
СтрокаПроверки.Количество = - СтрокаМестаХранения.Количество;
КонецЦикла;
// Проверить все ли позиции номенклатуры распределены
Если ПроверкаРаспределения Тогда
тзТовары.Свернуть("Номенклатура", "Количество");
Для каждого СтрокаПроверки Из тзТовары Цикл
Если НЕ СтрокаПроверки.Количество = 0 Тогда
СообщениеПользователю("Не размещено Парам1 в количестве: Парам2", Новый Структура("Парам1, Парам2", СтрокаПроверки.Номенклатура, СтрокаПроверки.Количество), ВыводитьСообщения);
Возврат Ложь
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат Истина;
КонецФункции
Функция "ПолучитьМеста" из общего модуля "СкладСервер" возвращает таблицу значений, содержащую перечень свободных и частично занятых мест на складе.
// Получить места по складу
//
Функция ПолучитьМеста(Период) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| МестаХранения.SS КАК SS,
| МестаХранения.NN КАК NN,
| МестаХранения.LL КАК LL,
| ПозицииТовараОстатки.Номенклатура КАК Номенклатура,
| ПозицииТовараОстатки.Номенклатура.КП КАК НоменклатураКП,
| ПозицииТовараОстатки.КоличествоОстаток КАК Количество,
| ВЫБОР
| КОГДА ПозицииТовараОстатки.Номенклатура ЕСТЬ NULL
| ТОГДА NULL
| ИНАЧЕ ПозицииТовараОстатки.Номенклатура.КП - ПозицииТовараОстатки.КоличествоОстаток
| КОНЕЦ КАК Места
|ИЗ
| РегистрСведений.МестаХранения КАК МестаХранения
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ПозицииТовара.Остатки(&Период, ) КАК ПозицииТовараОстатки
| ПО МестаХранения.SS = ПозицииТовараОстатки.SS
| И МестаХранения.NN = ПозицииТовараОстатки.NN
| И МестаХранения.LL = ПозицииТовараОстатки.LL
|
|УПОРЯДОЧИТЬ ПО
| Места";
Запрос.Параметры.Вставить("Период", Период);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции // ПолучитьМеста()
Процедура "СообщениеПользователю" из глобального модуля:
Процедура СообщениеПользователю(Знач Шаблон, Параметры = Неопределено, ВыводитьСообщение = Истина) Экспорт
Если НЕ ВыводитьСообщение Тогда
Возврат
КонецЕсли;
Если НЕ Параметры = Неопределено Тогда
Для каждого СтрокаПараметров Из Параметры Цикл
Шаблон = СтрЗаменить(Шаблон, СтрокаПараметров.Ключ, СтрокаПараметров.Значение);
КонецЦикла;
КонецЕсли;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = Шаблон;
Сообщение.Сообщить();
КонецПроцедуры // СообщениеПользователю()
Для автоматического распределения поступивших товаров создаем команду "Заполнить" и прописываем обработчик:
&НаСервере
Процедура ЗаполнитьНаСервере()
Объект.МестаХранения.Очистить();
тзТовары = Объект.Товары.Выгрузить();
тзТовары.Свернуть("Номенклатура", "Количество");
// Получить места по складу в порядке увеличения места
тзОстатки = СкладСервер.ПолучитьМеста(Объект.Дата);
Для каждого СтрокаТовары Из тзТовары Цикл
Если НЕ СтрокаТовары.Количество = 0 Тогда
ОсталосьРаспределить = СтрокаТовары.Количество;
// Смотрим где осталось место
Для каждого МестоХранения Из тзОстатки Цикл
Если НЕ МестоХранения.Номенклатура = СтрокаТовары.Номенклатура Тогда
Продолжить;
КонецЕсли;
Если НЕ МестоХранения.Места > 0 Тогда
Продолжить;
КонецЕсли;
НоваяСтрока = Объект.МестаХранения.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, МестоХранения);
Если ОсталосьРаспределить > МестоХранения.Места Тогда
НоваяСтрока.Количество = МестоХранения.Места;
Иначе
НоваяСтрока.Количество = ОсталосьРаспределить;
КонецЕсли;
ОсталосьРаспределить = ОсталосьРаспределить - НоваяСтрока.Количество;
МестоХранения.Места = МестоХранения.Места - НоваяСтрока.Количество;
Если ОсталосьРаспределить = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Если НЕ ОсталосьРаспределить = 0 Тогда
// Нужно искать целые места
// Условия задачи не требуют размещения номенклатуры рядом с одноименной
// Берем любое целое место
Для каждого МестоХранения Из тзОстатки Цикл
Если НЕ МестоХранения.Номенклатура = NULL Тогда
Продолжить;
КонецЕсли;
НоваяСтрока = Объект.МестаХранения.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, МестоХранения);
НоваяСтрока.Номенклатура = СтрокаТовары.Номенклатура;
Если ОсталосьРаспределить > СтрокаТовары.Номенклатура.КП Тогда
НоваяСтрока.Количество = СтрокаТовары.Номенклатура.КП;
Иначе
НоваяСтрока.Количество = ОсталосьРаспределить;
КонецЕсли;
ОсталосьРаспределить = ОсталосьРаспределить - НоваяСтрока.Количество;
МестоХранения.Номенклатура = НоваяСтрока.Номенклатура;
МестоХранения.Места = СтрокаТовары.Номенклатура.КП - НоваяСтрока.Количество;
Если ОсталосьРаспределить = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Строки из табличной части "Товары" выгружаем в таблицу значений и сворачиваем по Номенклатуре.
В таблицу значений тзОстатки помещаем перчень свободных мест, отсортированных в порядке увеличения места.
Далее для каждой номенклатуры подбираем свободное место. Если не удается заполнить существующие недозаполненные места, берем любое свободное место (можно и не любое, но тогда нужно подыскивать место рядом с одноименной номенклатурой, что не требовалось условиями задачи). В результате заполняется табличная часть "МестаХранения" документа. После выполнения процедуры заполнения, нужно выполнить функцию контроля корректности размещения товаров (приведена выше).
Обработка проведения документа:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// регистр ПозицииТовара Приход
Движения.ПозицииТовара.Записывать = Истина;
Для Каждого ТекСтрокаМестаХранения Из МестаХранения Цикл
Движение = Движения.ПозицииТовара.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаМестаХранения.Номенклатура;
Движение.SS = ТекСтрокаМестаХранения.SS;
Движение.NN = ТекСтрокаМестаХранения.NN;
Движение.LL = ТекСтрокаМестаХранения.LL;
Движение.Количество = ТекСтрокаМестаХранения.Количество;
КонецЦикла;
КонецПроцедуры
Отпуск товара
Далее создаем документ "ОтпускТовара". Можно скопировать предыдущий документ, потому как структура табличных частей идентична.
Функция контроля распределения товаров сходна с аналогичной из документа "ПриемТовара" с тем отличием, что необходимо проверить не наличие свободных мест, а наличие достаточного количества товара на выбранных местах:
// Проверить строки
//
&НаСервере
Функция МестаХраненияПроверитьСтроки(ПроверкаРаспределения = Истина, ВыводитьСообщения = Истина)
// Получить остатки по складу
тзОстатки = СкладСервер.ПолучитьОстатки(Объект.Дата);
тзМестаХранения = Объект.МестаХранения.Выгрузить();
тзМестаХранения.Свернуть("Номенклатура, SS, NN, LL", "Количество");
тзТовары = Объект.Товары.Выгрузить();
тзТовары.Свернуть("Номенклатура", "Количество");
// Проверка наличия остатков
Для каждого СтрокаМестаХранения Из тзМестаХранения Цикл
Отбор = Новый Структура();
Отбор.Вставить("SS", СтрокаМестаХранения.SS);
Отбор.Вставить("NN", СтрокаМестаХранения.NN);
Отбор.Вставить("LL", СтрокаМестаХранения.LL);
Строки = тзОстатки.НайтиСтроки(Отбор);
Если Строки.Количество() = 0 Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не найдено!", Новый Структура("Парам1, Парам2, Парам3", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли Строки.Количество() > 1 ИЛИ НЕ (Строки[0].Номенклатура = СтрокаМестаХранения.Номенклатура) Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не содержит нужную номенклатуру!", Новый Структура("Парам1, Парам2, Парам3", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли ?(Строки[0].Номенклатура = NULL,0,Строки[0].Количество) < СтрокаМестаХранения.Количество Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не достаточно количества! Доступно: Парам4", Новый Структура("Парам1, Парам2, Парам3, Парам4", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL, Строки[0].Количество), ВыводитьСообщения);
Возврат Ложь;
КонецЕсли;
СтрокаПроверки = тзТовары.Добавить();
СтрокаПроверки.Номенклатура = СтрокаМестаХранения.Номенклатура;
СтрокаПроверки.Количество = - СтрокаМестаХранения.Количество;
КонецЦикла;
// Проверить все ли позиции номенклатуры найдены
Если ПроверкаРаспределения Тогда
тзТовары.Свернуть("Номенклатура", "Количество");
Для каждого СтрокаПроверки Из тзТовары Цикл
Если НЕ СтрокаПроверки.Количество = 0 Тогда
СообщениеПользователю("Не найдено Парам1 в количестве: Парам2", Новый Структура("Парам1, Парам2", СтрокаПроверки.Номенклатура, СтрокаПроверки.Количество), ВыводитьСообщения);
Возврат Ложь
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат Истина;
КонецФункции
Функция "ПолучитьОстатки" из общего модуля "СкладСервер" возвращает таблицу значений, содержащую данные о размещении остатков на складе:
// Получить остатки по складу
//
Функция ПолучитьОстатки(Период) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПозицииТовараОстатки.Номенклатура КАК Номенклатура,
| ПозицииТовараОстатки.SS КАК SS,
| ПозицииТовараОстатки.NN КАК NN,
| ПозицииТовараОстатки.LL КАК LL,
| ПозицииТовараОстатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ПозицииТовара.Остатки(&Период, ) КАК ПозицииТовараОстатки
|ГДЕ
| ПозицииТовараОстатки.КоличествоОстаток > 0
|
|УПОРЯДОЧИТЬ ПО
| Количество";
Запрос.Параметры.Вставить("Период", Период);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции // ПолучитьОстатки()
Обработчик автоматического подбора позиций товаров выглядит так:
&НаСервере
Процедура ЗаполнитьНаСервере()
Объект.МестаХранения.Очистить();
тзТовары = Объект.Товары.Выгрузить();
тзТовары.Свернуть("Номенклатура", "Количество");
// Получить остатки по складу в порядке увеличения количества
тзОстатки = СкладСервер.ПолучитьОстатки(Объект.Дата);
Для каждого СтрокаТовары Из тзТовары Цикл
Если НЕ СтрокаТовары.Количество = 0 Тогда
ОсталосьРаспределить = СтрокаТовары.Количество;
// Смотрим где чего можно взять
Для каждого МестоХранения Из тзОстатки Цикл
Если НЕ МестоХранения.Номенклатура = СтрокаТовары.Номенклатура Тогда
Продолжить;
КонецЕсли;
НоваяСтрока = Объект.МестаХранения.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, МестоХранения);
Если ОсталосьРаспределить > МестоХранения.Количество Тогда
НоваяСтрока.Количество = МестоХранения.Количество;
Иначе
НоваяСтрока.Количество = ОсталосьРаспределить;
КонецЕсли;
ОсталосьРаспределить = ОсталосьРаспределить - НоваяСтрока.Количество;
Если ОсталосьРаспределить = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Подбор производится начиная с наименьших остатков, для освобождения большего количества мест. После подбора также производится контроль полноты и корректности подбора позиций товара.
Обработка проведения документа:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// регистр ПозицииТовара Приход
Движения.ПозицииТовара.Записывать = Истина;
Для Каждого ТекСтрокаМестаХранения Из МестаХранения Цикл
Движение = Движения.ПозицииТовара.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаМестаХранения.Номенклатура;
Движение.SS = ТекСтрокаМестаХранения.SS;
Движение.NN = ТекСтрокаМестаХранения.NN;
Движение.LL = ТекСтрокаМестаХранения.LL;
Движение.Количество = ТекСтрокаМестаХранения.Количество;
КонецЦикла;
КонецПроцедуры
В условии задачи требуется сформировать печатную форму документа с сортировкой по порядку обхода склада. Для этого можно воспользоваться конструктором печати, а для сортировки использовать реквизит "ПорядокОбхода" регистра сведений "МестаХранения".
Инвентаризация
Далее создаем документ "Инвентаризация". Для этого также можно скопировать документ поступления товара. Табличная часть здесь одна: "МестаХранения". Инвентаризация сводится к списанию остатков с указанных мест хранения и размещению других позиций товаров. Хотя, возможно, правильней не списывать остатки, а оставлять их как "потерянные" на одном измерении по номенклатуре. Алгоритм проверки корректности заполнения табличной части немного отличается от аналогичной в "Поступлении товара" и выглядит так:
// Проверить строки
//
&НаСервере
Функция МестаХраненияПроверитьСтроки(ПроверкаРаспределения = Истина, ВыводитьСообщения = Истина)
// Получить места по складу
тзМеста = СкладСервер.ПолучитьВсеМеста();
тзМестаХранения = Объект.МестаХранения.Выгрузить();
тзМестаХранения.Свернуть("Номенклатура, SS, NN, LL", "Количество");
// Проверка доступности мест хранения
Для каждого СтрокаМестаХранения Из тзМестаХранения Цикл
Отбор = Новый Структура();
Отбор.Вставить("SS", СтрокаМестаХранения.SS);
Отбор.Вставить("NN", СтрокаМестаХранения.NN);
Отбор.Вставить("LL", СтрокаМестаХранения.LL);
Строки = тзМеста.НайтиСтроки(Отбор);
Если Строки.Количество() = 0 Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не найдено!", Новый Структура("Парам1, Парам2, Парам3", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли НЕ (Строки[0].Номенклатура = СтрокаМестаХранения.Номенклатура ИЛИ Строки[0].Номенклатура = Справочники.Номенклатура.ПустаяСсылка()) Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 содержит различные номенклатуры!", Новый Структура("Парам1, Парам2, Парам3", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли СтрокаМестаХранения.Количество > СтрокаМестаХранения.Номенклатура.КП Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 переполнено! Доступно: Парам4", Новый Структура("Парам1, Парам2, Парам3, Парам4", СтрокаМестаХранения.SS, СтрокаМестаХранения.NN, СтрокаМестаХранения.LL, (?(Строки[0].Номенклатура = Справочники.Номенклатура.ПустаяСсылка(), СтрокаМестаХранения.Номенклатура.КП,Строки[0].НоменклатураКП))), ВыводитьСообщения);
Возврат Ложь;
КонецЕсли;
Строки[0].Номенклатура = СтрокаМестаХранения.Номенклатура;
КонецЦикла;
Возврат Истина;
КонецФункции
В обработке проведения сначала списываются существующие остатки, затем размещаются другие:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
тзОстатки = СкладСервер.ПолучитьОстатки(Дата);
тзМестаХранения = МестаХранения.Выгрузить();
тзМестаХранения.Свернуть("Номенклатура, SS, NN, LL", "Количество");
Движения.ПозицииТовара.Записывать = Истина;
Для Каждого ТекСтрокаМестаХранения Из тзМестаХранения Цикл
Отбор = Новый Структура();
Отбор.Вставить("SS", ТекСтрокаМестаХранения.SS);
Отбор.Вставить("NN", ТекСтрокаМестаХранения.NN);
Отбор.Вставить("LL", ТекСтрокаМестаХранения.LL);
Строки = тзОстатки.НайтиСтроки(Отбор);
Для каждого СтрокаРасход Из Строки Цикл
// регистр ПозицииТовара Расход
Движение = Движения.ПозицииТовара.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Дата;
Движение.Номенклатура = СтрокаРасход.Номенклатура;
Движение.SS = СтрокаРасход.SS;
Движение.NN = СтрокаРасход.NN;
Движение.LL = СтрокаРасход.LL;
Движение.Количество = СтрокаРасход.Количество;
КонецЦикла;
// регистр ПозицииТовара Приход
Движение = Движения.ПозицииТовара.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = ТекСтрокаМестаХранения.Номенклатура;
Движение.SS = ТекСтрокаМестаХранения.SS;
Движение.NN = ТекСтрокаМестаХранения.NN;
Движение.LL = ТекСтрокаМестаХранения.LL;
Движение.Количество = ТекСтрокаМестаХранения.Количество;
КонецЦикла;
КонецПроцедуры
Перемещение товара
По условиям задачи требуется реализовать процесс "Уплотнения склада", что является по сути перемещением товара с целью более оптимального его хранения. Создаем документ "ПеремещениеТовара". Табличные части у документа две: "Откуда" и "Куда" (колонки: Номенклатура, SS, NN, LL, Количество).
Функция проверки заполнения табличных частей должна проверять как наличие и правильность остатков, так и наличие свободных мест:
// Проверить строки
//
&НаСервере
Функция МестаХраненияПроверитьСтроки(ПроверкаРаспределения = Истина, ВыводитьСообщения = Истина)
// Получить остатки по складу
тзОстатки = СкладСервер.ПолучитьОстатки(Объект.Дата);
тзОткуда = Объект.Откуда.Выгрузить();
тзОткуда.Свернуть("Номенклатура, SS, NN, LL", "Количество");
тзТовары = Объект.Откуда.Выгрузить();
тзТовары.Свернуть("Номенклатура", "Количество");
// Проверка наличия остатков
Для каждого СтрокаОткуда Из тзОткуда Цикл
Отбор = Новый Структура();
Отбор.Вставить("SS", СтрокаОткуда.SS);
Отбор.Вставить("NN", СтрокаОткуда.NN);
Отбор.Вставить("LL", СтрокаОткуда.LL);
Строки = тзОстатки.НайтиСтроки(Отбор);
Если Строки.Количество() = 0 Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не найдено!", Новый Структура("Парам1, Парам2, Парам3", СтрокаОткуда.SS, СтрокаОткуда.NN, СтрокаОткуда.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли Строки.Количество() > 1 ИЛИ НЕ (Строки[0].Номенклатура = СтрокаОткуда.Номенклатура) Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не содержит нужную номенклатуру!", Новый Структура("Парам1, Парам2, Парам3", СтрокаОткуда.SS, СтрокаОткуда.NN, СтрокаОткуда.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли ?(Строки[0].Номенклатура = NULL,0,Строки[0].Количество) < СтрокаОткуда.Количество Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не достаточно количества! Доступно: Парам4", Новый Структура("Парам1, Парам2, Парам3, Парам4", СтрокаОткуда.SS, СтрокаОткуда.NN, СтрокаОткуда.LL, Строки[0].Количество), ВыводитьСообщения);
Возврат Ложь;
КонецЕсли;
КонецЦикла;
// Получить места по складу
тзМеста = СкладСервер.ПолучитьМеста(Объект.Дата);
тзКуда = Объект.Куда.Выгрузить();
тзКуда.Свернуть("Номенклатура, SS, NN, LL", "Количество");
// Проверка доступности мест хранения
Для каждого СтрокаКуда Из тзКуда Цикл
Отбор = Новый Структура();
Отбор.Вставить("SS", СтрокаКуда.SS);
Отбор.Вставить("NN", СтрокаКуда.NN);
Отбор.Вставить("LL", СтрокаКуда.LL);
Строки = тзМеста.НайтиСтроки(Отбор);
Если Строки.Количество() = 0 Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 не найдено!", Новый Структура("Парам1, Парам2, Парам3", СтрокаКуда.SS, СтрокаКуда.NN, СтрокаКуда.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли Строки.Количество() > 1 ИЛИ НЕ (Строки[0].Номенклатура = СтрокаКуда.Номенклатура ИЛИ Строки[0].Номенклатура = NULL) Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 содержит различные номенклатуры!", Новый Структура("Парам1, Парам2, Парам3", СтрокаКуда.SS, СтрокаКуда.NN, СтрокаКуда.LL), ВыводитьСообщения);
Возврат Ложь;
ИначеЕсли ?(Строки[0].Номенклатура = NULL,0,Строки[0].Количество) + СтрокаКуда.Количество > СтрокаКуда.Номенклатура.КП Тогда
СообщениеПользователю("Место хранения SS: Парам1 NN: Парам2 LL: Парам3 переполнено! Доступно: Парам4", Новый Структура("Парам1, Парам2, Парам3, Парам4", СтрокаКуда.SS, СтрокаКуда.NN, СтрокаКуда.LL, (?(Строки[0].Номенклатура = NULL, СтрокаКуда.Номенклатура.КП,Строки[0].НоменклатураКП) - ?(Строки[0].Номенклатура = NULL,0,Строки[0].Количество))), ВыводитьСообщения);
Возврат Ложь;
КонецЕсли;
СтрокаПроверки = тзТовары.Добавить();
СтрокаПроверки.Номенклатура = СтрокаКуда.Номенклатура;
СтрокаПроверки.Количество = - СтрокаКуда.Количество;
КонецЦикла;
// Проверить все ли позиции номенклатуры распределены
Если ПроверкаРаспределения Тогда
тзТовары.Свернуть("Номенклатура", "Количество");
Для каждого СтрокаПроверки Из тзТовары Цикл
Если НЕ СтрокаПроверки.Количество = 0 Тогда
СообщениеПользователю("Не размещено Парам1 в количестве: Парам2", Новый Структура("Парам1, Парам2", СтрокаПроверки.Номенклатура, СтрокаПроверки.Количество), ВыводитьСообщения);
Возврат Ложь
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат Истина;
КонецФункции
Процедура уплотнения склада выглядит так:
&НаСервере
Процедура ЗаполнитьНаСервере()
Объект.Куда.Очистить();
ПеремещатьТоЧтоВыбрано = НЕ Объект.Откуда.Количество() = 0;
Если ПеремещатьТоЧтоВыбрано Тогда
тзОстатки = Объект.Откуда.Выгрузить();
Иначе
// Получить остатки по складу в порядке увеличения количества
тзОстатки = СкладСервер.ПолучитьОстатки(Объект.Дата);
КонецЕсли;
// Получить места по складу в порядке увеличения места
тзМеста = СкладСервер.ПолучитьМеста(Объект.Дата);
СвободныхМестДо = тзМеста.НайтиСтроки(Новый Структура("Количество", NULL)).Количество();
// Выбираем что можно переместить
Для каждого СтрокаОткуда Из тзОстатки Цикл
Если (СтрокаОткуда.Количество > 0 И СтрокаОткуда.Количество < СтрокаОткуда.Номенклатура.КП) ИЛИ ПеремещатьТоЧтоВыбрано Тогда
ОсталосьРаспределить = СтрокаОткуда.Количество;
// Смотрим куда можно переместить
Для каждого СтрокаКуда Из тзМеста Цикл
Если НЕ СтрокаКуда.Номенклатура = СтрокаОткуда.Номенклатура Тогда
Продолжить;
КонецЕсли;
Если НЕ СтрокаКуда.Места > 0 Тогда
Продолжить;
КонецЕсли;
Если СтрокаКуда.NN = СтрокаОткуда.NN И СтрокаКуда.SS = СтрокаОткуда.SS И СтрокаКуда.LL = СтрокаОткуда.LL Тогда
Продолжить;
КонецЕсли;
НоваяСтрокаКуда = Объект.Куда.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрокаКуда, СтрокаКуда);
Если ОсталосьРаспределить > СтрокаКуда.Места Тогда
НоваяСтрокаКуда.Количество = СтрокаКуда.Места;
Иначе
НоваяСтрокаКуда.Количество = ОсталосьРаспределить;
КонецЕсли;
ОсталосьРаспределить = ОсталосьРаспределить - НоваяСтрокаКуда.Количество;
// Обновить строку свободных мест
СтрокаКуда.Места = СтрокаКуда.Места - НоваяСтрокаКуда.Количество;
Если НЕ ПеремещатьТоЧтоВыбрано Тогда
// Обновить строку остатков
СтрокаКудаОстатки = тзОстатки.НайтиСтроки(Новый Структура("SS, NN, LL", СтрокаКуда.SS, СтрокаКуда.NN, СтрокаКуда.LL))[0];
СтрокаКудаОстатки.Количество = СтрокаКудаОстатки.Количество - НоваяСтрокаКуда.Количество;
КонецЕсли;
// Обновить строку мест
СтрокаОткудаМеста = тзМеста.НайтиСтроки(Новый Структура("SS, NN, LL", СтрокаОткуда.SS, СтрокаОткуда.NN, СтрокаОткуда.LL))[0];
СтрокаОткудаМеста.Места = СтрокаОткудаМеста.Места + НоваяСтрокаКуда.Количество;
Если СтрокаОткудаМеста.Места = СтрокаОткудаМеста.Номенклатура.КП Тогда
СтрокаОткудаМеста.Места = NULL;
СтрокаОткудаМеста.Количество = NULL;
СтрокаОткудаМеста.Номенклатура = NULL;
КонецЕсли;
Если ОсталосьРаспределить = 0 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Если НЕ ПеремещатьТоЧтоВыбрано Тогда
// Заполнить ТЧ откуда
Если ОсталосьРаспределить < СтрокаОткуда.Количество Тогда
НоваяСтрокаОткуда = Объект.Откуда.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрокаОткуда, СтрокаОткуда);
НоваяСтрокаОткуда.Количество = СтрокаОткуда.Количество - ОсталосьРаспределить;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
СвободныхМестПосле = тзМеста.НайтиСтроки(Новый Структура("Количество", NULL)).Количество();
ЭтаФорма.Информация = "Свободных мест до: " + СвободныхМестДо + " после перемещения: " + СвободныхМестПосле;
КонецПроцедуры
Если табличная часть "Откуда" пустая, то производится поиск недозаполненных мест с целью их оптимального размещения. Если указаны конкретные позиции, то производится поиск места только под них (этого не требовалось по условиям задачи, но захотелось сделать).
Обработка проведения документа по аналогии с предыдущими документами.
Отчеты
Отчеты по остаткам товара и материальная ведомость ничего особенного собой не представляют. Для первого используется запрос к остаткам регистра накопления "ПозицииТовара", для материальной ведомости идет обращение к остаткам и оборотам этого же регистра.
Загруженность склада строится несколько сложнее. Используются три набора данных: один набор формирует перечень дат, на которые будет формироваться каждый "столбик" загруженности, второй набор - количество занятых мест, третий - общее количество мест. Устанавливаем связь между первым и вторым набором по параметру "Период", добавляем вычисляемое поле "Загруженность" и размещаем его на диаграмме.
Замечания проверяющего
1. Не оптимальный алгоритм приемки - все данные вытаскиваем из базы и потом с ними работаем, вместо того, чтобы получить только необходимую информацию. Например, нужны только ячейки, в которых есть остатки по товарам из документа и пустые ячейки.
2. Обращение к реквизитам справочника через точку в цикле, хотя до этого был запрос в котором все данные можно было по товару получить.
3.Алгоритмы проведения построены так, что есть возможно двум операторам произвести распределение а одну и ту же ячейку. Не используется блокировка в транзакции необходимого диапазона ресурсов.
4.Документ инвентаризация мог бы быть более эргономичным, например заполнить по остаткам учетной системы.
5.Структура регистров и справочников не оптимальная, при добавлении иной схемы адресации ячеек придется изменять 2 таблицы.
6.Не оптимальный алгоритм проведения уплотнения (в системе почему-то назван Перемещением). Опять получаем все данные из базы и потом в циклах пробегаем и ищем то, что нужно. Хотя нужно получить из базы только то, что необходимо уплотнять, т.е. где остаток меньше чем коэффициент паллетирования.
Выводы
Несмотря на отрицательный ответ, считаю, что задание выполнено успешно. Замечания касаются вопросов, явно условиями задачи не оговоренных. Обращение к реквизитам через точку в цикле это, конечно, плохо, но хорошо то, что легко исправить. В будущем следует сразу уточнять критерии оценки.
П.С. Чтобы код в статье выглядел нормально, использовал прежнюю разметку кода.