diff --git a/TODO.md b/TODO.md
index fb8d84eb..fa24b064 100644
--- a/TODO.md
+++ b/TODO.md
@@ -156,7 +156,6 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl
# * add ini, end dates on bill lines and breakup quanity into size(defaut:1) and metric
# * threshold for significative metric accountancy on services.handler
# * http://orchestra.pangea.org/admin/orders/order/6418/
-# * http://orchestra.pangea.org/admin/orders/order/6495/bill_selected_orders/
* move normurlpath to orchestra.utils from websites.utils
@@ -183,22 +182,11 @@ ugettext("Description")
* saas validate_creation generic approach, for all backends. standard output
-* html code x: × for bill line verbose quantity
-
-* periodic task to cleanup backendlogs, monitor data and metricstorage
-* create orchestrate databases.Database pk=1 -n --dry-run | --noinput --action save (default)|delete --backend name (limit to this backend) --help
-
-* uwsgi --max-requests=5000 \ # respawn processes after serving 5000 requests and
-celery max-tasks-per-child
-
-* generate settings.py more like django (installed_apps, middlewares, etc,,,)
+* periodic task to cleanup metricstorage
+# create orchestrate databases.Database pk=1 -n --dry-run | --noinput --action save (default)|delete --backend name (limit to this backend) --help
* postupgradeorchestra send signals in order to hook custom stuff
-* autoscale celery workers http://docs.celeryproject.org/en/latest/userguide/workers.html#autoscaling
-
-
-glic3rinu's django-fluent-dashboard
* gevent is not ported to python3 :'(
# FIXME account deletion generates an integrity error
@@ -248,8 +236,6 @@ https://code.djangoproject.com/ticket/24576
# Determine the difference between data serializer used for validation and used for the rest API!
# Make PluginApiView that fills metadata and other stuff like modeladmin plugin support
-# TODO orchestra related services code reload: celery/uwsgi reloading find aonther way without root and implement reload
-
# reset setting button
# admin edit relevant djanog settings
diff --git a/orchestra/contrib/lists/backends.py b/orchestra/contrib/lists/backends.py
index 0e759dad..6ca9f3a0 100644
--- a/orchestra/contrib/lists/backends.py
+++ b/orchestra/contrib/lists/backends.py
@@ -271,6 +271,7 @@ class MailmanTraffic(ServiceMonitor):
'Nov': '11',
'Dec': '12',
}}
+ mailman_addr = re.compile(r'.*-(admin|bounces|confirm|join|leave|owner|request|subscribe|unsubscribe)@.*')
def prepare(object_id, list_name, ini_date):
global lists
@@ -283,12 +284,15 @@ class MailmanTraffic(ServiceMonitor):
try:
with open(postlog, 'r') as postlog:
for line in postlog.readlines():
- month, day, time, year, __, __, __, list_name, __, __, size = line.split()[:11]
+ month, day, time, year, __, __, __, list_name, __, addr, size = line.split()[:11]
try:
list = lists[list_name]
except KeyError:
continue
else:
+ # discard mailman messages because of inconsistent POST logging
+ if mailman_addr.match(addr):
+ continue
date = year + months[month] + day + time.replace(':', '')
if list[0] < int(date) < end_date:
size = size[5:-1]
diff --git a/orchestra/contrib/resources/admin.py b/orchestra/contrib/resources/admin.py
index 56f8108f..f41bee06 100644
--- a/orchestra/contrib/resources/admin.py
+++ b/orchestra/contrib/resources/admin.py
@@ -202,6 +202,7 @@ class MonitorDataAdmin(ExtendedModelAdmin):
change_readonly_fields = fields
list_select_related = ('content_type',)
search_fields = ('content_object_repr',)
+ date_hierarchy = 'created_at'
display_created = admin_date('created_at', short_description=_("Created"))
diff --git a/orchestra/contrib/services/handlers.py b/orchestra/contrib/services/handlers.py
index cddb7732..7d5af6f5 100644
--- a/orchestra/contrib/services/handlers.py
+++ b/orchestra/contrib/services/handlers.py
@@ -189,7 +189,11 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
def get_price_size(self, ini, end):
rdelta = relativedelta.relativedelta(end, ini)
- if self.billing_period == self.MONTHLY:
+ anual_prepay_of_monthly_pricing = bool(
+ self.billing_period == self.ANUAL and
+ self.payment_style == self.PREPAY and
+ self.get_pricing_period() == self.MONTHLY)
+ if self.billing_period == self.MONTHLY or anual_prepay_of_monthly_pricing:
size = rdelta.years * 12
size += rdelta.months
days = calendar.monthrange(end.year, end.month)[1]
@@ -508,7 +512,9 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
recharges = []
rini = order.billed_on
rend = min(bp, order.billed_until)
- bmetric = order.billed_metric or 0
+ bmetric = order.billed_metric
+ if bmetric is None:
+ bmetric = order.get_metric(order.billed_on)
bsize = self.get_price_size(rini, order.billed_until)
prepay_discount = self.get_price(account, bmetric) * bsize
prepay_discount = round(prepay_discount, 2)
@@ -580,6 +586,17 @@ class ServiceHandler(plugins.Plugin, metaclass=plugins.PluginMount):
line = self.generate_line(order, price, cini, cend, metric=metric,
discounts=discounts)
lines.append(line)
+ elif self.get_pricing_period() in (self.MONTHLY, self.ANUAL):
+ if self.payment_style == self.PREPAY:
+ # Traffic Prepay
+ metric = order.get_metric(timezone.now().date())
+ if metric > 0:
+ price = self.get_price(account, metric)
+ for cini, cend in self.get_pricing_slots(ini, bp):
+ line = self.generate_line(order, price, cini, cend, metric=metric)
+ lines.append(line)
+ else:
+ raise NotImplementedError
else:
raise NotImplementedError
else:
diff --git a/orchestra/contrib/services/models.py b/orchestra/contrib/services/models.py
index d54aa868..a0ef7964 100644
--- a/orchestra/contrib/services/models.py
+++ b/orchestra/contrib/services/models.py
@@ -1,3 +1,4 @@
+import calendar
import decimal
from django.contrib.contenttypes.models import ContentType
@@ -70,9 +71,8 @@ class Service(models.Model):
" contractedplan.plan.name == 'association_fee''
"
" instance.active"))
handler_type = models.CharField(_("handler"), max_length=256, blank=True,
- help_text=_("Handler used for processing this Service. A handler "
- "enables customized behaviour far beyond what options "
- "here allow to."),
+ help_text=_("Handler used for processing this Service. A handler enables customized "
+ "behaviour far beyond what options here allow to."),
choices=ServiceHandler.get_choices())
is_active = models.BooleanField(_("active"), default=True)
ignore_superusers = models.BooleanField(_("ignore %s") % _ignore_types, default=True,
@@ -87,16 +87,16 @@ class Service(models.Model):
),
default=ANUAL, blank=True)
billing_point = models.CharField(_("billing point"), max_length=16,
- help_text=_("Reference point for calculating the renewal date "
- "on recurring invoices"),
+ help_text=_("Reference point for calculating the renewal date on recurring invoices"),
choices=(
(ON_REGISTER, _("Registration date")),
- (FIXED_DATE, _("Fixed billing date")),
+ (FIXED_DATE, _("Every %(month)s") % {
+ 'month': calendar.month_name[settings.SERVICES_SERVICE_ANUAL_BILLING_MONTH]
+ }),
),
default=FIXED_DATE)
is_fee = models.BooleanField(_("fee"), default=False,
- help_text=_("Designates whether this service should be billed as "
- " membership fee or not"))
+ help_text=_("Designates whether this service should be billed as membership fee or not"))
order_description = models.CharField(_("Order description"), max_length=128, blank=True,
help_text=_(
"Python expression "