Поиск дублей в регистре накопления
1. Что? Где? Когда?
"Дубли в регистре накопления" звучит достаточно бессмысленно.
Надо определиться с этим понятием, а сделать это лучше всего на примере.
Есть РН "Остатки", имеющий для пользователя следующую структуру:
Таким образом, дубли – это запись (или набор записей), отличающиеся только периодом и, может быть, регистратором. В нашем случае набор записей от 17.06.2015 8:24:00 Док2 дублирует набор записей от 16.06.2015 8:03:57 Док1. Маловероятно, но дублей может быть и больше 2.
Собираем статистику по продажам / приходам / расходам / возвратам / перемещениям с торговых точек ежедневно (плюс / минус). Если какой-то приход будет задвоен, то и остатки будут не очень правильные, а для анализа и прогноза это играет важную роль. Заценил масштаб трагедии? (УП)
Записи в РН генерируются при загрузке данных из базы SQL, дублировать такой большой объем в документах нет необходимости. Но нельзя так просто взять и без регистратора сделать запись в РН, Настя! Поэтому мы используем Док. Но раз это аж целый документ, почему бы не хранить более глобальную информацию, такую, как Операция / номер (если не ленивый оператор) / Время. Таким образом, у нас в Док появляется ТЧ "Накладные за смену".
№ | Не загружать | Операция | Номер накладной | Время |
---|---|---|---|---|
1 | Нет | Приход | ФРВЧ0001515 | 19.07.2015 20:29:13 |
2 | Нет | Приход | 1516 чуть-чуть ленивый оператор | 20.07.2015 2:33:46 |
3 | Нет | Приход | ооочень ленивый оператор | 20.07.2015 2:34:06 |
4 | Нет | Перемещение | ФРВЧ0001510 | 20.07.2015 2:34:24 |
5 | Да | Приход | ФРВЧ0001511 | 20.07.2015 8:30:20 |
Да, искать дубли продаж мы не будем и чеки не храним. Мы ж люди. Кто будет есть такой же бутерброд, как сосед по столику "Coca-Cola"? Нам важны такие операции, как приход / расход / перемещения, которые, в свою очередь, тоже могут делиться. Дубль – не обязательно дубль (Евклид). Решение о дубль / не дубль принимает в результате оператор, поэтому не делаем проверку на этапе загрузки.
Хотя… в любом бы случае не делали проверку на этапе загрузки.
Задачу, как смогла, описала.
2. От слов к делу
Блок-схема*:
*Подобие блок-схемы
Основные принципы и решения:
- Сравниваем набор с каждым последующим набором, пока не нашли дубль. При этом, если дублей почему-то больше 2, то мы третий из них найдем как дубль второго.
- "Нужно сравнивать" необходимо мне, потому что конкретно у меня дубли можно исключить, ориентируясь на номера накладных. Если они есть, конечно.
- Итоги именно по времени. По номеру, конечно, более универсально, но его отсутствие влечет за собой большую выборку в один набор, что не соответствует действительности.
3. Не придумала название
Непосредственно к 1С.
Запросом формируем наборы.
‘ВЫБРАТЬ РАЗЛИЧНЫЕ
| Док.Ссылка.ТТ КАК ТТ,
| Остатки.Операция КАК Операция,
| Док.Время КАК Время,
| ВЫБОР
| КОГДА Док.Номер = ‘‘‘‘
| ТОГДА Док.Время
| ИНАЧЕ Док.Номер
| КОНЕЦ КАК НомерДубль,
| Док.Ссылка КАК ДокДубль,
| Остатки.Номенклатура,
| Остатки.Количество
|ИЗ
| Документ.Док.Накладные КАК Док
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Остатки КАК Остатки
| ПО Док.Ссылка = Остатки.Регистратор
| И Док.Время = Остатки.Период
|ГДЕ
| Док.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И НЕ Док.НеЗагружать
| И Док.Ссылка.Проведен
|
|УПОРЯДОЧИТЬ ПО
| Дубль
|ИТОГИ ПО
| ТТ,
| Операция,
| Время,
| НомерДубль
|;’
Пристегнули ремни и поехали по циклам:
Результат = Запрос.ВыполнитьПакет();
ВыборкаТорговаяТочка = Результат[0].Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаТорговаяТочка.Следующий() Цикл
ВыборкаВидОперации = ВыборкаТорговаяТочка.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаВидОперации.Следующий() Цикл
ВыборкаВремяНакладной = ВыборкаВидОперации.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаВремяНакладной.Следующий() Цикл
ВыборкаНакладная = ВыборкаВремяНакладной.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНакладная.Следующий() Цикл
//ЗДЕСЬ МЫ СОХРАНИМ ПАРУ ДУБЛЕЙ, ЕСЛИ НАЙДЕМ
Дубль = новый Структура;
Дубль.Вставить(‘ТорговаяТочка’, ВыборкаВидОперации.ТТ);
Дубль.Вставить(‘ВидОперации’, ВыборкаВидОперации.Операция);
Дубль.Вставить(‘ТекНомер’, ); //Здесь сохраним данные
Дубль.Вставить(‘ДокТек,); //текущего набора
Дубль.Вставить(‘НомерДубль ‘,); //здесь сохраним данные
Дубль.Вставить(‘ДокДубль’,); //дублирующего набора
//А ЗДЕСЬ СОХРАНЯЕМ ТЕКУЩИЙ НАБОР
ТекущийНабор = Новый ТаблицаЗначений;
ТекущийНабор.Колонки.Добавить(‘Номенклатура’);
ТекущийНабор.Колонки.Добавить(‘Количество’);
ВыборкаДетальныеЗаписи = ВыборкаНакладная.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Строка = ТекущийНабор.Добавить();
ЗаполнитьЗначенияСвойств(Строка, ВыборкаДетальныеЗаписи);
ДокДубль = ВыборкаДетальныеЗаписи.ДокДубль;
КонецЦикла;
ДубльЕсть = НайтиДубли(ТекущийНабор, ВыборкаВремяНакладной.Время, ВыборкаВидОперации, Дубль, ВыборкаНакладная. НомерДубль, ДокДубль);
Если ДубльЕсть Тогда
… //Сохраняем куда надо
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЦикла;
И сама процедура поиска:
Функция НайтиДубли(Набор, ВремяНакладной, ВыборкаВидОперации, Дубль, НомерНакладной, ДокДубль)
ВыборкаВремяНакладной = ВыборкаВидОперации.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
//ТУТ МЫ ПЕРЕСКАКИВАЕМ НА ТЕКУЩИЙ НАБОР, ЧТОБЫ СРАВНИВАТЬ ЕГО СО ВСЕМИ ПОСЛЕДУЮЩИМИ
ВыборкаВремяНакладной.НайтиСледующий(ВремяНакладной,’Время’);
Пока ВыборкаВремяНакладной.Следующий() Цикл
ВыборкаНакладная = ВыборкаВремяНакладной.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаНакладная.Следующий() Цикл
ЭтоДубль = Истина;
Колво = 0;
НомерДубль = ВыборкаНакладная.НомерДубль;
ДокДубль = Документы.Док.ПустаяСсылка();
Если НужноИскатьДубль (НомерНакладной, НомерДубль ) Тогда
ВыборкаДетальныеЗаписи = ВыборкаНакладная.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() И ЭтоДубль Цикл
Поиск = Новый Структура;
Поиск.Вставить(‘Номенклатура’, ВыборкаДетальныеЗаписи.Номенклатура);
Поиск.Вставить(‘Количество’, ВыборкаДетальныеЗаписи.Количество);
//В ТЕКУЩЕМ НАБОРЕ ПЫТАЕМСЯ НАЙТИ СТРОКУ ‘НОМЕНКЛАТУРА, КОЛИЧЕСТВО’, ЕСЛИ НЕТ, ТО ЭТО УЖЕ НЕ ДУБЛЬ
ЭтоДубль = Набор.НайтиСтроки(Поиск).Количество()>0;
Колво = Колво+1;
ДокДубль = ВыборкаДетальныеЗаписи.ДокДубль;
КонецЦикла;
Иначе
ЭтоДубль = Ложь;
КонецЕсли;
//КОЛИЧЕСТВО СРАВНИВАЕМ, ТАК КАК НАБОР МОЖЕТ СОДЕРЖАТЬ В СЕБЕ ДРУГОЙ НАБОР, НО ДУБЛЯМИ НЕ БЫТЬ
Если ЭтоДубль И Колво = Набор.Количество() Тогда
Дубль. НомерДубль = НомерДубль ;
Дубль. ДокДубль = ДокДубль;
Дубль. ТекНомер = НомерНакладной;
Дубль. ДокТек = ДокДубль;
Возврат Истина;
КонецЕсли;
КонецЦикла;
КонецЦикла;
… //Пропускаю за ненадобностью
Возврат Ложь;
КонецФункции
Если показалось, что что-то можно было сделать по-другому, то нельзя было. Некоторые специфические, нужные только мне, моменты я просто убрала, но, возможно, не полностью, поэтому в коде могут быть нестыковки.
Итоги по "НомерДубль" в блок-схему я не включила намеренно, потому что не заметила их сразу. Этот итог нужен, чтобы сделать проверку "Нужно сравнивать" только один раз. Вообще, если предварительная проверка не нужна, то и итог этот тоже.
В итоге я получила достаточно интересный, хотя и малоприменимый, опыт.
(473) 202-20-10
ссылка на сайт автора обязательна
звоните!