diff --git a/TODO.md b/TODO.md index cfe50bb0..7034a32c 100644 --- a/TODO.md +++ b/TODO.md @@ -86,3 +86,9 @@ at + clock time, midnight, noon- At 3:30 p.m., At 4:01, At noon from django.utils import translation with translation.override('en'): * Plurals! + +* help_text on readonly_fields specialy Bill.state. (eg. A bill is in OPEN state when bla bla ) + +* Account link and "show all" button on the filters secction instead of object-tools + +* Create ProForma from orders orders.bill(proforma=True) diff --git a/orchestra/admin/forms.py b/orchestra/admin/forms.py index ccb82a51..b73e499e 100644 --- a/orchestra/admin/forms.py +++ b/orchestra/admin/forms.py @@ -1,5 +1,6 @@ +from django.contrib.admin import helpers +from django.forms.models import modelformset_factory, BaseModelFormSet from django.template import Template, Context -from django.contrib.admin.helpers import AdminForm class AdminFormMixin(object): @@ -9,10 +10,35 @@ class AdminFormMixin(object): fieldsets = [ (None, {'fields': self.fields.keys()}) ] - adminform = AdminForm(self, fieldsets, prepopulated_fields) + adminform = helpers.AdminForm(self, fieldsets, prepopulated_fields) template = Template( '{% for fieldset in adminform %}' ' {% include "admin/includes/fieldset.html" %}' '{% endfor %}' ) return template.render(Context({'adminform': adminform})) + + +class AdminFormSet(BaseModelFormSet): + def as_admin(self): + prepopulated = {} + fieldsets = [ + (None, {'fields': self.form().fields.keys()}) + ] + readonly = getattr(self.form.Meta, 'readonly_fields', ()) + if not hasattr(self.modeladmin, 'verbose_name_plural'): + opts = self.modeladmin.model._meta + self.modeladmin.verbose_name_plural = opts.verbose_name_plural + inline_admin_formset = helpers.InlineAdminFormSet(self.modeladmin, self, + fieldsets, prepopulated, readonly, model_admin=self.modeladmin) + template = Template( + '{% include "admin/edit_inline/tabular.html" %}' + ) + return template.render(Context({'inline_admin_formset': inline_admin_formset})) + + +def adminmodelformset_factory(modeladmin, form): + formset = modelformset_factory(modeladmin.model, extra=0, + form=form, formset=AdminFormSet) + formset.modeladmin = modeladmin + return formset diff --git a/orchestra/apps/bills/actions.py b/orchestra/apps/bills/actions.py index 7e25944c..63ec1bde 100644 --- a/orchestra/apps/bills/actions.py +++ b/orchestra/apps/bills/actions.py @@ -2,11 +2,16 @@ import StringIO import zipfile from django.contrib import messages +from django.contrib.admin import helpers from django.http import HttpResponse +from django.shortcuts import render from django.utils.translation import ugettext_lazy as _ +from orchestra.admin.forms import adminmodelformset_factory from orchestra.utils.html import html_to_pdf +from .forms import SelectSourceForm + def download_bills(modeladmin, request, queryset): if queryset.count() > 1: @@ -35,29 +40,33 @@ view_bill.verbose_name = _("View") view_bill.url_name = 'view' -from django import forms -from django.forms.models import BaseModelFormSet -from django.forms.formsets import formset_factory -from django.forms.models import modelformset_factory -from django.shortcuts import render - -from .forms import SelectPaymentSourceForm - def close_bills(modeladmin, request, queryset): queryset = queryset.filter(status=queryset.model.OPEN) if not queryset: messages.warning(request, _("Selected bills should be in open state")) return - SelectPaymentSourceFormSet = modelformset_factory(queryset.model, form=SelectPaymentSourceForm, extra=0) - if request.POST.get('action') == 'close_selected_bills': - formset = SelectPaymentSourceFormSet(request.POST, queryset=queryset) + SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm) + formset = SelectSourceFormSet(queryset=queryset) + if request.POST.get('post') == 'yes': + formset = SelectSourceFormSet(request.POST, request.FILES, queryset=queryset) if formset.is_valid(): for form in formset.forms: - form.save() + source = form.cleaned_data['source'] + form.instance.close(payment=source) messages.success(request, _("Selected bills have been closed")) return - formset = SelectPaymentSourceFormSet(queryset=queryset) - return render(request, 'admin/bills/close_confirmation.html', {'formset': formset}) + opts = modeladmin.model._meta + context = { + 'title': "Are you sure?", + 'action_value': 'close_bills', + 'deletable_objects': queryset, + 'queryset': queryset, + 'opts': opts, + 'app_label': opts.app_label, + 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, + 'formset': formset, + } + return render(request, 'admin/bills/close_confirmation.html', context) close_bills.verbose_name = _("Close") close_bills.url_name = 'close' diff --git a/orchestra/apps/bills/forms.py b/orchestra/apps/bills/forms.py index b120243f..840e034f 100644 --- a/orchestra/apps/bills/forms.py +++ b/orchestra/apps/bills/forms.py @@ -1,15 +1,28 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from orchestra.admin.utils import admin_link +from orchestra.forms.widgets import ShowTextWidget -class SelectPaymentSourceForm(forms.ModelForm): + +class SelectSourceForm(forms.ModelForm): + bill_link = forms.CharField(label=_("Number"), required=False, + widget=ShowTextWidget()) + account_link = forms.CharField(label=_("Account"), required=False) + display_total = forms.CharField(label=_("Total"), required=False) + display_type = forms.CharField(label=_("Type"), required=False, + widget=ShowTextWidget()) source = forms.ChoiceField(label=_("Source"), required=False) class Meta: - fields = ('number', 'source') + fields = ( + 'bill_link', 'display_type', 'account_link', 'display_total', + 'source' + ) + readonly_fields = ('account_link', 'display_total') def __init__(self, *args, **kwargs): - super(SelectPaymentSourceForm, self).__init__(*args, **kwargs) + super(SelectSourceForm, self).__init__(*args, **kwargs) bill = kwargs.get('instance') if bill: sources = bill.account.paymentsources.filter(is_active=True) @@ -19,6 +32,9 @@ class SelectPaymentSourceForm(forms.ModelForm): if not recharge or source.method_class().allow_recharge: choices.append((source.pk, str(source))) self.fields['source'].choices = choices + self.fields['source'].initial = choices[-1][0] + self.fields['bill_link'].initial = admin_link('__unicode__')(bill) + self.fields['display_type'].initial = bill.get_type_display() def clean_source(self): source_id = self.cleaned_data['source'] @@ -27,8 +43,8 @@ class SelectPaymentSourceForm(forms.ModelForm): source_model = self.instance.account.paymentsources.model return source_model.objects.get(id=source_id) + def has_changed(self): + return False + def save(self, commit=True): - if commit: - source = self.cleaned_data['source'] - self.instance.close(payment=source) - return self.instance + pass diff --git a/orchestra/apps/bills/models.py b/orchestra/apps/bills/models.py index b40a2059..2d2f9480 100644 --- a/orchestra/apps/bills/models.py +++ b/orchestra/apps/bills/models.py @@ -180,7 +180,7 @@ class Bill(models.Model): @cached def get_subtotals(self): subtotals = {} - for line in self.lines.all().prefetch_related('sublines'): + for line in self.lines.all(): subtotal, taxes = subtotals.get(line.tax, (0, 0)) subtotal += line.total for subline in line.sublines.all(): diff --git a/orchestra/apps/bills/templates/admin/bills/close_confirmation.html b/orchestra/apps/bills/templates/admin/bills/close_confirmation.html index 345f4e63..3dd3dd57 100644 --- a/orchestra/apps/bills/templates/admin/bills/close_confirmation.html +++ b/orchestra/apps/bills/templates/admin/bills/close_confirmation.html @@ -4,6 +4,7 @@ {% block extrastyle %} {{ block.super }} + {% endblock %} @@ -18,12 +19,16 @@
{% csrf_token %}
- {{ formset }} + + {{ formset.as_admin }} + +
{% for obj in queryset %} {% endfor %} - + +
diff --git a/orchestra/apps/payments/admin.py b/orchestra/apps/payments/admin.py index 27e7f64d..8303c2fe 100644 --- a/orchestra/apps/payments/admin.py +++ b/orchestra/apps/payments/admin.py @@ -82,6 +82,7 @@ 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, name='%s_%s_select_method' % info), diff --git a/orchestra/apps/payments/models.py b/orchestra/apps/payments/models.py index 34b76127..0a4538b9 100644 --- a/orchestra/apps/payments/models.py +++ b/orchestra/apps/payments/models.py @@ -54,12 +54,19 @@ class PaymentSource(models.Model): class TransactionQuerySet(models.QuerySet): group_by = group_by + + def create(self, **kwargs): + source = kwargs.get('source') + if source is None or not hasattr(source.method_class, 'process'): + # Manual payments don't need processing + kwargs['state']=self.model.WAITTING_CONFIRMATION + return super(TransactionQuerySet, self).create(**kwargs) # TODO lock transaction in waiting confirmation class Transaction(models.Model): - WAITTING_PROCESSING = 'WAITTING_PROCESSING' - WAITTING_CONFIRMATION = 'WAITTING_CONFIRMATION' + WAITTING_PROCESSING = 'WAITTING_PROCESSING' # CREATED + WAITTING_CONFIRMATION = 'WAITTING_CONFIRMATION' # PROCESSED CONFIRMED = 'CONFIRMED' REJECTED = 'REJECTED' DISCARTED = 'DISCARTED'