Fixes on billing with metric
This commit is contained in:
parent
8f1d05873c
commit
5a031b81cb
|
@ -11,7 +11,7 @@ from . import settings
|
|||
|
||||
class MailSystemUserBackend(ServiceController):
|
||||
verbose_name = _("Mail system user")
|
||||
model = 'mail.Mailbox'
|
||||
model = 'mails.Mailbox'
|
||||
# TODO related_models = ('resources__content_type') ??
|
||||
|
||||
DEFAULT_GROUP = 'postfix'
|
||||
|
@ -66,7 +66,7 @@ class MailSystemUserBackend(ServiceController):
|
|||
|
||||
class PostfixAddressBackend(ServiceController):
|
||||
verbose_name = _("Postfix address")
|
||||
model = 'mail.Address'
|
||||
model = 'mails.Address'
|
||||
|
||||
def include_virtdomain(self, context):
|
||||
self.append(
|
||||
|
@ -140,7 +140,7 @@ class AutoresponseBackend(ServiceController):
|
|||
|
||||
|
||||
class MaildirDisk(ServiceMonitor):
|
||||
model = 'email.Mailbox'
|
||||
model = 'mails.Mailbox'
|
||||
resource = ServiceMonitor.DISK
|
||||
verbose_name = _("Maildir disk usage")
|
||||
|
||||
|
|
|
@ -185,10 +185,10 @@ _excluded_models = (MetricStorage, LogEntry, Order, ContentType, MigrationRecord
|
|||
def cancel_orders(sender, **kwargs):
|
||||
if sender not in _excluded_models:
|
||||
instance = kwargs['instance']
|
||||
if hasattr(instance, 'account'):
|
||||
if sender in services:
|
||||
for order in Order.objects.by_object(instance).active():
|
||||
order.cancel()
|
||||
else:
|
||||
elif not hasattr(instance, 'account'):
|
||||
related = helpers.get_related_objects(instance)
|
||||
if related and related != instance:
|
||||
Order.update_orders(related)
|
||||
|
@ -198,9 +198,9 @@ def cancel_orders(sender, **kwargs):
|
|||
def update_orders(sender, **kwargs):
|
||||
if sender not in _excluded_models:
|
||||
instance = kwargs['instance']
|
||||
if hasattr(instance, 'account'):
|
||||
if sender in services:
|
||||
Order.update_orders(instance)
|
||||
else:
|
||||
elif not hasattr(instance, 'account'):
|
||||
related = helpers.get_related_objects(instance)
|
||||
if related and related != instance:
|
||||
Order.update_orders(related)
|
||||
|
|
|
@ -8,6 +8,9 @@ from django.db.models import F
|
|||
from django.utils import timezone
|
||||
|
||||
from orchestra.apps.accounts.models import Account
|
||||
from orchestra.apps.mails.models import Mailbox
|
||||
from orchestra.apps.miscellaneous.models import MiscService, Miscellaneous
|
||||
from orchestra.apps.resources.models import Resource, ResourceData, MonitorData
|
||||
from orchestra.apps.services.models import Service, Plan
|
||||
from orchestra.apps.services import settings as services_settings
|
||||
from orchestra.apps.users.models import User
|
||||
|
@ -17,7 +20,7 @@ from orchestra.utils.tests import BaseTestCase, random_ascii
|
|||
class BaseBillingTest(BaseTestCase):
|
||||
def create_account(self):
|
||||
account = Account.objects.create()
|
||||
user = User.objects.create_user(username='rata_palida', account=account)
|
||||
user = User.objects.create_user(username='account_%s' % random_ascii(5), account=account)
|
||||
account.user = user
|
||||
account.save()
|
||||
return account
|
||||
|
@ -109,10 +112,13 @@ class FTPBillingTest(BaseBillingTest):
|
|||
order = service.orders.order_by('-id').first()
|
||||
self.assertEqual(first_bp, order.billed_until)
|
||||
self.assertEqual(decimal.Decimal(0), bills[0].get_total())
|
||||
|
||||
def test_ftp_account_with_rates(self):
|
||||
pass
|
||||
|
||||
|
||||
class DomainBillingTest(BaseBillingTest):
|
||||
def create_domain_service(self):
|
||||
from orchestra.apps.miscellaneous.models import MiscService, Miscellaneous
|
||||
service = Service.objects.create(
|
||||
description="Domain .ES",
|
||||
content_type=ContentType.objects.get_for_model(Miscellaneous),
|
||||
|
@ -136,7 +142,6 @@ class DomainBillingTest(BaseBillingTest):
|
|||
return service
|
||||
|
||||
def create_domain(self, account=None):
|
||||
from orchestra.apps.miscellaneous.models import MiscService, Miscellaneous
|
||||
if not account:
|
||||
account = self.create_account()
|
||||
domain_name = '%s.es' % random_ascii(10)
|
||||
|
@ -278,7 +283,6 @@ class TrafficBillingTest(BaseBillingTest):
|
|||
return self.resource
|
||||
|
||||
def report_traffic(self, account, date, value):
|
||||
from orchestra.apps.resources.models import ResourceData, MonitorData
|
||||
ct = ContentType.objects.get_for_model(Account)
|
||||
object_id = account.pk
|
||||
MonitorData.objects.create(monitor='FTPTraffic', content_object=account.user, value=value, date=date)
|
||||
|
@ -310,3 +314,147 @@ class TrafficBillingTest(BaseBillingTest):
|
|||
order.metrics.filter(id=3).update(updated_on=F('updated_on')-delta)
|
||||
bills = service.orders.bill(proforma=True)
|
||||
self.assertEqual(900, bills[0].get_total())
|
||||
|
||||
def test_multiple_traffics(self):
|
||||
service = self.create_traffic_service()
|
||||
resource = self.create_traffic_resource()
|
||||
account1 = self.create_account()
|
||||
account2 = self.create_account()
|
||||
|
||||
|
||||
class MailboxBillingTest(BaseBillingTest):
|
||||
def create_mailbox_service(self):
|
||||
service = Service.objects.create(
|
||||
description="Mailbox",
|
||||
content_type=ContentType.objects.get_for_model(Mailbox),
|
||||
match="True",
|
||||
billing_period=Service.ANUAL,
|
||||
billing_point=Service.FIXED_DATE,
|
||||
is_fee=False,
|
||||
metric='',
|
||||
pricing_period=Service.NEVER,
|
||||
rate_algorithm=Service.STEP_PRICE,
|
||||
on_cancel=Service.DISCOUNT,
|
||||
payment_style=Service.PREPAY,
|
||||
tax=0,
|
||||
nominal_price=10
|
||||
)
|
||||
plan = Plan.objects.create(is_default=True, name='Default')
|
||||
service.rates.create(plan=plan, quantity=1, price=0)
|
||||
service.rates.create(plan=plan, quantity=5, price=10)
|
||||
return service
|
||||
|
||||
def create_mailbox_disk_service(self):
|
||||
service = Service.objects.create(
|
||||
description="Mailbox disk",
|
||||
content_type=ContentType.objects.get_for_model(Mailbox),
|
||||
match="True",
|
||||
billing_period=Service.ANUAL,
|
||||
billing_point=Service.FIXED_DATE,
|
||||
is_fee=False,
|
||||
metric='max((mailbox.resources.disk.allocated or 0) -1, 0)',
|
||||
pricing_period=Service.NEVER,
|
||||
rate_algorithm=Service.STEP_PRICE,
|
||||
on_cancel=Service.DISCOUNT,
|
||||
payment_style=Service.PREPAY,
|
||||
tax=0,
|
||||
nominal_price=10
|
||||
)
|
||||
plan = Plan.objects.create(is_default=True, name='Default')
|
||||
service.rates.create(plan=plan, quantity=1, price=0)
|
||||
service.rates.create(plan=plan, quantity=2, price=10)
|
||||
return service
|
||||
|
||||
def create_disk_resource(self):
|
||||
self.resource = Resource.objects.create(
|
||||
name='disk',
|
||||
content_type=ContentType.objects.get_for_model(Mailbox),
|
||||
period=Resource.LAST,
|
||||
verbose_name='Mailbox disk',
|
||||
unit='GB',
|
||||
scale=10**9,
|
||||
ondemand=False,
|
||||
monitors='MaildirDisk',
|
||||
)
|
||||
return self.resource
|
||||
|
||||
def allocate_disk(self, mailbox, value):
|
||||
data = ResourceData.get_or_create(mailbox, self.resource)
|
||||
data.allocated = value
|
||||
data.save()
|
||||
|
||||
def create_mailbox(self, account=None):
|
||||
if not account:
|
||||
account = self.create_account()
|
||||
mailbox_name = '%s@orchestra.lan' % random_ascii(10)
|
||||
return Mailbox.objects.create(name=mailbox_name, account=account)
|
||||
|
||||
def test_mailbox_size(self):
|
||||
service = self.create_mailbox_service()
|
||||
disk_service = self.create_mailbox_disk_service()
|
||||
self.create_disk_resource()
|
||||
account = self.create_account()
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
self.allocate_disk(mailbox, 10)
|
||||
bill = service.orders.bill()[0]
|
||||
self.assertEqual(0, bill.get_total())
|
||||
bill = disk_service.orders.bill()[0]
|
||||
for line in bill.lines.all():
|
||||
for discount in line.sublines.all():
|
||||
print discount.__dict__
|
||||
self.assertEqual(80, bill.get_total())
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
mailbox = self.create_mailbox(account=account)
|
||||
bill = service.orders.bill()[0]
|
||||
print disk_service.orders.bill()[0].get_total()
|
||||
|
||||
|
||||
class JobBillingTest(BaseBillingTest):
|
||||
def create_job_service(self):
|
||||
service = Service.objects.create(
|
||||
description="Random job",
|
||||
content_type=ContentType.objects.get_for_model(Miscellaneous),
|
||||
match="miscellaneous.is_active and miscellaneous.service.name.lower() == 'job'",
|
||||
billing_period=Service.MONTHLY,
|
||||
billing_point=Service.FIXED_DATE,
|
||||
is_fee=False,
|
||||
metric='mailbox.resources.disk.allocated',
|
||||
pricing_period=Service.BILLING_PERIOD,
|
||||
rate_algorithm=Service.STEP_PRICE,
|
||||
on_cancel=Service.NOTHING,
|
||||
payment_style=Service.POSTPAY,
|
||||
tax=0,
|
||||
nominal_price=10
|
||||
)
|
||||
plan = Plan.objects.create(is_default=True, name='Default')
|
||||
service.rates.create(plan=plan, quantity=1, price=0)
|
||||
service.rates.create(plan=plan, quantity=11, price=10)
|
||||
return service
|
||||
|
||||
def create_job(self, account=None):
|
||||
if not account:
|
||||
account = self.create_account()
|
||||
job_name = '%s.es' % random_ascii(10)
|
||||
job_service, __ = MiscService.objects.get_or_create(name='job', description='Random job')
|
||||
return Miscellaneous.objects.create(service=job_service, description=job_name, account=account)
|
||||
|
||||
def test_job(self):
|
||||
pass
|
||||
|
||||
|
||||
class PlanBillingTest(BaseBillingTest):
|
||||
def create_plan_service(self):
|
||||
pass
|
||||
|
||||
def create_plan(self):
|
||||
if not account:
|
||||
account = self.create_account()
|
||||
domain_name = '%s.es' % random_ascii(10)
|
||||
domain_service, __ = MiscService.objects.get_or_create(name='domain .es', description='Domain .ES')
|
||||
return Miscellaneous.objects.create(service=domain_service, description=domain_name, account=account)
|
||||
|
||||
def test_plan(self):
|
||||
pass
|
||||
|
|
|
@ -80,7 +80,7 @@ class Resource(models.Model):
|
|||
return "{}-{}".format(str(self.content_type), self.name)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# created = not self.pk
|
||||
created = not self.pk
|
||||
super(Resource, self).save(*args, **kwargs)
|
||||
# Create Celery periodic task
|
||||
name = 'monitor.%s' % str(self)
|
||||
|
@ -100,8 +100,8 @@ class Resource(models.Model):
|
|||
elif task.crontab != self.crontab:
|
||||
task.crontab = self.crontab
|
||||
task.save()
|
||||
# if created:
|
||||
# create_resource_relation()
|
||||
if created:
|
||||
create_resource_relation()
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super(Resource, self).delete(*args, **kwargs)
|
||||
|
|
|
@ -218,7 +218,6 @@ class ServiceHandler(plugins.Plugin):
|
|||
return dsize, cend
|
||||
|
||||
def get_register_or_renew_events(self, porders, ini, end):
|
||||
# TODO count intermediat billing points too
|
||||
counter = 0
|
||||
for order in porders:
|
||||
bu = getattr(order, 'new_billed_until', order.billed_until)
|
||||
|
@ -251,13 +250,14 @@ class ServiceHandler(plugins.Plugin):
|
|||
price = self.get_price(account, metric, position=position, rates=rates)
|
||||
price = price * size
|
||||
cprice = price * (size-csize)
|
||||
if order in prices:
|
||||
if order in priced:
|
||||
priced[order][0] += price
|
||||
priced[order][1] += cprice
|
||||
else:
|
||||
priced[order] = (price, cprice)
|
||||
lines = []
|
||||
for order, prices in priced.iteritems():
|
||||
discounts = ()
|
||||
# Generate lines and discounts from order.nominal_price
|
||||
price, cprice = prices
|
||||
# Compensations > new_billed_until
|
||||
|
@ -351,8 +351,8 @@ class ServiceHandler(plugins.Plugin):
|
|||
porders = related_orders.pricing_orders(ini, end)
|
||||
porders = list(set(orders).union(set(porders)))
|
||||
porders.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
||||
if self.billing_period != self.NEVER and self.get_pricing_period == self.NEVER:
|
||||
liens = self.bill_concurrent_orders(account, porders, rates, ini, end, commit=commit)
|
||||
if self.billing_period != self.NEVER and self.get_pricing_period() == self.NEVER:
|
||||
lines = self.bill_concurrent_orders(account, porders, rates, ini, end, commit=commit)
|
||||
else:
|
||||
# TODO compensation in this case?
|
||||
lines = self.bill_registered_or_renew_events(account, porders, rates, commit=commit)
|
||||
|
@ -392,10 +392,29 @@ class ServiceHandler(plugins.Plugin):
|
|||
# weighted metric; bill line per pricing period
|
||||
prev = None
|
||||
lines_info = []
|
||||
for ini, end in self.get_pricing_slots(ini, bp):
|
||||
metric = order.get_metric(ini, end)
|
||||
price = self.get_price(order, metric)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
if self.billing_period != self.NEVER:
|
||||
if self.get_pricing_period() == self.NEVER:
|
||||
# Changes
|
||||
for ini, end, metric in order.get_metric(ini, end, changes=True)
|
||||
size = self.get_price_size(ini, end)
|
||||
price = self.get_price(order, metric)
|
||||
price = price * size
|
||||
# TODO metric and size in invoice (period and quantity)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
else:
|
||||
# pricing_slots
|
||||
for ini, end in self.get_pricing_slots(ini, bp):
|
||||
metric = order.get_metric(ini, end)
|
||||
price = self.get_price(order, metric)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
else:
|
||||
if self.get_pricing_period() == self.NEVER:
|
||||
# get metric
|
||||
metric = order.get_metric(ini, end)
|
||||
price = self.get_price(order, metric)
|
||||
lines.append(self.generate_line(order, price, metric, ini, end))
|
||||
else:
|
||||
raise NotImplementedError
|
||||
if commit:
|
||||
order.billed_until = order.new_billed_until
|
||||
order.save()
|
||||
|
|
Loading…
Reference in New Issue