Browse Source

Теперь дата завершения услуги расчитывается при подключении услуги

devel
bashmak 9 years ago
parent
commit
e2bee3aee2
  1. 66
      abonapp/models.py
  2. 11
      abonapp/views.py
  3. 6
      tariff_app/base_intr.py
  4. 34
      tariff_app/custom_tariffs.py
  5. 2
      tariff_app/models.py
  6. 4
      templates/abonapp/activate_service.html

66
abonapp/models.py

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils import timezone from django.utils import timezone
from django.utils.datetime_safe import datetime
from django.db import models from django.db import models
from django.core.validators import DecimalValidator from django.core.validators import DecimalValidator
from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult from agent import Transmitter, AbonStruct, TariffStruct, NasFailedResult
@ -10,14 +9,7 @@ from accounts_app.models import UserProfile
class LogicError(Exception): class LogicError(Exception):
def __init__(self, value):
self.message = value
def __str__(self):
return repr(self.message)
def __str__(self):
return repr(self.message)
pass
class AbonGroup(models.Model): class AbonGroup(models.Model):
@ -56,6 +48,9 @@ class AbonTariff(models.Model):
# время начала действия, остальные что не начали действие - NULL # время начала действия, остальные что не начали действие - NULL
time_start = models.DateTimeField(null=True, blank=True, default=None) time_start = models.DateTimeField(null=True, blank=True, default=None)
# время завершения услуги
deadline = models.DateTimeField(null=True, blank=True, default=None)
def priority_up(self): def priority_up(self):
# ищем услугу с большим приоритетом(число приоритета меньше) # ищем услугу с большим приоритетом(число приоритета меньше)
target_abtar = AbonTariff.objects.filter( target_abtar = AbonTariff.objects.filter(
@ -108,21 +103,24 @@ class AbonTariff(models.Model):
# Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации) # Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации)
def calc_amount_service(self): def calc_amount_service(self):
calc_obj = self.tariff.get_calc_type()
calc_obj = self.tariff.get_calc_type()(self)
# calc_obj - instance of tariff_app.custom_tariffs.TariffBase # calc_obj - instance of tariff_app.custom_tariffs.TariffBase
amount = calc_obj.calc_amount(self)
amount = calc_obj.calc_amount()
return round(amount, 2) return round(amount, 2)
# Активируем тариф # Активируем тариф
def activate(self, current_user): def activate(self, current_user):
calc_obj = self.tariff.get_calc_type()(self)
amnt = self.calc_amount_service() amnt = self.calc_amount_service()
# если не хватает денег # если не хватает денег
if self.abon.ballance < amnt: if self.abon.ballance < amnt:
raise LogicError('Не хватает денег на счету') raise LogicError('Не хватает денег на счету')
# дата активации услуги
# считаем дату активации услуги
self.time_start = timezone.now() self.time_start = timezone.now()
# считаем дату завершения услуги
self.deadline = calc_obj.calc_deadline()
# снимаем деньги за услугу # снимаем деньги за услугу
self.abon.make_pay(current_user, amnt)
self.abon.make_pay(current_user, amnt, u_comment='Завершение и оплата услуги по истечению срока действия')
self.save() self.save()
# Используется-ли услуга сейчас, если время старта есть то он активирован # Используется-ли услуга сейчас, если время старта есть то он активирован
@ -209,7 +207,7 @@ class Abon(UserProfile):
self.ballance += amount self.ballance += amount
# покупаем тариф # покупаем тариф
def buy_tariff(self, tariff, author, comment=None):
def pick_tariff(self, tariff, author, comment=None):
assert isinstance(tariff, Tariff) assert isinstance(tariff, Tariff)
# выбераем связь ТарифАбонент с самым низким приоритетом # выбераем связь ТарифАбонент с самым низким приоритетом
@ -241,47 +239,29 @@ class Abon(UserProfile):
def activate_next_tariff(self, author): def activate_next_tariff(self, author):
ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority') ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority')
nw = datetime.now(tz=timezone.get_current_timezone())
nw = timezone.datetime.now()
for at in ats: for at in ats:
# Если активированный тариф
if not at.is_started():
return
# время к началу месяца
to_start_month = datetime(nw.year, nw.month, 1, tzinfo=timezone.get_current_timezone())
# проверяем расстояние от Сегодня до начала этого месяца.
# И от заказа тарифа до начала этого месяца
if (nw - at.time_start) > (nw - to_start_month):
# Заказ из прошлого месяца, срок действия закончен
print('Заказ из прошлого месяца, срок действия закончен')
# если услуга просрочена
if nw > at.deadline:
print("Услуга просрочена, отключаем, и подключаем новую")
# выберем следующую по приоритету # выберем следующую по приоритету
# next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon) # next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)
next_tarifs = filter(lambda tr: tr.tariff_priority > at.tariff_priority, ats)[:2]
next_tarifs = [tr for tr in ats if tr.tariff_priority > at.tariff_priority][:2]
#next_tarifs = filter(lambda tr: tr.tariff_priority > at.tariff_priority, ats)[:2]
# и если что-нибудь вернулось то активируем, давая время начала действия
# и если что-нибудь из списка следующих услуг вернулось - то активируем
if len(next_tarifs) > 0: if len(next_tarifs) > 0:
next_tarifs[0].time_start = nw
next_tarifs[0].save(update_fields=['time_start'])
next_tarifs[0].activate(author)
# завершаем текущую услугу.
# удаляем запись о текущей услугу.
at.delete() at.delete()
# Создаём лог о завершении услуги
AbonLog.objects.create(
abon=self,
amount=0,
author=author,
comment='Завершение услуги по истечению срока действия'
)
# есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access() # есть-ли доступ у абонента к услуге, смотрим в tariff_app.custom_tariffs.<TariffBase>.manage_access()
def is_access(self): def is_access(self):
trf = self.active_tariff() trf = self.active_tariff()
if not trf: return False if not trf: return False
ct = trf.get_calc_type()
ct = trf.get_calc_type()()
if ct.manage_access(self): if ct.manage_access(self):
return True return True
else: else:
@ -311,7 +291,7 @@ class InvoiceForPayment(models.Model):
author = models.ForeignKey(UserProfile, related_name='+') author = models.ForeignKey(UserProfile, related_name='+')
def __str__(self): def __str__(self):
return "%s -> %d $" % (self.abon.username, self.amount)
return "%s -> %.2f" % (self.abon.username, self.amount)
def set_ok(self): def set_ok(self):
self.status = True self.status = True

11
abonapp/views.py

@ -285,6 +285,7 @@ def abonhome(request, gid, uid):
}) })
@mydefs.require_ssl
def terminal_pay(request): def terminal_pay(request):
from .pay_systems import allpay from .pay_systems import allpay
ret_text = allpay(request) ret_text = allpay(request)
@ -329,7 +330,7 @@ def add_invoice(request, gid, uid):
@login_required @login_required
@permission_required('abonapp.can_buy_tariff') @permission_required('abonapp.can_buy_tariff')
def buy_tariff(request, gid, uid):
def pick_tariff(request, gid, uid):
frm = None frm = None
grp = get_object_or_404(models.AbonGroup, id=gid) grp = get_object_or_404(models.AbonGroup, id=gid)
abon = get_object_or_404(models.Abon, id=uid) abon = get_object_or_404(models.Abon, id=uid)
@ -338,7 +339,7 @@ def buy_tariff(request, gid, uid):
frm = forms.BuyTariff(request.POST) frm = forms.BuyTariff(request.POST)
if frm.is_valid(): if frm.is_valid():
cd = frm.cleaned_data cd = frm.cleaned_data
abon.buy_tariff(cd['tariff'], request.user)
abon.pick_tariff(cd['tariff'], request.user)
#abon.save() #abon.save()
messages.success(request, 'Тариф успешно выбран') messages.success(request, 'Тариф успешно выбран')
return redirect('abonapp:abon_services', gid=gid, uid=abon.id) return redirect('abonapp:abon_services', gid=gid, uid=abon.id)
@ -433,7 +434,7 @@ def activate_service(request, gid, uid, srvid):
abtar.activate(request.user) abtar.activate(request.user)
messages.success(request, 'Услуга активирована') messages.success(request, 'Услуга активирована')
return redirect('abonapp:abon_home', gid, uid)
return redirect('abonapp:abon_services', gid, uid)
except NasFailedResult as e: except NasFailedResult as e:
messages.error(request, e) messages.error(request, e)
@ -441,12 +442,14 @@ def activate_service(request, gid, uid, srvid):
messages.warning(request, e) messages.warning(request, e)
except models.LogicError as e: except models.LogicError as e:
messages.error(request, e) messages.error(request, e)
calc_obj = abtar.tariff.get_calc_type()(abtar)
return render(request, 'abonapp/activate_service.html', { return render(request, 'abonapp/activate_service.html', {
'abon': abtar.abon, 'abon': abtar.abon,
'abon_group': abtar.abon.group, 'abon_group': abtar.abon.group,
'abtar': abtar, 'abtar': abtar,
'amount': amount, 'amount': amount,
'diff': abtar.abon.ballance - amount
'diff': abtar.abon.ballance - amount,
'deadline': calc_obj.calc_deadline()
}) })

