Имя: Пароль:
1C
1С v8
Как бы половчее избежать запроса в цикле?
,
0 pessok
 
17.12.14
10:58
Коллеги, приветствую
УТ 10.3, несколько допиленная под нужды. В данном случае говорим о розничной продаже в разрезе заказов. Дополнительно сделан РН ТоварыНаБорту, соответственно кладовщик при отгрузке "отправляет" в машину товары, и уже именно из этих товаров формируются документы продажи.

Есть обработочка, которая по списку заказов формирует ЧекиККМ, а потом закрытие заказов, чтобы подчистить хвосты.
Ранее списание происходило с того склада, который был указан кладовщиком при отправке на борт. Теперь нужно реализовать нечто типа метода списания ФИФО в разрезе организаций, чтобы ЧекККМ создавался на ту организацию, где сейчас есть остатки (т.е. на один заказ может быть N чеков от разных организаций, не спрашивайте зачем, это "узбекский учет" (: ) Притом надо учесть, что созданные ранее чеки тоже виртуально списывают партии (хотя на самом деле спишет уже только ОРП). Чеки создаются группой в одной транзакции, соответственно для каждого конкретного заказа нужно проверить остатки с учетом уже созданных ранее в этой транзакции чеков. Я представляю себе только запрос в цикле (при обходе заказов формируем запрос остатков), а как сделать красивее?
1 gerthog
 
17.12.14
11:06
(0) Блин, два раза прочитал и ничего не понял(
2 spiller26
 
17.12.14
11:07
(1) та же ботва.
Кусок кода дайте.
3 pessok
 
17.12.14
11:10
щас, момент
4 H A D G E H O G s
 
17.12.14
11:14
(0) Бред какой-то. Почему не РТУ + ПКО?
5 H A D G E H O G s
 
17.12.14
11:15
(0) Заказ - это заказ покупателя?

Как один заказ покупателя может быть на несколько организаций?
6 pessok
 
17.12.14
11:17
(4) потому что хотят чеки. см. "узбекский учет". Продажа обезличена, но все равно ведется в разрезе заказов

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

Код, пока не дописан, т.к. я не уверен, что ход мыслей правильный. Пока писал все эти TODO уже, кажись, придумал относительно нормальное решение

Процедура СформироватьЧеки(МассивЗаказов) Экспорт
    
    Запрос = Новый Запрос(
    
    "ВЫБРАТЬ
    |    Заказы.Заказ КАК Заказ,
    |    Заказы.ДатаОтгрузки,
    |    Заказы.КассаПродажи,
    |    Заказы.Номенклатура,
    |    СУММА(Заказы.Количество) КАК Количество,
    |    Заказы.ЕдиницаИзмерения,
    |    Заказы.Цена КАК Цена,
    |    СУММА(Заказы.Сумма) КАК Сумма,
    |    СУММА(Заказы.СуммаНДС) КАК СуммаНДС,
    |    Заказы.Водитель
    |ПОМЕСТИТЬ Заказы
    |ИЗ
    |    (ВЫБРАТЬ
    |        ЗаказыПокупателей.ЗаказПокупателя.АдресТочки.КассаПродажи КАК КассаПродажи,
    |        ЗаказыПокупателей.ЗаказПокупателя КАК Заказ,
    |        ЗаказыПокупателей.ЗаказПокупателя.ДатаОтгрузки КАК ДатаОтгрузки,
    |        ТоварыНаБортуОстатки.Водитель КАК Водитель,
    |        ЗаказыПокупателей.Номенклатура КАК Номенклатура,
    |        ЗаказыПокупателей.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    |        ЗаказыПокупателей.Цена КАК Цена,
    |        ТоварыНаБортуОстатки.КоличествоОстаток КАК Количество,
    |        ТоварыНаБортуОстатки.КоличествоОстаток * ЗаказыПокупателей.Цена КАК Сумма,
    |        (ТоварыНаБортуОстатки.КоличествоОстаток * ЗаказыПокупателей.Цена / 1.18 - ТоварыНаБортуОстатки.КоличествоОстаток * ЗаказыПокупателей.Цена) * -1 КАК СуммаНДС
    |    ИЗ
    |        РегистрНакопления.ЗаказыПокупателей.Обороты(
    |                ,
    |                ,
    |                ,
    |                ЗаказПокупателя В (&Заказы)
    |                    И ДоговорКонтрагента.Владелец = ЗНАЧЕНИЕ(Справочник.Контрагенты.ФизЛицо)) КАК ЗаказыПокупателей
    |            ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаБорту.Остатки(, ЗаказПокупателя В (&Заказы)) КАК ТоварыНаБортуОстатки
    |            ПО ЗаказыПокупателей.ЗаказПокупателя = ТоварыНаБортуОстатки.ЗаказПокупателя
    |                И ЗаказыПокупателей.Номенклатура = ТоварыНаБортуОстатки.Номенклатура
    |    ГДЕ
    |        ТоварыНаБортуОстатки.КоличествоОстаток > 0) КАК Заказы
    |
    |СГРУППИРОВАТЬ ПО
    |    Заказы.Номенклатура,
    |    Заказы.ЕдиницаИзмерения,
    |    Заказы.Заказ,
    |    Заказы.Цена,
    |    Заказы.Водитель,
    |    Заказы.КассаПродажи,
    |    Заказы.ДатаОтгрузки
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ЧекККМ.Ссылка КАК ЧекККМ,
    |    ЧекККМ.ЗаказПокупателя,
    |    ЧекККМ.Организация
    |ПОМЕСТИТЬ ЧекиККМ
    |ИЗ
    |    Документ.ЧекККМ КАК ЧекККМ
    |ГДЕ
    |    НЕ ЧекККМ.ПометкаУдаления
    |
    |СГРУППИРОВАТЬ ПО
    |    ЧекККМ.Ссылка,
    |    ЧекККМ.ЗаказПокупателя,
    |    ЧекККМ.Организация
    |
    |ОБЪЕДИНИТЬ ВСЕ
    |
    |ВЫБРАТЬ
    |    ОтчетОРозничныхПродажахТовары.Ссылка,
    |    ОтчетОРозничныхПродажахТовары.ЗаказПокупателя,
    |    ОтчетОРозничныхПродажахТовары.Ссылка.Организация
    |ИЗ
    |    Документ.ОтчетОРозничныхПродажах.Товары КАК ОтчетОРозничныхПродажахТовары
    |ГДЕ
    |    НЕ ОтчетОРозничныхПродажахТовары.Ссылка.ПометкаУдаления
    |
    |СГРУППИРОВАТЬ ПО
    |    ОтчетОРозничныхПродажахТовары.Ссылка,
    |    ОтчетОРозничныхПродажахТовары.ЗаказПокупателя,
    |    ОтчетОРозничныхПродажахТовары.Ссылка.Организация
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    Заказы.Заказ КАК ЗаказПокупателя,
    |    Заказы.ДатаОтгрузки КАК Дата,
    |    Заказы.Водитель КАК Водитель,
    |    ЧекиККМ.ЧекККМ КАК ЧекККМ,
    |    Заказы.КассаПродажи,
    |    Заказы.Номенклатура,
    |    Заказы.Количество КАК Количество,
    |    Заказы.ЕдиницаИзмерения,
    |    Заказы.Цена,
    |    Заказы.Сумма КАК Сумма,
    |    Заказы.СуммаНДС КАК СуммаНДС
    |ИЗ
    |    Заказы КАК Заказы
    |        ЛЕВОЕ СОЕДИНЕНИЕ ЧекиККМ КАК ЧекиККМ
    |        ПО Заказы.Заказ = ЧекиККМ.ЗаказПокупателя
    |ИТОГИ
    |    СУММА(Количество),
    |    СУММА(Сумма),
    |    СУММА(СуммаНДС)
    |ПО
    |    ЗаказПокупателя");
    
    
    Запрос.УстановитьПараметр("Заказы", МассивЗаказов);
        
    ВыборкаЗаказ = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    Пока ВыборкаЗаказ.Следующий() Цикл
        
        МассивЗаказов = Новый Массив();
        МассивЗаказов.Добавить(ВыборкаЗаказ.Заказ);
        
        ТаблицаОстатков = СформироватьТаблицуОстатковПартийПоЗаказам(МассивЗаказов); // получение остатков для заказа с учетом чеков
        
        //TODO сделать обход остатков по товарам, сформировать таблицу организаций, складов, товаров под спиание
        //пройтись циклом по полученной таблице и создать документы
        Документ = Документы.ЧекККМ.СоздатьДокумент();
        ЗаполнениеДокументов.ЗаполнитьШапкуДокумента(Документ, ПараметрыСеанса.ТекущийПользователь);                        
        
        ЗаполнитьЗначенияСвойств(Документ, ВыборкаЗаказ);
        Документ.Организация = Справочники.Организации.НайтиПоКоду("Р00000002");  //TODO заполнение должно идти по таблице, сформированной для остатков
        Документ.Комментарий = "Создан на основании "+ВыборкаЗаказ.ЗаказПокупателя+" по складу "; //TODO заполнение должно идти по таблице, сформированной для остатков            
        
        ВыборкаДетали = ВыборкаЧекККМ.Выбрать();
        
        Пока ВыборкаДетали.Следующий() Цикл //TODO список товаров и количество уже должно заполняться по таблице остатков
            Товар = Документ.Товары.Добавить();
            ЗаполнитьЗначенияСвойств(Товар, ВыборкаДетали);
            Товар.Коэффициент = Товар.ЕдиницаИзмерения.Коэффициент;                
        КонецЦикла;
        
        Документ.СуммаДокумента = Документ.Товары.Итог("Сумма");
        
        НСтр = Документ.Оплата.Добавить();
        НСтр.ВидОплаты = Справочники.ВидыОплатЧекаККМ.Наличные;
        НСтр.Сумма = Документ.СуммаДокумента;
        Попытка
            Документ.Записать(РежимЗаписиДокумента.Проведение);
            Сообщить("Записан "+Документ.Ссылка);
        Исключение
            Сообщить("Ошибка "+ОписаниеОшибки());
        КонецПопытки;
            
    КонецЦикла;
    
КонецПроцедуры



Функция СформироватьТаблицуОстатковПартийПоЗаказам(МассивЗаказов) Экспорт
    
    //формируем массив номенклатуры    и дат
    ТаблицаНоменклатураЗаказ = Новый ТаблицаЗначений;
    ТаблицаНоменклатураЗаказ.Колонки.Добавить("ЗаказПокупателя",    Новый ОписаниеТипов("ДокументСсылка.ЗаказПокупателя"));
    ТаблицаНоменклатураЗаказ.Колонки.Добавить("Номенклатура",     Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
    ТаблицаНоменклатураЗаказ.Колонки.Добавить("ДатаОтгрузки", Новый ОписаниеТипов("Дата"));
    ТаблицаНоменклатураЗаказ.Колонки.Добавить("АдресТочки", Новый ОписаниеТипов("СправочникСсылка.ТорговыеТочки"));
    
    СписокДат = Новый ТаблицаЗначений;
    СписокДат.Колонки.Добавить("ДатаОтгрузки", Новый ОписаниеТипов("Дата"));
    
    Для Каждого ЗаказПокупателя Из МассивЗаказов Цикл
        ЗаполнитьЗначенияСвойств(СписокДат.Добавить(), ЗаказПокупателя);
        Для Каждого ТекСтр Из ЗаказПокупателя.Товары Цикл
            НовСтр = ТаблицаНоменклатураЗаказ.Добавить();
            НовСтр.ЗаказПокупателя = ЗаказПокупателя;
            НовСтр.Номенклатура = ТекСтр.Номенклатура;
            ЗаполнитьЗначенияСвойств(НовСтр, ЗаказПокупателя);
        КонецЦикла;
    КонецЦикла;
    СписокНоменклатуры = ОбщегоНазначения.УдалитьПовторяющиесяЭлементыМассива(ТаблицаНоменклатураЗаказ.ВыгрузитьКолонку("Номенклатура"));
    СписокДат.Свернуть("ДатаОтгрузки");
    СписокДат.Сортировать("ДатаОтгрузки Возр");
    
    //получаем остатки
    
    Запрос = Новый Запрос();
    Запрос.Текст =
    "ВЫБРАТЬ
    |    ДатыОтгрузки.ДатаОтгрузки КАК Период
    |ПОМЕСТИТЬ ВТДатыОтгрузки
    |ИЗ
    |    &ДатыОтгрузки КАК ДатыОтгрузки
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ЗаказыНоменклатура.ЗаказПокупателя,
    |    ЗаказыНоменклатура.ДатаОтгрузки,
    |    ЗаказыНоменклатура.Номенклатура,
    |    ЗаказыНоменклатура.АдресТочки
    |ПОМЕСТИТЬ ВТЗаказыНоменклатура
    |ИЗ
    |    &ЗаказыНоменклатура КАК ЗаказыНоменклатура
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗЛИЧНЫЕ
    |    ТорговыеТочкиДоступныеСклады.Склад
    |ПОМЕСТИТЬ ВТДоступныеСклады
    |ИЗ
    |    Справочник.ТорговыеТочки.ДоступныеСклады КАК ТорговыеТочкиДоступныеСклады
    |ГДЕ
    |    ТорговыеТочкиДоступныеСклады.Ссылка В
    |            (ВЫБРАТЬ
    |                ВТЗаказыНоменклатура.АдресТочки
    |            ИЗ
    |                ВТЗаказыНоменклатура)
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВложенныйЗапросОстатков.Период,
    |    ВложенныйЗапросОстатков.Организация,
    |    ВложенныйЗапросОстатков.Склад,
    |    ВложенныйЗапросОстатков.Номенклатура,
    |    ВложенныйЗапросОстатков.КоличествоКонечныйОстаток
    |ИЗ
    |    (ВЫБРАТЬ
    |        ВТДатыОтгрузки.Период КАК Период,
    |        ОстаткиПартий.Организация КАК Организация,
    |        ОстаткиПартий.Склад КАК Склад,
    |        ОстаткиПартий.Номенклатура КАК Номенклатура,
    |        ОстаткиПартий.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток
    |    ИЗ
    |        ВТДатыОтгрузки КАК ВТДатыОтгрузки
    |            ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
    |                Оборот1.Дата КАК ДатаС,
    |                МИНИМУМ(ВЫБОР
    |                        КОГДА Оборот2.Дата ЕСТЬ NULL
    |                            ТОГДА КОНЕЦПЕРИОДА(&КонецПериода, ДЕНЬ)
    |                        ИНАЧЕ КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(Оборот2.Дата, ДЕНЬ, -1), ДЕНЬ)
    |                    КОНЕЦ) КАК ДатаПо,
    |                Оборот1.Организация КАК Организация,
    |                Оборот1.Склад КАК Склад,
    |                Оборот1.Номенклатура КАК Номенклатура,
    |                Оборот1.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток
    |            ИЗ
    |                (ВЫБРАТЬ
    |                    ПартииТоваровНаСкладахОстаткиИОбороты.Период КАК Дата,
    |                    ПартииТоваровНаСкладахОстаткиИОбороты.Регистратор.Организация КАК Организация,
    |                    ПартииТоваровНаСкладахОстаткиИОбороты.Склад КАК Склад,
    |                    ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура,
    |                    ПартииТоваровНаСкладахОстаткиИОбороты.КоличествоКонечныйОстаток КАК КоличествоКонечныйОстаток
    |                ИЗ
    |                    РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(
    |                            &НачалоПериода,
    |                            &КонецПериода,
    |                            Запись,
    |                            ,
    |                            Номенклатура В (&СписокНоменклатуры)
    |                                И Склад В
    |                                    (ВЫБРАТЬ
    |                                        ВТДоступныеСклады.Склад
    |                                    ИЗ
    |                                        ВТДоступныеСклады)) КАК ПартииТоваровНаСкладахОстаткиИОбороты) КАК Оборот1
    |                    ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
    |                        ПартииТоваровНаСкладахОстаткиИОбороты.Период КАК Дата,
    |                        ПартииТоваровНаСкладахОстаткиИОбороты.Регистратор.Организация КАК Организация,
    |                        ПартииТоваровНаСкладахОстаткиИОбороты.Склад КАК Склад,
    |                        ПартииТоваровНаСкладахОстаткиИОбороты.Номенклатура КАК Номенклатура
    |                    ИЗ
    |                        РегистрНакопления.ПартииТоваровНаСкладах.ОстаткиИОбороты(
    |                                &НачалоПериода,
    |                                &КонецПериода,
    |                                Запись,
    |                                ,
    |                                Номенклатура В (&СписокНоменклатуры)
    |                                    И Склад В
    |                                        (ВЫБРАТЬ
    |                                            ВТДоступныеСклады.Склад
    |                                        ИЗ
    |                                            ВТДоступныеСклады)) КАК ПартииТоваровНаСкладахОстаткиИОбороты) КАК Оборот2
    |                    ПО Оборот1.Дата < Оборот2.Дата
    |                        И Оборот1.Номенклатура = Оборот2.Номенклатура
    |                        И Оборот1.Склад = Оборот2.Склад
    |                        И Оборот1.Организация = Оборот2.Организация
    |            
    |            СГРУППИРОВАТЬ ПО
    |                Оборот1.Дата,
    |                Оборот1.Номенклатура,
    |                Оборот1.КоличествоКонечныйОстаток,
    |                Оборот1.Склад,
    |                Оборот1.Организация) КАК ОстаткиПартий
    |            ПО (ВТДатыОтгрузки.Период МЕЖДУ НАЧАЛОПЕРИОДА(ОстаткиПартий.ДатаС, ДЕНЬ) И КОНЕЦПЕРИОДА(ОстаткиПартий.ДатаПо, ДЕНЬ))) КАК ВложенныйЗапросОстатков
    |        ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
    |            ТоварыНаСкладахОбороты.Регистратор.Организация КАК Организация,
    |            ТоварыНаСкладахОбороты.Номенклатура КАК Номенклатура,
    |            СУММА(ТоварыНаСкладахОбороты.КоличествоРасход) КАК КоличествоСписанийПоЧекам
    |        ИЗ
    |            РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, Запись, ) КАК ТоварыНаСкладахОбороты
    |        ГДЕ
    |            ТоварыНаСкладахОбороты.Регистратор ССЫЛКА Документ.ЧекККМ
    |        
    |        СГРУППИРОВАТЬ ПО
    |            ТоварыНаСкладахОбороты.Номенклатура,
    |            ТоварыНаСкладахОбороты.Регистратор.Организация) КАК СписанныеЧекамиТовары
    |        ПО ВложенныйЗапросОстатков.Организация = СписанныеЧекамиТовары.Организация
    |            И ВложенныйЗапросОстатков.Номенклатура = СписанныеЧекамиТовары.Номенклатура
    |АВТОУПОРЯДОЧИВАНИЕ";
    
    Запрос.УстановитьПараметр("ДатыОтгрузки", СписокДат);
    Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);
    Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(СписокДат[0].ДатаОтгрузки));
    Запрос.УстановитьПараметр("КонецПериода", КонецДня(СписокДат[СписокДат.Количество()-1].ДатаОтгрузки));
    Запрос.УстановитьПараметр("ЗаказыНоменклатура", ТаблицаНоменклатураЗаказ);
    
    Возврат Запрос.Выполнить().Выгрузить();
    
