Каков алгоритм списания бонусов у клиента по истечению времени.
С каждой покупки клиенту начисляются n-% бонусов. клиент копит копит копит. Потом тратит. По истечению 2х лет бонусы начислены в самом начале должны сгорать. Помогите с алгоритмом и структурой в БД.
Нужно как то интересно их начислять и списывать.
Потому что в момент списания (использования) берется сумма всех бонусов накоплены клиентом и списывается на покупку А по истечению какого то времени нужно их сжигать в хронологическом порядке. от старого к новому.
PS: Если по истечению времени списывать столько же бонуса сколько начислено ( мы загоняем клиента в минус )
Нужно пропорционально списывать как то бонусы.
Вот бы еще понять как от старых к новым записям набирать и останавливать в нужный момент когда хватает для списания. И как крайний списывать.
т.е
Есть 3 купона предположим по 100 единиц. Списывать нужно 250. Как списать 2 купона в 0 и 1 на 50
Т.е сам процесс пока не укладывается как правильно списать сколько то купонов в 0 а крайний на остатки.
Константин МоргуновКонстантин, «как» – пошагово. Получить массив записей из БД отсортированных по dt_created по возрастанию из тех, что dt_void ещё не наступил и value_out - value_in > 0. При этом можно считать running sum и оставновиться, когда баллов наберётся больше, чем нужно для списания.
Из этих брать по одному, опять же от старых к новым и обновлять их. Сколько с самого старого (-100), обновили его value_out на 100. Сколько из следующего (опять -100). Сколько из третьего (-50), обновили его value_out на 50.
Всё это желательно делать целиком на стороне SQL и обернуть в транзакцию.
использовать метку сгорания. Причем, некоторые бонусы могут работать неделю, а некоторые и несколько месяцев.
количество бонусов | дата загрузки | дата сгорания | id удаленной транзакции.
И каждый день кроном списывать неактуальные бонусы в виде транзакции.
По крону не прокатит. У нас в одной таблице идет id трнзакции / наименование товара / сколько получено бонусов / сколько потрачено
то есть картина может быть такая.
3 / товар 3 100р / 10 / 50
2 / товар 2 200р / 20 / 50 ( в этой покупке на момент продажи эти самые 20 бонусов не активны т.е на момент продажи оно как бы 0 )
1 / товар 1 1000р / 100 / 0
Как их потом пропорционально списать ?
бонусы с 1 покупки (50) потратили на вторую покупку осталось 50 + 20 упало
На вторую покупку опять списали 50. Осталось 20 + 10 упало.
И предположим бонусы должны начать гореть. Самый первый бонус в виде 100. И никакой записи по идее нету о том что он плавно растекся на прошлые покупки. т.е я смотрю есть 100 списывали 0 дата подошла и спишу 100.. А по факту получается их всего 30 - 100 вот и минус
Ну хорошо, добавляете сколько бонусов потрачено из какого билета. не думаю что это проблема на стороне сервера вытащить карточки, доступные клиенту, и рассчитать на сервере все.
ид | бонусов пришло | родительский ид | дата создания | дата сгорания | тип транзакции
Например, создаете бонусные чеки
1 | 100 | null | 1 дек | 15 дек | бонусы
2 | 100 | null | 3 дек | 16 дек | бонусы
Далее, рассчитываетесь этими чеками , причем вам нужно будет указывать, к какому заказу это списание относится. нужно будет добавить в таблицу поля. 4 и 5 транзакции считаются на стороне сервера, и заполняются по мере списания определенных бонусов .
3 | -30 | null | 1 дек | 1 | оплата бонусами | заказ 1
4 | -70 | null | 1 дек | 1 | оплата бонусами | заказ 2
5 | -30 | null | 1 дек | 1 | оплата бонусами | заказ 2
Далее, мы можем выбрать доступные бонусные баллы, сделав join на таблицу, и получив сумму бонусов, дата которых еще не закончилась.
Дмитрий Байчапанов, Правильно ли я понимаю что таблицы 2 ? Одна таблица с начислением бонусов клиенту. А вторая со списаниями. 5 | -30 | null | 1 дек | 1 | оплата бонусами | заказ 2 ( что значит null и в 5 колонке 1 )
Логика проста:
1) вы создаете чек для погашения
2) вы постепенно погашаете заказ доступными чеками (на уровне сервера), маркируя, что транзакция относится к основной записи (как раз это и есть метка родительского элемента). для понимания, вы можете в родительский элемент насильно записать id транзакции. тогда расчеты будет делать немного проще.
3) вы закрываете, или ничего не трогаете с чеками, которые пропали.
select i.* , o.sum_paid, transaction_id from bonus i
right join (select sum(value) as sum_paid, transaction_id from bonus where transaction_id is not null group by transaction_id ) o on i.id=o.transaction_id
если вам нужны только актуальные бонусы, которые можно потратить, то добавляете
where date_end>'2017-09-12'
having sum>sum_paid
Или же вы можете отображать все бонусы, даже сгоревшие в ЛК пользователя. Просто делаете проверку на дату и на кол-во потраченных бонусов.
Например, делает запрос, и знаете, что у пользователя доступно 500 бонусов например.
Из них, человек например хочет потратить 300.
При генерации заказа, мы делаем выборку доступных бонусов у человека, и получаем следующий список
Бонус1 - доступно 40 бонусов
Бонус2 - доступно 50 бонусов
и тд
И по порядку делаем транзакции, чтобы покрыть 300 бонусов.
Дмитрий Байчапанов, Помогите до конца понять запрос.
таблица с колонками
id | sum_paid | transaction_id | date_start | date_end | comment
1 | | | | |
sum_paid (select sum(value) as sum_paid ) в вашем запросе это бонусы для начисления или списания ?
транзакция id это номер транзации для списания. ( не уникальное ) для списания бонусов частями с разных бонусов.
select i.* , o.sum_paid, transaction_id from bonus i
right join (select sum(value) as sum_paid, transaction_id from bonus where transaction_id is not null group by transaction_id ) o on i.id=o.transaction_id
Дмитрий Байчапанов, наконец то все встало на свои места. Осталось только понять как правильно расчитать количество купонов. Как эти купоны правильно спасать.
Например 3 купона 100 единиц. Клиент тратит 250 единиц. 2 купона списать в 0 и 1 на 50.
Вот, вам надо выписать 250 бонусов .
Вы достаете доступные чеки из базы сортируете их по мере того, когда они сгорят, и через сервер , списываете. В самом простом варианте,
Делаете while запрос на базу, с получением ближайшего свободного чека, и гасите его. Да запросов изначально много в этом варианте, но вы решаете задачу
Потом можно просто получать список доступных чеков и одним запросом в базу гасить их.
Запросом выбираешь остаток по ФИФО расписанный по датам начисления и списываешь те у которых дата больше года. PostgreSQL и Oracle позволяют получить такой остаток одним запросом.