You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
14 KiB
351 lines
14 KiB
# -*- coding: utf-8 -*-
|
|
from django.core.exceptions import MultipleObjectsReturned
|
|
from django.http import Http404
|
|
from django.shortcuts import get_object_or_404
|
|
from django.utils import timezone
|
|
from django.utils.datetime_safe import datetime
|
|
from agent import get_TransmitterClientKlass, Abonent, Tariff as AgentTariff
|
|
from ip_pool.models import IpPoolItem
|
|
from tariff_app.models import Tariff
|
|
from django.db import models
|
|
from djing import settings
|
|
from django.core.validators import DecimalValidator
|
|
from accounts_app.models import UserProfile
|
|
|
|
|
|
class LogicError(Exception):
|
|
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __unicode__(self):
|
|
return repr(self.value)
|
|
|
|
|
|
class AbonGroup(models.Model):
|
|
title = models.CharField(max_length=127)
|
|
address = models.CharField(max_length=256, blank=True, null=True)
|
|
|
|
class Meta:
|
|
db_table = 'abonent_groups'
|
|
|
|
def __unicode__(self):
|
|
return self.title
|
|
|
|
|
|
class AbonLog(models.Model):
|
|
abon = models.ForeignKey('Abon')
|
|
amount = models.FloatField(default=0.0)
|
|
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
|
|
comment = models.CharField(max_length=128)
|
|
date = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
db_table = 'abonent_log'
|
|
|
|
def __unicode__(self):
|
|
return self.comment
|
|
|
|
|
|
class AbonTariffManager(models.Manager):
|
|
|
|
def update_priorities(self, abonent):
|
|
abon_tariff_list = AbonTariff.objects.filter(abon=abonent).order_by('tariff_priority')
|
|
|
|
# Обновляем приоритеты, чтоб по порядку были
|
|
at_pr = 0
|
|
for at in abon_tariff_list:
|
|
at.tariff_priority = at_pr
|
|
at_pr += 1
|
|
at.save()
|
|
|
|
|
|
class AbonTariff(models.Model):
|
|
abon = models.ForeignKey('Abon')
|
|
tariff = models.ForeignKey(Tariff, related_name='linkto_tariff')
|
|
tariff_priority = models.PositiveSmallIntegerField(default=0)
|
|
|
|
# время начала действия, остальные что не начали действие - NULL
|
|
time_start = models.DateTimeField(null=True, blank=True, default=None)
|
|
|
|
objects = AbonTariffManager()
|
|
|
|
def priority_up(self):
|
|
# ищем услугу с большим приоритетом(число приоритета меньше)
|
|
target_abtar = AbonTariff.objects.filter(
|
|
abon=self.abon,
|
|
tariff_priority__lt=self.tariff_priority
|
|
).order_by('-tariff_priority')[:1]
|
|
if target_abtar.count() > 0:
|
|
target_abtar = target_abtar[0]
|
|
else:
|
|
return
|
|
|
|
# Ищем текущий тариф абонента
|
|
active_abtar = AbonTariff.objects.filter(
|
|
abon=self.abon
|
|
)[:1]
|
|
if active_abtar.count() > 0:
|
|
active_abtar = active_abtar[0]
|
|
else:
|
|
return
|
|
|
|
# Если услуга с которой хотим поменяться приоритетом является текущей то нельзя меняться
|
|
if active_abtar == target_abtar:
|
|
return
|
|
|
|
# Swap приоритетов у текущего и найденного с меньшим tariff_priority (большим приоритетом)
|
|
tmp_prior = target_abtar.tariff_priority
|
|
target_abtar.tariff_priority = self.tariff_priority
|
|
target_abtar.save()
|
|
self.tariff_priority = tmp_prior
|
|
self.save()
|
|
|
|
def priority_down(self):
|
|
# ищем услугу с меньшим приоритетом
|
|
target_abtar = AbonTariff.objects.filter(
|
|
abon=self.abon,
|
|
tariff_priority__gt=self.tariff_priority
|
|
)[:1]
|
|
if target_abtar.count() > 0:
|
|
target_abtar = target_abtar[0]
|
|
else:
|
|
# меньше нет, это самая последняя услуга
|
|
return
|
|
|
|
# Swap приоритетов у текущего и найденного с большим tariff_priority (меньшим приоритетом)
|
|
tmp_pr = self.tariff_priority
|
|
self.tariff_priority = target_abtar.tariff_priority
|
|
target_abtar.tariff_priority = tmp_pr
|
|
target_abtar.save()
|
|
self.save()
|
|
|
|
# Считает текущую стоимость услуг согласно выбранной для тарифа логики оплаты (см. в документации)
|
|
def calc_amount_service(self):
|
|
calc_obj = self.tariff.get_calc_type()
|
|
# calc_obj - instance of tariff_app.custom_tariffs.TariffBase
|
|
amount = calc_obj.calc_amount(self)
|
|
return round(amount, 2)
|
|
|
|
# досрочно завершает услугу
|
|
def finish_and_activate_next_tariff(self, author):
|
|
|
|
# выберем следующие по приоритету услуги
|
|
next_tarifs = AbonTariff.objects.filter(tariff_priority__gt = self.tariff_priority, abon=self.abon)[:1]
|
|
|
|
if next_tarifs.count() < 1:
|
|
raise LogicError(u'У абонента нет следующих назначенных услуг')
|
|
|
|
# 0й элемент это следующая подключаемая услуга
|
|
next_tarifs[0].time_start = timezone.now()
|
|
next_tarifs[0].save()
|
|
|
|
# сколько денег стоят потраченные ресурсы
|
|
used_services = self.calc_amount_service()
|
|
|
|
#теперь к текущему баллансу добавляем сумму не потраченных ресурсов, т.к. полная сумма тарифа списывается при покупке тарифа
|
|
ret_amount = self.tariff.amount - used_services
|
|
self.abon.ballance += ret_amount
|
|
self.abon.save()
|
|
|
|
AbonLog.objects.create(
|
|
abon = self.abon,
|
|
amount = ret_amount,
|
|
author = author,
|
|
comment = u'Досрочное завершение услуги %s' % (self.tariff.title)
|
|
)
|
|
|
|
def __unicode__(self):
|
|
return "%d: '%s' - '%s'" % (
|
|
self.tariff_priority,
|
|
self.tariff.title,
|
|
self.abon.get_short_name()
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ('tariff_priority',)
|
|
db_table = 'abonent_tariff'
|
|
unique_together = (('abon', 'tariff', 'tariff_priority'),)
|
|
|
|
|
|
class Abon(UserProfile):
|
|
current_tariffs = models.ManyToManyField(Tariff, through=AbonTariff)
|
|
group = models.ForeignKey(AbonGroup, models.SET_NULL, blank=True, null=True)
|
|
ballance = models.FloatField(default=0.0, validators=[DecimalValidator])
|
|
ip_address = models.OneToOneField(IpPoolItem, on_delete=models.SET_NULL, null=True, blank=True)
|
|
address = models.CharField(max_length=256)
|
|
|
|
_act_tar_cache = None
|
|
|
|
def active_tariff(self):
|
|
if self._act_tar_cache:
|
|
return self._act_tar_cache
|
|
|
|
ats = AbonTariff.objects.filter(abon=self)[:1]
|
|
|
|
if ats.count() > 0:
|
|
self._act_tar_cache = ats[0].tariff
|
|
return ats[0].tariff
|
|
else:
|
|
self._act_tar_cache = None
|
|
return
|
|
|
|
def save_form(self, abonform_instance):
|
|
try:
|
|
cd = abonform_instance.cleaned_data
|
|
tel = cd['telephone']
|
|
self.username = cd['username'] or tel[1:]
|
|
self.fio = cd['fio']
|
|
self.telephone = tel
|
|
self.is_admin = False
|
|
self.ip_address = get_object_or_404(IpPoolItem, ip=cd['ip_address'])
|
|
self.is_active = True
|
|
self.group = cd['group']
|
|
self.address = cd['address']
|
|
except Http404:
|
|
raise LogicError(u'Введённый IP адрес не добавлен в ip pool')
|
|
except MultipleObjectsReturned:
|
|
raise LogicError(u'Введённый IP адрес не определён')
|
|
|
|
class Meta:
|
|
db_table = 'abonent'
|
|
|
|
def make_pay(self, curuser, how_match_to_pay=0.0):
|
|
AbonLog.objects.create(
|
|
abon = self,
|
|
amount = -how_match_to_pay,
|
|
author = curuser,
|
|
comment = u'Снятие со счёта средств'
|
|
)
|
|
self.ballance -= how_match_to_pay
|
|
|
|
def add_ballance(self, current_user, amount):
|
|
AbonLog.objects.create(
|
|
abon = self,
|
|
amount = amount,
|
|
author = current_user,
|
|
comment = u'Пополнение счёта через админку'
|
|
)
|
|
self.ballance += amount
|
|
|
|
def buy_tariff(self, tariff, author):
|
|
if self.ballance >= tariff.amount:
|
|
# денег достаточно, можно покупать
|
|
self.ballance -= tariff.amount
|
|
|
|
# выбераем связь ТарифАбонент с самым низким приоритетом
|
|
abtrf = AbonTariff.objects.filter(abon=self).order_by('-tariff_priority')[:1]
|
|
abtrf = abtrf[0] if abtrf.count() > 0 else None
|
|
|
|
# создаём новую связь с приоритетом ещё ниже
|
|
new_abtar = AbonTariff(
|
|
abon=self,
|
|
tariff=tariff,
|
|
tariff_priority=abtrf.tariff_priority+1 if abtrf else -1
|
|
)
|
|
|
|
# Если это первая услуга в списке (фильтр по приоритету ничего не вернул)
|
|
if not abtrf:
|
|
# значит она сразу стаёт активной
|
|
new_abtar.time_start = timezone.now()
|
|
|
|
new_abtar.save()
|
|
|
|
# шлём сигнал о том что абонент купил первую услугу, а значит можно пользоваться инетом
|
|
# сигнал можно слать только после того как будет сохранён новый объект AbonTariff
|
|
if self.is_active and not abtrf:
|
|
tc = get_TransmitterClientKlass()()
|
|
act_tar = self.active_tariff()
|
|
agent_abon = Abonent(
|
|
self.id,
|
|
self.ip_address.int_ip(),
|
|
AgentTariff(
|
|
act_tar.id if act_tar else 0,
|
|
act_tar.speedIn if act_tar else 0.0,
|
|
act_tar.speedOut if act_tar else 0.0
|
|
)
|
|
)
|
|
tc.signal_abon_refresh_info(agent_abon)
|
|
tc.signal_abon_open_inet(agent_abon)
|
|
|
|
# Запись об этом в лог
|
|
AbonLog.objects.create(
|
|
abon = self, amount = -tariff.amount,
|
|
author = author,
|
|
comment = u'Покупка тарифного плана через админку, тариф "%s"' % tariff.title
|
|
)
|
|
else:
|
|
raise LogicError(u'Недостаточно денег на счету абонента')
|
|
|
|
# Пробует подключить новую услугу если пришло время
|
|
def activate_next_tariff(self, author):
|
|
ats = AbonTariff.objects.filter(abon=self).order_by('tariff_priority')
|
|
|
|
nw = datetime.now(tz=timezone.get_current_timezone())
|
|
|
|
for at in ats:
|
|
# Если времени активации нет, то это ещё не активированный тариф
|
|
if not at.time_start:
|
|
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 u'Заказ из прошлого месяца, срок действия закончен'
|
|
|
|
# выберем следующую по приоритету
|
|
#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]
|
|
|
|
# и если что-нибудь вернулось то активируем, давая время начала действия
|
|
if next_tarifs.count() > 0:
|
|
next_tarifs[0].time_start = nw
|
|
next_tarifs[0].save()
|
|
|
|
# завершаем текущую услугу.
|
|
at.delete()
|
|
|
|
# Создаём лог о завершении услуги
|
|
AbonLog.objects.create(
|
|
abon = self,
|
|
amount = 0,
|
|
author = author,
|
|
comment = u'Завершение услуги по истечению срока действия'
|
|
)
|
|
|
|
|
|
class InvoiceForPayment(models.Model):
|
|
abon = models.ForeignKey(Abon)
|
|
status = models.BooleanField(default=False)
|
|
amount = models.FloatField(default=0.0)
|
|
comment = models.CharField(max_length=128)
|
|
date_create = models.DateTimeField(auto_now_add=True)
|
|
date_pay = models.DateTimeField(blank=True, null=True)
|
|
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+')
|
|
|
|
def __unicode__(self):
|
|
return "%s -> %d $" % (self.abon.username, self.amount)
|
|
|
|
def set_ok(self):
|
|
self.status = True
|
|
self.date_pay = datetime.now()
|
|
|
|
def get_prev_invoice(self):
|
|
return self.objects.order
|
|
|
|
class Meta:
|
|
ordering = ('date_create',)
|
|
db_table = 'abonent_inv_pay'
|
|
|
|
|
|
#def abon_save_signal(sender, instance, **kwargs):
|
|
# if not kwargs['created']:
|
|
# # if not create (change only)
|
|
# print "Kw1", instance.username, instance.is_active
|
|
|
|
|
|
#models.signals.post_save.connect(abon_save_signal, sender=Abon)
|