MailboxBillingTest tests passing

This commit is contained in:
Marc 2014-09-23 14:01:58 +00:00
parent 5a031b81cb
commit 259bc07b71
4 changed files with 131 additions and 57 deletions

View file

@ -35,7 +35,7 @@ class BillsBackend(object):
# Create bill line
billine = bill.lines.create(
rate=service.nominal_price,
quantity=line.size,
quantity=line.metric*line.size,
subtotal=line.subtotal,
tax=service.tax,
description=self.get_line_description(line),
@ -54,11 +54,14 @@ class BillsBackend(object):
service = line.order.service
if service.is_fee:
return self.format_period(line.ini, line.end)
else:
description = line.order.description
if service.billing_period != service.NEVER:
description += " %s" % self.format_period(line.ini, line.end)
return description
description = line.order.description
if service.billing_period != service.NEVER:
description += " %s" % self.format_period(line.ini, line.end)
if service.metric and service.billing_period != service.NEVER and service.pricing_period == service.NEVER:
metric = format(line.metric, '.2f').rstrip('0').rstrip('.')
size = format(line.size, '.2f').rstrip('0').rstrip('.')
description += " (%s*%s)" % (metric, size)
return description
def create_sublines(self, line, discounts):
for discount in discounts:

View file

@ -141,14 +141,32 @@ class Order(models.Model):
self.save()
logger.info("CANCELLED order id: {id}".format(id=self.id))
def get_metric(self, ini, end):
return MetricStorage.get(self, ini, end)
def get_metric(self, ini, end, changes=False):
if changes:
result = []
prev = None
for metric in self.metrics.filter(created_on__lt=end).order_by('created_on'):
created = metric.created_on.date()
if created > ini:
cini = prev.created_on.date()
if not result:
cini = ini
result.append((cini, created, prev.value))
prev = metric
if created < end:
result.append((created, end, metric.value))
return result
try:
metrics = self.metrics.filter(updated_on__lt=end, updated_on__gte=ini)
return metrics.latest('updated_on').value
except MetricStorage.DoesNotExist:
return decimal.Decimal(0)
class MetricStorage(models.Model):
order = models.ForeignKey(Order, verbose_name=_("order"), related_name='metrics')
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
created_on = models.DateField(_("created"), auto_now_add=True)
created_on = models.DateTimeField(_("created"), auto_now_add=True)
updated_on = models.DateTimeField(_("updated"))
class Meta:
@ -170,25 +188,19 @@ class MetricStorage(models.Model):
else:
metric.updated_on = now
metric.save()
@classmethod
def get(cls, order, ini, end):
try:
return order.metrics.filter(updated_on__lt=end, updated_on__gte=ini).latest('updated_on').value
except cls.DoesNotExist:
return decimal.Decimal(0)
_excluded_models = (MetricStorage, LogEntry, Order, ContentType, MigrationRecorder.Migration)
@receiver(post_delete, dispatch_uid="orders.cancel_orders")
def cancel_orders(sender, **kwargs):
if sender not in _excluded_models:
instance = kwargs['instance']
if sender in services:
if hasattr(instance, 'account'):
for order in Order.objects.by_object(instance).active():
order.cancel()
elif not hasattr(instance, 'account'):
else:
related = helpers.get_related_objects(instance)
if related and related != instance:
Order.update_orders(related)
@ -198,12 +210,13 @@ def cancel_orders(sender, **kwargs):
def update_orders(sender, **kwargs):
if sender not in _excluded_models:
instance = kwargs['instance']
if sender in services:
if hasattr(instance, 'account'):
Order.update_orders(instance)
elif not hasattr(instance, 'account'):
else:
related = helpers.get_related_objects(instance)
if related and related != instance:
Order.update_orders(related)
accounts.register(Order)

View file

@ -311,7 +311,9 @@ class TrafficBillingTest(BaseBillingTest):
self.assertEqual(0, bills[0].get_total())
self.report_traffic(account, date, 10**10*9)
order.metrics.filter(id=3).update(updated_on=F('updated_on')-delta)
metric = order.metrics.latest()
metric.updated_on -= delta
metric.save()
bills = service.orders.bill(proforma=True)
self.assertEqual(900, bills[0].get_total())
@ -320,6 +322,11 @@ class TrafficBillingTest(BaseBillingTest):
resource = self.create_traffic_resource()
account1 = self.create_account()
account2 = self.create_account()
# TODO
def test_traffic_prepay(self):
pass
# TODO
class MailboxBillingTest(BaseBillingTest):
@ -361,8 +368,7 @@ class MailboxBillingTest(BaseBillingTest):
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)
service.rates.create(plan=plan, quantity=1, price=10)
return service
def create_disk_resource(self):
@ -398,18 +404,56 @@ class MailboxBillingTest(BaseBillingTest):
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())
bp = timezone.now().date() + relativedelta.relativedelta(years=1)
bill = disk_service.orders.bill(billing_point=bp, fixed_point=True)[0]
self.assertEqual(90, 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()
mailbox = self.create_mailbox(account=account)
bill = service.orders.bill(billing_point=bp, fixed_point=True)[0]
self.assertEqual(120, bill.get_total())
def test_mailbox_size_with_changes(self):
service = self.create_mailbox_disk_service()
self.create_disk_resource()
account = self.create_account()
mailbox = self.create_mailbox(account=account)
now = timezone.now()
bp = now.date() + relativedelta.relativedelta(years=1)
self.allocate_disk(mailbox, 10)
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
self.assertEqual(9*10, bill.get_total())
self.allocate_disk(mailbox, 20)
created_on = now+relativedelta.relativedelta(months=6)
order = service.orders.get()
metric = order.metrics.latest('id')
metric.created_on = created_on
metric.save()
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
self.assertEqual(9*10*0.5 + 19*10*0.5, bill.get_total())
self.allocate_disk(mailbox, 30)
created_on = now+relativedelta.relativedelta(months=9)
order = service.orders.get()
metric = order.metrics.latest('id')
metric.created_on = created_on
metric.save()
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
self.assertEqual(9*10*0.5 + 19*10*0.25 + 29*10*0.25, bill.get_total())
self.allocate_disk(mailbox, 10)
created_on = now+relativedelta.relativedelta(years=1)
order = service.orders.get()
metric = order.metrics.latest('id')
metric.created_on = created_on
metric.save()
bill = service.orders.bill(billing_point=bp, fixed_point=True, proforma=True, new_open=True)[0]
self.assertEqual(9*10*0.5 + 19*10*0.25 + 29*10*0.25, bill.get_total())
class JobBillingTest(BaseBillingTest):
@ -418,10 +462,10 @@ class JobBillingTest(BaseBillingTest):
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,
billing_period=Service.NEVER,
billing_point=Service.ON_REGISTER,
is_fee=False,
metric='mailbox.resources.disk.allocated',
metric='miscellaneous.amount',
pricing_period=Service.BILLING_PERIOD,
rate_algorithm=Service.STEP_PRICE,
on_cancel=Service.NOTHING,
@ -434,15 +478,19 @@ class JobBillingTest(BaseBillingTest):
service.rates.create(plan=plan, quantity=11, price=10)
return service
def create_job(self, account=None):
def create_job(self, amount, 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)
job_service, __ = MiscService.objects.get_or_create(name='job', description='Random job', has_amount=True)
return Miscellaneous.objects.create(service=job_service, description=job_name, account=account, amount=amount)
def test_job(self):
pass
service = self.create_job_service()
account = self.create_account()
job = self.create_job(10, account=account)
print service.orders.all()
print service.orders.bill()[0].get_total()
class PlanBillingTest(BaseBillingTest):

View file

@ -150,14 +150,29 @@ class ServiceHandler(plugins.Plugin):
'total': price,
}))
def generate_line(self, order, price, size, ini, end, discounts=[]):
subtotal = self.nominal_price * size
def generate_line(self, order, price, *dates, **kwargs):
if len(dates) == 2:
ini, end = dates
elif len(dates) == 1:
ini, end = dates[0], dates[0]
else:
raise AttributeError
metric = kwargs.pop('metric', 1)
discounts = kwargs.pop('discounts', ())
computed = kwargs.pop('computed', False)
if kwargs:
raise AttributeError
size = self.get_price_size(ini, end)
if not computed:
price = price * size
subtotal = self.nominal_price * size * metric
line = AttributeDict(**{
'order': order,
'subtotal': subtotal,
'size': size,
'ini': ini,
'end': end,
'size': size,
'metric': metric,
'discounts': [],
})
discounted = 0
@ -249,7 +264,7 @@ class ServiceHandler(plugins.Plugin):
csize += self.get_price_size(intersect.ini, intersect.end)
price = self.get_price(account, metric, position=position, rates=rates)
price = price * size
cprice = price * (size-csize)
cprice = price * csize
if order in priced:
priced[order][0] += price
priced[order][1] += cprice
@ -269,7 +284,7 @@ class ServiceHandler(plugins.Plugin):
size = self.get_price_size(order.new_billed_until, new_end)
price += price*size
order.new_billed_until = new_end
line = self.generate_line(order, price, size, ini, end, discounts=discounts)
line = self.generate_line(order, price, ini, new_end or end, discounts=discounts, computed=True)
lines.append(line)
if commit:
order.billed_until = order.new_billed_until
@ -302,8 +317,7 @@ class ServiceHandler(plugins.Plugin):
order.new_billed_until = new_end
end = new_end
size = self.get_price_size(ini, end)
price = price * size
line = self.generate_line(order, price, size, ini, end, discounts=discounts)
line = self.generate_line(order, price, ini, end, discounts=discounts)
lines.append(line)
if commit:
order.billed_until = order.new_billed_until
@ -322,6 +336,7 @@ class ServiceHandler(plugins.Plugin):
bp = None
ini = datetime.date.max
end = datetime.date.min
# TODO compensation with one time billing?
for order in orders:
cini = order.registered_on
if order.billed_until:
@ -370,8 +385,7 @@ class ServiceHandler(plugins.Plugin):
if new_end:
order.new_billed_until = new_end
end = new_end
size = self.get_price_size(ini, end)
line = self.generate_line(order, price*size, size, ini, end, discounts=discounts)
line = self.generate_line(order, price, ini, end, discounts=discounts)
lines.append(line)
if commit:
order.billed_until = order.new_billed_until
@ -379,40 +393,36 @@ class ServiceHandler(plugins.Plugin):
return lines
def bill_with_metric(self, orders, account, **options):
# TODO filter out orders with cancelled_on < billed_until ?
lines = []
commit = options.get('commit', True)
bp = None
for order in orders:
if order.billed_until and order.cancelled_on >= order.billed_until:
continue
bp = self.get_billing_point(order, bp=bp, **options)
ini = order.billed_until or order.registered_on
if bp <= ini:
# TODO except one time service
continue
order.new_billed_until = bp
# weighted metric; bill line per pricing period
prev = None
lines_info = []
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)
for ini, end, metric in order.get_metric(ini, bp, changes=True):
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))
lines.append(self.generate_line(order, price, ini, end, metric=metric))
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))
lines.append(self.generate_line(order, price, ini, end, metric=metric))
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))
lines.append(self.generate_line(order, price, ini, bp, metric=metric))
else:
raise NotImplementedError
if commit: