Addeded better support for bill sublines
This commit is contained in:
parent
5237a4130f
commit
5619141514
2
TODO.md
2
TODO.md
|
@ -161,3 +161,5 @@ textwrap.dedent( \\)
|
||||||
|
|
||||||
* Disable menu on tests, fucking overlapping
|
* Disable menu on tests, fucking overlapping
|
||||||
* service.name / verbose_name instead of .description ?
|
* service.name / verbose_name instead of .description ?
|
||||||
|
|
||||||
|
* Bills can have sublines?
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.contrib import admin
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.templatetags.static import static
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -26,23 +27,19 @@ PAYMENT_STATE_COLORS = {
|
||||||
|
|
||||||
class BillLineInline(admin.TabularInline):
|
class BillLineInline(admin.TabularInline):
|
||||||
model = BillLine
|
model = BillLine
|
||||||
fields = ('description', 'rate', 'quantity', 'tax', 'subtotal', 'get_total')
|
fields = ('description', 'rate', 'quantity', 'tax', 'subtotal', 'display_total')
|
||||||
readonly_fields = ('get_total',)
|
readonly_fields = ('display_total',)
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def display_total(self, line):
|
||||||
if obj and not obj.is_open:
|
total = line.get_total()
|
||||||
return self.fields
|
sublines = line.sublines.all()
|
||||||
return super(BillLineInline, self).get_readonly_fields(request, obj=obj)
|
if sublines:
|
||||||
|
content = '\n'.join(['%s: %s' % (sub.description, sub.total) for sub in sublines])
|
||||||
def has_add_permission(self, request):
|
img = static('admin/img/icon_alert.gif')
|
||||||
if request.__bill__ and not request.__bill__.is_open:
|
return '<span title="%s">%s<img src="%s"></img></span>' % (content, str(total), img)
|
||||||
return False
|
return total
|
||||||
return super(BillLineInline, self).has_add_permission(request)
|
display_total.short_description = _("Total")
|
||||||
|
display_total.allow_tags = True
|
||||||
def has_delete_permission(self, request, obj=None):
|
|
||||||
if obj and not obj.is_open:
|
|
||||||
return False
|
|
||||||
return super(BillLineInline, self).has_delete_permission(request, obj=obj)
|
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
|
@ -52,6 +49,40 @@ class BillLineInline(admin.TabularInline):
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'13'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'13'})
|
||||||
return super(BillLineInline, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(BillLineInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
qs = super(BillLineInline, self).get_queryset(request)
|
||||||
|
return qs.prefetch_related('sublines')
|
||||||
|
|
||||||
|
|
||||||
|
class ClosedBillLineInline(BillLineInline):
|
||||||
|
# TODO reimplement as nested inlines when upstream
|
||||||
|
# https://code.djangoproject.com/ticket/9025
|
||||||
|
|
||||||
|
fields = ('display_description', 'rate', 'quantity', 'tax', 'display_subtotal', 'display_total')
|
||||||
|
readonly_fields = fields
|
||||||
|
|
||||||
|
def display_description(self, line):
|
||||||
|
descriptions = [line.description]
|
||||||
|
for subline in line.sublines.all():
|
||||||
|
descriptions.append(' '*4+subline.description)
|
||||||
|
return '<br>'.join(descriptions)
|
||||||
|
display_description.short_description = _("Description")
|
||||||
|
display_description.allow_tags = True
|
||||||
|
|
||||||
|
def display_subtotal(self, line):
|
||||||
|
subtotals = [' ' + str(line.subtotal)]
|
||||||
|
for subline in line.sublines.all():
|
||||||
|
subtotals.append(str(subline.total))
|
||||||
|
return '<br>'.join(subtotals)
|
||||||
|
display_subtotal.short_description = _("Subtotal")
|
||||||
|
display_subtotal.allow_tags = True
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
|
@ -62,7 +93,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
add_fields = ('account', 'type', 'is_open', 'due_on', 'comments')
|
add_fields = ('account', 'type', 'is_open', 'due_on', 'comments')
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': ('number', 'display_total', 'account_link', 'type',
|
'fields': ('number', 'type', 'account_link', 'display_total',
|
||||||
'display_payment_state', 'is_sent', 'due_on', 'comments'),
|
'display_payment_state', 'is_sent', 'due_on', 'comments'),
|
||||||
}),
|
}),
|
||||||
(_("Raw"), {
|
(_("Raw"), {
|
||||||
|
@ -74,7 +105,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
change_view_actions = [view_bill, download_bills, send_bills, close_bills]
|
change_view_actions = [view_bill, download_bills, send_bills, close_bills]
|
||||||
change_readonly_fields = ('account_link', 'type', 'is_open')
|
change_readonly_fields = ('account_link', 'type', 'is_open')
|
||||||
readonly_fields = ('number', 'display_total', 'is_sent', 'display_payment_state')
|
readonly_fields = ('number', 'display_total', 'is_sent', 'display_payment_state')
|
||||||
inlines = [BillLineInline]
|
inlines = [BillLineInline, ClosedBillLineInline]
|
||||||
|
|
||||||
created_on_display = admin_date('created_on')
|
created_on_display = admin_date('created_on')
|
||||||
|
|
||||||
|
@ -134,9 +165,10 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
return [action for action in actions if action.__name__ not in exclude]
|
return [action for action in actions if action.__name__ not in exclude]
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
# Make parent object available for inline.has_add_permission()
|
inlines = super(BillAdmin, self).get_inline_instances(request, obj=obj)
|
||||||
request.__bill__ = obj
|
if obj and not obj.is_open:
|
||||||
return super(BillAdmin, self).get_inline_instances(request, obj=obj)
|
return [inline for inline in inlines if not type(inline) == BillLineInline]
|
||||||
|
return [inline for inline in inlines if not type(inline) == ClosedBillLineInline]
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
|
|
|
@ -316,6 +316,7 @@ class BillSubline(models.Model):
|
||||||
# TODO: order info for undoing
|
# TODO: order info for undoing
|
||||||
line = models.ForeignKey(BillLine, verbose_name=_("bill line"), related_name='sublines')
|
line = models.ForeignKey(BillLine, verbose_name=_("bill line"), related_name='sublines')
|
||||||
description = models.CharField(_("description"), max_length=256)
|
description = models.CharField(_("description"), max_length=256)
|
||||||
|
# TODO rename to subtotal
|
||||||
total = models.DecimalField(max_digits=12, decimal_places=2)
|
total = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
type = models.CharField(_("type"), max_length=16, choices=TYPES, default=OTHER)
|
type = models.CharField(_("type"), max_length=16, choices=TYPES, default=OTHER)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue