Improvements on billing

This commit is contained in:
Marc Aymerich 2015-07-08 10:21:19 +00:00
parent 75c72ce8a5
commit 41163b5e52
8 changed files with 69 additions and 24 deletions

View File

@ -4,7 +4,7 @@ from django.contrib import admin, messages
from django.contrib.admin.utils import unquote
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import F, Sum
from django.db.models import F, Sum, Prefetch
from django.db.models.functions import Coalesce
from django.templatetags.static import static
from django.utils.safestring import mark_safe
@ -17,8 +17,10 @@ from orchestra.contrib.accounts.admin import AccountAdminMixin, AccountAdmin
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
from . import settings, actions
from .filters import BillTypeListFilter, HasBillContactListFilter, TotalListFilter, PaymentStateListFilter
from .models import Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, ProForma, BillLine, BillContact
from .filters import (BillTypeListFilter, HasBillContactListFilter, TotalListFilter,
PaymentStateListFilter, AmendedListFilter)
from .models import (Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, ProForma, BillLine,
BillContact)
PAYMENT_STATE_COLORS = {
@ -185,8 +187,12 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
'number', 'type_link', 'account_link', 'created_on_display',
'num_lines', 'display_total', 'display_payment_state', 'is_open', 'is_sent'
)
list_filter = (BillTypeListFilter, 'is_open', 'is_sent', TotalListFilter, PaymentStateListFilter)
list_filter = (
BillTypeListFilter, 'is_open', 'is_sent', TotalListFilter, PaymentStateListFilter,
AmendedListFilter
)
add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on', 'comments')
change_list_template = 'admin/bills/change_list.html'
fieldsets = (
(None, {
'fields': ('number', 'type', 'amend_of_link', 'account_link', 'display_total',
@ -243,9 +249,13 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
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 += '*'
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}">{name}</a>'.format(
url=url, color=color, name=state)
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")
@ -309,6 +319,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
(F('lines__subtotal') + Coalesce(F('lines__sublines__total'), 0)) * (1+F('lines__tax')/100)
),
)
qs = qs.prefetch_related(Prefetch('amends', queryset=Bill.objects.filter(is_open=False), to_attr='closed_amends'))
return qs
def change_view(self, request, object_id, **kwargs):

View File

@ -91,7 +91,6 @@ class PaymentStateListFilter(SimpleListFilter):
('PAID', _("Paid")),
('PENDING', _("Pending")),
('BAD_DEBT', _("Bad debt")),
('AMENDED', _("Amended")),
)
def queryset(self, request, queryset):
@ -128,7 +127,29 @@ class PaymentStateListFilter(SimpleListFilter):
Q(transactions__state=Transaction.REJECTED) |
Q(transactions__isnull=True)
)
elif self.value() == 'AMENDED':
amendeds = queryset.filter(type__in=Bill.AMEND_MAP.values(), is_open=False)
amendeds_ids = amendeds.values_list('amend_of', flat=True)
return queryset.filter(id__in=amendeds)
class AmendedListFilter(SimpleListFilter):
title = _("amended")
parameter_name = 'amended'
def lookups(self, request, model_admin):
return (
('1', _("Closed amends")),
('-1', _("Open or closed amends")),
('0', _("No 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)

View File

@ -140,8 +140,6 @@ class Bill(models.Model):
def payment_state(self):
if self.is_open or self.get_type() == self.PROFORMA:
return self.OPEN
elif self.amends.filter(is_open=False).exists():
return self.AMENDED
secured = 0
pending = 0
created = False

View File

@ -0,0 +1,12 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls %}
{% block object-tools-items %}
<li>
{% url 'admin:bills_bill_add' as add_url %}
<a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">
{% trans "Add bill" %}
</a>
</li>
{% endblock %}

View File

@ -19,19 +19,19 @@ class MailmanVirtualDomainBackend(ServiceController):
('LISTS_VIRTUAL_ALIAS_DOMAINS_PATH',)
)
def is_local_domain(self, domain):
def is_hosted_domain(self, domain):
""" whether or not domain MX points to this server """
return domain.has_default_mx()
def include_virtual_alias_domain(self, context):
domain = context['address_domain']
if domain and self.is_local_domain(domain):
if domain and self.is_hosted_domain(domain):
self.append(textwrap.dedent("""
# Add virtual domain %(address_domain)s
[[ $(grep '^\s*%(address_domain)s\s*$' %(virtual_alias_domains)s) ]] || {
echo '%(address_domain)s' >> %(virtual_alias_domains)s
UPDATED_VIRTUAL_ALIAS_DOMAINS=1
}""") % self.context
}""") % context
)
def is_last_domain(self, domain):
@ -108,8 +108,9 @@ class MailmanBackend(MailmanVirtualDomainBackend):
context['suffix'] = suffix
# Because mailman doesn't properly handle lists aliases we need two virtual aliases
aliases.append("%(address_name)s%(suffix)s@%(domain)s\t%(name)s%(suffix)s" % context)
# And another with the original list name; Mailman generates links with it
aliases.append("%(name)s%(suffix)s@%(domain)s\t%(name)s%(suffix)s" % context)
if context['address_name'] != context['name']:
# And another with the original list name; Mailman generates links with it
aliases.append("%(name)s%(suffix)s@%(domain)s\t%(name)s%(suffix)s" % context)
return '\n'.join(aliases)
def save(self, mail_list):
@ -122,7 +123,10 @@ class MailmanBackend(MailmanVirtualDomainBackend):
}""") % context)
# Custom domain
if mail_list.address:
context['aliases'] = self.get_virtual_aliases(context)
context.update({
'aliases': self.get_virtual_aliases(context),
'num_entries': 2 if context['address_name'] != context['name'] else 1,
})
self.append(textwrap.dedent("""\
# Create list alias for custom domain
aliases='%(aliases)s'
@ -130,7 +134,7 @@ class MailmanBackend(MailmanVirtualDomainBackend):
echo "${aliases}" >> %(virtual_alias)s
UPDATED_VIRTUAL_ALIAS=1
else
if [[ ! $(grep '^\s*%(address_name)s@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s) ]]; then
if [[ $(grep -E '^\s*(%(address_name)s|%(name)s)@%(address_domain)s\s\s*%(name)s\s*$' %(virtual_alias)s|wc -l) -ne %(num_entries)s ]]; then
sed -i -e '/^.*\s%(name)s\(%(suffixes_regex)s\)\s*$/d' \\
-e 'N; /^\s*\\n\s*$/d; P; D' %(virtual_alias)s
echo "${aliases}" >> %(virtual_alias)s

View File

@ -22,7 +22,7 @@ class Operation():
def __hash__(self):
""" set() """
return hash(self.backend) + hash(self.instance) + hash(self.action)
return hash((self.backend, self.instance, self.action))
def __eq__(self, operation):
""" set() """

View File

@ -109,9 +109,9 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
exclude = []
if obj:
if obj.state == Transaction.WAITTING_PROCESSING:
exclude = ['mark_as_executed', 'mark_as_secured', 'mark_as_rejected']
exclude = ['mark_as_executed', 'mark_as_secured']
elif obj.state == Transaction.WAITTING_EXECUTION:
exclude = ['process_transactions', 'mark_as_secured', 'mark_as_rejected']
exclude = ['process_transactions', 'mark_as_secured']
if obj.state == Transaction.EXECUTED:
exclude = ['process_transactions', 'mark_as_executed']
elif obj.state in [Transaction.REJECTED, Transaction.SECURED]:

View File

@ -192,7 +192,6 @@ WEBAPPS_PHP_DISABLED_FUNCTIONS = Setting('WEBAPPS_PHP_DISABLED_FUNCTION', (
'system',
'proc_open',
'popen',
'curl_exec',
'curl_multi_exec',
'show_source',
'pcntl_exec',