Random fixes

This commit is contained in:
Marc Aymerich 2016-05-18 14:08:12 +00:00
parent a0dbf96c8a
commit 2490ff83c8
12 changed files with 145 additions and 104 deletions

View file

@ -430,7 +430,6 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# Automatically re-run backends until success? only timedout executions?
# TODO save serialized versions ob backendoperation.instance in order to allow backend reexecution of deleted objects
# upgrade to django 1.9 and make margins wider
# lets encrypt: DNS vs HTTP challange
# lets enctypt: autorenew
@ -459,8 +458,7 @@ with open(file) as handler:
os.unlink(file)
# change filter By PHP version: by detail
# Mark transaction process as executed should not override higher transaction states
# Bill amend and related transaction, what to do? allow edit transaction ammount of amends when their are pending execution
# mailbox.addresses get_Queryset SQL contact @ with mailboxes and forwards
# DASHBOARD: Show owned tickets, scheduled actions, maintenance operations (diff domains)

View file

@ -142,6 +142,7 @@ class ChangeViewActionsMixin(object):
view.tool_description = tool_description
view.css_class = getattr(action, 'css_class', 'historylink')
view.help_text = getattr(action, 'help_text', '')
view.hidden = getattr(action, 'hidden', False)
views.append(view)
return views
@ -150,7 +151,7 @@ class ChangeViewActionsMixin(object):
kwargs['extra_context'] = {}
obj = self.get_object(request, unquote(object_id))
kwargs['extra_context']['object_tools_items'] = [
action.__dict__ for action in self.get_change_view_actions(obj)
action.__dict__ for action in self.get_change_view_actions(obj) if not action.hidden
]
return super().change_view(request, object_id, **kwargs)

View file

@ -32,6 +32,7 @@ def view_bill(modeladmin, request, queryset):
return HttpResponse(html)
view_bill.tool_description = _("View")
view_bill.url_name = 'view'
view_bill.hidden = True
@transaction.atomic

View file

