Как найти пропущенные номера документов ЧекККМ в 1С?

Программист 1С:Розница Управленческий учет Торговля и дистрибуция
← На главную

В процессе работы, особенно в розничной торговле, возникает задача контроля целостности нумерации документов, например, ЧекККМ. Пропуски в номерах могут сигнализировать о серьезных проблемах: чек был пробит на кассовом аппарате (фискальном регистраторе), но по какой-то причине не сохранился в базе данных 1С. Это может произойти из-за сбоя связи, ошибки программы или оборудования. Своевременное выявление таких "потерянных" чеков необходимо для корректного ведения учета и сверки с данными фискального накопителя. Рассмотрим несколько эффективных способов решения этой задачи.

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

Метод 1: Генерация полной последовательности номеров и сравнение

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

Разберем реализацию по шагам:

  1. Определяем границы диапазона. Сначала необходимо найти минимальный и максимальный номер документа ЧекККМ за интересующий нас период. Это легко сделать с помощью запроса.
    
    ВЫБРАТЬ
        МИНИМУМ(ЧекиККМ.Номер) КАК МинНомер,
        МАКСИМУМ(ЧекиККМ.Номер) КАК МаксНомер
    ИЗ
        Документ.ЧекККМ КАК ЧекиККМ
    ГДЕ
        ЧекиККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
        И ЧекиККМ.Проведен
    

    Важно: на этом этапе мы получаем строковые представления номеров. Их нужно будет преобразовать в числа.

  2. Создаем временную таблицу со всеми номерами. Поскольку язык запросов 1С не имеет встроенных генераторов последовательностей, этот шаг выполняется программно. Мы создаем ТаблицуЗначений и в цикле заполняем ее всеми числами от минимального до максимального.
    
    // Предположим, что МинНомерЧисло и МаксНомерЧисло - это числовые значения,
    // полученные после преобразования результатов запроса.
    
    ТаблицаВсехНомеров = Новый ТаблицаЗначений;
    ТаблицаВсехНомеров.Колонки.Добавить("Номер");
    
    Для Счетчик = МинНомерЧисло По МаксНомерЧисло Цикл
        НоваяСтрока = ТаблицаВсехНомеров.Добавить();
        НоваяСтрока.Номер = Счетчик;
    КонецЦикла;
    
  3. Находим пропуски с помощью запроса. Теперь у нас есть две таблицы: временная с полной последовательностью номеров и таблица реальных документов в базе. С помощью запроса с ЛЕВЫМ СОЕДИНЕНИЕМ мы можем их сравнить и найти расхождения.
    
    ВЫБРАТЬ
        ЭталонныеНомера.Номер
    ИЗ
        &ТаблицаВсехНомеров КАК ЭталонныеНомера
            ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЧекККМ КАК ЧекиККМ
            ПО ВЫРАЗИТЬ(ПОДСТРОКА(ЧекиККМ.Номер, 3, 11) КАК ЧИСЛО(15, 0)) = ЭталонныеНомера.Номер
    ГДЕ
        ЧекиККМ.Ссылка ЕСТЬ NULL
        И ЭталонныеНомера.Номер > 0 // Дополнительная проверка
    
    УПОРЯДОЧИТЬ ПО
        ЭталонныеНомера.Номер
    

    В этом запросе мы соединяем нашу эталонную таблицу ЭталонныеНомера с реальными документами ЧекККМ. Ключевым моментом является условие соединения, где мы на лету преобразуем строковый номер документа в число. Функция ПОДСТРОКА используется для извлечения числовой части номера (параметры нужно подбирать под ваш формат нумерации), а ВЫРАЗИТЬ(... КАК ЧИСЛО) — для преобразования типа. Условие ГДЕ ЧекиККМ.Ссылка ЕСТЬ NULL отбирает именно те номера из эталонного списка, для которых не нашлось соответствия в базе данных.

Метод 2: Поиск разрывов через самосоединение таблицы

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

Рассмотрим шаги:

  1. Подготавливаем данные. Создаем вложенный запрос, в котором выбираем все номера документов за период и сразу преобразуем их в числовой тип.
  2. Выполняем самосоединение. Соединяем полученную таблицу саму с собой. Для каждой строки из левой таблицы (назовем ее Таб1) мы ищем все строки из правой таблицы (Таб2), у которых номер меньше.
  3. Находим ближайший предыдущий номер. Группируем результат по номеру из Таб1 и с помощью агрегатной функции МАКСИМУМ() находим самый большой из предыдущих номеров. Это и будет номер, идущий непосредственно перед текущим в существующей последовательности.
  4. Анализируем разницу. В основном запросе вычисляем разницу между текущим номером и максимальным предыдущим. Если она больше 1, мы нашли пропуск.

Пример такого запроса:


ВЫБРАТЬ
    Таб1.ЧисловойНомер КАК КонецПропуска,
    МАКСИМУМ(Таб2.ЧисловойНомер) КАК НачалоПропуска,
    Таб1.ЧисловойНомер - МАКСИМУМ(Таб2.ЧисловойНомер) - 1 КАК КоличествоПропущенных
ИЗ
    (ВЫБРАТЬ
        ВЫРАЗИТЬ(ПОДСТРОКА(ЧекККМ.Номер, 3, 11) КАК ЧИСЛО(15, 0)) КАК ЧисловойНомер
    ИЗ
        Документ.ЧекККМ КАК ЧекККМ
    ГДЕ
        ЧекККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода) КАК Таб1
        ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
            ВЫРАЗИТЬ(ПОДСТРОКА(ЧекККМ.Номер, 3, 11) КАК ЧИСЛО(15, 0)) КАК ЧисловойНомер
        ИЗ
            Документ.ЧекККМ КАК ЧекККМ
        ГДЕ
            ЧекККМ.Дата МЕЖДУ &НачалоПериода И &КонецПериода) КАК Таб2
        ПО Таб1.ЧисловойНомер > Таб2.ЧисловойНомер

СГРУППИРОВАТЬ ПО
    Таб1.ЧисловойНомер

ИМЕЮЩИЕ
    Таб1.ЧисловойНомер - МАКСИМУМ(Таб2.ЧисловойНомер) > 1

УПОРЯДОЧИТЬ ПО
    КонецПропуска

Этот запрос вернет таблицу, где каждая строка описывает один разрыв в нумерации: номер документа, после которого начинается пропуск (НачалоПропуска), номер, которым заканчивается пропуск (КонецПропуска), и количество пропущенных документов.

Метод 3: Программный перебор в цикле

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

Алгоритм следующий:

  1. Выполнить простой запрос для получения всех номеров документов за период, отсортированных по возрастанию.
  2. Выгрузить результат запроса в Массив или ТаблицуЗначений.
  3. В цикле обойти полученную коллекцию, начиная со второго элемента.
  4. На каждой итерации сравнивать числовое значение текущего номера с предыдущим.
  5. Если ТекущийНомер - ПредыдущийНомер > 1, значит, обнаружен пропуск. Все номера в диапазоне от (ПредыдущийНомер + 1) до (ТекущийНомер - 1) являются пропущенными. Их можно добавить в отдельный список для дальнейшей обработки.

Этот метод прост в реализации, но вся нагрузка по перебору и сравнению ложится на сервер 1С, а не на СУБД, что может быть значительно медленнее, чем методы на основе запросов.

В заключение, выбор метода зависит от конкретной ситуации. Для максимальной производительности и гибкости рекомендуется использовать Метод 1 с генерацией эталонной последовательности. Если нужно быстрое решение без создания временных таблиц, подойдет Метод 2. Программный перебор стоит использовать только при небольших объемах данных. Независимо от выбранного способа, ключевым является корректное преобразование строкового номера документа в число для выполнения математических сравнений.

← На главную