Как в 1С спроектировать учет и контроль разнопериодных лимитов при записи на прием?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Сфера услуг, туризм и социальный сектор
← На главную

Рассмотрим распространенную прикладную задачу: необходимо реализовать систему лимитов на количество обслуживаемых объектов или клиентов с контролем по разным периодам одновременно. В качестве классического примера выступает медицинская регистратура, где действуют квоты на прием определенных категорий пациентов (например, по ОМС, ДМС или льготным программам) в разрезе врача, его специальности, отделения и больницы. При этом ограничения задаются сразу на день, неделю и месяц (например, не более 2 пациентов в день, 5 в неделю и 15 в месяц), а при формировании сетки расписания или проведении документа записи система должна контролировать остаток свободного лимита.

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

Архитектурный подход 1: Разделение плана и факта (Регистр сведений + Оборотный регистр накопления)

Наиболее масштабируемым и каноническим подходом для задач бюджетирования и лимитирования в платформе 1С является разделение нормативных ограничений (плана) и фактических записей (факта).

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

Оптимальная схема предполагает использование двух регистров:

  1. Регистр сведений «УстановленныеЛимиты» (периодический, в пределах дня или месяца):
    Служит для хранения действующих нормативов.
    Измерения: Подразделение, Специальность, Сотрудник, СегментПациентов (или группа аналитики), Периодичность (перечисление: День, Неделя, Месяц).
    Ресурсы: Лимит (Число).
  2. Регистр накопления «ЗаписиПациентов» (вид: Обороты):
    Фиксирует реальные или запланированные талоны на прием.
    Измерения: Подразделение, Специальность, Сотрудник, СегментПациентов.
    Ресурсы: Количество (Число).

В оборотный регистр движение делается документом записи строго на конкретную дату и время приема. Нам не нужно дублировать запись для недели или месяца — квантом учета выступает день прихода. Суммарный факт за неделю и месяц рассчитывается виртуальной таблицей оборотов за требуемый интервал.

Посмотрим на пример структуры запроса, который собирает фактические обороты за день, неделю и месяц для сопоставления с плановыми квотами:


ВЫБРАТЬ
    ТаблицаОборотовДень.Сотрудник КАК Сотрудник,
    ТаблицаОборотовДень.СегментПациентов КАК СегментПациентов,
    ЕСТЬNULL(ТаблицаОборотовДень.КоличествоОборот, 0) КАК ФактДень,
    ЕСТЬNULL(ТаблицаОборотовНеделя.КоличествоОборот, 0) КАК ФактНеделя,
    ЕСТЬNULL(ТаблицаОборотовМесяц.КоличествоОборот, 0) КАК ФактМесяц
ПОМЕСТИТЬ ВТ_Факты
ИЗ
    РегистрНакопления.ЗаписиПациентов.Обороты(
            НАЧАЛОПЕРИОДА(&ДатаПриема, ДЕНЬ),
            КОНЕЦПЕРИОДА(&ДатаПриема, ДЕНЬ),
            День,
            Сотрудник = &Врач И СегментПациентов = &Сегмент) КАК ТаблицаОборотовДень
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаписиПациентов.Обороты(
                НАЧАЛОПЕРИОДА(&ДатаПриема, НЕДЕЛЯ),
                КОНЕЦПЕРИОДА(&ДатаПриема, НЕДЕЛЯ),
                Авто,
                Сотрудник = &Врач И СегментПациентов = &Сегмент) КАК ТаблицаОборотовНеделя
            ПО ТаблицаОборотовДень.Сотрудник = ТаблицаОборотовНеделя.Сотрудник
                И ТаблицаОборотовДень.СегментПациентов = ТаблицаОборотовНеделя.СегментПациентов
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаписиПациентов.Обороты(
                НАЧАЛОПЕРИОДА(&ДатаПриема, МЕСЯЦ),
                КОНЕЦПЕРИОДА(&ДатаПриема, МЕСЯЦ),
                Авто,
                Сотрудник = &Врач И СегментПациентов = &Сегмент) КАК ТаблицаОборотовМесяц
            ПО ТаблицаОборотовДень.Сотрудник = ТаблицаОборотовМесяц.Сотрудник
                И ТаблицаОборотовДень.СегментПациентов = ТаблицаОборотовМесяц.СегментПациентов
;

ВЫБРАТЬ
    ЛимитыСрез.Периодичность КАК Периодичность,
    ЛимитыСрез.Лимит КАК Лимит,
    ВЫБОР
        КОГДА ЛимитыСрез.Периодичность = ЗНАЧЕНИЕ(Перечисление.ПериодичностиЛимитов.День)
            ТОГДА Факты.ФактДень
        КОГДА ЛимитыСрез.Периодичность = ЗНАЧЕНИЕ(Перечисление.ПериодичностиЛимитов.Неделя)
            ТОГДА Факты.ФактНеделя
        КОГДА ЛимитыСрез.Периодичность = ЗНАЧЕНИЕ(Перечисление.ПериодичностиЛимитов.Месяц)
            ТОГДА Факты.ФактМесяц
        ИНАЧЕ 0
    КОНЕЦ КАК Факт
ИЗ
    РегистрСведений.УстановленныеЛимиты.СрезПоследних(&ДатаПриема, Сотрудник = &Врач И СегментПациентов = &Сегмент) КАК ЛимитыСрез
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Факты КАК Факты
            ПО ИСТИНА

Сравнивая поля Лимит и Факт, система определяет, доступна ли запись на выбранную дату. Если Факт + 1 > Лимит хотя бы по одной из периодичностей, выводится предупреждение или блокируется запись.

Архитектурный подход 2: Модель динамических остатков со счетчиками

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

Разберем логику этой модели:

  1. Элемент справочника СчетчикиЛимитов формируется динамически или регламентно и инкапсулирует в себе аналитику и конкретный интервал: например, «Иванов_Терапевт_ОМС_Неделя_14_2026».
  2. Документ планирования в начале периода делает движение «Приход» со значением установленной квоты.
  3. Каждый документ фактической записи делает расход сразу по нескольким счетчикам: по счетчику конкретного дня, счетчику текущей недели и счетчику месяца.
  4. Контроль сводится к типовой проверке: Остаток >= 1 по каждому счетчику.

Преимущество: Высокая скорость проверки при проведении документов, так как остаток по счетчику получается мгновенно из итогов регистра остатков без вычисления оборотов за период.

Недостатки: Избыточный рост справочника ключей аналитики (сотни тысяч записей при большом штате сотрудников и длинном горизонте планирования), а также высокая сложность пересчета движений при изменении правил лимитирования или даты приема «задним числом».

Оптимизация расчета расписания на диапазон дат

При открытии сетки расписания администратор видит календарь на неделю вперед по нескольким десяткам врачей. Если выполнять запрос на расчет лимитов в цикле для каждого слота или дня, система столкнется с критическим падением производительности.

Проанализируем правила оптимизации этой операции:

  1. Пакетное получение данных: Сформируем во временной таблице перечень всех рабочих дат недели и соединим их с графиком работы врачей.
  2. Агрегация границ периодов в запросе: Для каждой строки календаря сразу рассчитаем выражения НАЧАЛОПЕРИОДА(ТаблицаДат.Дата, НЕДЕЛЯ) и НАЧАЛОПЕРИОДА(ТаблицаДат.Дата, МЕСЯЦ). Это позволит соединить данные календаря с виртуальной таблицей оборотов за один пакетный запрос на всю отображаемую сетку.
  3. Фильтрация по графику: Соединяйте даты с календарем доступности врача (отпуска, больничные, смены), чтобы исключить нерабочие дни из расчета лимитов еще на уровне СУБД.

Контроль многоуровневой иерархии (Врач — Отделение — Больница)

Когда квота задается не только на конкретного врача, но и на отделение в целом (например, врач принимает не более 2 льготников в день, но все терапевты отделения суммарно не более 6), аналитику измерений в регистре сведений настраивают с возможностью незаполненных значений:

Если лимит общий на подразделение, измерение Сотрудник оставляют пустым. Проверка выполняется каскадно:

  1. Сначала проверяется персональный лимит врача по детальным оборотам.
  2. Затем в том же запросе суммируются обороты всех врачей отделения по указанной специальности и сопоставляются с общим лимитом подразделения.

Параллельная работа и блокировки данных

При одновременной записи пациентов несколькими регистраторами возникает риск состояния гонки (race condition): два оператора одновременно прочитают доступный остаток в 1 талон и проведут документы, что приведет к превышению лимита.

Чтобы избежать этой коллизии, в транзакции записи документа необходимо выставлять управляемую блокировку данных:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("РегистрНакопления.ЗаписиПациентов");
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
ЭлементБлокировки.УстановитьЗначение("Сотрудник", Врач);
ЭлементБлокировки.УстановитьЗначение("СегментПациентов", Сегмент);
Блокировка.Заблокировать();

// Далее выполняется запрос контроля лимитов и запись движений

Такой механизм гарантирует целостность квот при высокой интенсивности работы пользователей регистратуры.

← На главную