JobBillingTest tests passing
This commit is contained in:
parent
259bc07b71
commit
c0e8e9f85d
3
TODO.md
3
TODO.md
|
@ -106,3 +106,6 @@ at + clock time, midnight, noon- At 3:30 p.m., At 4:01, At noon
|
||||||
@property
|
@property
|
||||||
def register_on(self):
|
def register_on(self):
|
||||||
return order.register_at.date()
|
return order.register_at.date()
|
||||||
|
|
||||||
|
|
||||||
|
* latest by 'id' *always*
|
||||||
|
|
|
@ -141,14 +141,15 @@ class Order(models.Model):
|
||||||
self.save()
|
self.save()
|
||||||
logger.info("CANCELLED order id: {id}".format(id=self.id))
|
logger.info("CANCELLED order id: {id}".format(id=self.id))
|
||||||
|
|
||||||
def get_metric(self, ini, end, changes=False):
|
def get_metric(self, *args, **kwargs):
|
||||||
if changes:
|
if kwargs.pop('changes', False):
|
||||||
|
ini, end = args
|
||||||
result = []
|
result = []
|
||||||
prev = None
|
prev = None
|
||||||
for metric in self.metrics.filter(created_on__lt=end).order_by('created_on'):
|
for metric in self.metrics.filter(created_on__lt=end).order_by('id'):
|
||||||
created = metric.created_on.date()
|
created = metric.created_on
|
||||||
if created > ini:
|
if created > ini:
|
||||||
cini = prev.created_on.date()
|
cini = prev.created_on
|
||||||
if not result:
|
if not result:
|
||||||
cini = ini
|
cini = ini
|
||||||
result.append((cini, created, prev.value))
|
result.append((cini, created, prev.value))
|
||||||
|
@ -156,8 +157,20 @@ class Order(models.Model):
|
||||||
if created < end:
|
if created < end:
|
||||||
result.append((created, end, metric.value))
|
result.append((created, end, metric.value))
|
||||||
return result
|
return result
|
||||||
try:
|
if kwargs:
|
||||||
|
raise AttributeError
|
||||||
|
if len(args) == 2:
|
||||||
|
ini, end = args
|
||||||
metrics = self.metrics.filter(updated_on__lt=end, updated_on__gte=ini)
|
metrics = self.metrics.filter(updated_on__lt=end, updated_on__gte=ini)
|
||||||
|
elif len(args) == 1:
|
||||||
|
date = args[0]
|
||||||
|
metrics = self.metrics.filter(updated_on__year=date.year,
|
||||||
|
updated_on__month=date.month, updated_on__day=date.day)
|
||||||
|
elif not args:
|
||||||
|
return self.metrics.latest('updated_on').value
|
||||||
|
else:
|
||||||
|
raise AttributeError
|
||||||
|
try:
|
||||||
return metrics.latest('updated_on').value
|
return metrics.latest('updated_on').value
|
||||||
except MetricStorage.DoesNotExist:
|
except MetricStorage.DoesNotExist:
|
||||||
return decimal.Decimal(0)
|
return decimal.Decimal(0)
|
||||||
|
@ -166,11 +179,11 @@ class Order(models.Model):
|
||||||
class MetricStorage(models.Model):
|
class MetricStorage(models.Model):
|
||||||
order = models.ForeignKey(Order, verbose_name=_("order"), related_name='metrics')
|
order = models.ForeignKey(Order, verbose_name=_("order"), related_name='metrics')
|
||||||
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
|
value = models.DecimalField(_("value"), max_digits=16, decimal_places=2)
|
||||||
created_on = models.DateTimeField(_("created"), auto_now_add=True)
|
created_on = models.DateField(_("created"), auto_now_add=True)
|
||||||
updated_on = models.DateTimeField(_("updated"))
|
updated_on = models.DateTimeField(_("updated"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'created_on'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return unicode(self.order)
|
return unicode(self.order)
|
||||||
|
|
|
@ -38,7 +38,7 @@ class FTPBillingTest(BaseBillingTest):
|
||||||
metric='',
|
metric='',
|
||||||
pricing_period=Service.NEVER,
|
pricing_period=Service.NEVER,
|
||||||
rate_algorithm=Service.STEP_PRICE,
|
rate_algorithm=Service.STEP_PRICE,
|
||||||
on_cancel=Service.DISCOUNT,
|
on_cancel=Service.COMPENSATE,
|
||||||
payment_style=Service.PREPAY,
|
payment_style=Service.PREPAY,
|
||||||
tax=0,
|
tax=0,
|
||||||
nominal_price=10,
|
nominal_price=10,
|
||||||
|
@ -269,7 +269,6 @@ class TrafficBillingTest(BaseBillingTest):
|
||||||
return service
|
return service
|
||||||
|
|
||||||
def create_traffic_resource(self):
|
def create_traffic_resource(self):
|
||||||
from orchestra.apps.resources.models import Resource
|
|
||||||
self.resource = Resource.objects.create(
|
self.resource = Resource.objects.create(
|
||||||
name='traffic',
|
name='traffic',
|
||||||
content_type=ContentType.objects.get_for_model(Account),
|
content_type=ContentType.objects.get_for_model(Account),
|
||||||
|
@ -341,7 +340,7 @@ class MailboxBillingTest(BaseBillingTest):
|
||||||
metric='',
|
metric='',
|
||||||
pricing_period=Service.NEVER,
|
pricing_period=Service.NEVER,
|
||||||
rate_algorithm=Service.STEP_PRICE,
|
rate_algorithm=Service.STEP_PRICE,
|
||||||
on_cancel=Service.DISCOUNT,
|
on_cancel=Service.COMPENSATE,
|
||||||
payment_style=Service.PREPAY,
|
payment_style=Service.PREPAY,
|
||||||
tax=0,
|
tax=0,
|
||||||
nominal_price=10
|
nominal_price=10
|
||||||
|
@ -467,15 +466,15 @@ class JobBillingTest(BaseBillingTest):
|
||||||
is_fee=False,
|
is_fee=False,
|
||||||
metric='miscellaneous.amount',
|
metric='miscellaneous.amount',
|
||||||
pricing_period=Service.BILLING_PERIOD,
|
pricing_period=Service.BILLING_PERIOD,
|
||||||
rate_algorithm=Service.STEP_PRICE,
|
rate_algorithm=Service.MATCH_PRICE,
|
||||||
on_cancel=Service.NOTHING,
|
on_cancel=Service.NOTHING,
|
||||||
payment_style=Service.POSTPAY,
|
payment_style=Service.POSTPAY,
|
||||||
tax=0,
|
tax=0,
|
||||||
nominal_price=10
|
nominal_price=20
|
||||||
)
|
)
|
||||||
plan = Plan.objects.create(is_default=True, name='Default')
|
plan = Plan.objects.create(is_default=True, name='Default')
|
||||||
service.rates.create(plan=plan, quantity=1, price=0)
|
service.rates.create(plan=plan, quantity=1, price=20)
|
||||||
service.rates.create(plan=plan, quantity=11, price=10)
|
service.rates.create(plan=plan, quantity=10, price=15)
|
||||||
return service
|
return service
|
||||||
|
|
||||||
def create_job(self, amount, account=None):
|
def create_job(self, amount, account=None):
|
||||||
|
@ -488,9 +487,14 @@ class JobBillingTest(BaseBillingTest):
|
||||||
def test_job(self):
|
def test_job(self):
|
||||||
service = self.create_job_service()
|
service = self.create_job_service()
|
||||||
account = self.create_account()
|
account = self.create_account()
|
||||||
job = self.create_job(10, account=account)
|
|
||||||
print service.orders.all()
|
job = self.create_job(5, account=account)
|
||||||
print service.orders.bill()[0].get_total()
|
bill = service.orders.bill()[0]
|
||||||
|
self.assertEqual(5*20, bill.get_total())
|
||||||
|
|
||||||
|
job = self.create_job(100, account=account)
|
||||||
|
bill = service.orders.bill(new_open=True)[0]
|
||||||
|
self.assertEqual(100*15, bill.get_total())
|
||||||
|
|
||||||
|
|
||||||
class PlanBillingTest(BaseBillingTest):
|
class PlanBillingTest(BaseBillingTest):
|
||||||
|
|
|
@ -124,14 +124,13 @@ class ServiceHandler(plugins.Plugin):
|
||||||
day = ini.day
|
day = ini.day
|
||||||
month = ini.month
|
month = ini.month
|
||||||
period = self.get_pricing_period()
|
period = self.get_pricing_period()
|
||||||
|
rdelta = self.get_pricing_rdelta()
|
||||||
if period == self.MONTHLY:
|
if period == self.MONTHLY:
|
||||||
ini = datetime.datetime(year=ini.year, month=ini.month, day=day,
|
ini = datetime.datetime(year=ini.year, month=ini.month, day=day,
|
||||||
tzinfo=timezone.get_current_timezone()).date()
|
tzinfo=timezone.get_current_timezone()).date()
|
||||||
rdelta = relativedelta.relativedelta(months=1)
|
|
||||||
elif period == self.ANUAL:
|
elif period == self.ANUAL:
|
||||||
ini = datetime.datetime(year=ini.year, month=month, day=day,
|
ini = datetime.datetime(year=ini.year, month=month, day=day,
|
||||||
tzinfo=timezone.get_current_timezone()).date()
|
tzinfo=timezone.get_current_timezone()).date()
|
||||||
rdelta = relativedelta.relativedelta(years=1)
|
|
||||||
elif period == self.NEVER:
|
elif period == self.NEVER:
|
||||||
yield ini, end
|
yield ini, end
|
||||||
raise StopIteration
|
raise StopIteration
|
||||||
|
@ -144,6 +143,15 @@ class ServiceHandler(plugins.Plugin):
|
||||||
break
|
break
|
||||||
ini = next
|
ini = next
|
||||||
|
|
||||||
|
def get_pricing_rdelta(self):
|
||||||
|
period = self.get_pricing_period()
|
||||||
|
if period == self.MONTHLY:
|
||||||
|
return relativedelta.relativedelta(months=1)
|
||||||
|
elif period == self.ANUAL:
|
||||||
|
return relativedelta.relativedelta(years=1)
|
||||||
|
elif period == self.NEVER:
|
||||||
|
return None
|
||||||
|
|
||||||
def generate_discount(self, line, dtype, price):
|
def generate_discount(self, line, dtype, price):
|
||||||
line.discounts.append(AttributeDict(**{
|
line.discounts.append(AttributeDict(**{
|
||||||
'type': dtype,
|
'type': dtype,
|
||||||
|
@ -162,6 +170,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
computed = kwargs.pop('computed', False)
|
computed = kwargs.pop('computed', False)
|
||||||
if kwargs:
|
if kwargs:
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
|
|
||||||
size = self.get_price_size(ini, end)
|
size = self.get_price_size(ini, end)
|
||||||
if not computed:
|
if not computed:
|
||||||
price = price * size
|
price = price * size
|
||||||
|
@ -184,7 +193,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
self.generate_discount(line, 'volume', price-subtotal)
|
self.generate_discount(line, 'volume', price-subtotal)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
def assign_compensations(self, givers, receivers, commit=True):
|
def assign_compensations(self, givers, receivers, **options):
|
||||||
compensations = []
|
compensations = []
|
||||||
for order in givers:
|
for order in givers:
|
||||||
if order.billed_until and order.cancelled_on and order.cancelled_on < order.billed_until:
|
if order.billed_until and order.cancelled_on and order.cancelled_on < order.billed_until:
|
||||||
|
@ -202,7 +211,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
# TODO get min right
|
# TODO get min right
|
||||||
comp.order.new_billed_until = min(comp.order.billed_until, comp.ini,
|
comp.order.new_billed_until = min(comp.order.billed_until, comp.ini,
|
||||||
getattr(comp.order, 'new_billed_until', datetime.date.max))
|
getattr(comp.order, 'new_billed_until', datetime.date.max))
|
||||||
if commit:
|
if options.get('commit', True):
|
||||||
for order in givers:
|
for order in givers:
|
||||||
if hasattr(order, 'new_billed_until'):
|
if hasattr(order, 'new_billed_until'):
|
||||||
order.billed_until = order.new_billed_until
|
order.billed_until = order.new_billed_until
|
||||||
|
@ -246,7 +255,7 @@ class ServiceHandler(plugins.Plugin):
|
||||||
counter += 1
|
counter += 1
|
||||||
return counter
|
return counter
|
||||||
|
|
||||||
def bill_concurrent_orders(self, account, porders, rates, ini, end, commit=True):
|
def bill_concurrent_orders(self, account, porders, rates, ini, end):
|
||||||
# Concurrent
|
# Concurrent
|
||||||
# Get pricing orders
|
# Get pricing orders
|
||||||
priced = {}
|
priced = {}
|
||||||
|
@ -286,21 +295,14 @@ class ServiceHandler(plugins.Plugin):
|
||||||
order.new_billed_until = new_end
|
order.new_billed_until = new_end
|
||||||
line = self.generate_line(order, price, ini, new_end or end, discounts=discounts, computed=True)
|
line = self.generate_line(order, price, ini, new_end or end, discounts=discounts, computed=True)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
if commit:
|
|
||||||
order.billed_until = order.new_billed_until
|
|
||||||
order.save()
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def bill_registered_or_renew_events(self, account, porders, rates, commit=True):
|
def bill_registered_or_renew_events(self, account, porders, rates):
|
||||||
# Before registration
|
# Before registration
|
||||||
lines = []
|
lines = []
|
||||||
period = self.get_pricing_period()
|
rdelta = self.get_pricing_rdelta()
|
||||||
if period == self.MONTHLY:
|
if not rdelta:
|
||||||
rdelta = relativedelta.relativedelta(months=1)
|
raise NotImplementedError
|
||||||
elif period == self.ANUAL:
|
|
||||||
rdelta = relativedelta.relativedelta(years=1)
|
|
||||||
elif period == self.NEVER:
|
|
||||||
raise NotImplementedError("Rates with no pricing period?")
|
|
||||||
for position, order in enumerate(porders, start=1):
|
for position, order in enumerate(porders, start=1):
|
||||||
if hasattr(order, 'new_billed_until'):
|
if hasattr(order, 'new_billed_until'):
|
||||||
pend = order.billed_until or order.registered_on
|
pend = order.billed_until or order.registered_on
|
||||||
|
@ -319,9 +321,6 @@ class ServiceHandler(plugins.Plugin):
|
||||||
size = self.get_price_size(ini, end)
|
size = self.get_price_size(ini, end)
|
||||||
line = self.generate_line(order, price, ini, end, discounts=discounts)
|
line = self.generate_line(order, price, ini, end, discounts=discounts)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
if commit:
|
|
||||||
order.billed_until = order.new_billed_until
|
|
||||||
order.save()
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def bill_with_orders(self, orders, account, **options):
|
def bill_with_orders(self, orders, account, **options):
|
||||||
|
@ -329,14 +328,11 @@ class ServiceHandler(plugins.Plugin):
|
||||||
# date(2011, 1, 1) is equivalent to datetime(2011, 1, 1, 0, 0, 0)
|
# date(2011, 1, 1) is equivalent to datetime(2011, 1, 1, 0, 0, 0)
|
||||||
# In most cases:
|
# In most cases:
|
||||||
# ini >= registered_date, end < registered_date
|
# ini >= registered_date, end < registered_date
|
||||||
commit = options.get('commit', True)
|
|
||||||
|
|
||||||
# boundary lookup and exclude cancelled and billed
|
# boundary lookup and exclude cancelled and billed
|
||||||
orders_ = []
|
orders_ = []
|
||||||
bp = None
|
bp = None
|
||||||
ini = datetime.date.max
|
ini = datetime.date.max
|
||||||
end = datetime.date.min
|
end = datetime.date.min
|
||||||
# TODO compensation with one time billing?
|
|
||||||
for order in orders:
|
for order in orders:
|
||||||
cini = order.registered_on
|
cini = order.registered_on
|
||||||
if order.billed_until:
|
if order.billed_until:
|
||||||
|
@ -354,24 +350,33 @@ class ServiceHandler(plugins.Plugin):
|
||||||
|
|
||||||
# Compensation
|
# Compensation
|
||||||
related_orders = account.orders.filter(service=self.service)
|
related_orders = account.orders.filter(service=self.service)
|
||||||
if self.on_cancel == self.DISCOUNT:
|
if self.on_cancel == self.COMPENSATE:
|
||||||
# Get orders pending for compensation
|
# Get orders pending for compensation
|
||||||
givers = list(related_orders.givers(ini, end))
|
givers = list(related_orders.givers(ini, end))
|
||||||
givers.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
givers.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
||||||
orders.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
orders.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
||||||
self.assign_compensations(givers, orders, commit=commit)
|
self.assign_compensations(givers, orders, **options)
|
||||||
|
|
||||||
rates = self.get_rates(account)
|
rates = self.get_rates(account)
|
||||||
if rates:
|
has_billing_period = self.billing_period != self.NEVER
|
||||||
|
has_pricing_period = self.get_pricing_period() != self.NEVER
|
||||||
|
if rates and (has_billing_period or has_pricing_period):
|
||||||
|
concurrent = has_billing_period and not has_pricing_period
|
||||||
|
if not concurrent:
|
||||||
|
rdelta = self.get_pricing_rdelta()
|
||||||
|
ini -= rdelta
|
||||||
porders = related_orders.pricing_orders(ini, end)
|
porders = related_orders.pricing_orders(ini, end)
|
||||||
porders = list(set(orders).union(set(porders)))
|
porders = list(set(orders).union(set(porders)))
|
||||||
porders.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
porders.sort(cmp=helpers.cmp_billed_until_or_registered_on)
|
||||||
if self.billing_period != self.NEVER and self.get_pricing_period() == self.NEVER:
|
if concurrent:
|
||||||
lines = self.bill_concurrent_orders(account, porders, rates, ini, end, commit=commit)
|
# Periodic billing with no pricing period
|
||||||
|
lines = self.bill_concurrent_orders(account, porders, rates, ini, end)
|
||||||
else:
|
else:
|
||||||
# TODO compensation in this case?
|
# TODO compensation in this case?
|
||||||
lines = self.bill_registered_or_renew_events(account, porders, rates, commit=commit)
|
# Periodic and one-time billing with pricing period
|
||||||
|
lines = self.bill_registered_or_renew_events(account, porders, rates)
|
||||||
else:
|
else:
|
||||||
|
# No rates optimization or one-time billing without pricing period
|
||||||
lines = []
|
lines = []
|
||||||
price = self.nominal_price
|
price = self.nominal_price
|
||||||
# Calculate nominal price
|
# Calculate nominal price
|
||||||
|
@ -387,47 +392,45 @@ class ServiceHandler(plugins.Plugin):
|
||||||
end = new_end
|
end = new_end
|
||||||
line = self.generate_line(order, price, ini, end, discounts=discounts)
|
line = self.generate_line(order, price, ini, end, discounts=discounts)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
if commit:
|
|
||||||
order.billed_until = order.new_billed_until
|
|
||||||
order.save()
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def bill_with_metric(self, orders, account, **options):
|
def bill_with_metric(self, orders, account, **options):
|
||||||
lines = []
|
lines = []
|
||||||
commit = options.get('commit', True)
|
|
||||||
bp = None
|
bp = None
|
||||||
for order in orders:
|
for order in orders:
|
||||||
if order.billed_until and order.cancelled_on >= order.billed_until:
|
if order.billed_until and order.cancelled_on and order.cancelled_on >= order.billed_until:
|
||||||
continue
|
continue
|
||||||
|
if self.billing_period != self.NEVER:
|
||||||
bp = self.get_billing_point(order, bp=bp, **options)
|
bp = self.get_billing_point(order, bp=bp, **options)
|
||||||
ini = order.billed_until or order.registered_on
|
ini = order.billed_until or order.registered_on
|
||||||
|
# Periodic billing
|
||||||
if bp <= ini:
|
if bp <= ini:
|
||||||
# TODO except one time service
|
|
||||||
continue
|
continue
|
||||||
order.new_billed_until = bp
|
order.new_billed_until = bp
|
||||||
if self.billing_period != self.NEVER:
|
|
||||||
if self.get_pricing_period() == self.NEVER:
|
if self.get_pricing_period() == self.NEVER:
|
||||||
# Changes
|
# Changes (Mailbox disk-like)
|
||||||
for ini, end, metric in order.get_metric(ini, bp, changes=True):
|
for ini, end, metric in order.get_metric(ini, bp, changes=True):
|
||||||
price = self.get_price(order, metric)
|
price = self.get_price(order, metric)
|
||||||
lines.append(self.generate_line(order, price, ini, end, metric=metric))
|
lines.append(self.generate_line(order, price, ini, end, metric=metric))
|
||||||
else:
|
else:
|
||||||
# pricing_slots
|
# pricing_slots (Traffic-like)
|
||||||
for ini, end in self.get_pricing_slots(ini, bp):
|
for ini, end in self.get_pricing_slots(ini, bp):
|
||||||
metric = order.get_metric(ini, end)
|
metric = order.get_metric(ini, end)
|
||||||
price = self.get_price(order, metric)
|
price = self.get_price(order, metric)
|
||||||
lines.append(self.generate_line(order, price, ini, end, metric=metric))
|
lines.append(self.generate_line(order, price, ini, end, metric=metric))
|
||||||
else:
|
else:
|
||||||
|
# One-time billing
|
||||||
|
if order.billed_until:
|
||||||
|
continue
|
||||||
|
date = order.registered_on
|
||||||
|
order.new_billed_until = date
|
||||||
if self.get_pricing_period() == self.NEVER:
|
if self.get_pricing_period() == self.NEVER:
|
||||||
# get metric
|
# get metric (Job-like)
|
||||||
metric = order.get_metric(ini, end)
|
metric = order.get_metric(date)
|
||||||
price = self.get_price(order, metric)
|
price = self.get_price(order, metric)
|
||||||
lines.append(self.generate_line(order, price, ini, bp, metric=metric))
|
lines.append(self.generate_line(order, price, date, metric=metric))
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
if commit:
|
|
||||||
order.billed_until = order.new_billed_until
|
|
||||||
order.save()
|
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
def generate_bill_lines(self, orders, account, **options):
|
def generate_bill_lines(self, orders, account, **options):
|
||||||
|
@ -437,4 +440,8 @@ class ServiceHandler(plugins.Plugin):
|
||||||
lines = self.bill_with_orders(orders, account, **options)
|
lines = self.bill_with_orders(orders, account, **options)
|
||||||
else:
|
else:
|
||||||
lines = self.bill_with_metric(orders, account, **options)
|
lines = self.bill_with_metric(orders, account, **options)
|
||||||
|
if options.get('commit', True):
|
||||||
|
for line in lines:
|
||||||
|
line.order.billed_until = line.order.new_billed_until
|
||||||
|
line.order.save()
|
||||||
return lines
|
return lines
|
||||||
|
|
|
@ -89,6 +89,7 @@ class Service(models.Model):
|
||||||
CONCURRENT = 'CONCURRENT'
|
CONCURRENT = 'CONCURRENT'
|
||||||
NOTHING = 'NOTHING'
|
NOTHING = 'NOTHING'
|
||||||
DISCOUNT = 'DISCOUNT'
|
DISCOUNT = 'DISCOUNT'
|
||||||
|
COMPENSATE = 'COMPENSATE'
|
||||||
REFOUND = 'REFOUND'
|
REFOUND = 'REFOUND'
|
||||||
PREPAY = 'PREPAY'
|
PREPAY = 'PREPAY'
|
||||||
POSTPAY = 'POSTPAY'
|
POSTPAY = 'POSTPAY'
|
||||||
|
@ -173,6 +174,7 @@ class Service(models.Model):
|
||||||
choices=(
|
choices=(
|
||||||
(NOTHING, _("Nothing")),
|
(NOTHING, _("Nothing")),
|
||||||
(DISCOUNT, _("Discount")),
|
(DISCOUNT, _("Discount")),
|
||||||
|
(COMPENSATE, _("Compensat")),
|
||||||
(REFOUND, _("Refound")),
|
(REFOUND, _("Refound")),
|
||||||
),
|
),
|
||||||
default=DISCOUNT)
|
default=DISCOUNT)
|
||||||
|
@ -266,13 +268,13 @@ class Service(models.Model):
|
||||||
"""
|
"""
|
||||||
if rates is None:
|
if rates is None:
|
||||||
rates = self.get_rates(account)
|
rates = self.get_rates(account)
|
||||||
|
if rates:
|
||||||
|
rates = self.rate_method(rates, metric)
|
||||||
if not rates:
|
if not rates:
|
||||||
rates = [{
|
rates = [{
|
||||||
'quantity': metric,
|
'quantity': metric,
|
||||||
'price': self.nominal_price,
|
'price': self.nominal_price,
|
||||||
}]
|
}]
|
||||||
else:
|
|
||||||
rates = self.rate_method(rates, metric)
|
|
||||||
counter = 0
|
counter = 0
|
||||||
if position is None:
|
if position is None:
|
||||||
ant_counter = 0
|
ant_counter = 0
|
||||||
|
|
|
@ -44,7 +44,6 @@ def _compute(rates, metric):
|
||||||
|
|
||||||
def step_price(rates, metric):
|
def step_price(rates, metric):
|
||||||
# Step price
|
# Step price
|
||||||
# TODO allow multiple plans
|
|
||||||
group = []
|
group = []
|
||||||
minimal = (sys.maxint, [])
|
minimal = (sys.maxint, [])
|
||||||
for plan, rates in rates.group_by('plan').iteritems():
|
for plan, rates in rates.group_by('plan').iteritems():
|
||||||
|
@ -104,18 +103,22 @@ def match_price(rates, metric):
|
||||||
selected = False
|
selected = False
|
||||||
prev = None
|
prev = None
|
||||||
for rate in rates.distinct():
|
for rate in rates.distinct():
|
||||||
if prev and prev.plan != rate.plan:
|
if prev:
|
||||||
|
if prev.plan != rate.plan:
|
||||||
if not selected and prev.quantity <= metric:
|
if not selected and prev.quantity <= metric:
|
||||||
candidates.append(prev)
|
candidates.append(prev)
|
||||||
selected = False
|
selected = False
|
||||||
if not selected and rate.quantity > metric:
|
if not selected and rate.quantity > metric:
|
||||||
|
if prev.quantity <= metric:
|
||||||
candidates.append(prev)
|
candidates.append(prev)
|
||||||
selected = True
|
selected = True
|
||||||
prev = rate
|
prev = rate
|
||||||
if not selected and prev.quantity <= metric:
|
if not selected and prev.quantity <= metric:
|
||||||
candidates.append(prev)
|
candidates.append(prev)
|
||||||
candidates.sort(key=lambda r: r.price)
|
candidates.sort(key=lambda r: r.price)
|
||||||
|
if candidates:
|
||||||
return [AttributeDict(**{
|
return [AttributeDict(**{
|
||||||
'quantity': metric,
|
'quantity': metric,
|
||||||
'price': candidates[0].price,
|
'price': candidates[0].price,
|
||||||
})]
|
})]
|
||||||
|
return None
|
||||||
|
|
Loading…
Reference in New Issue