КонецФункции
7 batman69
 
17.12.14
11:21
(6) мля..
8 pessok
 
17.12.14
11:21
+(6) более верное, на мой взгляд, решение появилось:
сначала сформировать полную таблицу остатков, а потом уже непосредственно в ней при создании чеков уменьшать количество остатка по организации. И для этой таблице при обходе искать соответствия
9 H A D G E H O G s
 
17.12.14
11:21
(6) Удачи вам, мистер Горски.
10 pessok
 
17.12.14
11:21
(7) конкретнее, пожалуйста ;)
11 pessok
 
17.12.14
11:22
(9) да кто спорит, что это жутота, но делать надо
12 spiller26
 
17.12.14
11:24
(6) ВыборкаЗаказ = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам)
Зачем группировка??? Зачем в запросе Итоги?
13 Garykom
 
гуру
17.12.14
11:24
Ух ты "рюкзачки"
14 pessok
 
17.12.14
11:26
(12) это еще старый вариант просто, когда для одного заказа четко создавался один чек по организации заказа. Заполнять чеки удобнее по группировкам. Итоги не нужны, согласен
15 Garykom
 
гуру
17.12.14
11:26
Кстати запросы считают что то (когда + и -) медленно, они тока выборку быстро делают данных (по индексам)

Так что самое быстрое и без запросов в цикле, это запросом строим таблицу(ы) и дальше по ней(ним) работаем
16 pessok
 
17.12.14
11:28
(15) ну вот я так и подумал. в (8) описал
17 spiller26
 
17.12.14
11:32
Можно сделать все в одном запросе, но долго
"МассивЗаказов" загнать в ВТ_МассивЗаказов
Функцию СформироватьТаблицуОстатковПартийПоЗаказам(МассивЗаказов) переработать в запрос
18 pessok
 
17.12.14
11:35
(17) не можно, к сожалению. мне нужно проверять чеки, которые уже созданы в этой транзакции. Т.е:
В массиве заказов два заказа. в каждом "Номенклатура А" - 1 шт
У нас есть по одной партии этого товара на каждой из двух организаций.

Соответственно после того, как создался первый чек остатки уже изменились, второй чек должен создаваться с учетом этого
19 pessok
 
17.12.14
11:36
(18) посему оптимальное, на мой взгляд, сразу создать таблицу остатков и уже при создании чеков убирать из нее "списанную" чеком номенклатуру
20 Михаил Козлов
 
17.12.14
11:48
(19)+. Как модуле партионного учета.
21 mdocs
 
17.12.14
11:56
(0) набор умных слов. Сначала надо написать работающий код, потом думать об оптимизации. Если можете сразу все продумать оптимально, то это отлично. Если нет - то пишите хоть как нибудь но чтоб на выходе были верные данные.
22 pessok
 
17.12.14
12:02
(21) вот это как раз таки плохой подход. надо сразу писать нормально. потому что в противной случае этот копрокод так и останется навсегда