Added services templates

This commit is contained in:
Marc Aymerich 2015-04-24 14:03:42 +00:00
parent c2b0186034
commit e8759578b5
7 changed files with 179 additions and 27 deletions

View File

@ -112,9 +112,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
inlines = [AutoresponseInline]
search_fields = (
'name', 'domain__name', 'forward', 'mailboxes__name', 'account__username', 'computed_email'
)
search_fields = ('forward', 'mailboxes__name', 'account__username', 'computed_email')
readonly_fields = ('account_link', 'domain_link', 'email_link')
filter_by_account_fields = ('domain', 'mailboxes')
filter_horizontal = ['mailboxes']
@ -148,6 +146,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
return '<br>'.join(values)
display_forward.short_description = _("Forward")
display_forward.allow_tags = True
display_forward.admin_order_field = 'forward'
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'forward':

View File

@ -78,7 +78,8 @@ class Rate(models.Model):
service = models.ForeignKey('services.Service', verbose_name=_("service"),
related_name='rates')
plan = models.ForeignKey(Plan, verbose_name=_("plan"), related_name='rates')
quantity = models.PositiveIntegerField(_("quantity"), null=True, blank=True)
quantity = models.PositiveIntegerField(_("quantity"), null=True, blank=True,
help_text=_("See rate algorihm help text."))
price = models.DecimalField(_("price"), max_digits=12, decimal_places=2)
objects = RateQuerySet.as_manager()

View File

@ -120,7 +120,8 @@ def step_price(rates, metric):
minimal = min(minimal, (value, result), key=lambda v: v[0])
return minimal[1]
step_price.verbose_name = _("Step price")
step_price.help_text = _("All price rates with a lower metric are applied.")
step_price.help_text = _("All rates with a quantity lower than the metric are applied. "
"Nominal price will be used when initial block is missing.")
def match_price(rates, metric):
@ -149,4 +150,5 @@ def match_price(rates, metric):
})]
return None
match_price.verbose_name = _("Match price")
match_price.help_text = _("Only the rate with inmediate inferior metric is applied.")
match_price.help_text = _("Only <b>the rate</b> with a) inmediate inferior metric and b) lower price is applied. "
"Nominal price will be used when initial block is missing.")

View File

@ -1,5 +1,5 @@
{% extends "orchestra/admin/change_form.html" %}
{% load i18n admin_urls admin_static admin_modify %}
{% load i18n admin_urls admin_static admin_modify utils %}
{% block object-tools %}
@ -7,8 +7,9 @@
<ul class="object-tools">
<li><select name="forma" onchange="location = this.options[this.selectedIndex].value;" style="margin: -3px 0 0 0;">
<option selected disabled>{% trans "Templates" %}</option>
{% oneliner %}
<option value="./?description=Mailbox&
content_type=10&
content_type={{ 'mailboxes.mailbox'|content_type_id }}&
match=mailbox.active&
handler_type=&
is_active=True&
@ -22,9 +23,27 @@
nominal_price=28.10&
tax=21&
pricing_period=BILLING_PERIOD&
rate_algorithm=MATCH_PRICE&
rate_algorithm=STEP_PRICE&
on_cancel=COMPENSATE&
payment_style=PREPAY">Mailbox</option>
<option value="./?description=Mailbox%20allocated%20disk&
content_type=10&
match=mailbox.active&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=ANUAL&
billing_point=ON_FIXED_DATE&
is_fee=&
order_description=&
ignore_period=TEN_DAYS&
metric=max(logsteps(mailbox.resources.disk.allocated%20or%200)%20-2,%200)&
nominal_price=20.00&
tax=21&
pricing_period=&
rate_algorithm=MATCH_PRICE&
on_cancel=DISCOUNT&
payment_style=PREPAY">Mailbox allocated disk</option>
<option value="./?description=Database&
content_type=19&
match=database.account.is_active&handler_type=&
@ -42,6 +61,115 @@
rate_algorithm=STEP_PRICE&
on_cancel=COMPENSATE&
payment_style=PREPAY">Database</option>
<option value="./?description=Basic%20domain&
content_type=34&
match=miscellaneous.active%20and%20miscellaneous.service.name%20==%20%27domain-registration%27&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=ANUAL&
billing_point=ON_REGISTER&
is_fee=&
order_description=&
ignore_period=&
metric=&
nominal_price=19.01&
tax=21&
pricing_period=BILLING_PERIOD&
rate_algorithm=STEP_PRICE&
on_cancel=NOTHING&
payment_style=PREPAY">Basic domain</option>
<option value="./?description=Domain%20.cat%20extra&
content_type=34&
match=miscellaneous.active%20and%20miscellaneous.service.name%20==%20%27domain-registration%27%20and%20miscellaneous.identifier.endswith(%27.cat%27)&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=ANUAL&
billing_point=ON_REGISTER&
is_fee=&
order_description=&
ignore_period=&
metric=&
nominal_price=26.59&
tax=21&
pricing_period=BILLING_PERIOD&
rate_algorithm=STEP_PRICE&
on_cancel=NOTHING&
payment_style=PREPAY">Domain .cat extra</option>
<option value="./?description=FTP%20account&
content_type=9&
match=systemuser.active%20and%20not%20systemuser.is_main&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=ANUAL&
billing_point=ON_FIXED_DATE&
is_fee=&
order_description=&
ignore_period=TEN_DAYS&
metric=&
nominal_price=28.10&
tax=21&
pricing_period=BILLING_PERIOD&
rate_algorithm=STEP_PRICE&
on_cancel=COMPENSATE&
payment_style=PREPAY">FTP account</option>
<option value="./?description=Traffic&
content_type=1&
match=True&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=MONTHLY&
billing_point=ON_FIXED_DATE&
is_fee=&
order_description=&
ignore_period=TEN_DAYS&
metric=logsteps(max((account.resources.traffic.used%20or%200)%20-%20getattr(account.miscellaneous.filter(is_active=True,%20service__name=%27traffic-prepay%27).last(),%20%27amount%27,%200),%200),%20size=0.05)&
nominal_price=4.50&
tax=21&
pricing_period=BILLING_PERIOD&
rate_algorithm=MATCH_PRICE&
on_cancel=NOTHING&
payment_style=POSTPAY">Traffic</option>
<option value="./?description=Traffic%20prepay&
content_type=34&
match=miscellaneous.active%20and%20miscellaneous.service.name%20==%20%27traffic-prepay%27&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=ANUAL&
billing_point=ON_FIXED_DATE&
is_fee=&
order_description=&
ignore_period=TEN_DAYS&
metric=miscellaneous.amount&
nominal_price=3.00&
tax=21&
pricing_period=MONTHLY&
rate_algorithm=MATCH_PRICE&
on_cancel=REFUND&
payment_style=PREPAY">Traffic prepay</option>
<option value="./?description=Development&
content_type=34&
match=miscellaneous.active%20and%20miscellaneous.service.name%20==%20%27development%27&
handler_type=&
is_active=True&
ignore_superusers=True&
billing_period=&
billing_point=ON_FIXED_DATE&
is_fee=&
order_description=&
ignore_period=TEN_DAYS&
metric=miscellaneous.amount&
nominal_price=40.00&
tax=21&
pricing_period=BILLING_PERIOD&
rate_algorithm=STEP_PRICE&
on_cancel=NOTHING&
payment_style=PREPAY">Develompent</option>
{% endoneliner %}
</select></li>
<li>
<a href="./help/" class="historylink">{% trans "Help" %}</a>