@ -102,6 +102,7 @@ class ClosedBillLineInline(BillLineInline):
'display_subtotal', 'display_total'
)
readonly_fields = fields
can_delete = False
def display_description(self, line):
descriptions = [line.description]
@ -127,9 +128,6 @@ class ClosedBillLineInline(BillLineInline):
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
class BillLineAdmin(admin.ModelAdmin):
@ -243,7 +241,77 @@ class BillLineManagerAdmin(BillLineAdmin):
return super().changelist_view(request, context)
class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
class BillAdminMixin(AccountAdminMixin):
def display_total_with_subtotals(self, bill):
if bill.pk:
currency = settings.BILLS_CURRENCY.lower()
subtotals = []
for tax, subtotal in bill.compute_subtotals().items():
subtotals.append(_("Subtotal %s%% VAT %s &%s;") % (tax, subtotal[0], currency))
subtotals.append(_("Taxes %s%% VAT %s &%s;") % (tax, subtotal[1], currency))
subtotals = '\n'.join(subtotals)
return '<span title="%s">%s &%s;</span>' % (subtotals, bill.compute_total(), currency)
display_total_with_subtotals.allow_tags = True
display_total_with_subtotals.short_description = _("total")
display_total_with_subtotals.admin_order_field = 'approx_total'
def display_payment_state(self, bill):
if bill.pk:
t_opts = bill.transactions.model._meta
if bill.get_type() == bill.PROFORMA:
return '<span title="Pro forma">---</span>'
transactions = bill.transactions.all()
if len(transactions) == 1:
args = (transactions[0].pk,)
view = 'admin:%s_%s_change' % (t_opts.app_label, t_opts.model_name)
url = reverse(view, args=args)
else:
url = reverse('admin:%s_%s_changelist' % (t_opts.app_label, t_opts.model_name))
url += '?bill=%i' % bill.pk
state = bill.get_payment_state_display().upper()
title = ''
if bill.closed_amends:
state = '<strike>%s*</strike>' % state
title = _("This bill has been amended, this value may not be valid.")
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')
return '<a href="{url}" style="color:{color}" title="{title}">{name}</a>'.format(
url=url, color=color, name=state, title=title)
display_payment_state.allow_tags = True
display_payment_state.short_description = _("Payment")
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.annotate(
models.Count('lines'),
# FIXME https://code.djangoproject.com/ticket/10060
approx_total=Coalesce(Sum(
(F('lines__subtotal') + Coalesce('lines__sublines__total', 0)) * (1+F('lines__tax')/100),
), 0),
)
qs = qs.prefetch_related(
Prefetch('amends', queryset=Bill.objects.filter(is_open=False), to_attr='closed_amends')
)
return qs.defer('html')
class AmendInline(BillAdminMixin, admin.TabularInline):
model = Bill
fields = (
'self_link', 'type', 'display_total_with_subtotals', 'display_payment_state', 'is_open',
'is_sent'
)
readonly_fields = fields
verbose_name_plural = _("Amends")
can_delete = False
extra = 0
self_link = admin_link('__str__')
def has_add_permission(self, *args, **kwargs):
return False
class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
list_display = (
'number', 'type_link', 'account_link', 'closed_on_display', 'updated_on_display',
'num_lines', 'display_total', 'display_payment_state', 'is_sent'
@ -256,9 +324,8 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
change_list_template = 'admin/bills/bill/change_list.html'
fieldsets = (
(None, {
'fields': ['number', 'type', 'amend_of_link', 'account_link',
'display_total_with_subtotals', 'display_payment_state',
'is_sent', 'comments'],
'fields': ['number', 'type', (), 'account_link', 'display_total_with_subtotals',
'display_payment_state', 'is_sent', 'comments'],
}),
(_("Dates"), {
'classes': ('collapse',),
@ -281,31 +348,26 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
actions.amend_bills, actions.bill_report, actions.service_report,
actions.close_send_download_bills, list_accounts,
]
change_readonly_fields = (
'account_link', 'type', 'is_open', 'amend_of_link', 'amend_links'
)
change_readonly_fields = ('account_link', 'type', 'is_open', 'amend_of_link')
readonly_fields = (
'number', 'display_total', 'is_sent', 'display_payment_state', 'created_on_display',
'closed_on_display', 'updated_on_display', 'display_total_with_subtotals',
)
inlines = [BillLineInline, ClosedBillLineInline]
date_hierarchy = 'closed_on'
# TODO when merged https://github.com/django/django/pull/5213
#approximate_date_hierarchy = admin.ApproximateWith.MONTHS
created_on_display = admin_date('created_on', short_description=_("Created"))
closed_on_display = admin_date('closed_on', short_description=_("Closed"))
updated_on_display = admin_date('updated_on', short_description=_("Updated"))
amend_of_link = admin_link('amend_of')
def amend_links(self, bill):
links = []
for amend in bill.amends.all():
url = reverse('admin:bills_bill_change', args=(amend.id,))
links.append('<a href="{url}">{num}</a>'.format(url=url, num=amend.number))
return '<br>'.join(links)
amend_links.short_description = _("Amends")
amend_links.allow_tags = True
# def amend_links(self, bill):
# links = []
# for amend in bill.amends.all():
# url = reverse('admin:bills_bill_change', args=(amend.id,))
# links.append('<a href="{url}">{num}</a>'.format(url=url, num=amend.number))
# return '<br>'.join(links)
# amend_links.short_description = _("Amends")
# amend_links.allow_tags = True
def num_lines(self, bill):
return bill.lines__count
@ -319,18 +381,6 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
display_total.short_description = _("total")
display_total.admin_order_field = 'approx_total'
def display_total_with_subtotals(self, bill):
currency = settings.BILLS_CURRENCY.lower()
subtotals = []
for tax, subtotal in bill.compute_subtotals().items():
subtotals.append(_("Subtotal %s%% VAT %s &%s;") % (tax, subtotal[0], currency))
subtotals.append(_("Taxes %s%% VAT %s &%s;") % (tax, subtotal[1], currency))
subtotals = '\n'.join(subtotals)
return '<span title="%s">%s &%s;</span>' % (subtotals, bill.compute_total(), currency)
display_total_with_subtotals.allow_tags = True
display_total_with_subtotals.short_description = _("total")
display_total_with_subtotals.admin_order_field = 'approx_total'
def type_link(self, bill):
bill_type = bill.type.lower()
url = reverse('admin:bills_%s_changelist' % bill_type)
@ -339,27 +389,6 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
type_link.short_description = _("type")
type_link.admin_order_field = 'type'
def display_payment_state(self, bill):
t_opts = bill.transactions.model._meta
transactions = bill.transactions.all()
if len(transactions) == 1:
args = (transactions[0].pk,)
view = 'admin:%s_%s_change' % (t_opts.app_label, t_opts.model_name)
url = reverse(view, args=args)
else:
url = reverse('admin:%s_%s_changelist' % (t_opts.app_label, t_opts.model_name))
url += '?bill=%i' % bill.pk
state = bill.get_payment_state_display().upper()
title = ''
if bill.closed_amends:
state = '<strike>%s*</strike>' % state
title = _("This bill has been amended, this value may not be valid.")
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')
return '<a href="{url}" style="color:{color}" title="{title}">{name}</a>'.format(
url=url, color=color, name=state, title=title)
display_payment_state.allow_tags = True
display_payment_state.short_description = _("Payment")
def get_urls(self):
""" Hook bill lines management URLs on bill admin """
urls = super().get_urls()
@ -381,10 +410,11 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
fieldsets = super().get_fieldsets(request, obj)
if obj:
# Switches between amend_of_link and amend_links fields
fields = fieldsets[0][1]['fields']
if obj.amend_of_id:
fieldsets[0][1]['fields'][2] = 'amend_of_link'
fields[2] = 'amend_of_link'
else:
fieldsets[0][1]['fields'][2] = 'amend_links'
fields[2] = ()
if obj.is_open:
fieldsets = fieldsets[0:-1]
return fieldsets
@ -400,10 +430,15 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
return [action for action in actions if action.__name__ not in exclude]
def get_inline_instances(self, request, obj=None):
inlines = super().get_inline_instances(request, obj)
cls = type(self)
if obj and not obj.is_open:
return [inline for inline in inlines if type(inline) != BillLineInline]
return [inline for inline in inlines if type(inline) != ClosedBillLineInline]
if obj.amends.all():
cls.inlines = [AmendInline, ClosedBillLineInline]
else:
cls.inlines = [ClosedBillLineInline]
else:
cls.inlines = [BillLineInline]
return super().get_inline_instances(request, obj)
def formfield_for_dbfield(self, db_field, **kwargs):
""" Make value input widget bigger """
@ -416,20 +451,6 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
formfield.queryset = formfield.queryset.filter(is_open=False)
return formfield
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.annotate(
models.Count('lines'),
# FIXME https://code.djangoproject.com/ticket/10060
approx_total=Coalesce(Sum(
(F('lines__subtotal') + Coalesce('lines__sublines__total', 0)) * (1+F('lines__tax')/100),
), 0),
)
qs = qs.prefetch_related(
Prefetch('amends', queryset=Bill.objects.filter(is_open=False), to_attr='closed_amends')
)
return qs.defer('html')
def change_view(self, request, object_id, **kwargs):
# TODO raise404, here and everywhere
bill = self.get_object(request, unquote(object_id))

View file

@ -140,20 +140,21 @@ class AmendedListFilter(SimpleListFilter):
def lookups(self, request, model_admin):
return (
('1', _("Closed amends")),
('-1', _("Open or closed amends")),
('0', _("No closed amends")),
('-0', _("No amends")),
('1', _("Amended")),
('2', _("Open amends")),
('3', _("Closed amends")),
('0', _("No amends")),
)
def queryset(self, request, queryset):
if self.value() is None:
return queryset
amended = queryset.filter(type__in=Bill.AMEND_MAP.values(), amend_of__isnull=False)
if not self.value().startswith('-'):
amended = amended.filter(is_open=False)
amended_ids = amended.distinct().values_list('amend_of_id', flat=True)
if self.value().endswith('1'):
return queryset.filter(id__in=amended_ids)
else:
return queryset.exclude(id__in=amended_ids)
amended = queryset.filter(amends__isnull=False)
if self.value() == '1':
return amended.distinct()
elif self.value() == '2':
return amended.filter(amends__is_open=True).distinct()
elif self.value() == '3':
return amended.filter(amends__is_open=False).distinct()
elif self.value() == '0':
return queryset.filter(amends__isnull=True).distinct()

View file

@ -1,6 +1,7 @@
import datetime
from dateutil.relativedelta import relativedelta
from django.core.urlresolvers import reverse
from django.core.validators import ValidationError, RegexValidator
from django.db import models
from django.db.models import F, Sum
@ -254,6 +255,9 @@ class Bill(models.Model):
return now + payment.get_due_delta()
return now + relativedelta(months=1)
def get_absolute_url(self):
return reverse('admin:bills_bill_view', args=(self.pk,))
def close(self, payment=False):
if not self.is_open:
raise TypeError("Bill not in Open state.")

View file

@ -7,3 +7,7 @@
{% endwith %}
</style>
{% endblock %}
{% block payment %}
{% endblock %}

View file

@ -140,6 +140,7 @@
</div>
</div>
<div id="footer-column-2">
{% block payment %}
<div id="payment">
<span class="title">{% trans "PAYMENT" %}</span>
{% if payment.message %}
@ -153,6 +154,8 @@
<strong>{{ seller_info.bank_account }}</strong>
{% endif %}
</div>
{% endblock %}
{% block questions %}
<div id="questions">
<span class="title">{% trans "QUESTIONS" %}</span>
{% blocktrans with type=bill.get_type_display.lower email=seller_info.email %}
@ -161,6 +164,7 @@
your message.
{% endblocktrans %}
</div>
{% endblock %}
</div>
</div>
</div>

View file

@ -109,7 +109,6 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
search_fields = ('bill__number', 'bill__account__username', 'id')
actions = change_view_actions + (actions.report, list_accounts,)
filter_by_account_fields = ('bill', 'source')
change_readonly_fields = ('amount', 'currency')
readonly_fields = (
'bill_link', 'display_state', 'process_link', 'account_link', 'source_link',
'display_created_at', 'display_modified_at'
@ -133,6 +132,11 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
del actions['delete_selected']
return actions
def get_change_readonly_fields(self, request, obj):
if obj.state in (Transaction.WAITTING_PROCESSING, Transaction.WAITTING_EXECUTION):
return ()
return ('amount', 'currency')
def get_change_view_actions(self, obj=None):
actions = super(TransactionAdmin, self).get_change_view_actions()
exclude = []

View file

@ -10,21 +10,21 @@
<table id="result_list">
<thead>
<tr>
<th scope="col"><div class="text"><span>Action</span</div></th>
<th scope="col"><div class="text"><span>ID</span</div></th>
<th scope="col"><div class="text"><span>Service</span</div></th>
<th scope="col"><div class="text"><span>Account</span</div></th>
<th scope="col"><div class="text"><span>Content object</span</div></th>
<th scope="col"><div class="text"><span>Registered on</span</div></th>
<th scope="col"><div class="text"><span>Billed until</span</div></th>
<th scope="col"><div class="text"><span>Cancelled on</span</div>
</th>
<th scope="col"><div class="text">Action</div></th>
<th scope="col"><div class="text">ID</div></th>
<th scope="col"><div class="text">Service</div></th>
<th scope="col"><div class="text">Account</div></th>
<th scope="col"><div class="text">Content object</div></th>
<th scope="col"><div class="text">Registered on</div></th>
<th scope="col"><div class="text">Billed until</div></th>
<th scope="col"><div class="text">Cancelled on</div></th>
<th scope="col"><div class="text">Ignored</div></th>
</tr>
</thead>
<tbody>
{% for order, action in updates %}
<tr class="{% if forloop.counter|divisibleby:2 %}row2{% else %}row1{% endif %}">
<th>{{ action }}</th>
<th>{{ action.capitalize }}</th>
<td>{% if order.pk %}<a href="{{ order | admin_url }}">{{ order.id }}</a>{% endif %}</td>
<td><a href="{{ order.service | admin_url }}">{{ order.service }}</a></td>
<td><a href="{{ order.account | admin_url }}">{{ order.account }}</a></td>
@ -32,6 +32,7 @@
<td><span title="{{ order.registered_on }}">{{ order.registered_on }}</span></td>
<td><span title="{{ order.billed_unitl }}">{{ order.billed_unitl }}</span></td>
<td>{{ order.canncelled_on }}</td>
<td><img src="/static/admin/img/icon-{% if order.ignore %}yes{% else %}no{% endif %}.svg"></td>
</tr>
{% endfor %}
</tbody>

View file

@ -262,7 +262,7 @@ class UNIXUserDisk(ServiceMonitor):
super(UNIXUserDisk, self).prepare()
self.append(textwrap.dedent("""\
function monitor () {
{ du -bs "$1" || echo 0; } | awk {'print $1'}
{ SIZE=$(du -bs "$1") && echo $SIZE || echo 0; } | awk {'print $1'}
}"""
))

View file

@ -27,7 +27,7 @@ class DetailListFilter(SimpleListFilter):
parameter_name = 'detail'
def lookups(self, request, model_admin):
ret = set()
ret = set([('empty', _("Empty"))])
lookup_map = {}
for apptype in AppType.get_plugins():
for field, values in apptype.get_detail_lookups().items():
@ -35,11 +35,13 @@ class DetailListFilter(SimpleListFilter):
lookup_map[value[0]] = field
ret.add(value)
self.lookup_map = lookup_map
return sorted(list(ret))
return sorted(list(ret), key=lambda e: e[1])
def queryset(self, request, queryset):
value = self.value()
if value:
if value == 'empty':
return queryset.filter(data={})
try:
field = self.lookup_map[value]
except KeyError: