Preliminar implementation of SEPA payment system
This commit is contained in:
parent
bef7af084b
commit
30cc1d9922
|
@ -2,7 +2,7 @@ from django import forms
|
|||
from django.contrib import admin
|
||||
from django.forms.models import BaseInlineFormSet
|
||||
|
||||
from .utils import set_default_filter
|
||||
from .utils import set_url_query
|
||||
|
||||
|
||||
class ExtendedModelAdmin(admin.ModelAdmin):
|
||||
|
@ -55,9 +55,9 @@ class ChangeListDefaultFilter(object):
|
|||
def changelist_view(self, request, extra_context=None):
|
||||
""" Default filter as 'my_nodes=True' """
|
||||
defaults = []
|
||||
for queryarg, value in self.default_changelist_filters:
|
||||
set_default_filter(queryarg, request, value)
|
||||
defaults.append(queryarg)
|
||||
for key, value in self.default_changelist_filters:
|
||||
set_url_query(request, key, value)
|
||||
defaults.append(key)
|
||||
# hack response cl context in order to hook default filter awaearness into search_form.html template
|
||||
response = super(ChangeListDefaultFilter, self).changelist_view(request, extra_context=extra_context)
|
||||
if hasattr(response, 'context_data') and 'cl' in response.context_data:
|
||||
|
|
|
@ -63,13 +63,13 @@ def wrap_admin_view(modeladmin, view):
|
|||
return update_wrapper(wrapper, view)
|
||||
|
||||
|
||||
def set_default_filter(queryarg, request, value):
|
||||
def set_url_query(request, key, value):
|
||||
""" set default filters for changelist_view """
|
||||
if queryarg not in request.GET:
|
||||
if key not in request.GET:
|
||||
request_copy = request.GET.copy()
|
||||
if callable(value):
|
||||
value = value(request)
|
||||
request_copy[queryarg] = value
|
||||
request_copy[key] = value
|
||||
request.GET = request_copy
|
||||
request.META['QUERY_STRING'] = request.GET.urlencode()
|
||||
|
||||
|
|
|
@ -5,10 +5,11 @@ from django.contrib.admin.util import unquote
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.six.moves.urllib.parse import parse_qsl
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.admin import ExtendedModelAdmin
|
||||
from orchestra.admin.utils import wrap_admin_view, admin_link
|
||||
from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query
|
||||
from orchestra.core import services, accounts
|
||||
|
||||
from .filters import HasMainUserListFilter
|
||||
|
@ -129,6 +130,8 @@ class AccountAdminMixin(object):
|
|||
""" Provide basic account support to ModelAdmin and AdminInline classes """
|
||||
readonly_fields = ('account_link',)
|
||||
filter_by_account_fields = []
|
||||
change_list_template = 'admin/accounts/account/change_list.html'
|
||||
change_form_template = 'admin/accounts/account/change_form.html'
|
||||
|
||||
def account_link(self, instance):
|
||||
account = instance.account if instance.pk else self.account
|
||||
|
@ -162,6 +165,48 @@ class AccountAdminMixin(object):
|
|||
formfield.queryset = formfield.queryset.filter(account=self.account)
|
||||
return formfield
|
||||
|
||||
def get_account_from_preserve_filters(self, request):
|
||||
preserved_filters = self.get_preserved_filters(request)
|
||||
preserved_filters = dict(parse_qsl(preserved_filters))
|
||||
cl_filters = preserved_filters.get('_changelist_filters')
|
||||
if cl_filters:
|
||||
return dict(parse_qsl(cl_filters)).get('account')
|
||||
|
||||
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
|
||||
account_id = self.get_account_from_preserve_filters(request)
|
||||
verb = 'change' if object_id else 'add'
|
||||
if not object_id:
|
||||
if account_id:
|
||||
# Preselect account
|
||||
set_url_query(request, 'account', account_id)
|
||||
context = {
|
||||
'from_account': bool(account_id),
|
||||
'account': not account_id or Account.objects.get(pk=account_id),
|
||||
'account_opts': Account._meta,
|
||||
}
|
||||
context.update(extra_context or {})
|
||||
return super(AccountAdminMixin, self).changeform_view(request,
|
||||
object_id=object_id, form_url=form_url, extra_context=context)
|
||||
|
||||
def changelist_view(self, request, extra_context=None):
|
||||
account_id = request.GET.get('account')
|
||||
context = {
|
||||
'from_account': False
|
||||
}
|
||||
if account_id:
|
||||
opts = self.model._meta
|
||||
account = Account.objects.get(pk=account_id)
|
||||
context = {
|
||||
'from_account': True,
|
||||
'title': _("Select %s to change for %s") % (
|
||||
opts.verbose_name, account.name),
|
||||
'account': not account_id or Account.objects.get(pk=account_id),
|
||||
'account_opts': Account._meta,
|
||||
}
|
||||
context.update(extra_context or {})
|
||||
return super(AccountAdminMixin, self).changelist_view(request,
|
||||
extra_context=context)
|
||||
|
||||
|
||||
class SelectAccountAdminMixin(AccountAdminMixin):
|
||||
""" Provides support for accounts on ModelAdmin """
|
||||
|
@ -196,14 +241,21 @@ class SelectAccountAdminMixin(AccountAdminMixin):
|
|||
def add_view(self, request, form_url='', extra_context=None):
|
||||
""" Redirects to select account view if required """
|
||||
if request.user.is_superuser:
|
||||
if 'account' in request.GET or Account.objects.count() == 1:
|
||||
from_account_id = self.get_account_from_preserve_filters(request)
|
||||
if from_account_id:
|
||||
set_url_query(request, 'account', from_account_id)
|
||||
account_id = request.GET.get('account')
|
||||
if account_id or Account.objects.count() == 1:
|
||||
kwargs = {}
|
||||
if 'account' in request.GET:
|
||||
kwargs = dict(pk=request.GET['account'])
|
||||
if account_id:
|
||||
kwargs = dict(pk=account_id)
|
||||
self.account = Account.objects.get(**kwargs)
|
||||
opts = self.model._meta
|
||||
context = {
|
||||
'title': _("Add %s for %s") % (opts.verbose_name, self.account.name)
|
||||
'title': _("Add %s for %s") % (opts.verbose_name, self.account.name),
|
||||
'from_account': bool(from_account_id),
|
||||
'account': self.account,
|
||||
'account_opts': Account._meta,
|
||||
}
|
||||
context.update(extra_context or {})
|
||||
return super(AccountAdminMixin, self).add_view(request,
|
||||
|
|
|
@ -2,18 +2,34 @@
|
|||
{% load i18n admin_urls admin_static admin_modify %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
{% if from_account %}
|
||||
› <a href="{% url 'admin:app_list' app_label=account_opts.app_label %}">{{ account_opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url account_opts|admin_urlname:'changelist' %}">{{ account_opts.verbose_name_plural|capfirst }}</a>
|
||||
› <a href="{% url account_opts|admin_urlname:'change' account.pk|admin_urlquote %}">{{ account|truncatewords:"18" }}</a>
|
||||
› {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}?account={{ account.pk }}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
|
||||
{% else %}
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
|
||||
{% endif %}
|
||||
› {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block object-tools-items %}
|
||||
|
||||
|
||||
{% if services %}
|
||||
{% for service in services %}
|
||||
<li>
|
||||
<a href="{% url service|admin_urlname:'changelist' %}?account={{ original.pk }}" class="historylink">{{ service.verbose_name_plural|capfirst }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
|
||||
<h5 style="visibility:hidden; margin: 1.5em 1.5em 0;">Account</h5>
|
||||
{% endif %}
|
||||
{% if accounts %}
|
||||
<ul class="object-tools">
|
||||
{% for account in accounts %}
|
||||
<li>
|
||||
|
@ -21,10 +37,9 @@
|
|||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
<h5 style="visibility:hidden; margin: 1.5em 1.5em 0;">a</h5>
|
||||
|
||||
<ul class="object-tools">
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="disable/" class="historylink">{% trans "Disable" %}</a>
|
||||
</li>
|
||||
|
@ -32,6 +47,5 @@
|
|||
{% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
|
||||
<a href="{% add_preserved_filters history_url %}" class="historylink">{% trans "History" %}</a>
|
||||
</li>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
{% extends "admin/change_list.html" %}
|
||||
{% load i18n admin_urls %}
|
||||
|
||||
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
{% if from_account %}
|
||||
› <a href="{% url 'admin:app_list' app_label=account_opts.app_label %}">{{ account_opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url account_opts|admin_urlname:'changelist' %}">{{ account_opts.verbose_name_plural|capfirst }}</a>
|
||||
› <a href="{% url account_opts|admin_urlname:'change' account.pk|admin_urlquote %}">{{ account|truncatewords:"18" }}</a>
|
||||
{% else %}
|
||||
› <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
|
||||
{% endif %}
|
||||
› {{ cl.opts.verbose_name_plural|capfirst }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block object-tools-items %}
|
||||
{% if from_account %}
|
||||
<li>
|
||||
<a href="./" class="historylink">{% trans 'Show all' %}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{% url cl.opts|admin_urlname:'add' as add_url %}
|
||||
<a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">
|
||||
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
|
||||
</a>
|
||||
</li>
|
||||
{% endblock %}
|
|
@ -12,9 +12,16 @@ from .models import (Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, Budget,
|
|||
|
||||
class BillLineInline(admin.TabularInline):
|
||||
model = BillLine
|
||||
fields = (
|
||||
'description', 'initial_date', 'final_date', 'price', 'amount', 'tax'
|
||||
)
|
||||
|
||||
|
||||
class BudgetLineInline(admin.TabularInline):
|
||||
model = Budget
|
||||
fields = (
|
||||
'description', 'initial_date', 'final_date', 'price', 'amount', 'tax'
|
||||
)
|
||||
|
||||
|
||||
class BillAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from django import forms
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
|
||||
from orchestra.admin import AtLeastOneRequiredInlineFormSet
|
||||
from orchestra.admin.utils import insertattr
|
||||
from orchestra.apps.accounts.admin import AccountAdmin, AccountAdminMixin
|
||||
|
||||
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
|
||||
from .filters import HasInvoiceContactListFilter
|
||||
from .models import Contact, InvoiceContact
|
||||
|
||||
|
@ -19,6 +20,50 @@ class ContactAdmin(AccountAdminMixin, admin.ModelAdmin):
|
|||
'contact__user__username', 'short_name', 'full_name', 'phone', 'phone2',
|
||||
'email'
|
||||
)
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'classes': ('wide',),
|
||||
'fields': ('account_link', 'short_name', 'full_name')
|
||||
}),
|
||||
(_("Email"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('email', 'email_usage',)
|
||||
}),
|
||||
(_("Phone"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('phone', 'phone2'),
|
||||
}),
|
||||
(_("Postal address"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('address', ('zipcode', 'city'), 'country')
|
||||
}),
|
||||
)
|
||||
add_fieldsets = (
|
||||
(None, {
|
||||
'classes': ('wide',),
|
||||
'fields': ('account', 'short_name', 'full_name')
|
||||
}),
|
||||
(_("Email"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('email', 'email_usage',)
|
||||
}),
|
||||
(_("Phone"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('phone', 'phone_alternative'),
|
||||
}),
|
||||
(_("Postal address"), {
|
||||
'classes': ('wide',),
|
||||
'fields': ('address', ('zip_code', 'city'), 'country')
|
||||
}),
|
||||
)
|
||||
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
""" Make value input widget bigger """
|
||||
if db_field.name == 'address':
|
||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
||||
if db_field.name == 'email_usage':
|
||||
kwargs['widget'] = paddingCheckboxSelectMultiple(130)
|
||||
return super(ContactAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
|
||||
|
||||
admin.site.register(Contact, ContactAdmin)
|
||||
|
@ -32,6 +77,8 @@ class InvoiceContactInline(admin.StackedInline):
|
|||
""" Make value input widget bigger """
|
||||
if db_field.name == 'address':
|
||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
|
||||
if db_field.name == 'email_usage':
|
||||
kwargs['widget'] = paddingCheckboxSelectMultiple(45)
|
||||
return super(InvoiceContactInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
|
||||
|
||||
|
@ -62,6 +109,9 @@ insertattr(AccountAdmin, 'inlines', ContactInline)
|
|||
insertattr(AccountAdmin, 'inlines', InvoiceContactInline)
|
||||
insertattr(AccountAdmin, 'list_display', has_invoice)
|
||||
insertattr(AccountAdmin, 'list_filter', HasInvoiceContactListFilter)
|
||||
for field in ('contacts__short_name', 'contacts__full_name', 'contacts__phone',
|
||||
'contacts__phone2', 'contacts__email'):
|
||||
search_fields = (
|
||||
'contacts__short_name', 'contacts__full_name', 'contacts__phone',
|
||||
'contacts__phone2', 'contacts__email'
|
||||
)
|
||||
for field in search_fields:
|
||||
insertattr(AccountAdmin, 'search_fields', field)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.core import accounts
|
||||
from orchestra.models.fields import MultiSelectField
|
||||
|
||||
from . import settings
|
||||
|
@ -15,8 +16,8 @@ class Contact(models.Model):
|
|||
email_usage = MultiSelectField(_("email usage"), max_length=256, blank=True,
|
||||
choices=settings.CONTACTS_EMAIL_USAGES,
|
||||
default=settings.CONTACTS_DEFAULT_EMAIL_USAGES)
|
||||
phone = models.CharField(_("Phone"), max_length=32, blank=True)
|
||||
phone2 = models.CharField(_("Alternative Phone"), max_length=32, blank=True)
|
||||
phone = models.CharField(_("phone"), max_length=32, blank=True)
|
||||
phone2 = models.CharField(_("alternative phone"), max_length=32, blank=True)
|
||||
address = models.TextField(_("address"), blank=True)
|
||||
city = models.CharField(_("city"), max_length=128, blank=True,
|
||||
default=settings.CONTACTS_DEFAULT_CITY)
|
||||
|
@ -39,3 +40,6 @@ class InvoiceContact(models.Model):
|
|||
country = models.CharField(_("country"), max_length=20,
|
||||
default=settings.CONTACTS_DEFAULT_COUNTRY)
|
||||
vat = models.CharField(_("VAT number"), max_length=64)
|
||||
|
||||
|
||||
accounts.register(Contact)
|
||||
|
|
|
@ -88,7 +88,7 @@ class OrderAdmin(AccountAdminMixin, ChangeListDefaultFilter, admin.ModelAdmin):
|
|||
('is_active', 'True'),
|
||||
)
|
||||
|
||||
content_object_link = admin_link('content_object')
|
||||
content_object_link = admin_link('content_object', order=False)
|
||||
display_registered_on = admin_date('registered_on')
|
||||
display_cancelled_on = admin_date('cancelled_on')
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ class Service(models.Model):
|
|||
def clean(self):
|
||||
content_type = self.handler.get_content_type()
|
||||
if self.content_type != content_type:
|
||||
msg =_("Content type must be equal to '%s'." % str(content_type))
|
||||
msg =_("Content type must be equal to '%s'.") % str(content_type)
|
||||
raise ValidationError(msg)
|
||||
if not self.match:
|
||||
msg =_("Match should be provided")
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
import random
|
||||
import string
|
||||
from lxml import etree
|
||||
from lxml.builder import E
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django_iban.validators import IBANValidator, IBAN_COUNTRY_CODE_LENGTH
|
||||
from rest_framework import serializers
|
||||
|
@ -45,6 +51,116 @@ class BankTransfer(PaymentMethod):
|
|||
form = BankTransferForm
|
||||
serializer = BankTransferSerializer
|
||||
|
||||
def set_id(self):
|
||||
size=6
|
||||
chars=string.ascii_uppercase + string.digits
|
||||
self.payment_id = ''.join(random.choice(chars) for _ in range(size))
|
||||
|
||||
def _process_transactions(self, transactions):
|
||||
for transaction in transactions:
|
||||
account = transaction.account
|
||||
data = transaction.data
|
||||
transaction.info = self.payment_id
|
||||
transaction.state = transaction.WAITTING_CONFIRMATION
|
||||
transaction.save()
|
||||
yield E.DrctDbtTxInf( # Direct Debit Transaction Info
|
||||
E.PmtId( # Payment Id
|
||||
E.EndToEndId(str(transaction.id)) # Payment Id/End to End
|
||||
),
|
||||
E.InstdAmt(transaction.amount, Ccy="EUR"), # Instructed Amount
|
||||
E.DrctDbtTx( # Direct Debit Transaction
|
||||
E.MndtRltdInf( # Mandate Related Info
|
||||
E.MndtId(str(account.id)), # Mandate Id
|
||||
E.DtOfSgntr( # Date of Signature
|
||||
account.registered_on.strfrm("%Y-%m-%d")
|
||||
)
|
||||
)
|
||||
),
|
||||
E.DbtrAgt( # Debtor Agent
|
||||
E.FinInstnId( # Financial Institution Id
|
||||
E.Othr(
|
||||
E.Id('NOTPROVIDED')
|
||||
)
|
||||
)
|
||||
),
|
||||
E.Dbtr( # Debtor
|
||||
E.Nm(account.name), # Name
|
||||
),
|
||||
E.DbtrAcct( # Debtor Account
|
||||
E.Id(
|
||||
E.IBAN(data['iban'])
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
def process(self, transactions)
|
||||
self.set_id()
|
||||
creditor_name = settings.PAYMENTS_DD_CREDITOR_NAME
|
||||
creditor_iban = settings.PAYMENTS_DD_CREDITOR_IBAN
|
||||
creditor_bic = settings.PAYMENTS_DD_CREDITOR_BIC
|
||||
creditor_at02_id = settings.PAYMENTS_DD_CREDITOR_AT02_ID
|
||||
now = timezone.now()
|
||||
total = str(sum([transaction.amount for transaction in transactions]))
|
||||
sepa = E.Document(
|
||||
E.CstmrDrctDbtInitn(
|
||||
E.GrpHdr( # Group Header
|
||||
E.MsgId(self.payment_id), # Message Id
|
||||
E.CreDtTm(now.strftime("%Y-%m-%dT%H:%M:%S")), # Creation Date Time
|
||||
E.NbOfTxs(str(len(transactions))), # Number of Transactions
|
||||
E.CtrlSum(total), # Control Sum
|
||||
E.InitgPty( # Initiating Party
|
||||
E.Nm(creditor_name), # Name
|
||||
E.Id( # Identification
|
||||
E.OrgId( # Organisation Id
|
||||
E.Othr(
|
||||
E.Id(creditor_at_02)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
E.PmtInf( # Payment Info
|
||||
E.PmtInfId(self.payment_id), # Payment Id
|
||||
E.PmtMtd("DD"), # Payment Method
|
||||
E.NbOfTxs(str(len(transactions))), # Number of Transactions
|
||||
E.CtrlSum(total), # Control Sum
|
||||
E.PmtTpInf( # Payment Type Info
|
||||
E.SvcLvl( # Service Level
|
||||
E.Cd("SEPA") # Code
|
||||
),
|
||||
E.LclInstrm( # Local Instrument
|
||||
E.Cd("CORE") # Code
|
||||
),
|
||||
E.SeqTp("RCUR") # Sequence Type
|
||||
),
|
||||
E.ReqdColltnDt(now.strfrm("%Y-%m-%d")), # Requested Collection Date
|
||||
E.Cdtr( # Creditor
|
||||
E.Nm(creditor_name)
|
||||
),
|
||||
E.CdtrAcct( # Creditor Account
|
||||
E.Id(
|
||||
E.IBAN(creditor_iban)
|
||||
)
|
||||
),
|
||||
E.CdtrAgt( # Creditor Agent
|
||||
E.FinInstnId( # Financial Institution Id
|
||||
E.BIC(creditor_bic)
|
||||
)
|
||||
),
|
||||
*list(self._process_transactions(transactions)) # Transactions
|
||||
)
|
||||
), {
|
||||
'xmlns': "urn:iso:std:iso:20022:tech:xsd:pain.008.001.02",
|
||||
'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance"
|
||||
}
|
||||
)
|
||||
# http://www.iso20022.org/documents/messages/1_0_version/pain/schemas/pain.008.001.02.zip
|
||||
schema = etree.parse('pain.008.001.02.xsd')
|
||||
schema.assertValid(sepa)
|
||||
# TODO where to save this shit?
|
||||
# TODO new model? Payment with batch support, How this relates to transaction?
|
||||
return etree.tostring(page, pretty_print=True, xml_declaration=True)
|
||||
|
||||
|
||||
class CreditCard(PaymentMethod):
|
||||
verbose_name = _("Credit card")
|
||||
|
|
|
@ -2,3 +2,13 @@ from django.conf import settings
|
|||
|
||||
|
||||
PAYMENT_CURRENCY = getattr(settings, 'PAYMENT_CURRENCY', 'Eur')
|
||||
|
||||
|
||||
PAYMENTS_DD_CREDITOR_NAME = getattr(settings, 'PAYMENTS_DD_CREDITOR_NAME',
|
||||
'Orchestra')
|
||||
PAYMENTS_DD_CREDITOR_IBAN = getattr(settings, 'PAYMENTS_DD_CREDITOR_IBAN',
|
||||
'InvalidIBAN')
|
||||
PAYMENTS_DD_CREDITOR_BIC = getattr(settings, 'PAYMENTS_DD_CREDITOR_BIC',
|
||||
'InvalidBIC')
|
||||
PAYMENTS_DD_CREDITOR_AT02_ID = getattr(settings, 'PAYMENTS_DD_CREDITOR_AT02_ID',
|
||||
'InvalidAT02ID')
|
||||
|
|
|
@ -89,7 +89,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
|||
|
||||
def display_mailboxes(self, address):
|
||||
boxes = []
|
||||
for mailbox in address.mailboxes():
|
||||
for mailbox in address.mailboxes.all():
|
||||
user = mailbox.user
|
||||
url = reverse('admin:users_user_mailbox_change', args=(user.pk,))
|
||||
boxes.append('<a href="%s">%s</a>' % (url, user.username))
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import re
|
||||
|
||||
from django import forms
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.encoding import force_text
|
||||
|
@ -20,9 +22,13 @@ class ShowTextWidget(forms.Widget):
|
|||
else:
|
||||
final_value = '<br/>'.join(value.split('\n'))
|
||||
if self.warning:
|
||||
final_value = u'<ul class="messagelist"><li class="warning">%s</li></ul>' %(final_value)
|
||||
final_value = (
|
||||
u'<ul class="messagelist"><li class="warning">%s</li></ul>'
|
||||
% final_value)
|
||||
if self.hidden:
|
||||
final_value = u'%s<input type="hidden" name="%s" value="%s"/>' % (final_value, name, value)
|
||||
final_value = (
|
||||
u'%s<input type="hidden" name="%s" value="%s"/>'
|
||||
% (final_value, name, value))
|
||||
return mark_safe(final_value)
|
||||
|
||||
def _has_changed(self, initial, data):
|
||||
|
@ -44,3 +50,16 @@ class ReadOnlyWidget(forms.Widget):
|
|||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
return self.original_value
|
||||
|
||||
|
||||
def paddingCheckboxSelectMultiple(padding):
|
||||
""" Ugly hack to render this widget nicely on Django admin """
|
||||
widget = forms.CheckboxSelectMultiple()
|
||||
old_render = widget.render
|
||||
def render(self, *args, **kwargs):
|
||||
value = old_render(self, *args, **kwargs)
|
||||
value = re.sub(r'^<ul id=(.*)>',
|
||||
r'<ul id=\1 style="padding-left:%ipx">' % padding, value, 1)
|
||||
return mark_safe(value)
|
||||
widget.render = render
|
||||
return widget
|
||||
|
|
Loading…
Reference in a new issue