diff --git a/orchestra/contrib/bills/admin.py b/orchestra/contrib/bills/admin.py index 4c5c55fb..10301e09 100644 --- a/orchestra/contrib/bills/admin.py +++ b/orchestra/contrib/bills/admin.py @@ -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 '{name}'.format( - url=url, color=color, name=state) + return '{name}'.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): diff --git a/orchestra/contrib/bills/filters.py b/orchestra/contrib/bills/filters.py index 8df9ffee..dae95794 100644 --- a/orchestra/contrib/bills/filters.py +++ b/orchestra/contrib/bills/filters.py @@ -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) + diff --git a/orchestra/contrib/bills/models.py b/orchestra/contrib/bills/models.py index d7d933ea..84166d81 100644 --- a/orchestra/contrib/bills/models.py +++ b/orchestra/contrib/bills/models.py @@ -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 diff --git a/orchestra/contrib/bills/templates/admin/bills/change_list.html b/orchestra/contrib/bills/templates/admin/bills/change_list.html new file mode 100644 index 00000000..da8cbc0e --- /dev/null +++ b/orchestra/contrib/bills/templates/admin/bills/change_list.html @@ -0,0 +1,12 @@ +{% extends "admin/change_list.html" %} +{% load i18n admin_urls %} + + +{% block object-tools-items %} +
  • + {% url 'admin:bills_bill_add' as add_url %} + + {% trans "Add bill" %} + +
  • +{% endblock %} diff --git a/orchestra/contrib/lists/backends.py b/orchestra/contrib/lists/backends.py index 758d70b3..5ee8f23d 100644 --- a/orchestra/contrib/lists/backends.py +++ b/orchestra/contrib/lists/backends.py @@ -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 diff --git a/orchestra/contrib/orchestration/__init__.py b/orchestra/contrib/orchestration/__init__.py index 3e717bdd..76880092 100644 --- a/orchestra/contrib/orchestration/__init__.py +++ b/orchestra/contrib/orchestration/__init__.py @@ -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() """ diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py index 8bff0a46..85edd775 100644 --- a/orchestra/contrib/payments/admin.py +++ b/orchestra/contrib/payments/admin.py @@ -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]: diff --git a/orchestra/contrib/webapps/settings.py b/orchestra/contrib/webapps/settings.py index 8f2b5363..5c49b587 100644 --- a/orchestra/contrib/webapps/settings.py +++ b/orchestra/contrib/webapps/settings.py @@ -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',