diff --git a/orchestra/contrib/bills/actions.py b/orchestra/contrib/bills/actions.py
index e58baa72..6204ef3f 100644
--- a/orchestra/contrib/bills/actions.py
+++ b/orchestra/contrib/bills/actions.py
@@ -370,6 +370,6 @@ def service_report(modeladmin, request, queryset):
return render(request, 'admin/bills/billline/report.html', context)
-def get_ids(modeladmin, request, queryset):
- ids = ','.join(map(str, queryset.values_list('id', flat=True)))
- return HttpResponseRedirect('?id__in=%s' % ids)
+def list_bills(modeladmin, request, queryset):
+ ids = ','.join(map(str, queryset.values_list('bill_id', flat=True).distinct()))
+ return HttpResponseRedirect('../bill/?id__in=%s' % ids)
diff --git a/orchestra/contrib/bills/admin.py b/orchestra/contrib/bills/admin.py
index a16953e4..9b721b9c 100644
--- a/orchestra/contrib/bills/admin.py
+++ b/orchestra/contrib/bills/admin.py
@@ -138,7 +138,8 @@ class BillLineAdmin(admin.ModelAdmin):
'tax', 'subtotal', 'display_sublinetotal', 'display_total'
)
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 = (
(None, {
@@ -171,8 +172,9 @@ class BillLineAdmin(admin.ModelAdmin):
display_is_open.boolean = True
def display_sublinetotal(self, instance):
- return instance.subline_total or ''
- display_sublinetotal.short_description = _("Subline")
+ total = instance.subline_total
+ return total if total is not None else '---'
+ display_sublinetotal.short_description = _("Sublines")
display_sublinetotal.admin_order_field = 'subline_total'
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),
)
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):
@@ -216,19 +223,21 @@ class BillLineManagerAdmin(BillLineAdmin):
messages.error(request, _("No bills selected."))
return redirect('..')
self.bill_ids = bill_ids
+ bill = None
if len(bill_ids) == 1:
bill_url = reverse('admin:bills_bill_change', args=(bill_ids[0],))
bill = Bill.objects.get(pk=bill_ids[0])
bill_link = '%s' % (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:
messages.warning(request, _("Bill not in open state."))
else:
if Bill.objects.filter(id__in=bill_ids, is_open=False).exists():
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 = {
'title': title,
+ 'bill': bill,
}
context.update(extra_context or {})
return super().changelist_view(request, context)
@@ -244,7 +253,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
AmendedListFilter
)
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 = (
(None, {
'fields': ['number', 'type', 'amend_of_link', 'account_link',
@@ -270,7 +279,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
actions = [
actions.manage_lines, actions.download_bills, actions.close_bills, actions.send_bills,
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 = (
'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',
)
inlines = [BillLineInline, ClosedBillLineInline]
- date_hierarchy = 'closed_on'
+ #date_hierarchy = 'closed_on'
created_on_display = admin_date('created_on', short_description=_("Created"))
closed_on_display = admin_date('closed_on', short_description=_("Closed"))
diff --git a/orchestra/contrib/bills/models.py b/orchestra/contrib/bills/models.py
index 1296e977..254da7a1 100644
--- a/orchestra/contrib/bills/models.py
+++ b/orchestra/contrib/bills/models.py
@@ -423,7 +423,7 @@ class BillLine(models.Model):
def get_verbose_quantity(self):
return self.verbose_quantity or self.quantity
- def clean():
+ def clean(self):
if not self.verbose_quantity:
quantity = str(self.quantity)
# Strip trailing zeros
diff --git a/orchestra/contrib/bills/templates/admin/bills/change_list.html b/orchestra/contrib/bills/templates/admin/bills/bill/change_list.html
similarity index 60%
rename from orchestra/contrib/bills/templates/admin/bills/change_list.html
rename to orchestra/contrib/bills/templates/admin/bills/bill/change_list.html
index da8cbc0e..220dbfe3 100644
--- a/orchestra/contrib/bills/templates/admin/bills/change_list.html
+++ b/orchestra/contrib/bills/templates/admin/bills/bill/change_list.html
@@ -3,6 +3,12 @@
{% block object-tools-items %}
+
+ {% url 'admin:bills_billline_changelist' as list_url %}
+
+ {% trans "Lines" %}
+
+
{% url 'admin:bills_bill_add' as add_url %}
diff --git a/orchestra/contrib/bills/templates/admin/bills/billline/change_list.html b/orchestra/contrib/bills/templates/admin/bills/billline/change_list.html
new file mode 100644
index 00000000..ea8dc4cb
--- /dev/null
+++ b/orchestra/contrib/bills/templates/admin/bills/billline/change_list.html
@@ -0,0 +1,12 @@
+{% extends "admin/change_list.html" %}
+{% load i18n admin_urls %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
diff --git a/orchestra/contrib/issues/models.py b/orchestra/contrib/issues/models.py
index 590c2caf..f5529291 100644
--- a/orchestra/contrib/issues/models.py
+++ b/orchestra/contrib/issues/models.py
@@ -143,19 +143,19 @@ class Ticket(models.Model):
def reject(self):
self.state = Ticket.REJECTED
- self.save(update_fields=['state'])
+ self.save(update_fields=('state', 'updated_at'))
def resolve(self):
self.state = Ticket.RESOLVED
- self.save(update_fields=['state'])
+ self.save(update_fields=('state', 'updated_at'))
def close(self):
self.state = Ticket.CLOSED
- self.save(update_fields=['state'])
+ self.save(update_fields=('state', 'updated_at'))
def take(self, user):
self.owner = user
- self.save(update_fields=['state'])
+ self.save(update_fields=('state', 'updated_at'))
class Message(models.Model):
diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py
index 1c770fc3..139dc6a4 100644
--- a/orchestra/contrib/payments/admin.py
+++ b/orchestra/contrib/payments/admin.py
@@ -4,7 +4,7 @@ from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
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.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.plugins.admin import SelectPluginAdminMixin
@@ -61,7 +61,7 @@ class TransactionInline(admin.TabularInline):
class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
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'
)
list_filter = ('source__method', 'state')
@@ -78,6 +78,10 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
'process_link'
)
}),
+ (_("Dates"), {
+ 'classes': ('wide',),
+ 'fields': ('display_created_at', 'display_modified_at'),
+ }),
)
add_fieldsets = (
(None, {
@@ -100,7 +104,8 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_by_account_fields = ('bill', 'source')
change_readonly_fields = ('amount', 'currency')
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')
date_hierarchy = 'created_at'
@@ -109,6 +114,8 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
source_link = admin_link('source')
process_link = admin_link('process', short_description=_("proc"))
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):
actions = super(TransactionAdmin, self).get_change_view_actions()
@@ -135,7 +142,7 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
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')
readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at')
list_prefetch_related = ('transactions',)
@@ -145,6 +152,8 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
)
actions = change_view_actions + (actions.delete_selected,)
+ display_created_at = admin_date('created_at', short_description=_("Created"))
+
def file_url(self, process):
if process.file:
return '%s' % (process.file.url, process.file.name)
@@ -159,8 +168,8 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
color = STATE_COLORS.get(trans.state, 'black')
state = trans.get_state_display()
ids.append('%i' % (color, state, trans.id))
- counter += 1
- if counter > 10:
+ counter += 1 + len(str(trans.id))
+ if counter > 125:
counter = 0
lines.append(','.join(ids))
ids = []
diff --git a/orchestra/contrib/payments/helpers.py b/orchestra/contrib/payments/helpers.py
index 22111062..9fde2489 100644
--- a/orchestra/contrib/payments/helpers.py
+++ b/orchestra/contrib/payments/helpers.py
@@ -27,7 +27,7 @@ def post_delete_processes(modeladmin, request, related_transactions):
num = 0
for transaction in related_transactions:
transaction.state = Transaction.WAITTING_PROCESSING
- transaction.save(update_fields=('state',))
+ transaction.save(update_fields=('state', 'modified_at'))
num += 1
modeladmin.log_change(request, transaction, _("Unprocessed"))
messages.success(request, ungettext(
diff --git a/orchestra/contrib/payments/methods/sepadirectdebit.py b/orchestra/contrib/payments/methods/sepadirectdebit.py
index 26c51b07..9fe453c0 100644
--- a/orchestra/contrib/payments/methods/sepadirectdebit.py
+++ b/orchestra/contrib/payments/methods/sepadirectdebit.py
@@ -200,7 +200,7 @@ class SEPADirectDebit(PaymentMethod):
for transaction in transactions:
transaction.process = process
transaction.state = transaction.WAITTING_EXECUTION
- transaction.save(update_fields=['state', 'process'])
+ transaction.save(update_fields=('state', 'process', 'modified_at'))
account = transaction.account
data = transaction.source.data
yield E.DrctDbtTxInf( # Direct Debit Transaction Info
@@ -245,7 +245,7 @@ class SEPADirectDebit(PaymentMethod):
for transaction in transactions:
transaction.process = process
transaction.state = transaction.WAITTING_EXECUTION
- transaction.save(update_fields=['state', 'process'])
+ transaction.save(update_fields=('state', 'process', 'modified_at'))
account = transaction.account
data = transaction.source.data
yield E.CdtTrfTxInf( # Credit Transfer Transaction Info
diff --git a/orchestra/contrib/payments/models.py b/orchestra/contrib/payments/models.py
index 460a0407..1dc27c43 100644
--- a/orchestra/contrib/payments/models.py
+++ b/orchestra/contrib/payments/models.py
@@ -190,16 +190,16 @@ class TransactionProcess(models.Model):
self.state = self.EXECUTED
for transaction in self.transactions.all():
transaction.mark_as_executed()
- self.save(update_fields=('state',))
+ self.save(update_fields=('state', 'updated_at'))
def abort(self):
self.state = self.ABORTED
for transaction in self.transaction.all():
transaction.mark_as_aborted()
- self.save(update_fields=('state',))
+ self.save(update_fields=('state', 'updated_at'))
def commit(self):
self.state = self.COMMITED
for transaction in self.transactions.processing():
transaction.mark_as_secured()
- self.save(update_fields=('state',))
+ self.save(update_fields=('state', 'updated_at'))