diff --git a/orchestra/admin/decorators.py b/orchestra/admin/decorators.py index 6554413f..80a19074 100644 --- a/orchestra/admin/decorators.py +++ b/orchestra/admin/decorators.py @@ -55,7 +55,7 @@ def action_with_confirmation(action_name, extra_context={}, (action_name, objects_name), "action_name": action_name.capitalize(), "action_value": action_value, - "deletable_objects": queryset, + "display_objects": queryset, 'queryset': queryset, "opts": opts, "app_label": app_label, diff --git a/orchestra/admin/menu.py b/orchestra/admin/menu.py index e0015028..93639046 100644 --- a/orchestra/admin/menu.py +++ b/orchestra/admin/menu.py @@ -3,7 +3,7 @@ from django.core.urlresolvers import reverse from django.utils.text import capfirst from django.utils.translation import ugettext_lazy as _ -from orchestra.core import services +from orchestra.core import services, accounts from orchestra.utils.apps import isinstalled @@ -28,47 +28,39 @@ def api_link(context): def get_services(): - result = [] + childrens = [] for model, options in services.get().iteritems(): if options.get('menu', True): opts = model._meta url = reverse('admin:{}_{}_changelist'.format( opts.app_label, opts.model_name)) name = capfirst(options.get('verbose_name_plural')) - result.append(items.MenuItem(name, url)) - return sorted(result, key=lambda i: i.title) + childrens.append(items.MenuItem(name, url)) + return sorted(childrens, key=lambda i: i.title) -def get_account_items(): +def get_accounts(): childrens = [ items.MenuItem(_("Accounts"), reverse('admin:accounts_account_changelist')) ] - if isinstalled('orchestra.apps.contacts'): - url = reverse('admin:contacts_contact_changelist') - childrens.append(items.MenuItem(_("Contacts"), url)) if isinstalled('orchestra.apps.users'): url = reverse('admin:users_user_changelist') childrens.append(items.MenuItem(_("Users"), url)) - if isinstalled('orchestra.apps.orders'): - url = reverse('admin:orders_plan_changelist') - childrens.append(items.MenuItem(_("Plans"), url)) - url = reverse('admin:orders_order_changelist') - childrens.append(items.MenuItem(_("Orders"), url)) - if isinstalled('orchestra.apps.bills'): - url = reverse('admin:bills_bill_changelist') - childrens.append(items.MenuItem(_("Bills"), url)) if isinstalled('orchestra.apps.payments'): - url = reverse('admin:payments_transaction_changelist') - childrens.append(items.MenuItem(_("Transactions"), url)) url = reverse('admin:payments_transactionprocess_changelist') childrens.append(items.MenuItem(_("Transaction processes"), url)) - url = reverse('admin:payments_paymentsource_changelist') - childrens.append(items.MenuItem(_("Payment sources"), url)) if isinstalled('orchestra.apps.issues'): url = reverse('admin:issues_ticket_changelist') childrens.append(items.MenuItem(_("Tickets"), url)) - return childrens + for model, options in accounts.get().iteritems(): + if options.get('menu', True): + opts = model._meta + url = reverse('admin:{}_{}_changelist'.format( + opts.app_label, opts.model_name)) + name = capfirst(options.get('verbose_name_plural')) + childrens.append(items.MenuItem(name, url)) + return sorted(childrens, key=lambda i: i.title) def get_administration_items(): @@ -128,7 +120,7 @@ class OrchestraMenu(Menu): items.MenuItem( _("Accounts"), reverse('admin:accounts_account_changelist'), - children=get_account_items() + children=get_accounts() ), items.MenuItem( _("Administration"), diff --git a/orchestra/apps/bills/actions.py b/orchestra/apps/bills/actions.py index ded70292..cc78921b 100644 --- a/orchestra/apps/bills/actions.py +++ b/orchestra/apps/bills/actions.py @@ -48,7 +48,7 @@ def close_bills(modeladmin, request, queryset): SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm, extra=0) formset = SelectSourceFormSet(queryset=queryset) - if request.POST.get('post') == 'yes': + if request.POST.get('post') == 'generic_confirmation': formset = SelectSourceFormSet(request.POST, request.FILES, queryset=queryset) if formset.is_valid(): for form in formset.forms: @@ -58,17 +58,19 @@ def close_bills(modeladmin, request, queryset): return opts = modeladmin.model._meta context = { - 'title': "Are you sure?", + 'title': _("Are you sure about closing the following bills?"), + 'content_message': _("Once a bill is closed it can not be further modified.
" + "Please select a payment source for the selected bills"), + 'action_name': 'Close bills', 'action_value': 'close_bills', - 'deletable_objects': queryset, + 'display_objects': [], 'queryset': queryset, 'opts': opts, 'app_label': opts.app_label, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'formset': formset, } - # TODO use generic confirmation template - return render(request, 'admin/bills/close_confirmation.html', context) + return render(request, 'admin/orchestra/generic_confirmation.html', context) close_bills.verbose_name = _("Close") close_bills.url_name = 'close' diff --git a/orchestra/apps/bills/admin.py b/orchestra/apps/bills/admin.py index a86064ba..9bf70ac7 100644 --- a/orchestra/apps/bills/admin.py +++ b/orchestra/apps/bills/admin.py @@ -51,7 +51,6 @@ class BudgetLineInline(admin.TabularInline): fields = ('description', 'rate', 'amount', 'tax', 'total') -# TODO hide raw when status = oPen class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): list_display = ( 'number', 'status', 'type_link', 'account_link', 'created_on_display', @@ -83,9 +82,10 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): num_lines.short_description = _("lines") def display_total(self, bill): - return "%s &%s;" % (bill.get_total(), settings.BILLS_CURRENCY.lower()) + return "%s &%s;" % (bill.total, settings.BILLS_CURRENCY.lower()) display_total.allow_tags = True display_total.short_description = _("total") + display_total.admin_order_field = 'total' def type_link(self, bill): bill_type = bill.type.lower() @@ -101,6 +101,12 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin): fields += self.add_fields return fields + def get_fieldsets(self, request, obj=None): + fieldsets = super(BillAdmin, self).get_fieldsets(request, obj=obj) + if obj and obj.status == obj.OPEN: + fieldsets = (fieldsets[0],) + return fieldsets + def get_change_view_actions(self, obj=None): actions = super(BillAdmin, self).get_change_view_actions(obj) discard = [] diff --git a/orchestra/apps/bills/forms.py b/orchestra/apps/bills/forms.py index 840e034f..599c642a 100644 --- a/orchestra/apps/bills/forms.py +++ b/orchestra/apps/bills/forms.py @@ -26,7 +26,7 @@ class SelectSourceForm(forms.ModelForm): bill = kwargs.get('instance') if bill: sources = bill.account.paymentsources.filter(is_active=True) - recharge = bool(bill.get_total() < 0) + recharge = bool(bill.total < 0) choices = [(None, '-----------')] for source in sources: if not recharge or source.method_class().allow_recharge: diff --git a/orchestra/apps/bills/migrations/0003_bill_total.py b/orchestra/apps/bills/migrations/0003_bill_total.py new file mode 100644 index 00000000..6f51c303 --- /dev/null +++ b/orchestra/apps/bills/migrations/0003_bill_total.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('bills', '0002_bill_closed_on'), + ] + + operations = [ + migrations.AddField( + model_name='bill', + name='total', + field=models.DecimalField(default=10, max_digits=12, decimal_places=2), + preserve_default=False, + ), + ] diff --git a/orchestra/apps/bills/models.py b/orchestra/apps/bills/models.py index 2d2f9480..b3f0b9d6 100644 --- a/orchestra/apps/bills/models.py +++ b/orchestra/apps/bills/models.py @@ -57,10 +57,8 @@ class Bill(models.Model): closed_on = models.DateTimeField(_("closed on"), blank=True, null=True) due_on = models.DateField(_("due on"), null=True, blank=True) last_modified_on = models.DateTimeField(_("last modified on"), auto_now=True) - #base = models.DecimalField(max_digits=12, decimal_places=2) - #tax = models.DecimalField(max_digits=12, decimal_places=2) + total = models.DecimalField(max_digits=12, decimal_places=2) comments = models.TextField(_("comments"), blank=True) - # TODO rename to HTML-agnostic term like.. RAW ? html = models.TextField(_("HTML"), blank=True) objects = BillManager() @@ -121,10 +119,9 @@ class Bill(models.Model): payment = self.account.paymentsources.get_default() if not self.due_on: self.due_on = self.get_due_date(payment=payment) + self.total = self.get_total() self.html = self.render(payment=payment) - self.transactions.create( - bill=self, source=payment, amount=self.get_total() - ) + self.transactions.create(bill=self, source=payment, amount=self.total) self.closed_on = timezone.now() self.status = self.CLOSED self.save() @@ -173,6 +170,8 @@ class Bill(models.Model): def save(self, *args, **kwargs): if not self.type: self.type = self.get_type() + if self.status == self.OPEN: + self.total = self.get_total() if not self.number or (self.number.startswith('O') and self.status != self.OPEN): self.set_number() super(Bill, self).save(*args, **kwargs) @@ -190,7 +189,6 @@ class Bill(models.Model): @cached def get_total(self): - # TODO self.total = self.get_total on self.save() total = 0 for tax, subtotal in self.get_subtotals().iteritems(): subtotal, taxes = subtotal diff --git a/orchestra/apps/bills/templates/admin/bills/close_confirmation.html b/orchestra/apps/bills/templates/admin/bills/close_confirmation.html deleted file mode 100644 index 3dd3dd57..00000000 --- a/orchestra/apps/bills/templates/admin/bills/close_confirmation.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load i18n l10n staticfiles admin_urls %} - -{% block extrastyle %} -{{ block.super }} - - -{% endblock %} - - -{% block breadcrumbs %} -{% endblock %} - - -{% block content %} -
Once a bill is closed it can not be further modified.
-Please select a payment source for the selected bills
- -{% endblock %} - - diff --git a/orchestra/apps/domains/admin.py b/orchestra/apps/domains/admin.py index 233250c1..6691d7e9 100644 --- a/orchestra/apps/domains/admin.py +++ b/orchestra/apps/domains/admin.py @@ -112,7 +112,7 @@ class DomainAdmin(ChangeListDefaultFilter, AccountAdminMixin, ExtendedModelAdmin def get_queryset(self, request): """ Order by structured name and imporve performance """ qs = super(DomainAdmin, self).get_queryset(request) - qs = qs.select_related('top', 'account__user') + qs = qs.select_related('top') # qs = qs.select_related('top') # For some reason if we do this we know for sure that join table will be called T4 __ = str(qs.query) diff --git a/orchestra/apps/payments/admin.py b/orchestra/apps/payments/admin.py index 8303c2fe..b86abadf 100644 --- a/orchestra/apps/payments/admin.py +++ b/orchestra/apps/payments/admin.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse from django.shortcuts import render, redirect from django.utils.translation import ugettext_lazy as _ -from orchestra.admin.utils import admin_colored, admin_link +from orchestra.admin.utils import admin_colored, admin_link, wrap_admin_view from orchestra.apps.accounts.admin import AccountAdminMixin from .actions import process_transactions @@ -82,9 +82,8 @@ class PaymentSourceAdmin(AccountAdminMixin, admin.ModelAdmin): opts = self.model._meta info = opts.app_label, opts.model_name select_urls = patterns("", - # TODO wrap for authentication url("/select-method/$", - self.select_method_view, + wrap_admin_view(self, self.select_method_view), name='%s_%s_select_method' % info), ) return select_urls + urls diff --git a/orchestra/apps/payments/templates/admin/payments/payment_source/select_method.html b/orchestra/apps/payments/templates/admin/payments/payment_source/select_method.html deleted file mode 100644 index 0ca0e702..00000000 --- a/orchestra/apps/payments/templates/admin/payments/payment_source/select_method.html +++ /dev/null @@ -1,28 +0,0 @@ -{% extends "admin/base_site.html" %} -{% load i18n l10n staticfiles admin_urls %} - -{% block extrastyle %} -{{ block.super }} - -{% endblock %} - - -{% block breadcrumbs %} -TODO -{% endblock %} - - -{% block content %} -