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'))