Продажи · Цены

Согласование цен позиции заказа: 4 статуса, встречное предложение, автозакрытие

Каждое снижение цены позиции заказа фиксируется отдельной заявкой на согласование. Менеджер запрашивает новую цену, система считает разницу в рублях и в процентах (от текущей цены), руководитель одобряет либо отклоняет, либо делает встречное предложение со своей итоговой ценой. Повышение цены применяется сразу, без согласования. Зачёркнутая «старая» цена сохраняется отдельно, чтобы интерфейс показывал «было / стало». Если позицию реализовали, все связанные заявки автоматически закрываются. Если у позиции не указана закупочная цена — менеджерам по закупу с правом видеть закупочные цены уходит уведомление.

4
статуса заявки
10
фильтров
11
действий в интерфейсе
3
областей кэша
Все возможности
Преимущества

Что получает бизнес от модуля

Каждое снижение фиксируется

Снизить цену позиции «по-тихому» нельзя. Сразу создаётся заявка с текущей ценой, запрошенной ценой, разницей в рублях и в процентах — статус «Ожидает» до решения руководителя. Без согласования цена не меняется.

Повышение — без согласования

Если запрошенная цена выше текущей — система сразу обновляет позицию и сохраняет старую цену как «зачёркнутую», заявка не создаётся. Согласование нужно только для снижения, чтобы не замедлять рост среднего чека.

Встречное предложение одной кнопкой

При одобрении руководитель может ввести свою итоговую цену — одобрить «не на той цене, что просили». Менеджер просил 18 000, согласующий ставит 19 500 — позиция уходит в работу по 19 500, оригинальный запрос остаётся в журнале.

4 статуса заявки

Ожидает рассмотрения, Одобрена, Отклонена, Закрыта. Одобрить или отклонить можно только из статуса «Ожидает» — повторно «передоговориться» по одной и той же заявке нельзя, нужно создавать новую.

Автозакрытие при продаже

При переводе позиции в статус «Реализована» все активные заявки по этой позиции уходят в статус «Закрыта». Не висят «зомби»-заявки после фактической реализации.

Уведомления по правам

При создании заявки уведомление уходит всем, кто имеет право одобрять цены. При решении — обратно инициатору. Если у позиции нет закупочной цены — будят держателей права видеть закупочные цены: «проставьте закуп».

Массовое одобрение и отклонение

Можно обработать пачку заявок одним действием: на выходе — счётчик одобренных, счётчик не прошедших и список ошибок с причинами. Идеально для пачки однотипных заявок от одного менеджера, опта по одному клиенту, по одной товарной категории.

«Зачёркнутая» цена для интерфейса

При одобрении заявки или прямом повышении цены, если у позиции ещё нет «зачёркнутой» цены, туда записывается старая цена. Интерфейс показывает её зачёркнутой над новой — клиент видит, что это согласованная скидка, а не «исходная маленькая».

Полный журнал и статистика

В журнал пишутся создание, изменение, удаление, восстановление. Мягкое удаление — данные не теряются. Эндпоинт статистики возвращает счётчики по статусам, эндпоинт инициаторов — список тех, кто чаще подаёт заявки. Группировка по инициатору — список по сотрудникам для разбора «кто чаще всех просит скидку».

Возможности модуля

Что входит в Цены

  1. 01

    Что хранится в заявке

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

  2. 02

    4 значения статуса

    Ожидает рассмотрения, Одобрена, Отклонена, Закрыта. Доступна полная карта статусов с русскими названиями. Хелперы для каждого статуса позволяют ветвить логику без сравнения строк, проверки «можно одобрить» и «можно отклонить» возвращают истину только для «Ожидает».

  3. 03

    Расчёт разницы

    Принимает текущую и запрошенную цену, возвращает разницу в рублях и разницу в процентах с округлением до 2 знаков. Деление на ноль защищено: если текущая цена равна 0, проценты тоже 0. Используется и при создании заявки, и при одобрении со встречным предложением.

  4. 04

    Защита от двух активных заявок

    Перед созданием система ищет уже существующую заявку в статусе «Ожидает» по той же позиции. Если есть — возвращается ошибка «уже есть активная заявка». На одну позицию — одна активная заявка одновременно, никаких гонок.

  5. 05

    Итоговая цена при одобрении

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

  6. 06

    Автоматическое применение при повышении

    Если запрошенная цена выше текущей, заявка не создаётся вообще. Позиция обновляется сразу (новая цена и «зачёркнутая» старая, если её ещё не было). Возвращается результат с сообщением «цена повышена напрямую» — для интерфейса «обновлено сразу».

  7. 07

    Контроль закупа: уведомление о незаполненной закупочной цене

    При создании заявки: если закупочная цена позиции не положительная, в фоне отправляется уведомление. Оно находит всех с правом видеть закупочные цены и шлёт каждому сообщение «Не указана закупочная стоимость по товару „X" в заказе №Y». Менеджеры закупа видят и проставляют закуп прямо со страницы согласований.

  8. 08

    Установка закупочной цены

    Отдельное действие на заявке. Принимает закупочную цену. Система проверяет, что у позиции ещё нет закупа (если уже есть — отказ), и обновляет закупочную и первоначальную закупочную цены одной транзакцией. Доступ — право видеть закупочные цены.

  9. 09

    Автозакрытие при продаже

    Фоновый процесс автоматически закрывает все активные заявки по проданной позиции. Запускается при переходе позиции в «Реализована». Внутри — массовое обновление статусов в «Закрыта» с проставлением даты закрытия.

  10. 10

    События «создана»/«рассмотрена»

    Событие «создана» возникает при создании, «рассмотрена» — при одобрении или отклонении (с указанием действия). На них подписаны слушатели для трансляции в реальном времени, обновления счётчика «новые согласования» в шапке у пользователей с правом одобрять, и метрики.

  11. 11

    Уведомление при создании

    При создании уведомление уходит всем, у кого есть право одобрять цены. Текст содержит знак изменения: «увеличение на N%» или «уменьшение на N%». Это для уведомления в мобильном приложении и записи в колокольчик.

  12. 12

    Уведомление при решении

    При одобрении или отклонении уведомление уходит обратно инициатору. Текст: «Ваша заявка на изменение цены одобрена ✅» либо «отклонена ❌». Менеджер моментально видит результат без перезагрузки страницы.

  13. 13

    10 фильтров на список

    Идентификатор (массив), позиция (массив), статус (массив), инициатор (массив), согласующий (массив или «без согласующего» — для непросмотренных), диапазон дат создания, заказ (через связь с позицией), поиск (по названию товара, артикулу и номеру заказа), сортировка, направление сортировки, группировка.

  14. 14

    Группировка по инициатору

    Группировка по инициатору сбрасывает дефолтную сортировку и применяет порядок: сначала по инициатору, затем по дате создания (по убыванию). Интерфейс получает плоский список, заранее отсортированный для расстановки заголовков групп по сотрудникам, без отдельных подзапросов.

  15. 15

    Разрешённые сортировки

    Сортировка ограничена списком: идентификатор, дата создания, статус, текущая цена, запрошенная цена, разница в процентах. Направление — по возрастанию или убыванию. Несуществующее поле — игнорируется (защита от подмены параметров).

  16. 16

    Массовые операции с подробным отчётом

    Массовое одобрение и отклонение обходят список идентификаторов, вызывают одобрение/отклонение по одной, собирают одобренные и не прошедшие (идентификатор + причина). Возвращают «одобрено столько-то, не прошло столько-то, перечень не прошедших с причинами» — интерфейс показывает, какие не прошли и почему.

  17. 17

    Карточка заявки для интерфейса

    Помимо стандартных полей, карточка подтягивает закупочную цену и минимальную закупочную со склада прямо на верхний уровень (страница согласований читает оттуда), плюс полную позицию (текущая цена, «зачёркнутая», первоначальная, закупочная, первоначальная закупочная, закупочная с расходами, закупочная скидка, статус, заказ, товар), инициатора с должностью, согласующего.

  18. 18

    Сброс кэша по трём областям

    После любого изменения сбрасываются кэши: согласования (списки и статистика), позиции заказа (карточка позиции — там же показывается флаг «есть активная заявка»), заказы (сводный счётчик согласований на заказе). После каждого одобрения или отклонения кэш на трёх уровнях освежается, и интерфейс показывает свежие данные на следующий запрос.

  19. 19

    Права и роли

    Раздел «Согласования цен» с тремя правами: подать заявку (для менеджеров), одобрить или отклонить (для руководителей), читать список (для всех, кому положено). Отдельно — право видеть закупочные цены, оно даёт менеджерам закупа доступ к закупочной цене и установке закупа.

Интерфейс

Как это выглядит в системе

Заявки на согласование: статус, %, инициатор, дата
Карточка заявки: текущая, запрошенная, встречное предложение
История по заявке: создана → встречное предложение → одобрена → закрыта при продаже
Уведомления: новая заявка, одобрена, не указан закуп
Статистика по статусам и инициаторам
Группировка по инициатору: одна виза руководителя на пачку
FAQ

Частые вопросы

Когда возникает заявка на согласование цены?

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

Сколько у заявки статусов?

Четыре: Ожидает рассмотрения, Одобрена, Отклонена, Закрыта. В интерфейсе доступна полная карта с русскими названиями. Из «Ожидает» можно перейти в «Одобрена» или «Отклонена»; «Закрыта» выставляется автоматически, когда позицию реализовали.

Что такое встречное предложение?

Это возможность одобрить заявку «не на той цене, что просили». При одобрении можно ввести свою итоговую цену. Если задана — заявка одобряется именно по этой цене: обновляется запрошенная цена в заявке, пересчитываются разница и проценты по той же формуле, новая цена прокидывается в позицию. Менеджер просил 18 000, согласующий ставит 19 500 — заказ уходит в работу по 19 500.

Как считается разница в процентах?

Разница в рублях = запрошенная − текущая, разница в процентах = разница в рублях ÷ текущая × 100, округление до 2 знаков. Если текущая = 0 (вырожденный случай), проценты выставляются в 0, чтобы не было деления на ноль.

Что хранится в «зачёркнутой» цене?

Это «старая» цена — та, что была до согласования. При одобрении или при автоматическом повышении, если у позиции ещё нет «зачёркнутой» цены, туда записывается старая цена. Интерфейс показывает её зачёркнутой над новой, чтобы клиент и аудитор видели «было / стало», а не одну плоскую цифру.

Можно ли создать две активные заявки на одну позицию?

Нет. Перед созданием система ищет существующую заявку в статусе «Ожидает» по этой позиции. Если есть — возвращается ошибка «уже есть активная заявка». Сначала решите текущую, потом подавайте новую — это убирает гонки и противоречивые согласования.

Кто получает уведомление о новой заявке?

Все, у кого есть право одобрять цены. Каждому отправляется уведомление: «Новая заявка на согласование цены: уменьшение/увеличение на N%». В данных уведомления — идентификатор заявки, позиции, направление изменения, разница в процентах, ссылка.

А кто получает уведомление о решении?

Только инициатор. Текст: «Ваша заявка на изменение цены одобрена ✅» либо «отклонена ❌». В данных — идентификатор заявки, позиции, действие (одобрено/отклонено), ссылка.

Что значит «закрыта»?

Финальный статус для случая, когда заявка не была одобрена и не была отклонена, а перестала иметь смысл. Главный сценарий: при переводе позиции в «Реализована» фоновый процесс закрывает все активные заявки по позиции, дата закрытия проставляется автоматически.

Что происходит, если у позиции нет закупа?

В момент создания заявки система проверяет закупочную цену позиции. Если она не положительная, в фоне уходит уведомление. Оно собирает всех с правом видеть закупочные цены и шлёт каждому сообщение «Не указана закупочная стоимость по товару „X" в заказе №Y. Проставьте закуп». Это критично для расчёта маржи и решения «согласовывать или нет».

Как менеджер закупа проставляет закуп?

Через отдельное действие на заявке. Принимается закупочная цена. Система найдёт заявку и позицию, проверит, что закупочная цена не задана (иначе ошибка «закупочная цена уже указана»), затем обновит и закупочную, и первоначальную закупочную цены на округлённое значение. Право — видеть закупочные цены.

Что в заявке хранится как «комментарии»?

Три поля: комментарий инициатора (от менеджера, опционально, до 1000 символов — «клиент платит сразу, постоянный»), комментарий согласующего (от руководителя, опционально — «согласовано как разовая акция»), причина отклонения (обязательно при отклонении, до 1000 символов — «маржа ниже 5%, не проходим»).

Какие массовые действия?

Массовое одобрение и массовое отклонение. Запрос принимает список идентификаторов и общие параметры (комментарий согласующего, итоговая цена для одобрения; причина отклонения и комментарий для отклонения). Система обходит список, вызывает одобрение/отклонение по одной, собирает «одобрено столько-то, не прошло столько-то, перечень не прошедших с причинами» — интерфейс показывает таблицу непрошедших с причиной.

Какие фильтры поддерживаются?

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

Можно ли сортировать?

Да, по списку из 6 полей: идентификатор, дата создания, статус, текущая цена, запрошенная цена, разница в процентах. Направление — по возрастанию или убыванию. Поля вне списка игнорируются (защита от подмены параметров).

Что такое группировка по инициатору?

Меняет сортировку на «сначала по инициатору, затем по дате создания (по убыванию)». Интерфейс получает плоский список, уже сгруппированный по сотрудникам — расставляет заголовки групп без второго запроса. Удобно для «кто чаще всех просит снижения» и «одной визой одобрить всю пачку этого менеджера».

Какие действия в интерфейсе?

Список с фильтрами, статистика (счётчики по статусам), инициаторы (список с количествами), карточка одной заявки, создание, массовое одобрение и массовое отклонение, одобрение и отклонение конкретной заявки, установка закупочной цены, удаление. Действия охватывают весь жизненный цикл от создания до архивации.

Кто пишет журнал?

Система — автоматически на создание, изменение, удаление, восстановление, принудительное удаление. Разница полей (статус, запрошенная цена, итоговая цена, причина отклонения) остаётся в записи. Плюс параллельно срабатывают события «создана» и «рассмотрена» для метрик и трансляции.

Что сбрасывается в кэше?

Три области: согласования (списки и статистика), позиции заказа (карточка позиции — там же показывается флаг «есть активная заявка»), заказы (сводный счётчик согласований). Это даёт согласованную картину в интерфейсе сразу после одобрения или отклонения.

Удаляются ли заявки физически?

Нет. У заявки включено мягкое удаление. Удаление помечает запись как удалённую, восстановление возможно. Это важно для аудита: если кто-то «спрятал» неудобную заявку, её всё равно видно при разборе.

Какие права на эту функцию?

Раздел «Согласования цен» с тремя правами: подать заявку (менеджеры), одобрить или отклонить (руководители), читать список (все, у кого должен быть доступ). Отдельно — право видеть закупочные цены для установки закупа и просмотра закупочной цены в карточке заявки.

Что хранит первоначальная цена на позиции?

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

Что после отклонения? Можно подать заявку ещё раз?

Да. Отклонение выставляет статус «Отклонена», заявка перестаёт быть активной (поиск активной заявки её не вернёт). Менеджер может подать новую заявку с другой ценой или с другим обоснованием. Старая остаётся в журнале с причиной отклонения — видно историю переговоров.

Кейсы

Что меняется на реальной сети

Сеть бытовой техники
24 магазина · 1 200 заказов в день
Было
Менеджеры на местах снижали цену «в моменте», чтобы спасти сделку — кто-то на 5%, кто-то на 20%. Маржа гуляла, отчёт показывал «продали много, заработали как обычно». Найти, кто и где «продавил» цену, можно было только разбором базы вручную, разбор занимал полдня по одному инциденту.
Сделали
Включили согласование цен. Любое снижение цены позиции — заявка с текущей ценой, запрошенной ценой и разницей в процентах. Подачу могут делать менеджеры (право «подать заявку»), одобрять — только их руководители (право «одобрить или отклонить»). Все события пишутся в журнал, инициатор и одобряющий зафиксированы.
Стало
Несанкционированных снижений не осталось. Руководители видят список «ожидает» в одном окне с фильтром по проценту и магазину, одобряют пачкой массовым действием. Разбор «кто продавил» — теперь не нужен: вся история в журнале, можно фильтровать по инициатору и сравнивать со средними.
0
нелегальных скидок
+1.8 пп
к средней марже
< 30 сек
на разбор инцидента
B2B-дистрибьютор автозапчастей
8 ключевых менеджеров · 18 000 позиций
Было
Менеджеры тратили по полдня в неделю на пинг руководителя в Telegram: «глянь скидку», «глянь скидку». Скидки одобрялись «на словах», подтверждение оставалось в чате. При увольнении менеджера новый не понимал, какие условия согласованы по каким клиентам.
Сделали
Запретили снижать цену без заявки. Подача — это форма с запрошенной ценой и комментарием инициатора («постоянный клиент, оборот 4 млн/мес»). Одобрение или отклонение видит цепочка руководителей в одном окне. Встречное предложение убирает пинг-понг: согласующий сразу ставит цену, на которую готов, и нажимает «Одобрить».
Стало
Пинг-понг исчез. Согласующий тратит ~10 секунд на типовую заявку, для нестандартных пишет комментарий как контекст. При переходе клиента к другому менеджеру вся история видна в журнале — никакой потери контекста. Скорость закрытия сделки выросла, отказов из-за «давай узнаю и перезвоню» нет.
−87%
переписок «глянь скидку»
< 10 сек
на одобрение типовой
+22%
доля закрытых сделок
Мебельная фабрика с торговым залом
6 точек · средний чек 80 000 ₽
Было
На крупные позиции (диваны, спальные комплекты) клиенты торговались. Менеджер делал скидку 8–12% «по чутью», иногда выходило в минус с учётом закупа и доставки. Закупочные цены при этом обновлялись отделом снабжения с задержкой — менеджер мог и не знать о новом закупе.
Сделали
При создании заявки система проверяет закупочную цену позиции. Если её нет — в фоне отправляется уведомление: держателям права видеть закупочные цены приходит «Проставьте закуп по товару „Диван Прага" в заказе №…». Менеджер закупа открывает заявку, через установку закупа проставляет цену — и руководитель видит реальную маржу прямо в карточке заявки (закупочная цена на верхнем уровне карточки).
Стало
Решения принимаются по полной картине — руководитель видит и запрошенную цену, и закуп, и минимальную закупочную со склада. «Случайные» уходы в минус прекратились. Отдел снабжения тоже выиграл: уведомление приходит только когда правда нужно, без флуда.
0
отрицательных маржей
< 4 ч
до проставления закупа
+3.2 пп
средней маржи
Опт стройматериалов
4 склада · еженедельные тендеры
Было
На опт менеджер выставлял спецификацию из 50–120 позиций со скидками индивидуально по каждой. Согласование шло по одной — руководитель кликал «одобрить» 100 раз подряд. Если что-то не нравилось — отклонял пачкой, и менеджер пересоздавал всё с нуля.
Сделали
Включили массовое одобрение и отклонение с общим комментарием согласующего. Руководитель ставит галочки на нужные строки (фильтр по заказу), вводит общий комментарий «согласовано по тендеру №…», нажимает один раз. Система обходит список, возвращает «одобрено столько-то, не прошло столько-то, перечень не прошедших с причинами» — видно, какие конкретно не прошли.
Стало
Согласование тендерной спецификации из 100 позиций — 30 секунд вместо 15 минут. Отдельные «не прошедшие» сразу видны со своими причинами (например, «цена ниже закупа на 3 позициях»). Руководители больше не саботируют массовые операции «лень кликать» — теперь это один клик.
−96%
времени на тендер
100%
видимости отклонений
+14
тендеров в месяц
Сеть электроники
32 магазина · сезонные распродажи
Было
В пик сезона менеджеры подавали заявки на снижение, а пока ждали ответ — товар уходил по полной цене (либо клиент уходил без покупки). Висевшие в «Ожидает» заявки «зависали» после того, как позицию всё-таки реализовали по другой цене. Списки росли тысячами, среди которых трудно было найти актуальное.
Сделали
Подключили автозакрытие при продаже. При переводе позиции в «Реализована» фоновый процесс закрывает все активные заявки по этой позиции, дата закрытия проставляется автоматически. Параллельно фильтр «статус = Ожидает» в списке стал показывать только действительно ждущие решения.
Стало
«Зомби»-заявок не осталось. Список «Ожидает» — это реально те, по которым нужно решение, без шума. Руководители стали отвечать быстрее (раньше отговаривались «их там слишком много»). Скорость закрытия сделки в сезон выросла, потеря клиентов «пока согласовывали» сократилась.
−100%
«зомби»-заявок
−65%
времени до ответа
+9%
конверсии в сезон
Региональная сеть товаров для дома
12 магазинов · аудит финдиректора каждый месяц
Было
Финдиректор раз в месяц поднимал базу и считал, кто из менеджеров чаще всех просил снижение и кто из руководителей чаще всех соглашался. Расчёт делался выгрузкой в таблицу, занимал день, и за этот день данные успевали обновиться — точная цифра не выходила.
Сделали
Подключили группировку по инициатору и эндпоинты статистики и инициаторов. «Инициаторы» возвращает список с количеством заявок. «Статистика» — счётчики по статусам. Группировка в списке — заявки, сгруппированные по сотрудникам с реальными датами. Параллельно фильтр согласующего «без согласующего» позволяет увидеть «Ожидает» без рассмотрения.
Стало
Финдиректор формирует отчёт «топ-10 запрашивающих» и «топ-5 согласующих» прямо в интерфейсе, без выгрузки. Видны паттерны: один менеджер просит скидку в 4 раза чаще остальных при том же чеке — повод поговорить. Один руководитель одобряет почти всё подряд — другая беседа. Решения принимаются на данных, а не на «кажется».
< 1 мин
на отчёт
−40%
необоснованных запросов
+0.9 пп
средней маржи квартала
Подробнее

Как модуль помогает розничной сети

Отдельная сущность под каждое снижение цены

В AIERP цена позиции заказа — это контрактное значение. Менять её «по ходу» нельзя без следа: любое снижение проходит через отдельную заявку на согласование, которая фиксирует, кто запросил, сколько было, сколько просят, насколько это меньше (в рублях и процентах), кто одобрил или отклонил и с каким комментарием. Это не «галочка в интерфейсе» — это полноценная сущность с мягким удалением, поиском по ключевым полям и полным журналом изменений.

Структура записи: привязка к позиции, текущая цена (до изменения), запрошенная цена (что просят), разница в рублях (с точностью до сотых), разница в процентах (с точностью до сотых), комментарий инициатора (контекст от менеджера), комментарий согласующего (контекст от руководителя), причина отклонения (если отказ), статус, инициатор, согласующий, дата рассмотрения, дата закрытия. Каждое поле важно — это материал и для решения «сейчас», и для аудита «потом».

Расчёт разницы — общая формула: принимает текущую и запрошенную цену, возвращает разницу в рублях и в процентах, оба округлены до 2 знаков. Деление на ноль защищено: если текущая цена равна 0 (странный случай, но возможный при подарках), проценты выставляются в 0. Формула используется и при создании заявки, и при одобрении со встречным предложением — гарантия, что цифры считаются одинаково.

Повышение цены — без согласования, понижение — только с заявкой

Бизнес-смысл согласования — защитить маржу. Повышение цены позиции маржу не угрожает, поэтому система пропускает повышения мимо очереди заявок. Если запрошенная цена выше текущей, позиция обновляется сразу. Дополнительно, если у позиции ещё нет «зачёркнутой» цены, туда записывается старая цена — чтобы интерфейс мог показать «было / стало».

Сценарий: менеджер видит, что клиент готов взять три позиции по чуть более высокой цене (например, +3% за срочную сборку). Раньше пришлось бы создавать «согласование на повышение» (что абсурд) или править цену напрямую (без следа). Теперь он подаёт «заявку на повышение» через ту же операцию — но получает мгновенное обновление с сообщением «цена повышена напрямую», без участия руководителя. «Зачёркнутая» цена сохраняет историю, журнал фиксирует изменение позиции.

Снижение цены — другая история. Сразу создаётся заявка со статусом «Ожидает», возникает событие «создана» и в фоне отправляется уведомление всем пользователям с правом одобрять цены. Пока статус «Ожидает», цена позиции не меняется — даже если позицию в заказе обновляют по другим полям, цена остаётся прежней.

4 статуса жизненного цикла заявки и защита от двух активных

Статусы фиксированы: Ожидает рассмотрения (по умолчанию), Одобрена, Отклонена, Закрыта. В интерфейсе доступна полная карта статусов с русскими названиями. Хелперы для каждого статуса позволяют ветвить логику без сравнения строк.

Из «Ожидает» можно перейти только в «Одобрена» или «Отклонена» — проверки «можно одобрить» и «можно отклонить» возвращают истину только для этого статуса. Повторно «передоговориться» по одной и той же заявке нельзя. Если менеджеру нужна другая цена после отклонения — он подаёт новую заявку с новым обоснованием. Это сохраняет историю переговоров.

«Закрыта» — финальный, для случая «заявка перестала быть актуальной». Главный сценарий — автозакрытие при продаже: когда позицию переводят в «Реализована» (позиция реализована — деньги получены, товар у клиента), фоновый процесс массово переводит активные заявки этой позиции в «Закрыта» и проставляет дату закрытия. Так в списке «Ожидает» не висят «зомби» по уже завершённым позициям.

Защита от двух активных заявок: при создании система проверяет, есть ли уже заявка в статусе «Ожидает» по этой позиции. Если есть — возвращается ошибка «уже есть активная заявка». На одну позицию — одна активная заявка в один момент времени. Это устраняет гонки и противоречивые решения «один одобрил на 18 000, другой — на 16 000».

Встречное предложение: альтернатива «отклонить и переподать»

Стандартный сценарий «согласовать или отказать» работает в простых случаях, но в реальной торговле руководитель часто думает «не на той цене, что просят, а где-то посередине». В AIERP это решено через встречное предложение: при одобрении можно ввести свою итоговую цену.

Если итоговая цена задана — система одобряет заявку именно по ней, а не по запрошенной. Внутри: итоговая цена = переданное значение или запрошенная цена (если не передали). Позиция обновляется: новая цена = итоговая, «зачёркнутая» — если её не было. Заявка обновляется так: статус «Одобрена», согласующий, комментарий, запрошенная цена заменяется на фактически согласованную, разница и проценты пересчитываются по новой цене, дата рассмотрения фиксируется. Оригинальный запрос менеджера остаётся в комментарии согласующего («одобрено по 19 500 вместо запрошенных 18 000»).

Это убирает пинг-понг «отклонил → переподай → отклонил → переподай». Согласующий сразу ставит цену, на которую готов, и инициатор моментально видит через уведомление результат: «одобрено по 19 500». Если не устраивает — менеджер обсуждает с руководителем напрямую, но процесс в системе уже завершён.

Контроль закупа: уведомление о незаполненной закупочной и установка закупа

Решение «согласовывать или нет» зависит от маржи. Маржа = цена − закупочная цена. Если закупочная не проставлена, маржу не посчитать, и руководитель решает «на ощупь». AIERP это закрывает двумя механизмами.

Первый: при создании заявки система проверяет закупочную цену позиции. Если она не положительная, в фоне отправляется уведомление. Оно находит всех с правом видеть закупочные цены — это менеджеры закупа — и каждому шлёт сообщение «Не указана закупочная стоимость по товару „X" в заказе №Y. Проставьте закуп». В данных уведомления — идентификатор позиции, заказа, название товара, ссылка.

Второй: отдельное действие на заявке «установить закуп». Принимает закупочную цену. Система обновляет закупочную и первоначальную закупочную цены. Защита: если у позиции уже есть закупочная цена — возвращается ошибка «закупочная цена уже указана» (нельзя перезаписать закуп задним числом, для этого есть отдельный путь). Право — видеть закупочные цены.

Когда закуп проставлен, карточка заявки подтягивает закупочную цену (и минимальную закупочную со склада) прямо на верхний уровень. Интерфейс страницы согласования читает оттуда и показывает руководителю реальную маржу в строке заявки: «было 54 900, просят 49 900, закуп 38 200, маржа упадёт с 30.4% до 23.4%». Решение принимается на цифрах, а не на чутье.

Уведомления, события, журнал

Жизненный цикл заявки полностью прокачан через события и фоновые задачи. При создании возникает событие «создана», при одобрении или отклонении — «рассмотрена» (с указанием действия: одобрено/отклонено). Слушатели транслируют изменения в реальном времени, обновляют счётчик «новые согласования» в шапке у пользователей с правом одобрять, и пишут метрики.

Параллельно работает уведомление в двух режимах. «Создана»: рассылка всем с правом одобрять цены, текст содержит знак изменения («увеличение/уменьшение на N%»). «Рассмотрена»: точечно инициатору, текст «одобрена ✅» либо «отклонена ❌» с эмодзи. Для интеграции с Telegram/email — те же фоновые задачи отправки уведомлений.

Журнал — двухслойный. Автоматическое логирование пишет создание, изменение, удаление, восстановление, принудительное удаление с разницей полей. Параллельно мягкое удаление гарантирует, что удалённые заявки физически остаются — восстановление возможно. Это критично, если кто-то «спрятал» неудобную заявку: её всё равно найдут.

Сброс кэша работает на тот же события. Сбрасываются три области: согласования (списки и статистика), позиции заказа (карточка позиции), заказы (сводный счётчик согласований на заказе). После каждого одобрения или отклонения кэш на трёх уровнях освежается, и интерфейс показывает свежие данные на следующий запрос.

Фильтры, группировка, статистика, массовые действия

Десять параметров поиска: идентификатор (массив), позиция (массив), статус (массив значений «Ожидает», «Одобрена», «Отклонена», «Закрыта»), инициатор (массив), согласующий (массив или «без согласующего» для непросмотренных), диапазон дат создания, заказ (через связь с позицией с массивом идентификаторов), поиск (по названию товара, артикулу и номеру заказа), сортировка с направлением, группировка. Каждый параметр — отдельная обработка, легко расширяется.

Сортировка ограничена списком: идентификатор, дата создания, статус, текущая цена, запрошенная цена, разница в процентах. Направление — по возрастанию или убыванию. Поле вне списка игнорируется — защита от подмены параметров через сортировку, типичная уязвимость старых систем учёта. Дефолтная сортировка перезаписывается, что важно для группировки.

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

Действия «статистика» (счётчики по статусам с учётом фильтров) и «инициаторы» (список инициаторов с количествами) — для виджетов дашборда и отчётов. Массовые операции принимают список идентификаторов и общие параметры (комментарий согласующего, итоговая цена; причина отклонения). Система обходит список, вызывает одобрение или отклонение по одной (с сохранением всей валидации и журнала), и возвращает «одобрено столько-то, не прошло столько-то, перечень не прошедших с причинами». Интерфейс показывает таблицу «прошли» / «не прошли с причиной» — никакого «всё или ничего».

Готовы автоматизировать?

Покажем модуль на ваших данных и подключим за 1–2 недели.