Загрузка данных
//++ Варнин А.А. - Задача №68771
// Пакетная версия подбора номенклатуры.
// Было: 1 запрос (полное сканирование Номенклатуры + виртуальная таблица остатков)
// на КАЖДУЮ строку КАЖДОГО чека.
// Стало: 1 запрос кандидатов на всю загрузку + 1 запрос остатков на каждую
// уникальную дату чека. Метаданные (счёт 41.01, индекс субконто "Склады")
// читаются один раз.
// Логика подбора, приоритеты, накопитель свободных остатков и разбиение строк
// не изменены.
#Область ПрограммныйИнтерфейс
Функция НоваяТаблицаСоответствияСкладов() Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("СкладЗПЧ");
Таблица.Колонки.Добавить("СкладБП");
Возврат Таблица;
КонецФункции
Функция ОбработатьЧекиВФоне(ПараметрыЗагрузки) Экспорт
Результат = Новый Структура;
Результат.Вставить("Лог", НоваяТаблицаЛога());
Результат.Вставить("СозданныеДокументы", Новый Массив);
Результат.Вставить("ОбработаноЧеков", 0);
Результат.Вставить("ЕстьОшибки", Ложь);
ДанныеДокументов = ПараметрыЗагрузки.Данные;
ЗаполнитьНоменклатуруЧеков(ДанныеДокументов, ПараметрыЗагрузки, Результат.Лог);
Результат.ОбработаноЧеков = ДанныеДокументов.Количество();
ДлительныеОперации.СообщитьПрогресс(100, НСтр("ru = 'Готово'"));
Для Каждого СтрокаЛога Из Результат.Лог Цикл
Если СтрокаЛога.ЭтоОшибка Тогда
Результат.ЕстьОшибки = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
#Область ОсновнойАлгоритм
Процедура ЗаполнитьНоменклатуруЧеков(ДанныеДокументов, ПараметрыЗагрузки, Лог)
СоответствиеСкладов = ПараметрыЗагрузки.СоответствиеСкладов;
Документы = ДокументыПоВозрастаниюДаты(ДанныеДокументов);
ДлительныеОперации.СообщитьПрогресс(5, НСтр("ru = 'Подготовка данных'"));
Подготовка = СобратьКлючиПоиска(Документы, СоответствиеСкладов);
// Метаданные читаем один раз на всю загрузку, а не на каждую строку.
ИндексСклада = ИндексСубконтоСклады();
СчетТоваров = ПланыСчетов.Хозрасчетный.НайтиПоКоду("41.01");
МенеджерВТ = Новый МенеджерВременныхТаблиц;
ДлительныеОперации.СообщитьПрогресс(15, НСтр("ru = 'Подбор кандидатов'"));
КэшКандидатов = ПостроитьКэшКандидатов(Подготовка, МенеджерВТ, Лог);
ДлительныеОперации.СообщитьПрогресс(35, НСтр("ru = 'Получение остатков'"));
КэшОстатков = ПостроитьКэшОстатков(Подготовка.Даты, МенеджерВТ, СчетТоваров, ИндексСклада, Лог);
МенеджерВТ.Закрыть();
СвободныеОстатки = Новый Соответствие;
КэшСкладов = Новый Соответствие;
ВсегоДокументов = Документы.Количество();
НомерДокумента = 0;
Для Каждого СтрокаДокумента Из Документы Цикл
НомерДокумента = НомерДокумента + 1;
ДлительныеОперации.СообщитьПрогресс(
40 + Окр(55 * НомерДокумента / ?(ВсегоДокументов = 0, 1, ВсегоДокументов)),
СтрШаблон(НСтр("ru = 'Подбор номенклатуры: чек %1 из %2'"),
НомерДокумента, ВсегоДокументов));
СкладБП = КэшСкладов.Получить(Строка(СтрокаДокумента.Склад));
Если СкладБП = Неопределено Тогда
СкладБП = СкладБППоСкладуЗПЧ(СтрокаДокумента.Склад, СоответствиеСкладов);
КэшСкладов.Вставить(Строка(СтрокаДокумента.Склад), СкладБП);
КонецЕсли;
ПрименятьСклад = (ИндексСклада > 0) И ЗначениеЗаполнено(СкладБП);
ДатаОстатков = КонецДня(Дата(СтрокаДокумента.ДатаДокумента));
ОстаткиНаДату = КэшОстатков.Получить(ДатаОстатков);
НовыеТовары = Новый Массив;
Для Каждого СтрокаТовара Из СтрокаДокумента.Товары Цикл
Требуется = СтрокаТовара.Количество;
Кандидаты = КандидатыНоменклатуры(СтрокаТовара, КэшКандидатов);
ЗаполнитьПриоритетыКандидатов(Кандидаты, СтрокаТовара);
ПодставитьСвободныеОстатки(Кандидаты, СвободныеОстатки, ОстаткиНаДату,
СкладБП, ПрименятьСклад);
Кандидаты.Сортировать("Приоритет Возр, Остаток Убыв");
Если Кандидаты.Количество() = 0 Тогда
ДобавитьВЛог(Лог, СтрокаТовара.Номенклатура,
СтрШаблон(НСтр("ru = 'Не найдена номенклатура БП (кат. номер %1)'"),
СтрокаТовара.КаталожныйНомерНоменклатуры), Истина);
ЧастьСтроки = КопияСтрокиТовара(СтрокаТовара, Требуется);
ЧастьСтроки.Вставить("НоменклатураБП", Справочники.Номенклатура.ПустаяСсылка());
НовыеТовары.Добавить(ЧастьСтроки);
Продолжить;
КонецЕсли;
ОбщийСвободный = Кандидаты.Итог("Остаток");
Если ОбщийСвободный < Требуется Тогда
ДобавитьВЛог(Лог, СтрокаТовара.Номенклатура,
СтрШаблон(НСтр("ru = 'Запрашиваемое количество %1 превышает свободный остаток %2'"),
Требуется, ОбщийСвободный), Истина);
КонецЕсли;
ПодходящийКандидат = Неопределено;
Для Каждого Кандидат Из Кандидаты Цикл
Если Кандидат.Остаток >= Требуется Тогда
ПодходящийКандидат = Кандидат;
Прервать;
КонецЕсли;
КонецЦикла;
Если ПодходящийКандидат <> Неопределено Тогда
СписатьОстаток(СвободныеОстатки, СкладБП, ПодходящийКандидат.Номенклатура, Требуется);
ЧастьСтроки = КопияСтрокиТовара(СтрокаТовара, Требуется);
ЧастьСтроки.Вставить("НоменклатураБП", ПодходящийКандидат.Номенклатура);
НовыеТовары.Добавить(ЧастьСтроки);
Продолжить;
КонецЕсли;
ОсталосьРаспределить = Требуется;
Для Каждого Кандидат Из Кандидаты Цикл
Если ОсталосьРаспределить <= 0 Тогда
Прервать;
КонецЕсли;
Если Кандидат.Остаток <= 0 Тогда
Продолжить;
КонецЕсли;
Взято = ?(Кандидат.Остаток < ОсталосьРаспределить, Кандидат.Остаток, ОсталосьРаспределить);
ЧастьСтроки = КопияСтрокиТовара(СтрокаТовара, Взято);
ЧастьСтроки.Вставить("НоменклатураБП", Кандидат.Номенклатура);
НовыеТовары.Добавить(ЧастьСтроки);
СписатьОстаток(СвободныеОстатки, СкладБП, Кандидат.Номенклатура, Взято);
ОсталосьРаспределить = ОсталосьРаспределить - Взято;
ДобавитьВЛог(Лог, СтрокаТовара.Номенклатура,
СтрШаблон(НСтр("ru = 'Строка разбита: %1 шт на карточку ""%2""'"),
Взято, Кандидат.Наименование), Ложь);
КонецЦикла;
Если ОсталосьРаспределить > 0 Тогда
ЧастьСтроки = КопияСтрокиТовара(СтрокаТовара, ОсталосьРаспределить);
ЧастьСтроки.Вставить("НоменклатураБП", Кандидаты[0].Номенклатура);
НовыеТовары.Добавить(ЧастьСтроки);
СписатьОстаток(СвободныеОстатки, СкладБП, Кандидаты[0].Номенклатура, ОсталосьРаспределить);
КонецЕсли;
КонецЦикла;
СтрокаДокумента.Вставить("Товары", НовыеТовары);
КонецЦикла;
КонецПроцедуры
#КонецОбласти
#Область ПодготовкаДанных
// Один проход по всем чекам: заполняет соответствие складов и собирает
// уникальные ключи поиска и уникальные даты остатков.
Функция СобратьКлючиПоиска(Документы, СоответствиеСкладов)
Артикулы = Новый Соответствие;
Наименования = Новый Соответствие;
Даты = Новый Соответствие;
Для Каждого СтрокаДокумента Из Документы Цикл
СоответствиеСкладовЧекиОбработка(СтрокаДокумента, СоответствиеСкладов);
Даты.Вставить(КонецДня(Дата(СтрокаДокумента.ДатаДокумента)), Истина);
Для Каждого СтрокаТовара Из СтрокаДокумента.Товары Цикл
Ключ = КлючПоиска(СтрокаТовара.КаталожныйНомерНоменклатуры);
Если НЕ ПустаяСтрока(Ключ) Тогда
Артикулы.Вставить(Ключ, Истина);
КонецЕсли;
Ключ = КлючПоиска(СтрокаТовара.Номенклатура);
Если НЕ ПустаяСтрока(Ключ) Тогда
Наименования.Вставить(Ключ, Истина);
КонецЕсли;
Если СтрокаТовара.Свойство("НоменклатураПоставщика")
И ЗначениеЗаполнено(СтрокаТовара.НоменклатураПоставщика) Тогда
Ключ = КлючПоиска(СтрокаТовара.НоменклатураПоставщика);
Если НЕ ПустаяСтрока(Ключ) Тогда
Наименования.Вставить(Ключ, Истина);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Результат = Новый Структура;
Результат.Вставить("ТаблицаАртикулов", ТаблицаКлючей(Артикулы, 100));
Результат.Вставить("ТаблицаНаименований", ТаблицаКлючей(Наименования, 250));
Результат.Вставить("Даты", МассивКлючей(Даты));
Возврат Результат;
КонецФункции
Функция ТаблицаКлючей(Ключи, ДлинаСтроки)
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Ключ",
Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(ДлинаСтроки)));
Для Каждого КлючИЗначение Из Ключи Цикл
Таблица.Добавить().Ключ = Лев(КлючИЗначение.Ключ, ДлинаСтроки);
КонецЦикла;
Возврат Таблица;
КонецФункции
Функция МассивКлючей(Соответствие)
Результат = Новый Массив;
Для Каждого КлючИЗначение Из Соответствие Цикл
Результат.Добавить(КлючИЗначение.Ключ);
КонецЦикла;
Возврат Результат;
КонецФункции
Функция КлючПоиска(Значение)
Возврат ВРег(СокрЛП(Строка(Значение)));
КонецФункции
#КонецОбласти
#Область Кэши
// Один запрос на всю загрузку.
// Вместо "Артикул = X ИЛИ Наименование = Y" (полное сканирование справочника)
// — соединение с временной таблицей ключей по каждому реквизиту отдельно,
// каждая ветка идёт по своему индексу.
// ВАЖНО: для реквизита "Артикул" справочника Номенклатура в конфигураторе
// должно быть установлено свойство Индексировать = Индексировать.
Функция ПостроитьКэшКандидатов(Подготовка, МенеджерВТ, Лог)
Кэш = Новый Соответствие;
ЕстьАртикулы = (Подготовка.ТаблицаАртикулов.Количество() > 0);
ЕстьНаименования = (Подготовка.ТаблицаНаименований.Количество() > 0);
Если НЕ ЕстьАртикулы И НЕ ЕстьНаименования Тогда
Возврат Кэш;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Ключ КАК Ключ
|ПОМЕСТИТЬ ВТАртикулы
|ИЗ
| &ТаблицаАртикулов КАК Т
|ИНДЕКСИРОВАТЬ ПО
| Ключ
|;
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Т.Ключ КАК Ключ
|ПОМЕСТИТЬ ВТНаименования
|ИЗ
| &ТаблицаНаименований КАК Т
|ИНДЕКСИРОВАТЬ ПО
| Ключ
|;
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| А.Ключ КАК Ключ,
| Ном.Ссылка КАК Номенклатура,
| Ном.Наименование КАК Наименование
|ПОМЕСТИТЬ ВТСвязи
|ИЗ
| ВТАртикулы КАК А
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
| ПО Ном.Артикул = А.Ключ
|ГДЕ
| НЕ Ном.ПометкаУдаления
| И НЕ Ном.ЭтоГруппа
|
|ОБЪЕДИНИТЬ
|
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| Н.Ключ,
| Ном.Ссылка,
| Ном.Наименование
|ИЗ
| ВТНаименования КАК Н
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
| ПО Ном.Наименование = Н.Ключ
|ГДЕ
| НЕ Ном.ПометкаУдаления
| И НЕ Ном.ЭтоГруппа
|;
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗЛИЧНЫЕ
| Св.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ ВТКандидаты
|ИЗ
| ВТСвязи КАК Св
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Св.Ключ КАК Ключ,
| Св.Номенклатура КАК Номенклатура,
| Св.Наименование КАК Наименование
|ИЗ
| ВТСвязи КАК Св";
Запрос.УстановитьПараметр("ТаблицаАртикулов", Подготовка.ТаблицаАртикулов);
Запрос.УстановитьПараметр("ТаблицаНаименований", Подготовка.ТаблицаНаименований);
Попытка
Выборка = Запрос.Выполнить().Выбрать();
Исключение
ДобавитьВЛог(Лог, "",
СтрШаблон(НСтр("ru = 'Ошибка запроса кандидатов: %1'"),
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())), Истина);
Возврат Кэш;
КонецПопытки;
Пока Выборка.Следующий() Цикл
Список = Кэш.Получить(Выборка.Ключ);
Если Список = Неопределено Тогда
Список = Новый Массив;
Кэш.Вставить(Выборка.Ключ, Список);
КонецЕсли;
Кандидат = Новый Структура("Номенклатура, Наименование",
Выборка.Номенклатура, Выборка.Наименование);
Список.Добавить(Кандидат);
КонецЦикла;
Возврат Кэш;
КонецФункции
// Один запрос остатков на каждую уникальную дату чека (обычно 1-2 на загрузку)
// вместо одного запроса на строку товара.
Функция ПостроитьКэшОстатков(Даты, МенеджерВТ, СчетТоваров, ИндексСклада, Лог)
Кэш = Новый Соответствие;
Если НЕ ЗначениеЗаполнено(СчетТоваров) Тогда
ДобавитьВЛог(Лог, "", НСтр("ru = 'Не найден счёт 41.01'"), Истина);
Возврат Кэш;
КонецЕсли;
ПолеСклада = ?(ИндексСклада > 0, СтрШаблон("Ост.Субконто%1 КАК Склад,", Строка(ИндексСклада)), "");
ГруппировкаСклада = ?(ИндексСклада > 0, СтрШаблон(",%1 Ост.Субконто%2", Символы.ПС, Строка(ИндексСклада)), "");
ТекстЗапроса = СтрЗаменить(
"ВЫБРАТЬ
| Ост.Субконто1 КАК Номенклатура,
| %ПолеСклада
| СУММА(ЕСТЬNULL(Ост.КоличествоОстатокДт, 0)) КАК Остаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(
| &ДатаОстатков,
| Счет = &Счет,
| ,
| Субконто1 В (ВЫБРАТЬ Т.Номенклатура ИЗ ВТКандидаты КАК Т)) КАК Ост
|
|СГРУППИРОВАТЬ ПО
| Ост.Субконто1%ГруппировкаСклада",
"%ПолеСклада", ПолеСклада);
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "%ГруппировкаСклада", ГруппировкаСклада);
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("Счет", СчетТоваров);
Для Каждого ДатаОстатков Из Даты Цикл
Запрос.УстановитьПараметр("ДатаОстатков", ДатаОстатков);
Попытка
Выборка = Запрос.Выполнить().Выбрать();
Исключение
ДобавитьВЛог(Лог, "",
СтрШаблон(НСтр("ru = 'Ошибка запроса остатков на %1: %2'"),
Формат(ДатаОстатков, "ДЛФ=DD"),
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке())), Истина);
Продолжить;
КонецПопытки;
ПоСкладу = Новый Соответствие;
Итого = Новый Соответствие;
Пока Выборка.Следующий() Цикл
КлючНоменклатуры = Строка(Выборка.Номенклатура.УникальныйИдентификатор());
Если ИндексСклада > 0 Тогда
Ключ = КлючОстатка(Выборка.Склад, Выборка.Номенклатура);
Текущий = ПоСкладу.Получить(Ключ);
ПоСкладу.Вставить(Ключ, ?(Текущий = Неопределено, 0, Текущий) + Выборка.Остаток);
КонецЕсли;
Текущий = Итого.Получить(КлючНоменклатуры);
Итого.Вставить(КлючНоменклатуры, ?(Текущий = Неопределено, 0, Текущий) + Выборка.Остаток);
КонецЦикла;
Кэш.Вставить(ДатаОстатков, Новый Структура("ПоСкладу, Итого", ПоСкладу, Итого));
КонецЦикла;
Возврат Кэш;
КонецФункции
#КонецОбласти
#Область ПодборНоменклатуры
// Кандидаты собираются из кэша по трём ключам строки, запросов к базе нет.
Функция КандидатыНоменклатуры(СтрокаТовара, КэшКандидатов)
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("Номенклатура");
Результат.Колонки.Добавить("Наименование");
Результат.Колонки.Добавить("Остаток", Новый ОписаниеТипов("Число"));
Результат.Колонки.Добавить("Приоритет", Новый ОписаниеТипов("Число"));
Добавленные = Новый Соответствие;
ДобавитьКандидатовПоКлючу(Результат, Добавленные, КэшКандидатов,
КлючПоиска(СтрокаТовара.КаталожныйНомерНоменклатуры));
ДобавитьКандидатовПоКлючу(Результат, Добавленные, КэшКандидатов,
КлючПоиска(СтрокаТовара.Номенклатура));
Если СтрокаТовара.Свойство("НоменклатураПоставщика")
И ЗначениеЗаполнено(СтрокаТовара.НоменклатураПоставщика) Тогда
ДобавитьКандидатовПоКлючу(Результат, Добавленные, КэшКандидатов,
КлючПоиска(СтрокаТовара.НоменклатураПоставщика));
КонецЕсли;
Возврат Результат;
КонецФункции
Процедура ДобавитьКандидатовПоКлючу(Результат, Добавленные, КэшКандидатов, Ключ)
Если ПустаяСтрока(Ключ) Тогда
Возврат;
КонецЕсли;
Список = КэшКандидатов.Получить(Ключ);
Если Список = Неопределено Тогда
Возврат;
КонецЕсли;
Для Каждого Кандидат Из Список Цикл
КлючКандидата = Строка(Кандидат.Номенклатура.УникальныйИдентификатор());
Если Добавленные.Получить(КлючКандидата) <> Неопределено Тогда
Продолжить;
КонецЕсли;
Добавленные.Вставить(КлючКандидата, Истина);
Стр = Результат.Добавить();
Стр.Номенклатура = Кандидат.Номенклатура;
Стр.Наименование = Кандидат.Наименование;
КонецЦикла;
КонецПроцедуры
Процедура ЗаполнитьПриоритетыКандидатов(Кандидаты, СтрокаТовара)
НаимЧека = ВРег(СокрЛП(Строка(СтрокаТовара.Номенклатура)));
НаимПоставщика = "";
Если СтрокаТовара.Свойство("НоменклатураПоставщика")
И ЗначениеЗаполнено(СтрокаТовара.НоменклатураПоставщика) Тогда
НаимПоставщика = ВРег(СокрЛП(Строка(СтрокаТовара.НоменклатураПоставщика)));
КонецЕсли;
Для Каждого Кандидат Из Кандидаты Цикл
НаимКандидата = ВРег(СокрЛП(Кандидат.Наименование));
Если НаимКандидата = НаимЧека Тогда
Кандидат.Приоритет = 0;
ИначеЕсли НЕ ПустаяСтрока(НаимПоставщика)
И (СтрНайти(НаимПоставщика, НаимКандидата) > 0
ИЛИ СтрНайти(НаимКандидата, НаимПоставщика) > 0) Тогда
Кандидат.Приоритет = 1;
Иначе
Кандидат.Приоритет = 2;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Первый раз остаток берётся из кэша (срез на дату чека),
// далее — только из накопителя списаний.
Процедура ПодставитьСвободныеОстатки(Кандидаты, СвободныеОстатки, ОстаткиНаДату, Склад, ПрименятьСклад)
Для Каждого Кандидат Из Кандидаты Цикл
Ключ = КлючОстатка(Склад, Кандидат.Номенклатура);
Сохранённый = СвободныеОстатки.Получить(Ключ);
Если Сохранённый <> Неопределено Тогда
Кандидат.Остаток = Сохранённый;
Продолжить;
КонецЕсли;
Кандидат.Остаток = ОстатокИзКэша(ОстаткиНаДату, Ключ, Кандидат.Номенклатура, ПрименятьСклад);
СвободныеОстатки.Вставить(Ключ, Кандидат.Остаток);
КонецЦикла;
КонецПроцедуры
Функция ОстатокИзКэша(ОстаткиНаДату, Ключ, Номенклатура, ПрименятьСклад)
Если ОстаткиНаДату = Неопределено Тогда
Возврат 0;
КонецЕсли;
Если ПрименятьСклад Тогда
Значение = ОстаткиНаДату.ПоСкладу.Получить(Ключ);
Иначе
Значение = ОстаткиНаДату.Итого.Получить(Строка(Номенклатура.УникальныйИдентификатор()));
КонецЕсли;
Возврат ?(Значение = Неопределено, 0, Значение);
КонецФункции
Процедура СписатьОстаток(СвободныеОстатки, Склад, Номенклатура, Количество)
Ключ = КлючОстатка(Склад, Номенклатура);
Текущий = СвободныеОстатки.Получить(Ключ);
Если Текущий = Неопределено Тогда
Текущий = 0;
КонецЕсли;
СвободныеОстатки.Вставить(Ключ, Текущий - Количество);
КонецПроцедуры
Функция КопияСтрокиТовара(СтрокаТовара, Количество)
Копия = Новый Структура;
Для Каждого КлючИЗначение Из СтрокаТовара Цикл
Копия.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;
ИсходноеКоличество = СтрокаТовара.Количество;
Копия.Вставить("Количество", Количество);
Если ИсходноеКоличество = 0 Тогда
Возврат Копия;
КонецЕсли;
Доля = Количество / ИсходноеКоличество;
Если Копия.Свойство("Сумма") Тогда
Копия.Вставить("Сумма", Окр(СтрокаТовара.Сумма * Доля, 2));
КонецЕсли;
Если Копия.Свойство("СуммаНДС") Тогда
Копия.Вставить("СуммаНДС", Окр(СтрокаТовара.СуммаНДС * Доля, 2));
КонецЕсли;
Возврат Копия;
КонецФункции
Функция ДокументыПоВозрастаниюДаты(ДанныеДокументов)
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Документ");
Таблица.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
Для Каждого СтрокаДокумента Из ДанныеДокументов Цикл
Стр = Таблица.Добавить();
Стр.Документ = СтрокаДокумента;
Стр.Дата = Дата(СтрокаДокумента.ДатаДокумента);
КонецЦикла;
Таблица.Сортировать("Дата Возр");
Результат = Новый Массив;
Для Каждого Стр Из Таблица Цикл
Результат.Добавить(Стр.Документ);
КонецЦикла;
Возврат Результат;
КонецФункции
Функция КлючОстатка(Склад, Номенклатура)
Возврат Строка(Склад.УникальныйИдентификатор()) + "|"
+ Строка(Номенклатура.УникальныйИдентификатор());
КонецФункции
#КонецОбласти
#Область Склады
Процедура СоответствиеСкладовЧекиОбработка(Строка, СоответствиеСкладовЧеки)
СкладЗПЧ = Строка.Склад;
ПараметрыОтбора = Новый Структура("СкладЗПЧ", СкладЗПЧ);
Если Не ЗначениеЗаполнено(СоответствиеСкладовЧеки.НайтиСтроки(ПараметрыОтбора)) Тогда
НоваяСтрока = СоответствиеСкладовЧеки.Добавить();
НоваяСтрока.СкладБП = ПодобратьСклад(СкладЗПЧ);
НоваяСтрока.Подразделение = ?(ЗначениеЗаполнено(НоваяСтрока.СкладБП),
ОбщегоНазначения.ЗначениеРеквизитаОбъекта(НоваяСтрока.СкладБП, "ПодразделениеОрганизации"),
Справочники.ПодразделенияОрганизаций.ПустаяСсылка());
НоваяСтрока.СкладЗПЧ = СкладЗПЧ;
КонецЕсли;
КонецПроцедуры
Функция ПодобратьСклад(СкладЗПЧ)
СкладВБазе = Справочники.Склады.НайтиПоНаименованию(СкладЗПЧ, Ложь);
Если ЗначениеЗаполнено(СкладВБазе) Тогда
Возврат СкладВБазе;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Склады.Ссылка КАК Ссылка
|ИЗ
| Справочник.Склады КАК Склады
|ГДЕ
| Склады.Наименование ПОДОБНО ""%"" + &Наименование + ""%"" ";
Запрос.УстановитьПараметр("Наименование", СкладЗПЧ);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
СкладВБазе = Выборка.Ссылка;
КонецЕсли;
Возврат СкладВБазе;
КонецФункции
Функция ИндексСубконтоСклады()
Счет = ПланыСчетов.Хозрасчетный.НайтиПоКоду("41.01");
Если НЕ ЗначениеЗаполнено(Счет) Тогда
Возврат 0;
КонецЕсли;
ВидСклады = ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Склады;
Индекс = 0;
Для Каждого СтрокаСубконто Из Счет.ВидыСубконто Цикл
Индекс = Индекс + 1;
Если СтрокаСубконто.ВидСубконто = ВидСклады Тогда
Возврат Индекс;
КонецЕсли;
КонецЦикла;
Возврат 0;
КонецФункции
Функция СкладБППоСкладуЗПЧ(СкладЗПЧ, СоответствиеСкладов)
Найденные = СоответствиеСкладов.НайтиСтроки(Новый Структура("СкладЗПЧ", СкладЗПЧ));
Если Найденные.Количество() > 0 Тогда
Возврат Найденные[0].СкладБП;
КонецЕсли;
Возврат Справочники.Склады.ПустаяСсылка();
КонецФункции
#КонецОбласти
#Область Лог
Функция НоваяТаблицаЛога()
Лог = Новый ТаблицаЗначений;
Лог.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("Строка"));
Лог.Колонки.Добавить("Текст", Новый ОписаниеТипов("Строка"));
Лог.Колонки.Добавить("ЭтоОшибка", Новый ОписаниеТипов("Булево"));
Возврат Лог;
КонецФункции
Процедура ДобавитьВЛог(Лог, Номенклатура = "", Текст = "", ЭтоОшибка = Ложь)
Стр = Лог.Добавить();
Стр.Номенклатура = Строка(Номенклатура);
Стр.Текст = Текст;
Стр.ЭтоОшибка = ЭтоОшибка;
КонецПроцедуры
#КонецОбласти
#КонецОбласти