Fixes on fees

This commit is contained in:
Marc 2014-09-03 14:51:07 +00:00
parent f4c8ca06ca
commit 5cfb48f8df
8 changed files with 92 additions and 48 deletions

View file

@ -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)

View file

@ -78,9 +78,6 @@ hr {
border: 2px solid #809708;
clear: left;
}
</style>
{% endblock %}
@ -90,28 +87,28 @@ hr {
</div>
<div id="buyer-details">
<span class="name">Aadults</span><br>
ES01939933<br>
Carrer nnoseque, 0<br>
08034 - Barcelona<br>
Spain<br>
<span class="name">{{ buyer.name }}</span><br>
{{ buyer.vat }}<br>
{{ buyer.address }}<br>
{{ buyer.zipcode }} - {{ buyer.city }}<br>
{{ buyer.country }}<br>
</div>
<div id="number" class="column-1">
<span id="number-title">Membership Fee</span><br>
<span id="number-value">Q20110232</span><br>
<span id="number-date">Nov, 2011</span><br>
<span id="number-value">{{ bill.number }}</span><br>
<span id="number-date">{{ bill.created_on | date }}</span><br>
</div>
<div id="amount" class="column-2">
<span id="amount-value">1232,00 &euro;</span><br>
<span id="amount-note">To pay before Oct 20, 2011<br>
<span id="amount-value">{{ bill.get_total }} &euro;</span><br>
<span id="amount-note">To pay before {{ bill.due_date }}<br>
on 213.232.322.232.332<br>
</span>
</div>
<div id="date" class="column-2">
from Apr 1, 2010 to Apr 1, 2011
From {{ bill.lines.get.description }}
</div>
{% endblock %}
{% block content %}

View file

@ -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 '<span title="{raw}" {color}>{human}</span>'.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')

View file

@ -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:

View file

@ -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

View file

@ -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):

View file

@ -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)

View file

@ -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)