Implemented payments source selection
This commit is contained in:
parent
4c603bf584
commit
cc85956e7b
6
TODO.md
6
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
|
from django.utils import translation
|
||||||
with translation.override('en'):
|
with translation.override('en'):
|
||||||
* Plurals!
|
* 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)
|
||||||
|
|
|
@ -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.template import Template, Context
|
||||||
from django.contrib.admin.helpers import AdminForm
|
|
||||||
|
|
||||||
|
|
||||||
class AdminFormMixin(object):
|
class AdminFormMixin(object):
|
||||||
|
@ -9,10 +10,35 @@ class AdminFormMixin(object):
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, {'fields': self.fields.keys()})
|
(None, {'fields': self.fields.keys()})
|
||||||
]
|
]
|
||||||
adminform = AdminForm(self, fieldsets, prepopulated_fields)
|
adminform = helpers.AdminForm(self, fieldsets, prepopulated_fields)
|
||||||
template = Template(
|
template = Template(
|
||||||
'{% for fieldset in adminform %}'
|
'{% for fieldset in adminform %}'
|
||||||
' {% include "admin/includes/fieldset.html" %}'
|
' {% include "admin/includes/fieldset.html" %}'
|
||||||
'{% endfor %}'
|
'{% endfor %}'
|
||||||
)
|
)
|
||||||
return template.render(Context({'adminform': adminform}))
|
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
|
||||||
|
|
|
@ -2,11 +2,16 @@ import StringIO
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
|
from django.contrib.admin import helpers
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.shortcuts import render
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orchestra.admin.forms import adminmodelformset_factory
|
||||||
from orchestra.utils.html import html_to_pdf
|
from orchestra.utils.html import html_to_pdf
|
||||||
|
|
||||||
|
from .forms import SelectSourceForm
|
||||||
|
|
||||||
|
|
||||||
def download_bills(modeladmin, request, queryset):
|
def download_bills(modeladmin, request, queryset):
|
||||||
if queryset.count() > 1:
|
if queryset.count() > 1:
|
||||||
|
@ -35,29 +40,33 @@ view_bill.verbose_name = _("View")
|
||||||
view_bill.url_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):
|
def close_bills(modeladmin, request, queryset):
|
||||||
queryset = queryset.filter(status=queryset.model.OPEN)
|
queryset = queryset.filter(status=queryset.model.OPEN)
|
||||||
if not queryset:
|
if not queryset:
|
||||||
messages.warning(request, _("Selected bills should be in open state"))
|
messages.warning(request, _("Selected bills should be in open state"))
|
||||||
return
|
return
|
||||||
SelectPaymentSourceFormSet = modelformset_factory(queryset.model, form=SelectPaymentSourceForm, extra=0)
|
SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm)
|
||||||
if request.POST.get('action') == 'close_selected_bills':
|
formset = SelectSourceFormSet(queryset=queryset)
|
||||||
formset = SelectPaymentSourceFormSet(request.POST, queryset=queryset)
|
if request.POST.get('post') == 'yes':
|
||||||
|
formset = SelectSourceFormSet(request.POST, request.FILES, queryset=queryset)
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
for form in formset.forms:
|
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"))
|
messages.success(request, _("Selected bills have been closed"))
|
||||||
return
|
return
|
||||||
formset = SelectPaymentSourceFormSet(queryset=queryset)
|
opts = modeladmin.model._meta
|
||||||
return render(request, 'admin/bills/close_confirmation.html', {'formset': formset})
|
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.verbose_name = _("Close")
|
||||||
close_bills.url_name = 'close'
|
close_bills.url_name = 'close'
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,28 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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)
|
source = forms.ChoiceField(label=_("Source"), required=False)
|
||||||
|
|
||||||
class Meta:
|
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):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SelectPaymentSourceForm, self).__init__(*args, **kwargs)
|
super(SelectSourceForm, self).__init__(*args, **kwargs)
|
||||||
bill = kwargs.get('instance')
|
bill = kwargs.get('instance')
|
||||||
if bill:
|
if bill:
|
||||||
sources = bill.account.paymentsources.filter(is_active=True)
|
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:
|
if not recharge or source.method_class().allow_recharge:
|
||||||
choices.append((source.pk, str(source)))
|
choices.append((source.pk, str(source)))
|
||||||
self.fields['source'].choices = choices
|
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):
|
def clean_source(self):
|
||||||
source_id = self.cleaned_data['source']
|
source_id = self.cleaned_data['source']
|
||||||
|
@ -27,8 +43,8 @@ class SelectPaymentSourceForm(forms.ModelForm):
|
||||||
source_model = self.instance.account.paymentsources.model
|
source_model = self.instance.account.paymentsources.model
|
||||||
return source_model.objects.get(id=source_id)
|
return source_model.objects.get(id=source_id)
|
||||||
|
|
||||||
|
def has_changed(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
if commit:
|
pass
|
||||||
source = self.cleaned_data['source']
|
|
||||||
self.instance.close(payment=source)
|
|
||||||
return self.instance
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ class Bill(models.Model):
|
||||||
@cached
|
@cached
|
||||||
def get_subtotals(self):
|
def get_subtotals(self):
|
||||||
subtotals = {}
|
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, taxes = subtotals.get(line.tax, (0, 0))
|
||||||
subtotal += line.total
|
subtotal += line.total
|
||||||
for subline in line.sublines.all():
|
for subline in line.sublines.all():
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
{% block extrastyle %}
|
{% block extrastyle %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
|
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "orchestra/css/hide-inline-id.css" %}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,12 +19,16 @@
|
||||||
<form action="" method="post">{% csrf_token %}
|
<form action="" method="post">{% csrf_token %}
|
||||||
<div>
|
<div>
|
||||||
<div style="margin:20px;">
|
<div style="margin:20px;">
|
||||||
{{ formset }}
|
|
||||||
|
{{ formset.as_admin }}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% for obj in queryset %}
|
{% for obj in queryset %}
|
||||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<input type="hidden" name="action" value="close_selected_bills"/>
|
<input type="hidden" name="action" value="{{ action_value }}"/>
|
||||||
|
<input type="hidden" name="post" value="yes"/>
|
||||||
<input type="submit" value="{% trans "Yes, close bills" %}" />
|
<input type="submit" value="{% trans "Yes, close bills" %}" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -82,6 +82,7 @@ class PaymentSourceAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
info = opts.app_label, opts.model_name
|
info = opts.app_label, opts.model_name
|
||||||
select_urls = patterns("",
|
select_urls = patterns("",
|
||||||
|
# TODO wrap for authentication
|
||||||
url("/select-method/$",
|
url("/select-method/$",
|
||||||
self.select_method_view,
|
self.select_method_view,
|
||||||
name='%s_%s_select_method' % info),
|
name='%s_%s_select_method' % info),
|
||||||
|
|
|
@ -54,12 +54,19 @@ class PaymentSource(models.Model):
|
||||||
|
|
||||||
class TransactionQuerySet(models.QuerySet):
|
class TransactionQuerySet(models.QuerySet):
|
||||||
group_by = group_by
|
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
|
# TODO lock transaction in waiting confirmation
|
||||||
class Transaction(models.Model):
|
class Transaction(models.Model):
|
||||||
WAITTING_PROCESSING = 'WAITTING_PROCESSING'
|
WAITTING_PROCESSING = 'WAITTING_PROCESSING' # CREATED
|
||||||
WAITTING_CONFIRMATION = 'WAITTING_CONFIRMATION'
|
WAITTING_CONFIRMATION = 'WAITTING_CONFIRMATION' # PROCESSED
|
||||||
CONFIRMED = 'CONFIRMED'
|
CONFIRMED = 'CONFIRMED'
|
||||||
REJECTED = 'REJECTED'
|
REJECTED = 'REJECTED'
|
||||||
DISCARTED = 'DISCARTED'
|
DISCARTED = 'DISCARTED'
|
||||||
|
|
Loading…
Reference in New Issue