View File

@ -32,13 +32,8 @@ class WebAppViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
data = meta.get_serializer_info(app_type.serializer())
else:
data = {}
options = []
for group, option in app_type.get_options():
options += [opt.name for opt in option]
app_types[app_type.get_name()] = {
'data': data,
'options': options,
}
data['option_groups'] = app_type.option_groups
app_types[app_type.get_name()] = data
metadata.data['actions']['types'] = app_types
# Options
options = {}

View File

@ -74,29 +74,29 @@ class WebsiteSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
return instance
def create(self, validated_data):
options_data = validated_data.pop('options')
directives_data = validated_data.pop('directives')
webapp = super(WebsiteSerializer, self).create(validated_data)
for key, value in options_data.items():
WebAppOption.objects.create(webapp=webapp, name=key, value=value)
for key, value in directives_data.items():
WebsiteDirective.objects.create(webapp=webapp, name=key, value=value)
return webap
def update(self, instance, validated_data):
options_data = validated_data.pop('options')
directives_data = validated_data.pop('directives')
instance = super(WebsiteSerializer, self).update(instance, validated_data)
existing = {}
for obj in instance.options.all():
for obj in instance.directives.all():
existing[obj.name] = obj
posted = set()
for key, value in options_data.items():
for key, value in directives_data.items():
posted.add(key)
try:
option = existing[key]
directive = existing[key]
except KeyError:
option = instance.options.create(name=key, value=value)
directive = instance.directives.create(name=key, value=value)
else:
if option.value != value:
option.value = value
option.save(update_fields=('value',))
if directive.value != value:
directive.value = value
directive.save(update_fields=('value',))
for to_delete in set(existing.keys())-posted:
existing[to_delete].delete()
return instance

View File

@ -1,6 +1,10 @@
import re
from django import template
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse, NoReverseMatch
from django.forms import CheckboxInput
from django.template.base import Node
from orchestra import get_version
from orchestra.admin.utils import change_url
@ -43,6 +47,22 @@ def rest_to_admin_url(context):
return reverse('admin:index')
class OneLinerNode(Node):
def __init__(self, nodelist):
self.nodelist = nodelist
def render(self, context):
line = self.nodelist.render(context).replace('\n', ' ')
return re.sub(r'\s\s+', '', line)
@register.tag
def oneliner(parser, token):
nodelist = parser.parse(('endoneliner',))
parser.delete_first_token()
return OneLinerNode(nodelist)
@register.filter
def size(value, length):
value = str(value)[:int(length)]
@ -55,6 +75,12 @@ def is_checkbox(field):
return isinstance(field.field.widget, CheckboxInput)
@register.filter
def content_type_id(label):
app_label, model = label.split('.')
return ContentType.objects.filter(app_label=app_label, model=model).values_list('id', flat=True)[0]
@register.filter
def admin_url(obj):
return change_url(obj)
@ -63,3 +89,4 @@ def admin_url(obj):
@register.filter
def isactive(obj):
return getattr(obj, 'is_active', True)