Improvements on billing
This commit is contained in:
parent
75c72ce8a5
commit
41163b5e52
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
|
@ -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
|
||||
|
|
|
@ -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() """
|
||||
|
|
|
@ -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]:
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in a new issue