Added services templates
This commit is contained in:
parent
c2b0186034
commit
e8759578b5
|
@ -112,9 +112,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
|
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
|
||||||
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
|
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
|
||||||
inlines = [AutoresponseInline]
|
inlines = [AutoresponseInline]
|
||||||
search_fields = (
|
search_fields = ('forward', 'mailboxes__name', 'account__username', 'computed_email')
|
||||||
'name', 'domain__name', 'forward', 'mailboxes__name', 'account__username', 'computed_email'
|
|
||||||
)
|
|
||||||
readonly_fields = ('account_link', 'domain_link', 'email_link')
|
readonly_fields = ('account_link', 'domain_link', 'email_link')
|
||||||
filter_by_account_fields = ('domain', 'mailboxes')
|
filter_by_account_fields = ('domain', 'mailboxes')
|
||||||
filter_horizontal = ['mailboxes']
|
filter_horizontal = ['mailboxes']
|
||||||
|
@ -148,6 +146,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
return '<br>'.join(values)
|
return '<br>'.join(values)
|
||||||
display_forward.short_description = _("Forward")
|
display_forward.short_description = _("Forward")
|
||||||
display_forward.allow_tags = True
|
display_forward.allow_tags = True
|
||||||
|
display_forward.admin_order_field = 'forward'
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'forward':
|
if db_field.name == 'forward':
|
||||||
|
|
|
@ -78,7 +78,8 @@ class Rate(models.Model):
|
||||||
service = models.ForeignKey('services.Service', verbose_name=_("service"),
|
service = models.ForeignKey('services.Service', verbose_name=_("service"),
|
||||||
related_name='rates')
|
related_name='rates')
|
||||||
plan = models.ForeignKey(Plan, verbose_name=_("plan"), 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)
|
price = models.DecimalField(_("price"), max_digits=12, decimal_places=2)
|
||||||
|
|
||||||
objects = RateQuerySet.as_manager()
|
objects = RateQuerySet.as_manager()
|
||||||
|
|
|
@ -120,7 +120,8 @@ def step_price(rates, metric):
|
||||||
minimal = min(minimal, (value, result), key=lambda v: v[0])
|
minimal = min(minimal, (value, result), key=lambda v: v[0])
|
||||||
return minimal[1]
|
return minimal[1]
|
||||||
step_price.verbose_name = _("Step price")
|
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):
|
def match_price(rates, metric):
|
||||||
|
@ -149,4 +150,5 @@ def match_price(rates, metric):
|
||||||
})]
|
})]
|
||||||
return None
|
return None
|
||||||
match_price.verbose_name = _("Match price")
|
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.")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "orchestra/admin/change_form.html" %}
|
{% 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 %}
|
{% block object-tools %}
|
||||||
|
@ -7,8 +7,9 @@
|
||||||
<ul class="object-tools">
|
<ul class="object-tools">
|
||||||
<li><select name="forma" onchange="location = this.options[this.selectedIndex].value;" style="margin: -3px 0 0 0;">
|
<li><select name="forma" onchange="location = this.options[this.selectedIndex].value;" style="margin: -3px 0 0 0;">
|
||||||
<option selected disabled>{% trans "Templates" %}</option>
|
<option selected disabled>{% trans "Templates" %}</option>
|
||||||
|
{% oneliner %}
|
||||||
<option value="./?description=Mailbox&
|
<option value="./?description=Mailbox&
|
||||||
content_type=10&
|
content_type={{ 'mailboxes.mailbox'|content_type_id }}&
|
||||||
match=mailbox.active&
|
match=mailbox.active&
|
||||||
handler_type=&
|
handler_type=&
|
||||||
is_active=True&
|
is_active=True&
|
||||||
|
@ -22,9 +23,27 @@
|
||||||
nominal_price=28.10&
|
nominal_price=28.10&
|
||||||
tax=21&
|
tax=21&
|
||||||
pricing_period=BILLING_PERIOD&
|
pricing_period=BILLING_PERIOD&
|
||||||
rate_algorithm=MATCH_PRICE&
|
rate_algorithm=STEP_PRICE&
|
||||||
on_cancel=COMPENSATE&
|
on_cancel=COMPENSATE&
|
||||||
payment_style=PREPAY">Mailbox</option>
|
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&
|
<option value="./?description=Database&
|
||||||
content_type=19&
|
content_type=19&
|
||||||
match=database.account.is_active&handler_type=&
|
match=database.account.is_active&handler_type=&
|
||||||
|
@ -42,6 +61,115 @@
|
||||||
rate_algorithm=STEP_PRICE&
|
rate_algorithm=STEP_PRICE&
|
||||||
on_cancel=COMPENSATE&
|
on_cancel=COMPENSATE&
|
||||||
payment_style=PREPAY">Database</option>
|
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>
|
</select></li>
|
||||||
<li>
|
<li>
|
||||||
<a href="./help/" class="historylink">{% trans "Help" %}</a>
|
<a href="./help/" class="historylink">{% trans "Help" %}</a>
|
||||||
|
|
|
@ -32,13 +32,8 @@ class WebAppViewSet(LogApiMixin, AccountApiMixin, viewsets.ModelViewSet):
|
||||||
data = meta.get_serializer_info(app_type.serializer())
|
data = meta.get_serializer_info(app_type.serializer())
|
||||||
else:
|
else:
|
||||||
data = {}
|
data = {}
|
||||||
options = []
|
data['option_groups'] = app_type.option_groups
|
||||||
for group, option in app_type.get_options():
|
app_types[app_type.get_name()] = data
|
||||||
options += [opt.name for opt in option]
|
|
||||||
app_types[app_type.get_name()] = {
|
|
||||||
'data': data,
|
|
||||||
'options': options,
|
|
||||||
}
|
|
||||||
metadata.data['actions']['types'] = app_types
|
metadata.data['actions']['types'] = app_types
|
||||||
# Options
|
# Options
|
||||||
options = {}
|
options = {}
|
||||||
|
|
|
@ -74,29 +74,29 @@ class WebsiteSerializer(AccountSerializerMixin, HyperlinkedModelSerializer):
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
options_data = validated_data.pop('options')
|
directives_data = validated_data.pop('directives')
|
||||||
webapp = super(WebsiteSerializer, self).create(validated_data)
|
webapp = super(WebsiteSerializer, self).create(validated_data)
|
||||||
for key, value in options_data.items():
|
for key, value in directives_data.items():
|
||||||
WebAppOption.objects.create(webapp=webapp, name=key, value=value)
|
WebsiteDirective.objects.create(webapp=webapp, name=key, value=value)
|
||||||
return webap
|
return webap
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
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)
|
instance = super(WebsiteSerializer, self).update(instance, validated_data)
|
||||||
existing = {}
|
existing = {}
|
||||||
for obj in instance.options.all():
|
for obj in instance.directives.all():
|
||||||
existing[obj.name] = obj
|
existing[obj.name] = obj
|
||||||
posted = set()
|
posted = set()
|
||||||
for key, value in options_data.items():
|
for key, value in directives_data.items():
|
||||||
posted.add(key)
|
posted.add(key)
|
||||||
try:
|
try:
|
||||||
option = existing[key]
|
directive = existing[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
option = instance.options.create(name=key, value=value)
|
directive = instance.directives.create(name=key, value=value)
|
||||||
else:
|
else:
|
||||||
if option.value != value:
|
if directive.value != value:
|
||||||
option.value = value
|
directive.value = value
|
||||||
option.save(update_fields=('value',))
|
directive.save(update_fields=('value',))
|
||||||
for to_delete in set(existing.keys())-posted:
|
for to_delete in set(existing.keys())-posted:
|
||||||
existing[to_delete].delete()
|
existing[to_delete].delete()
|
||||||
return instance
|
return instance
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
import re
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||||
from django.forms import CheckboxInput
|
from django.forms import CheckboxInput
|
||||||
|
from django.template.base import Node
|
||||||
|
|
||||||
from orchestra import get_version
|
from orchestra import get_version
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
|
@ -43,6 +47,22 @@ def rest_to_admin_url(context):
|
||||||
return reverse('admin:index')
|
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
|
@register.filter
|
||||||
def size(value, length):
|
def size(value, length):
|
||||||
value = str(value)[:int(length)]
|
value = str(value)[:int(length)]
|
||||||
|
@ -55,6 +75,12 @@ def is_checkbox(field):
|
||||||
return isinstance(field.field.widget, CheckboxInput)
|
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
|
@register.filter
|
||||||
def admin_url(obj):
|
def admin_url(obj):
|
||||||
return change_url(obj)
|
return change_url(obj)
|
||||||
|
@ -63,3 +89,4 @@ def admin_url(obj):
|
||||||
@register.filter
|
@register.filter
|
||||||
def isactive(obj):
|
def isactive(obj):
|
||||||
return getattr(obj, 'is_active', True)
|
return getattr(obj, 'is_active', True)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue