Каждый интегратор и системный администратор 1С рано или поздно сталкивается с мистической ситуацией: в настройках программы жестко активирован запрет на продажу товаров с отрицательным остатком, при тестировании под учетной записью администратора или кассира система честно блокирует пробитие «минусовых» позиций, но в конце недели в отчете о розничных продажах или ведомости по товарам на складах всё равно сияют отрицательные остатки.
Особенно остро эта проблема стоит в аптечном бизнесе на базе решений вроде Рарус: Аптека (построенных на конфигурации 1С:Розница), где учет ведется по сериям, срокам годности и дробным упаковкам. Разберем по шагам, какими путями товар просачивается в минус мимо бдительного контроля программы, и как закрыть эти уязвимости раз и навсегда.
Проанализируем структуру разграничения прав в конфигурации 1С:Розница 2. Контроль остатков здесь настраивается не в одном месте, а в двух параллельных контурах. Если вы упустите один из них, система даст сбой.
Рассмотрим подробнее сценарий, когда в настройках РМК флаг контроля остатков включен, но у группы пользователей, в которую входят кассиры, установлена привилегия РазрешитьПродажуБезКонтроляОстатков (или аналогичная ей). В таком случае программа приоритетно обратится к правам конкретного сеанса пользователя и пропустит чек, даже если глобально по магазину продажа в минус запрещена.
Решение: Тщательно проверьте справочник ДополнительныеПраваПользователей для всех профилей кассиров, администраторов и старших смен. Исключите любые пересечения ролей, позволяющие обходить контроль.
Выясним причину, связанную с функционалом откладывания чеков в РМК. Это классическая уязвимость многих релизов платформы. Сценарий обхода выглядит следующим образом:
В некоторых старых или кастомизированных релизах алгоритм проведения отложенного чека проверяет остатки только в момент добавления позиции в чек, но при самом фискальном пробитии (нажатии кнопки «Оплата») повторную валидацию регистра накопления ТоварыНаСкладах не инициирует. В результате чек пробивается, уводя позицию в минус.
Решение: Протестируйте этот сценарий в вашей базе. Если баг воспроизводится, потребуется внести изменения в код проведения чека ККМ, добавив принудительный вызов процедуры проверки остатков перед началом фискализации транзакции.
Посмотрим на ситуацию со стороны товарного учета. Зачастую отрицательный остаток формируется вовсе не в момент продажи, а уже после нее. Рассмотрим популярную схему, когда кассиры или товароведы маскируют пересортицу или недостачу:
При этом в Журнале регистрации удаление или перепроведение документов может быть замаскировано под «технический сбой» или «корректировку ошибок ввода».
Решение: Полностью заблокируйте для кассиров и рядовых сотрудников склада права на редактирование, проведение и удаление любых документов, кроме ЧекККМ. Настройте дату запрета изменения данных текущим днем.
В конфигурации Рарус: Аптека учет практически каждого лекарственного средства жестко завязан на серии и сроки годности. Это вносит свои особенности в алгоритмы проведения документов. Выясним, как это влияет на отрицательные остатки:
Решение: В настройках параметров учета необходимо активировать жесткий контроль остатков именно в разрезе серий и партий, а не только по ключевым свойствам номенклатуры.
В аптеках регулярно продают блистеры, ампулы или поштучные таблетки. В базе 1С это отражается дробным количеством (например, продажа 0.1 или 0.5 от целой упаковки).
При расчетах на уровне СУБД из-за особенностей хранения вещественных чисел с плавающей точкой в регистры может записаться мизерная погрешность. Например, при списании последней половины упаковки система может рассчитать остаток как 0.5000000001, а списывает ровно 0.5. В редких случаях происходит обратная ситуация: списывается чуть больше, чем числится в регистре, из-за чего возникает микроскопический минус (например, -0.00001), который в стандартных отчетах округляется и выводится как полноценный -1.
Решение: Периодически запускайте тестирование и исправление базы данных с пересчетом итогов. При необходимости доработайте процедуру округления долей при подборе дробного товара в РМК.
Проанализируем структуру складов предприятия. Часто в магазине создается структура из нескольких складов, например: «Основной склад» (куда поступает товар от поставщика) и «Торговый зал» (откуда происходит продажа через кассу).
Если в настройках рабочего места кассира (РМК) указано, что контроль ведется по магазину в целом, то при сканировании товара система видит: товар есть на «Основном складе» в количестве 5 штук. Программа одобряет продажу. Однако списание по чеку настроено строго со склада «Торговый зал». В итоге на складе «Торговый зал» образуется отрицательный баланс, так как физического перемещения между складами оформлено не было.
Решение: Установите в настройках РМК и правилах продаж жесткую проверку остатков непосредственно по складу списания (кассе), а не по магазину в целом.
Если в торговой точке установлены две или более кассы, работающие в одной локальной базе в режиме реального времени, возникает технический конфликт транзакций:
ТоварыНаСкладах выполняются параллельно с разницей в доли секунды. В итоге одна транзакция списывает остаток в 0, а вторая — уводит его в -1.Решение: Для критически важных позиций внедрите механизм блокировки записей на уровне СУБД при начале процедуры оплаты в РМК, либо используйте транзакционный режим проверки остатков непосредственно при проведении документа, избегая проверок исключительно при добавлении строки в табличную часть.
Проанализируем, как можно локализовать проблему с помощью отладки в конфигураторе. Нам нужно проверить, вызывается ли функция контроля остатков во всех возможных сценариях проведения чека.
Посмотрим на пример структуры процедуры, отвечающей за проведение документа ЧекККМ. Мы можем внедрить временное логирование, чтобы записывать в отдельный файл или журнал регистрации все случаи, когда документ проводится с отрицательными показателями.
Рассмотрим следующий пример кода на встроенном языке 1С, который можно интегрировать в модуль проведения документа для выявления виновников обхода контроля:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Проверим, выполняется ли проведение чека ККМ
Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
// Запрос к регистру остатков для анализа текущих позиций чека
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТабличнаяЧастьТовары.Номенклатура КАК Номенклатура,
| ТабличнаяЧастьТовары.Количество КАК КоличествоВЧеке,
| ЕСТЬNULL(ТоварыНаСкладахОстатки.ВНаличииОстаток, 0) КАК ОстатокНаСкладе
|ИЗ
| Документ.ЧекККМ.Товары КАК ТабличнаяЧастьТовары
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
| &Период,
| Склад = &СкладОтправитель
| ) КАК ТоварыНаСкладахОстатки
| ПО ТабличнаяЧастьТовары.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура
|ГДЕ
| ТабличнаяЧастьТовары.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ЭтотОбъект.Ссылка);
Запрос.УстановитьПараметр("Период", Новый Граница(ЭтотОбъект.Дата, ВидГраницы.Исключая));
Запрос.УстановитьПараметр("СкладОтправитель", ЭтотОбъект.Склад);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Если ВыборкаДетальныеЗаписи.ОстатокНаСкладе < ВыборкаДетальныеЗаписи.КоличествоВЧеке Тогда
// Запишем подозрительное событие в Журнал регистрации для анализа параметров сеанса
ЗаписьЖурналаРегистрации(
"КонтрольОстатков.ОбходКонтроля",
УровеньЖурналаРегистрации.Предупреждение,
ЭтотОбъект.Метаданные(),
ЭтотОбъект.Ссылка,
НСтр("ru = 'Попытка проведения чека в минус! Товар: "
+ ВыборкаДетальныеЗаписи.Номенклатура
+ ", Требуется: " + ВыборкаДетальныеЗаписи.КоличествоВЧеке
+ ", Доступно: " + ВыборкаДетальныеЗаписи.ОстатокНаСкладе + "'")
);
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Интегрировав подобный сбор логов, вы сможете в течение нескольких дней точно определить, в какой момент происходит обход: в момент ли интерактивной работы кассира в РМК, при автоматическом обмене данными (РИБ) или при программном формировании документов внешними обработками.