Fixes on payments

This commit is contained in:
Marc Aymerich 2016-04-07 11:14:44 +00:00
parent d849ec8867
commit 27aec2e5f0
10 changed files with 64 additions and 28 deletions

View File

@ -370,6 +370,6 @@ def service_report(modeladmin, request, queryset):
return render(request, 'admin/bills/billline/report.html', context) return render(request, 'admin/bills/billline/report.html', context)
def get_ids(modeladmin, request, queryset): def list_bills(modeladmin, request, queryset):
ids = ','.join(map(str, queryset.values_list('id', flat=True))) ids = ','.join(map(str, queryset.values_list('bill_id', flat=True).distinct()))
return HttpResponseRedirect('?id__in=%s' % ids) return HttpResponseRedirect('../bill/?id__in=%s' % ids)

View File

@ -138,7 +138,8 @@ class BillLineAdmin(admin.ModelAdmin):
'tax', 'subtotal', 'display_sublinetotal', 'display_total' 'tax', 'subtotal', 'display_sublinetotal', 'display_total'
) )
actions = ( actions = (
actions.undo_billing, actions.move_lines, actions.copy_lines, actions.service_report actions.undo_billing, actions.move_lines, actions.copy_lines, actions.service_report,
actions.list_bills,
) )
fieldsets = ( fieldsets = (
(None, { (None, {
@ -171,8 +172,9 @@ class BillLineAdmin(admin.ModelAdmin):
display_is_open.boolean = True display_is_open.boolean = True
def display_sublinetotal(self, instance): def display_sublinetotal(self, instance):
return instance.subline_total or '' total = instance.subline_total
display_sublinetotal.short_description = _("Subline") return total if total is not None else '---'
display_sublinetotal.short_description = _("Sublines")
display_sublinetotal.admin_order_field = 'subline_total' display_sublinetotal.admin_order_field = 'subline_total'
def display_total(self, instance): def display_total(self, instance):
@ -196,6 +198,11 @@ class BillLineAdmin(admin.ModelAdmin):
computed_total=(F('subtotal') + Sum(Coalesce('sublines__total', 0))) * (1+F('tax')/100), computed_total=(F('subtotal') + Sum(Coalesce('sublines__total', 0))) * (1+F('tax')/100),
) )
return qs return qs
def has_delete_permission(self, request, obj=None):
if obj and not obj.bill.is_open:
return False
return super().has_delete_permission(request, obj)
class BillLineManagerAdmin(BillLineAdmin): class BillLineManagerAdmin(BillLineAdmin):
@ -216,19 +223,21 @@ class BillLineManagerAdmin(BillLineAdmin):
messages.error(request, _("No bills selected.")) messages.error(request, _("No bills selected."))
return redirect('..') return redirect('..')
self.bill_ids = bill_ids self.bill_ids = bill_ids
bill = None
if len(bill_ids) == 1: if len(bill_ids) == 1:
bill_url = reverse('admin:bills_bill_change', args=(bill_ids[0],)) bill_url = reverse('admin:bills_bill_change', args=(bill_ids[0],))
bill = Bill.objects.get(pk=bill_ids[0]) bill = Bill.objects.get(pk=bill_ids[0])
bill_link = '<a href="%s">%s</a>' % (bill_url, bill.number) bill_link = '<a href="%s">%s</a>' % (bill_url, bill.number)
title = mark_safe(_("Manage %s bill lines.") % bill_link) title = mark_safe(_("Manage %s bill lines") % bill_link)
if not bill.is_open: if not bill.is_open:
messages.warning(request, _("Bill not in open state.")) messages.warning(request, _("Bill not in open state."))
else: else:
if Bill.objects.filter(id__in=bill_ids, is_open=False).exists(): if Bill.objects.filter(id__in=bill_ids, is_open=False).exists():
messages.warning(request, _("Not all bills are in open state.")) messages.warning(request, _("Not all bills are in open state."))
title = _("Manage bill lines of multiple bills.") title = _("Manage bill lines of multiple bills")
context = { context = {
'title': title, 'title': title,
'bill': bill,
} }
context.update(extra_context or {}) context.update(extra_context or {})
return super().changelist_view(request, context) return super().changelist_view(request, context)
@ -244,7 +253,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
AmendedListFilter AmendedListFilter
) )
add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on', 'comments') add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on', 'comments')
change_list_template = 'admin/bills/change_list.html' change_list_template = 'admin/bills/bill/change_list.html'
fieldsets = ( fieldsets = (
(None, { (None, {
'fields': ['number', 'type', 'amend_of_link', 'account_link', 'fields': ['number', 'type', 'amend_of_link', 'account_link',
@ -270,7 +279,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
actions = [ actions = [
actions.manage_lines, actions.download_bills, actions.close_bills, actions.send_bills, actions.manage_lines, actions.download_bills, actions.close_bills, actions.send_bills,
actions.amend_bills, actions.bill_report, actions.service_report, actions.amend_bills, actions.bill_report, actions.service_report,
actions.close_send_download_bills, list_accounts, actions.get_ids, actions.close_send_download_bills, list_accounts,
] ]
change_readonly_fields = ( change_readonly_fields = (
'account_link', 'type', 'is_open', 'amend_of_link', 'amend_links' 'account_link', 'type', 'is_open', 'amend_of_link', 'amend_links'
@ -280,7 +289,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
'closed_on_display', 'updated_on_display', 'display_total_with_subtotals', 'closed_on_display', 'updated_on_display', 'display_total_with_subtotals',
) )
inlines = [BillLineInline, ClosedBillLineInline] inlines = [BillLineInline, ClosedBillLineInline]
date_hierarchy = 'closed_on' #date_hierarchy = 'closed_on'
created_on_display = admin_date('created_on', short_description=_("Created")) created_on_display = admin_date('created_on', short_description=_("Created"))
closed_on_display = admin_date('closed_on', short_description=_("Closed")) closed_on_display = admin_date('closed_on', short_description=_("Closed"))

View File

@ -423,7 +423,7 @@ class BillLine(models.Model):
def get_verbose_quantity(self): def get_verbose_quantity(self):
return self.verbose_quantity or self.quantity return self.verbose_quantity or self.quantity
def clean(): def clean(self):
if not self.verbose_quantity: if not self.verbose_quantity:
quantity = str(self.quantity) quantity = str(self.quantity)
# Strip trailing zeros # Strip trailing zeros

View File

@ -3,6 +3,12 @@
{% block object-tools-items %} {% block object-tools-items %}
<li>
{% url 'admin:bills_billline_changelist' as list_url %}
<a href="{% add_preserved_filters list_url is_popup to_field %}" class="historylink">
{% trans "Lines" %}
</a>
</li>
<li> <li>
{% url 'admin:bills_bill_add' as add_url %} {% url 'admin:bills_bill_add' as add_url %}
<a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink"> <a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">

View File

@ -0,0 +1,12 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url 'admin:bills_bill_changelist' %}">{% trans "Bills" %}</a>
&rsaquo; {% if bill %}<a href="{% url 'admin:bills_bill_change' bill.pk %}">{{ bill }}</a>{% else %}{% trans 'Multiple bills' %}{% endif %}
&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}

View File

@ -143,19 +143,19 @@ class Ticket(models.Model):
def reject(self): def reject(self):
self.state = Ticket.REJECTED self.state = Ticket.REJECTED
self.save(update_fields=['state']) self.save(update_fields=('state', 'updated_at'))
def resolve(self): def resolve(self):
self.state = Ticket.RESOLVED self.state = Ticket.RESOLVED
self.save(update_fields=['state']) self.save(update_fields=('state', 'updated_at'))
def close(self): def close(self):
self.state = Ticket.CLOSED self.state = Ticket.CLOSED
self.save(update_fields=['state']) self.save(update_fields=('state', 'updated_at'))
def take(self, user): def take(self, user):
self.owner = user self.owner = user
self.save(update_fields=['state']) self.save(update_fields=('state', 'updated_at'))
class Message(models.Model): class Message(models.Model):

View File

@ -4,7 +4,7 @@ from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
from orchestra.admin.utils import admin_colored, admin_link from orchestra.admin.utils import admin_colored, admin_link, admin_date
from orchestra.contrib.accounts.actions import list_accounts from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.plugins.admin import SelectPluginAdminMixin from orchestra.plugins.admin import SelectPluginAdminMixin
@ -61,7 +61,7 @@ class TransactionInline(admin.TabularInline):
class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ( list_display = (
'id', 'bill_link', 'account_link', 'source_link', 'display_state', 'id', 'bill_link', 'account_link', 'source_link', 'display_created_at', 'display_modified_at', 'display_state',
'amount', 'process_link' 'amount', 'process_link'
) )
list_filter = ('source__method', 'state') list_filter = ('source__method', 'state')
@ -78,6 +78,10 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
'process_link' 'process_link'
) )
}), }),
(_("Dates"), {
'classes': ('wide',),
'fields': ('display_created_at', 'display_modified_at'),
}),
) )
add_fieldsets = ( add_fieldsets = (
(None, { (None, {
@ -100,7 +104,8 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_by_account_fields = ('bill', 'source') filter_by_account_fields = ('bill', 'source')
change_readonly_fields = ('amount', 'currency') change_readonly_fields = ('amount', 'currency')
readonly_fields = ( readonly_fields = (
'bill_link', 'display_state', 'process_link', 'account_link', 'source_link' 'bill_link', 'display_state', 'process_link', 'account_link', 'source_link',
'display_created_at', 'display_modified_at'
) )
list_select_related = ('source', 'bill__account', 'process') list_select_related = ('source', 'bill__account', 'process')
date_hierarchy = 'created_at' date_hierarchy = 'created_at'
@ -109,6 +114,8 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
source_link = admin_link('source') source_link = admin_link('source')
process_link = admin_link('process', short_description=_("proc")) process_link = admin_link('process', short_description=_("proc"))
account_link = admin_link('bill__account') account_link = admin_link('bill__account')
display_created_at = admin_date('created_at', short_description=_("Created"))
display_modified_at = admin_date('modified_at', short_description=_("Modified"))
def get_change_view_actions(self, obj=None): def get_change_view_actions(self, obj=None):
actions = super(TransactionAdmin, self).get_change_view_actions() actions = super(TransactionAdmin, self).get_change_view_actions()
@ -135,7 +142,7 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin): class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
list_display = ('id', 'file_url', 'display_transactions', 'created_at') list_display = ('id', 'file_url', 'display_transactions', 'display_created_at')
fields = ('data', 'file_url', 'created_at') fields = ('data', 'file_url', 'created_at')
readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at') readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at')
list_prefetch_related = ('transactions',) list_prefetch_related = ('transactions',)
@ -145,6 +152,8 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
) )
actions = change_view_actions + (actions.delete_selected,) actions = change_view_actions + (actions.delete_selected,)
display_created_at = admin_date('created_at', short_description=_("Created"))
def file_url(self, process): def file_url(self, process):
if process.file: if process.file:
return '<a href="%s">%s</a>' % (process.file.url, process.file.name) return '<a href="%s">%s</a>' % (process.file.url, process.file.name)
@ -159,8 +168,8 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
color = STATE_COLORS.get(trans.state, 'black') color = STATE_COLORS.get(trans.state, 'black')
state = trans.get_state_display() state = trans.get_state_display()
ids.append('<span style="color:%s" title="%s">%i</span>' % (color, state, trans.id)) ids.append('<span style="color:%s" title="%s">%i</span>' % (color, state, trans.id))
counter += 1 counter += 1 + len(str(trans.id))
if counter > 10: if counter > 125:
counter = 0 counter = 0
lines.append(','.join(ids)) lines.append(','.join(ids))
ids = [] ids = []

View File

@ -27,7 +27,7 @@ def post_delete_processes(modeladmin, request, related_transactions):
num = 0 num = 0
for transaction in related_transactions: for transaction in related_transactions:
transaction.state = Transaction.WAITTING_PROCESSING transaction.state = Transaction.WAITTING_PROCESSING
transaction.save(update_fields=('state',)) transaction.save(update_fields=('state', 'modified_at'))
num += 1 num += 1
modeladmin.log_change(request, transaction, _("Unprocessed")) modeladmin.log_change(request, transaction, _("Unprocessed"))
messages.success(request, ungettext( messages.success(request, ungettext(

View File

@ -200,7 +200,7 @@ class SEPADirectDebit(PaymentMethod):
for transaction in transactions: for transaction in transactions:
transaction.process = process transaction.process = process
transaction.state = transaction.WAITTING_EXECUTION transaction.state = transaction.WAITTING_EXECUTION
transaction.save(update_fields=['state', 'process']) transaction.save(update_fields=('state', 'process', 'modified_at'))
account = transaction.account account = transaction.account
data = transaction.source.data data = transaction.source.data
yield E.DrctDbtTxInf( # Direct Debit Transaction Info yield E.DrctDbtTxInf( # Direct Debit Transaction Info
@ -245,7 +245,7 @@ class SEPADirectDebit(PaymentMethod):
for transaction in transactions: for transaction in transactions:
transaction.process = process transaction.process = process
transaction.state = transaction.WAITTING_EXECUTION transaction.state = transaction.WAITTING_EXECUTION
transaction.save(update_fields=['state', 'process']) transaction.save(update_fields=('state', 'process', 'modified_at'))
account = transaction.account account = transaction.account
data = transaction.source.data data = transaction.source.data
yield E.CdtTrfTxInf( # Credit Transfer Transaction Info yield E.CdtTrfTxInf( # Credit Transfer Transaction Info

View File

@ -190,16 +190,16 @@ class TransactionProcess(models.Model):
self.state = self.EXECUTED self.state = self.EXECUTED
for transaction in self.transactions.all(): for transaction in self.transactions.all():
transaction.mark_as_executed() transaction.mark_as_executed()
self.save(update_fields=('state',)) self.save(update_fields=('state', 'updated_at'))
def abort(self): def abort(self):
self.state = self.ABORTED self.state = self.ABORTED
for transaction in self.transaction.all(): for transaction in self.transaction.all():
transaction.mark_as_aborted() transaction.mark_as_aborted()
self.save(update_fields=('state',)) self.save(update_fields=('state', 'updated_at'))
def commit(self): def commit(self):
self.state = self.COMMITED self.state = self.COMMITED
for transaction in self.transactions.processing(): for transaction in self.transactions.processing():
transaction.mark_as_secured() transaction.mark_as_secured()
self.save(update_fields=('state',)) self.save(update_fields=('state', 'updated_at'))