From e804a1d1028bbd5777197f498c646d3226c9f2d4 Mon Sep 17 00:00:00 2001 From: Marc Aymerich Date: Mon, 9 May 2016 12:02:10 +0000 Subject: [PATCH] Added support for reissue rejected transactions --- orchestra/contrib/accounts/admin.py | 2 +- orchestra/contrib/mailer/actions.py | 9 +++++++++ orchestra/contrib/mailer/admin.py | 15 +++++++++------ orchestra/contrib/payments/actions.py | 19 ++++++++++++++++++- orchestra/contrib/payments/admin.py | 25 +++++++++++++++++++------ orchestra/contrib/payments/models.py | 7 ++++++- 6 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 orchestra/contrib/mailer/actions.py diff --git a/orchestra/contrib/accounts/admin.py b/orchestra/contrib/accounts/admin.py index 1918a8ff..6df66eab 100644 --- a/orchestra/contrib/accounts/admin.py +++ b/orchestra/contrib/accounts/admin.py @@ -122,7 +122,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin) return views def get_actions(self, request): - actions = super(AccountAdmin, self).get_actions(request) + actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions diff --git a/orchestra/contrib/mailer/actions.py b/orchestra/contrib/mailer/actions.py new file mode 100644 index 00000000..ad94cd16 --- /dev/null +++ b/orchestra/contrib/mailer/actions.py @@ -0,0 +1,9 @@ +from django.core.urlresolvers import reverse +from django.shortcuts import redirect + + +def last(modeladmin, request, queryset): + last_id = queryset.order_by('id').values_list('id', flat=True).first() + url = reverse('admin:mailer_message_change', args=(last_id,)) + print(url) + return redirect(url) diff --git a/orchestra/contrib/mailer/admin.py b/orchestra/contrib/mailer/admin.py index e74ecf76..b979c9aa 100644 --- a/orchestra/contrib/mailer/admin.py +++ b/orchestra/contrib/mailer/admin.py @@ -8,9 +8,11 @@ from django.db.models import Count from django.shortcuts import redirect from django.utils.translation import ugettext_lazy as _ +from orchestra.admin import ExtendedModelAdmin from orchestra.admin.utils import admin_link, admin_colored, admin_date, wrap_admin_view from orchestra.contrib.tasks import task +from .actions import last from .engine import send_pending from .models import Message, SMTPLog @@ -25,13 +27,13 @@ COLORS = { } -class MessageAdmin(admin.ModelAdmin): +class MessageAdmin(ExtendedModelAdmin): list_display = ( 'display_subject', 'colored_state', 'priority', 'to_address', 'from_address', 'created_at_delta', 'display_retries', 'last_try_delta', ) list_filter = ('state', 'priority', 'retries') - list_prefetch_related = ('logs__id') + list_prefetch_related = ('logs',) search_fields = ('to_address', 'from_address', 'subject',) fieldsets = ( (None, { @@ -49,6 +51,7 @@ class MessageAdmin(admin.ModelAdmin): 'display_to', 'display_from', 'display_content', ) date_hierarchy = 'created_at' + change_view_actions = (last,) colored_state = admin_colored('state', colors=COLORS) created_at_delta = admin_date('created_at') @@ -114,7 +117,7 @@ class MessageAdmin(admin.ModelAdmin): def get_urls(self): from django.conf.urls import url - urls = super(MessageAdmin, self).get_urls() + urls = super().get_urls() info = self.model._meta.app_label, self.model._meta.model_name urls.insert(0, url(r'^send-pending/$', @@ -124,8 +127,8 @@ class MessageAdmin(admin.ModelAdmin): return urls def get_queryset(self, request): - qs = super(MessageAdmin, self).get_queryset(request) - return qs.annotate(Count('logs')).prefetch_related('logs').defer('content') + qs = super().get_queryset(request) + return qs.annotate(Count('logs')).defer('content') def send_pending_view(self, request): task(send_pending).apply_async() @@ -135,7 +138,7 @@ class MessageAdmin(admin.ModelAdmin): def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name == 'subject': kwargs['widget'] = forms.TextInput(attrs={'size':'100'}) - return super(MessageAdmin, self).formfield_for_dbfield(db_field, **kwargs) + return super().formfield_for_dbfield(db_field, **kwargs) class SMTPLogAdmin(admin.ModelAdmin): diff --git a/orchestra/contrib/payments/actions.py b/orchestra/contrib/payments/actions.py index 26e55d2e..43ccb2a1 100644 --- a/orchestra/contrib/payments/actions.py +++ b/orchestra/contrib/payments/actions.py @@ -2,8 +2,9 @@ from functools import partial from django.contrib import messages from django.contrib.admin import actions +from django.core.urlresolvers import reverse from django.db import transaction -from django.shortcuts import render +from django.shortcuts import render, redirect from django.utils.safestring import mark_safe from django.utils.text import capfirst from django.utils.translation import ungettext, ugettext_lazy as _ @@ -205,3 +206,19 @@ def report(modeladmin, request, queryset): 'transactions': transactions, } return render(request, 'admin/payments/transaction/report.html', context) + + +def reissue(modeladmin, request, queryset): + if len(queryset) != 1: + messages.error(request, _("One transaction should be selected.")) + return + trans = queryset[0] + url = reverse('admin:payments_transaction_add') + url += '?account=%i&bill=%i&source=%s&amount=%s¤cy=%s' % ( + trans.bill.account_id, + trans.bill_id, + trans.source_id or '', + trans.amount, + trans.currency, + ) + return redirect(url) diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py index e8caa2d8..4c5a02e0 100644 --- a/orchestra/contrib/payments/admin.py +++ b/orchestra/contrib/payments/admin.py @@ -105,9 +105,10 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): ) change_view_actions = ( actions.process_transactions, actions.mark_as_executed, actions.mark_as_secured, - actions.mark_as_rejected, + actions.mark_as_rejected, actions.reissue ) - actions = change_view_actions + (actions.report, list_accounts) + 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 = ( @@ -124,17 +125,28 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): display_created_at = admin_date('created_at', short_description=_("Created")) display_modified_at = admin_date('modified_at', short_description=_("Modified")) + def has_delete_permission(self, *args, **kwargs): + return False + + def get_actions(self, request): + actions = super().get_actions(request) + if 'delete_selected' in actions: + del actions['delete_selected'] + return actions + def get_change_view_actions(self, obj=None): actions = super(TransactionAdmin, self).get_change_view_actions() exclude = [] if obj: if obj.state == Transaction.WAITTING_PROCESSING: - exclude = ['mark_as_executed', 'mark_as_secured'] + exclude = ['mark_as_executed', 'mark_as_secured', 'reissue'] elif obj.state == Transaction.WAITTING_EXECUTION: - exclude = ['process_transactions', 'mark_as_secured'] + exclude = ['process_transactions', 'mark_as_secured', 'reissue'] if obj.state == Transaction.EXECUTED: - exclude = ['process_transactions', 'mark_as_executed'] - elif obj.state in [Transaction.REJECTED, Transaction.SECURED]: + exclude = ['process_transactions', 'mark_as_executed', 'reissue'] + elif obj.state == Transaction.REJECTED: + exclude = ['process_transactions', 'mark_as_executed', 'mark_as_secured', 'mark_as_rejected'] + elif obj.state == Transaction.SECURED: return [] return [action for action in actions if action.__name__ not in exclude] @@ -154,6 +166,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin): ) list_filter = ('state',) fields = ('data', 'file_url', 'created_at') + search_fields = ('transactions__bill__number', 'transactions__bill__account__username', 'id') readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at') list_prefetch_related = ('transactions',) inlines = [TransactionInline] diff --git a/orchestra/contrib/payments/models.py b/orchestra/contrib/payments/models.py index 8d08e3f5..0c65345d 100644 --- a/orchestra/contrib/payments/models.py +++ b/orchestra/contrib/payments/models.py @@ -135,7 +135,12 @@ class Transaction(models.Model): if not self.pk: amount = self.bill.transactions.exclude(state=self.REJECTED).amount() if amount >= self.bill.total: - raise ValidationError(_("New transactions can not be allocated for this bill.")) + raise ValidationError( + _("Bill %(number)s already has valid transactions that cover bill total amount (%(amount)s).") % { + 'number': self.bill.number, + 'amount': amount, + } + ) def get_state_help(self): if self.source: