Скидки и надбавки
iikoRms имеет встроенную дисконтную систему. Сторонние системы лояльности (plazius, iikoCard и прочие) в данной статье не рассматриваются.
Дисконтная система iikoRms позволяет начислять скидки и надбавки по различным условиям.
Под скидкой понимается уменьшение стоимости элемента заказа на определённую сумму, а под набавкой — увеличение. Поскольку разница между скидкой и надбавкой заключается лишь в знаке числа, вычитаемого из стоимости, для упрощения терминологии и то, и другое будем называть скидками (в коде DiscountType
, DiscountItem
, DiscountCard
и так далее), подразумевая, что положительное значение DiscountSum
в предметной области соответствует скидке, а отрицательное — надбавке.
Вообще говоря, скидку можно настроить таким образом, что в пределах заказа на одно блюдо она будет снижать стоимость, то есть действовать как скидка, а на другое — повышать, то есть действовать как надбавка, поэтому разделение на скидки и надбавки можно считать весьма условным.
Ограничения
Правила и условия применения скидок (DiscountItem
) определяются типом (DiscountType
), это может быть фиксированная сумма (ваучер), процент от стоимости, округление (отбрасывание копеек) и так далее, но результат применения скидки (AppliedDiscountItem
) — это всегда абсолютная величина, конкретное число.
Алгоритмы применения скидок зависят от категории блюда, отделения заказа, суммы заказа (FullSum
), режима обслуживания, текущего времени и времени сервисной печати, скидки могут комбинироваться или не комбинироваться между собой, применяться как параллельно (к полной стоимости), так и последовательно (с учётом применения предыдущих скидок), все эти условия являются деталью реализации и не опубликованы в API.
В API доступны лишь общие настройки, такие как:
IsActive
— действует ли скидка в текущей группе,IsAutomatic
— может ли скидка добавляться в заказы автоматически,CanApplyManually
— может ли скидка быть добавлена в заказ вручную, путём выбора из списка,CanApplyByCardNumber
— может ли скидка быть добавлена в заказ по номеру дисконтной карты (путём ввода номера карты на цифровой клавиатуре),CanApplyByDiscountCard
— может ли скидка быть добавлена в заказ по дисконтной карте (путём считывания трека карты),DiscountByFlexibleSum
— требуется ли при добавлении скидки в заказ указать её сумму,CanApplySelectively
— можно ли выборочно применить скидку к нескольким элементам заказа.
Сумма скидки вычисляется отдельно для каждого элемента заказа, даже если скидка действует на весь заказ. Таким образом, скидка на заказ — это сумма скидок на его элементы. Сумма скидки, как и любая другая денежная величина, кратна минимальному номиналу валюты. Сумма всех действующих на элемент заказа скидок не превышает его стоимости, иными словами, результат вычитания скидок из стоимости может достичь нуля (скидка 100%), но отрицательным стать не может. Для надбавок такого ограничения нет, к стоимости элемента заказа можно прибавить сколь угодно большое значение.
Жизненный цикл
Добавленная в заказ скидка может находиться в одном из двух состояний — зафиксирована, либо не зафиксирована.
Возможны переходы между этими состояниями, но напрямую управлять этим нельзя, состояние привязано к жизненному циклу заказа.
Сейчас скидки фиксируются при переводе заказа в статус Bill
и возвращаются в незафиксированное состояние при переводе в статус New
, но это деталь реализации, которая может измениться в любой момент.
Скидка не зафиксирована
При добавлении скидки в заказ запоминаются данные лишь о том, как её вычислять. Суммы вычисляются на лету по требованию, результаты вычислений не запоминаются. Поскольку при каждом вычислении используются последние доступные значения параметров алгоритма применения скидки, включая настройки из iikoOffice и текущее время, результаты могут получаться разные. Соответственно, может меняться и итоговая стоимость заказа.
Автоматически добавляемые скидки (IsAutomatic
) неявно присутствуют во всех заказах с незафиксированными скидками, до фиксации их не будет в списке Discounts
каждого заказа, а после фиксации — зависит от наличия эффекта применения к заказу.
Скидка зафиксирована
При достижении заказом момента, когда его итоговая стоимость больше не должна меняться, вычисляемые на лету значения (цены, скидки и т. п.) фиксируются. Результат последнего вычисления сохраняется и используется при последующих обращениях вместо повторного вычисления. Это позволяет не зависеть от изменения настроек или других параметров применения скидок. С этого момента помимо алгоритма и параметров применения скидки запоминаются конкретные суммы по каждому элементу заказа. Однако, если на момент фиксации скидка фактически не действовала (имела нулевой эффект), она удаляется из заказа.
Структуры данных
DiscountType
— тип скидки, элемент справочника скидок. Весь справочник можно получить, вызвав методGetDiscountTypes
.DiscountItem
— добавленная в заказ скидка. Скидку в заказ можно добавить методамиAddDiscount
,AddFlexibleSumDiscount
,AddDiscountByCardNumber
,AddFlexibleSumDiscountByCardNumber
. Удалить скидку можно с помощьюDeleteDiscount
. Список добавленных в заказ скидок —Discounts
.AppliedDiscountItem
— результат применения ранее добавленной в заказ скидки, можно получить с помощью методаGetOrderAppliedDiscounts
, при этом скидки могут быть применены на лету в момент запроса, либо может быть возвращён результат предыдущего применения, если он был зафиксирован в заказе.FullSum
— полная стоимость заказа без учёта скидок (подытог).ResultSum
— итоговая стоимость заказа с учётом скидок.
Примечание: не следует определять суммарную скидку по заказу как разницу между двумя последними значениями, поскольку они отличаются не только учётом скидок, но и учётом налога на добавленную стоимость (НДС, VAT), не включённого в цену товаров. Сумму скидок лучше считать, суммируя DiscountSum
.
Выборочное применение скидок
По умолчанию скидки действуют на все блюда, включая те, что добавлены после назначения скидки.
Однако, скидки с включённой настройкой CanApplySelectively
можно выборочно применить к одному или нескольким элементам заказа, и тогда они будут действовать только на них.
Например, при торговле выпечкой ещё горячие изделия могут продаваться по полной стоимости, а те же самые, но уже остывшие — со скидкой.
Элемент номенклатуры в обоих случаях один и тот же.
Решение принимает пользователь (или плагин), задавая ограничение, на какие блюда и модификаторы будет действовать скидка.
Это ограничение работает как белый список (whitelist), поэтому выборочно применённая скидка не будет действовать на блюда, добавленные позднее.
Все прочие ограничения (по категории, по времени, по сумме и т. п.) остаются в силе, то есть фактически скидка подействует только на те элементы белого списка, которые удовлетворяют и прочим условиям.
Выборочно применённая скидка действует только непосредственно на заданные элементы заказа. Например, если в заказе есть блюдо с платными модификаторами, и скидка выборочно применена только к блюду, только на блюдо она и будет действовать. Чтобы выборочно применить скидку и к блюдам, и к модификаторам, необходимо явно задать и блюдо, и модификаторы.
Ключевые функции:
ChangeSelectiveDiscount
— позволяет задать белый список элементов заказа, на которые может действовать скидка. Учитывая разные типы элементов, фактически это три списка — простых блюд, компонентов составных блюд и модификаторов. Если все три списка задатьnull
, скидка перестанет применяться выборочно, будет действовать на весь заказ.GetSelectiveDiscountItemSettings
— метод возвращает параметры выборочного применения (те самые три списка), либоnull
, если скидка действует на весь заказ без ограничения по элементам.IsSelectivelyApplied
— показывает, применяется ли скидка выборочно или на весь заказ.
Примеры
Пример выборочного применения скидки можно посмотреть во входящем в состав SDK плагине Resto.Front.Api.SamplePlugin (EditorTester.AddSelectiveDiscount
):
/// <summary>
/// Добавление скидки с возможностью выбора блюд.
/// </summary>
private void AddSelectiveDiscount()
{
var order = PluginContext.Operations.GetOrders().Last();
var selectedDish = new List<IOrderProductItemStub> { order.Items.OfType<IOrderProductItem>().Last() };
var discountType = PluginContext.Operations.GetDiscountTypes().Last(x => !x.Deleted && x.IsActive && x.CanApplySelectively);
var editSession = PluginContext.Operations.CreateEditSession();
editSession.AddDiscount(discountType, order);
editSession.ChangeSelectiveDiscount(order, discountType, selectedDish, null, null);
PluginContext.Operations.SubmitChanges(PluginContext.Operations.GetCredentials(), editSession);
}