diff --git a/orchestra/apps/bills/models.py b/orchestra/apps/bills/models.py
index 5f550530..33a902bf 100644
--- a/orchestra/apps/bills/models.py
+++ b/orchestra/apps/bills/models.py
@@ -54,7 +54,7 @@ class Bill(models.Model):
status = models.CharField(_("status"), max_length=16, choices=STATUSES,
default=OPEN)
created_on = models.DateTimeField(_("created on"), auto_now_add=True)
- due_on = models.DateTimeField(_("due on"), null=True, blank=True)
+ due_on = models.DateField(_("due on"), null=True, blank=True)
last_modified_on = models.DateTimeField(_("last modified on"), auto_now=True)
#base = models.DecimalField(max_digits=12, decimal_places=2)
#tax = models.DecimalField(max_digits=12, decimal_places=2)
diff --git a/orchestra/apps/bills/templates/bills/microspective-fee.html b/orchestra/apps/bills/templates/bills/microspective-fee.html
index 7df25b5a..90ba630d 100644
--- a/orchestra/apps/bills/templates/bills/microspective-fee.html
+++ b/orchestra/apps/bills/templates/bills/microspective-fee.html
@@ -78,9 +78,6 @@ hr {
border: 2px solid #809708;
clear: left;
}
-
-
-
{% endblock %}
@@ -90,28 +87,28 @@ hr {
- Aadults
- ES01939933
- Carrer nnoseque, 0
- 08034 - Barcelona
- Spain
+ {{ buyer.name }}
+ {{ buyer.vat }}
+ {{ buyer.address }}
+ {{ buyer.zipcode }} - {{ buyer.city }}
+ {{ buyer.country }}
Membership Fee
- Q20110232
- Nov, 2011
+ {{ bill.number }}
+ {{ bill.created_on | date }}
- 1232,00 €
- To pay before Oct 20, 2011
+ {{ bill.get_total }} €
+ To pay before {{ bill.due_date }}
on 213.232.322.232.332
-from Apr 1, 2010 to Apr 1, 2011
+From {{ bill.lines.get.description }}
{% endblock %}
{% block content %}
diff --git a/orchestra/apps/orders/admin.py b/orchestra/apps/orders/admin.py
index 8923da9d..8288405b 100644
--- a/orchestra/apps/orders/admin.py
+++ b/orchestra/apps/orders/admin.py
@@ -3,6 +3,7 @@ from django.db import models
from django.contrib import admin
from django.core.urlresolvers import reverse
from django.utils import timezone
+from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ChangeListDefaultFilter
@@ -10,9 +11,10 @@ from orchestra.admin.filters import UsedContentTypeFilter
from orchestra.admin.utils import admin_link, admin_date
from orchestra.apps.accounts.admin import AccountAdminMixin
from orchestra.core import services
+from orchestra.utils.humanize import naturaldate
from .actions import BillSelectedOrders
-from .filters import ActiveOrderListFilter
+from .filters import ActiveOrderListFilter, BilledOrderListFilter
from .models import Service, Order, MetricStorage
@@ -81,7 +83,7 @@ class OrderAdmin(AccountAdminMixin, ChangeListDefaultFilter, admin.ModelAdmin):
'display_registered_on', 'display_billed_until', 'display_cancelled_on'
)
list_display_links = ('id', 'service')
- list_filter = (ActiveOrderListFilter, 'service',)
+ list_filter = (ActiveOrderListFilter, BilledOrderListFilter, 'service',)
actions = (BillSelectedOrders(),)
date_hierarchy = 'registered_on'
default_changelist_filters = (
@@ -90,9 +92,20 @@ class OrderAdmin(AccountAdminMixin, ChangeListDefaultFilter, admin.ModelAdmin):
content_object_link = admin_link('content_object', order=False)
display_registered_on = admin_date('registered_on')
- display_billed_until = admin_date('billed_until')
display_cancelled_on = admin_date('cancelled_on')
+ def display_billed_until(self, order):
+ value = order.billed_until
+ color = ''
+ if value and value < timezone.now():
+ color = 'style="color:red;"'
+ return '{human}'.format(
+ raw=escape(str(value)), color=color, human=escape(naturaldate(value)),
+ )
+ display_billed_until.short_description = _("billed until")
+ display_billed_until.allow_tags = True
+ display_billed_until.admin_order_field = 'billed_until'
+
def get_queryset(self, request):
qs = super(OrderAdmin, self).get_queryset(request)
return qs.select_related('service').prefetch_related('content_object')
diff --git a/orchestra/apps/orders/backends.py b/orchestra/apps/orders/backends.py
index 93bd7c9d..4d7606e8 100644
--- a/orchestra/apps/orders/backends.py
+++ b/orchestra/apps/orders/backends.py
@@ -6,19 +6,26 @@ from orchestra.apps.bills.models import Invoice, Fee, BillLine, BillSubline
class BillsBackend(object):
def create_bills(self, account, lines):
invoice = None
- fees = []
+ bills = []
for order, nominal_price, size, ini, end, discounts in lines:
service = order.service
if service.is_fee:
- fee = Fee.objects.get_or_create(account=account, status=Fee.OPEN)
- line = fee.lines.create(rate=service.nominal_price, amount=size,
- total=nominal_price, tax=0)
+ fee, __ = Fee.objects.get_or_create(account=account, status=Fee.OPEN)
+ line = fee.lines.create(
+ rate=service.nominal_price,
+ amount=size,
+ total=nominal_price, tax=0,
+ description="{ini} to {end}".format(
+ ini=ini.strftime("%b, %Y"),
+ end=(end-datetime.timedelta(seconds=1)).strftime("%b, %Y")),
+ )
self.create_sublines(line, discounts)
- fees.append(fee)
+ bills.append(fee)
else:
if invoice is None:
invoice, __ = Invoice.objects.get_or_create(account=account,
status=Invoice.OPEN)
+ bills.append(invoice)
description = order.description
if service.billing_period != service.NEVER:
description += " {ini} to {end}".format(
@@ -32,7 +39,7 @@ class BillsBackend(object):
tax=service.tax,
)
self.create_sublines(line, discounts)
- return [invoice] + fees
+ return bills
def create_sublines(self, line, discounts):
for name, value in discounts:
diff --git a/orchestra/apps/orders/filters.py b/orchestra/apps/orders/filters.py
index b02f51f5..87d1c7a7 100644
--- a/orchestra/apps/orders/filters.py
+++ b/orchestra/apps/orders/filters.py
@@ -1,10 +1,12 @@
from django.contrib.admin import SimpleListFilter
+from django.db.models import Q
+from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class ActiveOrderListFilter(SimpleListFilter):
""" Filter tickets by created_by according to request.user """
- title = _("Orders")
+ title = _("is active")
parameter_name = 'is_active'
def lookups(self, request, model_admin):
@@ -26,3 +28,29 @@ class ActiveOrderListFilter(SimpleListFilter):
choices = iter(super(ActiveOrderListFilter, self).choices(cl))
choices.next()
return choices
+
+
+class BilledOrderListFilter(SimpleListFilter):
+ """ Filter tickets by created_by according to request.user """
+ title = _("billed")
+ parameter_name = 'pending'
+
+ def lookups(self, request, model_admin):
+ return (
+ ('to_date', _("To date")),
+ ('full', _("Full period")),
+ ('not', _("Not billed")),
+ )
+
+ def queryset(self, request, queryset):
+ if self.value() == 'to_date':
+ return queryset.filter(billed_until__isnull=False,
+ billed_until__gte=timezone.now())
+ elif self.value() == 'full':
+ raise NotImplementedError
+ elif self.value() == 'not':
+ return queryset.filter(
+ Q(billed_until__isnull=True) |
+ Q(billed_until__lt=timezone.now())
+ )
+ return queryset
diff --git a/orchestra/apps/orders/models.py b/orchestra/apps/orders/models.py
index 89de5229..d8dc0b7c 100644
--- a/orchestra/apps/orders/models.py
+++ b/orchestra/apps/orders/models.py
@@ -144,7 +144,7 @@ class Service(models.Model):
(POSTPAY, _("Postpay (on demand)")),
),
default=PREPAY)
- trial_period = models.CharField(_("trial period"), max_length=16,
+ trial_period = models.CharField(_("trial period"), max_length=16, blank=True,
help_text=_("Period in which no charge will be issued"),
choices=(
(NEVER, _("No trial")),
@@ -161,7 +161,7 @@ class Service(models.Model):
(ONE_MONTH, _("One month")),
(ALWAYS, _("Always refound")),
),
- default=NEVER)
+ default=NEVER, blank=True)
@property
def nominal_price(self):
diff --git a/orchestra/apps/prices/models.py b/orchestra/apps/prices/models.py
index ae39206d..0c4c297b 100644
--- a/orchestra/apps/prices/models.py
+++ b/orchestra/apps/prices/models.py
@@ -2,7 +2,7 @@ from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
-from orchestra.core import accounts
+from orchestra.core import accounts, services
from . import settings
@@ -33,3 +33,4 @@ class Rate(models.Model):
accounts.register(Pack)
+services.register(Pack, menu=False)
diff --git a/orchestra/utils/humanize.py b/orchestra/utils/humanize.py
index dc821394..6d8e37b5 100644
--- a/orchestra/utils/humanize.py
+++ b/orchestra/utils/humanize.py
@@ -6,26 +6,26 @@ from django.utils.translation import ungettext, ugettext as _
def pluralize_year(n):
return ungettext(
- _('{ahead}{num:.1f} year{ago}'),
- _('{ahead}{num:.1f} years{ago}'), n)
+ _('{num:.1f} year{ago}'),
+ _('{num:.1f} years{ago}'), n)
def pluralize_month(n):
return ungettext(
- _('{ahead}{num:.1f} month{ago}'),
- _('{ahead}{num:.1f} months{ago}'), n)
+ _('{num:.1f} month{ago}'),
+ _('{num:.1f} months{ago}'), n)
def pluralize_week(n):
return ungettext(
- _('{ahead}{num:.1f} week{ago}'),
- _('{ahead}{num:.1f} weeks {ago}'), n)
+ _('{num:.1f} week{ago}'),
+ _('{num:.1f} weeks {ago}'), n)
def pluralize_day(n):
return ungettext(
- _('{ahead}{num:.1f} day{ago}'),
- _('{ahead}{num:.1f} days{ago}'), n)
+ _('{num:.1f} day{ago}'),
+ _('{num:.1f} days{ago}'), n)
OLDER_CHUNKS = (
@@ -57,10 +57,8 @@ def naturaldate(date, include_seconds=False):
seconds = delta.seconds
ago = ' ago'
- ahead = ''
if days < 0:
ago = ''
- ahead = 'in '
days = abs(days)
if days == 0:
@@ -68,22 +66,22 @@ def naturaldate(date, include_seconds=False):
if minutes > 0:
minutes += float(seconds)/60
return ungettext(
- _('{ahead}{minutes:.1f} minute{ago}'),
- _('{ahead}{minutes:.1f} minutes{ago}'), minutes
- ).format(minutes=minutes, ago=ago, ahead=ahead)
+ _('{minutes:.1f} minute{ago}'),
+ _('{minutes:.1f} minutes{ago}'), minutes
+ ).format(minutes=minutes, ago=ago)
else:
if include_seconds and seconds:
return ungettext(
- _('{ahead}{seconds} second{ago}'),
- _('{ahead}{seconds} seconds{ago}'), seconds
- ).format(seconds=seconds, ago=ago, ahead=ahead)
+ _('{seconds} second{ago}'),
+ _('{seconds} seconds{ago}'), seconds
+ ).format(seconds=seconds, ago=ago)
return _('just now')
else:
hours += float(minutes)/60
return ungettext(
- _('{ahead}{hours:.1f} hour{ago}'),
- _('{ahead}{hours:.1f} hours{ago}'), hours
- ).format(hours=hours, ago=ago, ahead=ahead)
+ _('{hours:.1f} hour{ago}'),
+ _('{hours:.1f} hours{ago}'), hours
+ ).format(hours=hours, ago=ago)
if delta_midnight.days == 0:
return _('yesterday at {time}').format(time=date.strftime('%H:%M'))
@@ -93,9 +91,9 @@ def naturaldate(date, include_seconds=False):
if days < 7.0:
count = days + float(hours)/24
fmt = pluralize_day(count)
- return fmt.format(num=count, ago=ago, ahead=ahead)
+ return fmt.format(num=count, ago=ago)
if days >= chunk:
count = (delta_midnight.days + 1) / chunk
count = abs(count)
fmt = pluralizefun(count)
- return fmt.format(num=count, ago=ago, ahead=ahead)
+ return fmt.format(num=count, ago=ago)