6
tariff_app/base_intr.py

@ -5,12 +5,12 @@ from abc import ABCMeta, abstractmethod
class TariffBase(metaclass=ABCMeta): class TariffBase(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def calc_amount(self, abon_tariff):
def calc_amount(self):
"""Считает итоговую сумму платежа""" """Считает итоговую сумму платежа"""
@abstractmethod @abstractmethod
def get_avail_time(self):
"""Возвращает оставшееся время услуги в секундах"""
def calc_deadline(self):
"""Считаем дату, до которой действкет тариф"""
@staticmethod @staticmethod
def description(): def description():

34
tariff_app/custom_tariffs.py

@ -1,42 +1,46 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from datetime import timedelta
from django.utils import timezone from django.utils import timezone
from .base_intr import TariffBase from .base_intr import TariffBase
from calendar import monthrange
# from abonapp import AbonTariff # from abonapp import AbonTariff
class TariffDefault(TariffBase): class TariffDefault(TariffBase):
# Базовый функционал считает стоимость пропорционально использованному времени
def calc_amount(self, abon_tariff):
def __init__(self, abon_tariff):
#assert isinstance(abon_tariff, AbonTariff) #assert isinstance(abon_tariff, AbonTariff)
self.abon_tariff = abon_tariff
# Базовый функционал считает стоимость пропорционально использованному времени
def calc_amount(self):
# сейчас # сейчас
nw = timezone.now() nw = timezone.now()
# сколько прошло с начала действия услуги # сколько прошло с начала действия услуги
# если времени начала нет то это начало действия, использованное время 0 # если времени начала нет то это начало действия, использованное время 0
time_diff = nw - abon_tariff.time_start if abon_tariff.time_start else timedelta(0)
time_diff = nw - self.abon_tariff.time_start if self.abon_tariff.time_start else timedelta(0)
# времени в этом месяце # времени в этом месяце
curr_month_time = datetime(nw.year, nw.month if nw.month == 12 else nw.month + 1, 1) - timedelta(days=1)
curr_month_time = timezone.datetime(nw.year, nw.month if nw.month == 12 else nw.month + 1, 1) - timedelta(days=1)
curr_month_time = timedelta(days=curr_month_time.day) curr_month_time = timedelta(days=curr_month_time.day)
# Сколько это в процентах от всего месяца (k - коеффициент) # Сколько это в процентах от всего месяца (k - коеффициент)
k = time_diff.total_seconds() / curr_month_time.total_seconds() k = time_diff.total_seconds() / curr_month_time.total_seconds()
# результат - это полная стоимость тарифа умноженная на k # результат - это полная стоимость тарифа умноженная на k
res = k * abon_tariff.tariff.amount
res = k * self.abon_tariff.tariff.amount
return float(res) return float(res)
# возвращаем сколько времени осталось до завершения услуги (конца месяца)
def get_avail_time(self):
from calendar import monthrange
# Тут мы расчитываем конец действия услуги, завершение будет в конце месяца
def calc_deadline(self):
nw = timezone.now() nw = timezone.now()
last_day = monthrange(nw.year, nw.month)[1] last_day = monthrange(nw.year, nw.month)[1]
last_month_date = datetime(year=nw.year, month=nw.month, day=last_day,
hour=23,minute=59, second=59,tzinfo=nw.tzinfo)
return last_month_date - nw
last_month_date = timezone.datetime(year=nw.year, month=nw.month, day=last_day,
hour=23, minute=59, second=59)
return timezone.make_aware(last_month_date)
@staticmethod @staticmethod
def description(): def description():
@ -47,8 +51,8 @@ class TariffDp(TariffDefault):
# в IS снимается вся стоимость тарифа вне зависимости от времени использования # в IS снимается вся стоимость тарифа вне зависимости от времени использования
# просто возвращаем всю стоимость тарифа # просто возвращаем всю стоимость тарифа
def calc_amount(self, abon_tariff):
return float(abon_tariff.tariff.amount)
def calc_amount(self):
return float(self.abon_tariff.tariff.amount)
@staticmethod @staticmethod
def description(): def description():
@ -56,7 +60,7 @@ class TariffDp(TariffDefault):
class TariffCp(TariffDefault): class TariffCp(TariffDefault):
def calc_amount(self, abon_tariff):
def calc_amount(self):
return 12.6 return 12.6
@staticmethod @staticmethod

2
tariff_app/models.py

@ -28,7 +28,7 @@ class Tariff(models.Model):
if len(ob) > 0: if len(ob) > 0:
res_type = ob[0][1] res_type = ob[0][1]
assert issubclass(res_type, TariffBase) assert issubclass(res_type, TariffBase)
return res_type()
return res_type
def __str__(self): def __str__(self):
return "%s (%.2f)" % (self.title, self.amount) return "%s (%.2f)" % (self.title, self.amount)

4
templates/abonapp/activate_service.html

@ -24,7 +24,9 @@
<p>Вы уверены что хотите активировать абоненту эту услугу?<br> <p>Вы уверены что хотите активировать абоненту эту услугу?<br>
Обратите внимание что с его счёта <b>снимутся деньги</b> и откроется доступ к ресурсам оплаченной Обратите внимание что с его счёта <b>снимутся деньги</b> и откроется доступ к ресурсам оплаченной
услуги.<br> услуги.<br>
Стоимость услуги: {{ amount }}руб. На счету {{ abon.ballance }} руб, останется {{ diff }} руб.</p>
Стоимость услуги: {{ amount }}руб. На счету {{ abon.ballance }} руб, останется {{ diff }} руб.<br>
Услуга будет действовать до {{ deadline|date:'d F Y, H:i:s' }}
</p>
<button type="submit" class="btn btn-sm btn-primary"> <button type="submit" class="btn btn-sm btn-primary">
<span class="glyphicon glyphicon-save"></span> Подтвердить <span class="glyphicon glyphicon-save"></span> Подтвердить

Loading…
Cancel
Save