diff --git a/TODO.md b/TODO.md
index 4309f35e..79391137 100644
--- a/TODO.md
+++ b/TODO.md
@@ -413,3 +413,14 @@ touch /tmp/somefile
# inherit registers from parent?
# Disable pagination on membership fees (allways one page)
+
+# datetime metric storage granularity: otherwise innacurate detection of billed metric on order.billed_on
+
+# Serializers.validation migration to DRF3: grep -r 'attrs, source' *|grep -v '~'
+serailzer self.instance on create.
+
+# generate Direct debit q19 on a protected path, or store it on the transaction.proc
+# regenerate direct debit q19
+# add transproc.method for regeneration
+
+# TODO wrapp admin delete: delete proc undo processing on related transactions
diff --git a/orchestra/contrib/accounts/models.py b/orchestra/contrib/accounts/models.py
index 13a62e3a..f19a2cf9 100644
--- a/orchestra/contrib/accounts/models.py
+++ b/orchestra/contrib/accounts/models.py
@@ -14,9 +14,9 @@ from . import settings
class Account(auth.AbstractBaseUser):
- # Username max_length determined by LINUX system user lentgh: 32
+ # Username max_length determined by LINUX system user/group lentgh: 32
username = models.CharField(_("username"), max_length=32, unique=True,
- help_text=_("Required. 64 characters or fewer. Letters, digits and ./-/_ only."),
+ help_text=_("Required. 32 characters or fewer. Letters, digits and ./-/_ only."),
validators=[
validators.RegexValidator(r'^[\w.-]+$', _("Enter a valid username."), 'invalid')
])
@@ -102,7 +102,7 @@ class Account(auth.AbstractBaseUser):
extra_context.update(context)
with translation.override(self.language):
send_email_template(
- template, extra_context, ('marcay@pangea.org',), email_from=email_from, html=html,
+ template, extra_context, email_to, email_from=email_from, html=html,
attachments=attachments)
def get_full_name(self):
diff --git a/orchestra/contrib/bills/actions.py b/orchestra/contrib/bills/actions.py
index 62c5de22..592d3b53 100644
--- a/orchestra/contrib/bills/actions.py
+++ b/orchestra/contrib/bills/actions.py
@@ -76,8 +76,8 @@ def close_bills(modeladmin, request, queryset):
if num == 1:
url = change_url(transactions[0])
else:
- url = reverse('admin:transactions_transaction_changelist')
- url += 'id__in=%s' % ','.join(map(str, transactions))
+ url = reverse('admin:payments_transaction_changelist')
+ url += 'id__in=%s' % ','.join([str(t.id) for t in transactions])
context = {
'url': url,
'num': num,
@@ -109,19 +109,15 @@ close_bills.url_name = 'close'
def send_bills(modeladmin, request, queryset):
- num = 0
for bill in queryset:
if not validate_contact(request, bill):
return
- num += 1
- if num == 1:
- bill.send()
- else:
- # Batch email
- queryset.send()
+ num = 0
for bill in queryset:
+ bill.send()
modeladmin.log_change(request, bill, 'Sent')
- messages.success(request, ungetetx(
+ num += 1
+ messages.success(request, ungettext(
_("One bill has been sent."),
_("%i bills have been sent.") % num,
num))
diff --git a/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.mo b/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.mo
index e2aeb0f7..0e97045d 100644
Binary files a/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.mo and b/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.mo differ
diff --git a/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.po b/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.po
index 5939399a..67cc7bf4 100644
--- a/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.po
+++ b/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-05-28 09:23+0000\n"
+"POT-Creation-Date: 2015-05-29 09:39+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -28,61 +28,63 @@ msgstr "Vista"
#: actions.py:55
msgid "Selected bills should be in open state"
-msgstr ""
+msgstr "Les factures seleccionades han d'estar en estat obert"
#: actions.py:73
msgid "Selected bills have been closed"
-msgstr ""
+msgstr "Les factures seleccionades han estat tancades"
#: actions.py:86
#, python-format
msgid "One related transaction has been created"
-msgstr ""
+msgstr "S'ha creat una transacció"
#: actions.py:87
#, python-format
msgid "%(num)i related transactions have been created"
-msgstr ""
+msgstr "S'han creat les %(num)i següents transaccions"
#: actions.py:93
msgid "Are you sure about closing the following bills?"
-msgstr ""
+msgstr "Estàs a punt de tancar les següents factures, estàs segur?"
#: actions.py:94
msgid ""
"Once a bill is closed it can not be further modified.
Please select a "
"payment source for the selected bills"
msgstr ""
+"Una vegada la factura estigui tancada no podrà ser modificada.
Si us "
+"plau selecciona un mètode de pagament per les factures seleccionades"
#: actions.py:107
msgid "Close"
-msgstr ""
+msgstr "Tanca"
#: actions.py:125
msgid "One bill has been sent."
-msgstr ""
+msgstr "S'ha creat una factura"
#: actions.py:126
#, python-format
msgid "%i bills have been sent."
-msgstr ""
+msgstr "S'han enviat %i factures."
#: actions.py:128
msgid "Resend"
-msgstr ""
+msgstr "Reenviat"
#: actions.py:189
#, python-format
msgid "%(norders)s orders and %(nlines)s lines undoed."
-msgstr ""
+msgstr "%(norders)s ordres i %(nlines)s línies desfetes."
#: actions.py:208
msgid "Lines moved"
-msgstr ""
+msgstr "Línies mogudes"
#: admin.py:49 admin.py:93 admin.py:128 forms.py:11
msgid "Total"
-msgstr ""
+msgstr "Total"
#: admin.py:80
msgid "Description"
@@ -90,52 +92,52 @@ msgstr "Descripció"
#: admin.py:88
msgid "Subtotal"
-msgstr ""
+msgstr "Subtotal"
#: admin.py:118
msgid "Is open"
-msgstr ""
+msgstr "És oberta"
#: admin.py:123
msgid "Subline"
-msgstr ""
+msgstr "Sublínia"
#: admin.py:157
msgid "No bills selected."
-msgstr ""
+msgstr "No hi ha factures seleccionades"
#: admin.py:164
#, python-format
msgid "Manage %s bill lines."
-msgstr ""
+msgstr "Gestiona %s línies de factura."
#: admin.py:166
msgid "Bill not in open state."
-msgstr ""
+msgstr "La factura no està en estat obert"
#: admin.py:169
msgid "Not all bills are in open state."
-msgstr ""
+msgstr "No totes les factures estan en estat obert"
#: admin.py:170
msgid "Manage bill lines of multiple bills."
-msgstr ""
+msgstr "Gestiona línies de factura de multiples factures."
#: admin.py:190
msgid "Raw"
-msgstr ""
+msgstr "Raw"
#: admin.py:208
msgid "Created"
-msgstr ""
+msgstr "Creada"
#: admin.py:213
msgid "lines"
-msgstr ""
+msgstr "línies"
#: admin.py:218 templates/bills/microspective.html:118
msgid "total"
-msgstr ""
+msgstr "total"
#: admin.py:226 models.py:88 models.py:352
msgid "type"
@@ -167,11 +169,11 @@ msgstr "Rectificació de quota de soci"
#: filters.py:22
msgid "Pro-forma"
-msgstr ""
+msgstr "Pro-forma"
#: filters.py:41
msgid "positive price"
-msgstr ""
+msgstr "preu positiu"
#: filters.py:46 filters.py:64
msgid "Yes"
@@ -183,69 +185,71 @@ msgstr "No"
#: filters.py:59
msgid "has bill contact"
-msgstr ""
+msgstr "té contacte de facturació"
#: forms.py:9
msgid "Number"
-msgstr ""
+msgstr "Número"
#: forms.py:10
msgid "Account"
-msgstr ""
+msgstr "Compte"
#: forms.py:12
msgid "Type"
-msgstr ""
+msgstr "Tipus"
#: forms.py:13
msgid "Source"
-msgstr ""
+msgstr "Font"
#: helpers.py:10
msgid ""
"{relation} account \"{account}\" does not have a declared invoice contact. "
"You should provide one"
msgstr ""
+"{relation} compte \"{account}\" no te un contacte de facturació. Hauries de "
+"proporcionar un"
#: helpers.py:17
msgid "Related"
-msgstr ""
+msgstr "Relacionat"
#: helpers.py:24
msgid "Main"
-msgstr ""
+msgstr "Principal"
#: models.py:23 models.py:86
msgid "account"
-msgstr ""
+msgstr "compte"
#: models.py:25
msgid "name"
-msgstr ""
+msgstr "nom"
#: models.py:26
msgid "Account full name will be used when left blank."
-msgstr ""
+msgstr "S'emprarà el nom complet del compte quan es deixi en blanc."
#: models.py:27
msgid "address"
-msgstr ""
+msgstr "adreça"
#: models.py:28
msgid "city"
-msgstr ""
+msgstr "ciutat"
#: models.py:30
msgid "zip code"
-msgstr ""
+msgstr "codi postal"
#: models.py:31
msgid "Enter a valid zipcode."
-msgstr ""
+msgstr "Introdueix un codi postal vàlid."
#: models.py:32
msgid "country"
-msgstr ""
+msgstr "país"
#: models.py:35
msgid "VAT number"
@@ -253,51 +257,51 @@ msgstr "NIF"
#: models.py:67
msgid "Paid"
-msgstr ""
+msgstr "Pagat"
#: models.py:68
msgid "Pending"
-msgstr ""
+msgstr "Pendent"
#: models.py:69
msgid "Bad debt"
-msgstr ""
+msgstr "Incobrable"
#: models.py:81
msgid "Amendment Fee"
-msgstr ""
+msgstr "Rectificació de quota de soci"
#: models.py:82
msgid "Pro forma"
-msgstr ""
+msgstr "Pro forma"
#: models.py:85
msgid "number"
-msgstr ""
+msgstr "número"
#: models.py:89
msgid "created on"
-msgstr ""
+msgstr "creat el"
#: models.py:90
msgid "closed on"
-msgstr ""
+msgstr "tancat el"
#: models.py:91
msgid "open"
-msgstr ""
+msgstr "obert"
#: models.py:92
msgid "sent"
-msgstr ""
+msgstr "enviat"
#: models.py:93
msgid "due on"
-msgstr ""
+msgstr "es deu"
#: models.py:94
msgid "updated on"
-msgstr ""
+msgstr "actualitzada el"
#: models.py:97
msgid "comments"
@@ -305,11 +309,11 @@ msgstr "comentaris"
#: models.py:98
msgid "HTML"
-msgstr ""
+msgstr "HTML"
#: models.py:285
msgid "bill"
-msgstr ""
+msgstr "factura"
#: models.py:286 models.py:350 templates/bills/microspective.html:73
msgid "description"
@@ -332,7 +336,7 @@ msgstr "quantitat"
#: models.py:290 templates/bills/microspective.html:77
#: templates/bills/microspective.html:111
msgid "subtotal"
-msgstr ""
+msgstr "subtotal"
#: models.py:291
msgid "tax"
@@ -340,67 +344,69 @@ msgstr "impostos"
#: models.py:292
msgid "start"
-msgstr ""
+msgstr "iniciar"
#: models.py:293
msgid "end"
-msgstr ""
+msgstr "finalitzar"
#: models.py:295
msgid "Informative link back to the order"
-msgstr ""
+msgstr "Enllaç informatiu de l'ordre"
#: models.py:296
msgid "order billed"
-msgstr ""
+msgstr "ordre facturada"
#: models.py:297
msgid "order billed until"
-msgstr ""
+msgstr "ordre facturada fins a"
#: models.py:298
msgid "created"
-msgstr ""
+msgstr "creada"
#: models.py:300
msgid "amended line"
-msgstr ""
+msgstr "línia rectificada"
#: models.py:343
msgid "Volume"
-msgstr ""
+msgstr "Volum"
#: models.py:344
msgid "Compensation"
-msgstr ""
+msgstr "Compensació"
#: models.py:345
msgid "Other"
-msgstr ""
+msgstr "Altre"
#: models.py:349
msgid "bill line"
-msgstr ""
+msgstr "línia de factura"
#: templates/bills/microspective-fee.html:107
msgid "Due date"
-msgstr ""
+msgstr "Data de pagament"
#: templates/bills/microspective-fee.html:108
#, python-format
msgid "On %(bank_account)s"
-msgstr ""
+msgstr "Al %(bank_account)s"
#: templates/bills/microspective-fee.html:114
#, python-format
msgid "From %(ini)s to %(end)s"
-msgstr ""
+msgstr "De %(ini)s a %(end)s"
#: templates/bills/microspective-fee.html:121
msgid ""
"\n"
"With your membership you are supporting ...\n"
msgstr ""
+"\n"
+"Amb la teva quota de soci estàs donant suport ...\n"
#: templates/bills/microspective.html:49
msgid "DUE DATE"
@@ -411,18 +417,17 @@ msgid "TOTAL"
msgstr "TOTAL"
#: templates/bills/microspective.html:57
-#, fuzzy, python-format
-#| msgid "%(bill_type|upper)s DATE "
+#, python-format
msgid "%(bill_type)s DATE"
-msgstr "DATA %(bill_type|upper)s"
+msgstr "DATA %(bill_type)s"
#: templates/bills/microspective.html:74
msgid "period"
-msgstr ""
+msgstr "període"
#: templates/bills/microspective.html:75
msgid "hrs/qty"
-msgstr "hrs/quant"
+msgstr "hrs/qnt"
#: templates/bills/microspective.html:76
msgid "rate/price"
@@ -446,15 +451,6 @@ msgid "PAYMENT"
msgstr "PAGAMENT"
#: templates/bills/microspective.html:140
-#, fuzzy, python-format
-#| msgid ""
-#| "\n"
-#| " You can pay our %(type)s by bank transfer. "
-#| "
\n"
-#| " Please make sure to state your name and the "
-#| "%(type)s number.\n"
-#| " Our bank account number is
\n"
-#| " "
msgid ""
"\n"
" You can pay our %(type)s by bank transfer.
\n"
@@ -464,8 +460,8 @@ msgid ""
" "
msgstr ""
"\n"
-"Pots pagar aquesta %(type)s per transferencia banacaria.
Inclou el "
-"teu nom i el numero de %(type)s. El nostre compte bancari és"
+"Pots pagar aquesta %(type)s per transferència bancaria.
Inclou el "
+"teu nom i el número de %(type)s. El nostre compte bancari és"
#: templates/bills/microspective.html:149
msgid "QUESTIONS"
@@ -481,3 +477,9 @@ msgid ""
" your message.\n"
" "
msgstr ""
+"\n"
+" Si tens algun dubte o pregunta sobre la teva %(type)s, si "
+"us plau\n"
+" contacta amb nosaltres a %(email)s. Et respondrem el més "
+"ràpidament possible.\n"
+" "
diff --git a/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..258c2c4c
Binary files /dev/null and b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo differ
diff --git a/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po
index 962304c8..406aa281 100644
--- a/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po
+++ b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-05-28 09:23+0000\n"
+"POT-Creation-Date: 2015-05-28 12:31+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -20,47 +20,49 @@ msgstr ""
#: actions.py:37
msgid "Download"
-msgstr ""
+msgstr "Descarga"
#: actions.py:47
msgid "View"
-msgstr ""
+msgstr "Vista"
#: actions.py:55
msgid "Selected bills should be in open state"
-msgstr ""
+msgstr "Las facturas seleccionadas están en estado abierto"
#: actions.py:73
msgid "Selected bills have been closed"
-msgstr ""
+msgstr "Las facturas seleccionadas han sido cerradas"
#: actions.py:86
#, python-format
msgid "One related transaction has been created"
-msgstr ""
+msgstr "Se ha creado una transacción"
#: actions.py:87
#, python-format
msgid "%(num)i related transactions have been created"
-msgstr ""
+msgstr "Se han creado %(num)i transacciones"
#: actions.py:93
msgid "Are you sure about closing the following bills?"
-msgstr ""
+msgstr "Estás a punto de cerrar las sigüientes facturas. ¿Estás seguro?"
#: actions.py:94
msgid ""
"Once a bill is closed it can not be further modified.
Please select a "
"payment source for the selected bills"
msgstr ""
+"Una vez cerrada la factura ya no se podrá modificar.
Por favor "
+"seleciona un metodo de pago para las facturas seleccionadas"
#: actions.py:107
msgid "Close"
-msgstr ""
+msgstr "Cerrar"
#: actions.py:125
msgid "One bill has been sent."
-msgstr ""
+msgstr "Se ha enviado una factura"
#: actions.py:126
#, python-format
@@ -143,7 +145,7 @@ msgstr ""
#: admin.py:243
msgid "Payment"
-msgstr ""
+msgstr "Pago"
#: filters.py:17
msgid "All"
@@ -151,19 +153,19 @@ msgstr ""
#: filters.py:18 models.py:78
msgid "Invoice"
-msgstr ""
+msgstr "Factura"
#: filters.py:19 models.py:79
msgid "Amendment invoice"
-msgstr ""
+msgstr "Factura rectificative"
#: filters.py:20 models.py:80
msgid "Fee"
-msgstr ""
+msgstr "Quota de socio"
#: filters.py:21
msgid "Amendment fee"
-msgstr ""
+msgstr "Quota rectificativa"
#: filters.py:22
msgid "Pro-forma"
@@ -382,17 +384,17 @@ msgstr ""
#: templates/bills/microspective-fee.html:107
msgid "Due date"
-msgstr ""
+msgstr "Fecha de pago"
#: templates/bills/microspective-fee.html:108
#, python-format
msgid "On %(bank_account)s"
-msgstr ""
+msgstr "En %(bank_account)s"
#: templates/bills/microspective-fee.html:114
#, python-format
msgid "From %(ini)s to %(end)s"
-msgstr ""
+msgstr "Desde %(ini)s hasta %(end)s"
#: templates/bills/microspective-fee.html:121
msgid ""
@@ -402,45 +404,45 @@ msgstr ""
#: templates/bills/microspective.html:49
msgid "DUE DATE"
-msgstr ""
+msgstr "VENCIMIENTO"
#: templates/bills/microspective.html:53
msgid "TOTAL"
-msgstr ""
+msgstr "TOTAL"
#: templates/bills/microspective.html:57
#, python-format
msgid "%(bill_type)s DATE"
-msgstr ""
+msgstr "FECHA %(bill_type)s"
#: templates/bills/microspective.html:74
msgid "period"
-msgstr ""
+msgstr "periodo"
#: templates/bills/microspective.html:75
msgid "hrs/qty"
-msgstr ""
+msgstr "hrs/cant"
#: templates/bills/microspective.html:76
msgid "rate/price"
-msgstr ""
+msgstr "tarifa/precio"
#: templates/bills/microspective.html:111
#: templates/bills/microspective.html:114
msgid "VAT"
-msgstr ""
+msgstr "IVA"
#: templates/bills/microspective.html:114
msgid "taxes"
-msgstr ""
+msgstr "impuestos"
#: templates/bills/microspective.html:130
msgid "COMMENTS"
-msgstr ""
+msgstr "COMENTARIOS"
#: templates/bills/microspective.html:136
msgid "PAYMENT"
-msgstr ""
+msgstr "PAGO"
#: templates/bills/microspective.html:140
#, python-format
@@ -452,6 +454,9 @@ msgid ""
" Our bank account number is
\n"
" "
msgstr ""
+"\n"
+"Puedes pagar esta %(type)s por transferencia bancaria.
Incloye tu "
+"nombre y el número de %(type)s. Nuestra cuenta bancaria es"
#: templates/bills/microspective.html:149
msgid "QUESTIONS"
@@ -467,3 +472,9 @@ msgid ""
" your message.\n"
" "
msgstr ""
+"\n"
+" Si tienes alguna duda o pregunta sobre tu %(type)s, por "
+"favor\n"
+" contacta con nosotros en %(email)s. Te responderemos lo más "
+"rapidamente posible.\n"
+" "
diff --git a/orchestra/contrib/bills/models.py b/orchestra/contrib/bills/models.py
index 8ad88c96..64707f94 100644
--- a/orchestra/contrib/bills/models.py
+++ b/orchestra/contrib/bills/models.py
@@ -142,7 +142,7 @@ class Bill(models.Model):
def get_type(self):
return self.type or self.get_class_type()
- def set_number(self):
+ def get_number(self):
cls = type(self)
bill_type = self.get_type()
if bill_type == self.BILL:
@@ -162,7 +162,7 @@ class Bill(models.Model):
number_length = settings.BILLS_NUMBER_LENGTH
zeros = (number_length - len(str(number))) * '0'
number = zeros + str(number)
- self.number = '{prefix}{year}{number}'.format(prefix=prefix, year=year, number=number)
+ return '{prefix}{year}{number}'.format(prefix=prefix, year=year, number=number)
def get_due_date(self, payment=None):
now = timezone.now()
@@ -178,13 +178,14 @@ class Bill(models.Model):
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)
transaction = None
if self.get_type() != self.PROFORMA:
transaction = self.transactions.create(bill=self, source=payment, amount=self.total)
self.closed_on = timezone.now()
self.is_open = False
self.is_sent = False
+ self.number = self.get_number()
+ self.html = self.render(payment=payment)
self.save()
return transaction
@@ -207,37 +208,37 @@ class Bill(models.Model):
self.save(update_fields=['is_sent'])
def render(self, payment=False, language=None):
- if payment is False:
- payment = self.account.paymentsources.get_default()
- context = Context({
- 'bill': self,
- 'lines': self.lines.all().prefetch_related('sublines'),
- 'seller': self.seller,
- 'buyer': self.buyer,
- 'seller_info': {
- 'phone': settings.BILLS_SELLER_PHONE,
- 'website': settings.BILLS_SELLER_WEBSITE,
- 'email': settings.BILLS_SELLER_EMAIL,
- 'bank_account': settings.BILLS_SELLER_BANK_ACCOUNT,
- },
- 'currency': settings.BILLS_CURRENCY,
- 'payment': payment and payment.get_bill_context(),
- 'default_due_date': self.get_due_date(payment=payment),
- 'now': timezone.now(),
- })
- template_name = 'BILLS_%s_TEMPLATE' % self.get_type()
- template = getattr(settings, template_name, settings.BILLS_DEFAULT_TEMPLATE)
- bill_template = loader.get_template(template)
with translation.override(language or self.account.language):
+ if payment is False:
+ payment = self.account.paymentsources.get_default()
+ context = Context({
+ 'bill': self,
+ 'lines': self.lines.all().prefetch_related('sublines'),
+ 'seller': self.seller,
+ 'buyer': self.buyer,
+ 'seller_info': {
+ 'phone': settings.BILLS_SELLER_PHONE,
+ 'website': settings.BILLS_SELLER_WEBSITE,
+ 'email': settings.BILLS_SELLER_EMAIL,
+ 'bank_account': settings.BILLS_SELLER_BANK_ACCOUNT,
+ },
+ 'currency': settings.BILLS_CURRENCY,
+ 'payment': payment and payment.get_bill_context(),
+ 'default_due_date': self.get_due_date(payment=payment),
+ 'now': timezone.now(),
+ })
+ template_name = 'BILLS_%s_TEMPLATE' % self.get_type()
+ template = getattr(settings, template_name, settings.BILLS_DEFAULT_TEMPLATE)
+ bill_template = loader.get_template(template)
html = bill_template.render(context)
- html = html.replace('-pageskip-', '')
+ html = html.replace('-pageskip-', '')
return html
def save(self, *args, **kwargs):
if not self.type:
self.type = self.get_type()
- if not self.number or (self.number.startswith('O') and not self.is_open):
- self.set_number()
+ if not self.number:
+ self.number = self.get_number()
super(Bill, self).save(*args, **kwargs)
def get_subtotals(self):
diff --git a/orchestra/contrib/bills/templates/bills/microspective.css b/orchestra/contrib/bills/templates/bills/microspective.css
index d2e8f4dd..7e6730cf 100644
--- a/orchestra/contrib/bills/templates/bills/microspective.css
+++ b/orchestra/contrib/bills/templates/bills/microspective.css
@@ -175,12 +175,12 @@ a:hover {
}
#lines .column-description {
- width: 40%;
+ width: 39%;
text-align: left;
}
#lines .column-period {
- width: 22%;
+ width: 23%;
}
#lines .column-quantity {
diff --git a/orchestra/contrib/bills/templates/bills/microspective.html b/orchestra/contrib/bills/templates/bills/microspective.html
index 1b2e7162..e20e2b0b 100644
--- a/orchestra/contrib/bills/templates/bills/microspective.html
+++ b/orchestra/contrib/bills/templates/bills/microspective.html
@@ -135,7 +135,7 @@
{% trans "PAYMENT" %}
{% if payment.message %}
- {{ payment.message | safe }}
+ {{ payment.message|safe }}
{% else %}
{% blocktrans with type=bill.get_type_display.lower %}
You can pay our
{{ type }} by bank transfer.
diff --git a/orchestra/contrib/mailer/backends.py b/orchestra/contrib/mailer/backends.py
index f078e7ef..a520ee4a 100644
--- a/orchestra/contrib/mailer/backends.py
+++ b/orchestra/contrib/mailer/backends.py
@@ -12,11 +12,10 @@ class EmailBackend(BaseEmailBackend):
"""
A wrapper that manages a queued SMTP system.
"""
- messages = 0
-
def send_messages(self, email_messages):
if not email_messages:
return
+ # Count messages per request
cache = get_request_cache()
key = 'mailer.sent_messages'
sent_messages = cache.get(key) or 0
@@ -24,7 +23,7 @@ class EmailBackend(BaseEmailBackend):
cache.set(key, sent_messages)
is_bulk = len(email_messages) > 1
- if sent_messages > settings.MAILER_NON_QUEUED_MAILS_PER_REQUEST_THRESHOLD:
+ if sent_messages > settings.MAILER_NON_QUEUED_PER_REQUEST_THRESHOLD:
is_bulk = True
default_priority = Message.NORMAL if is_bulk else Message.CRITICAL
num_sent = 0
diff --git a/orchestra/contrib/mailer/engine.py b/orchestra/contrib/mailer/engine.py
index da6f4dbc..b70e6e9f 100644
--- a/orchestra/contrib/mailer/engine.py
+++ b/orchestra/contrib/mailer/engine.py
@@ -14,6 +14,8 @@ from .models import Message
def send_message(message, num=0, connection=None, bulk=100):
+ if not message.pk:
+ message.save()
if num >= bulk:
connection.close()
connection = None
diff --git a/orchestra/contrib/mailer/models.py b/orchestra/contrib/mailer/models.py
index be952cb8..fb7217f6 100644
--- a/orchestra/contrib/mailer/models.py
+++ b/orchestra/contrib/mailer/models.py
@@ -50,7 +50,7 @@ class Message(models.Model):
def sent(self):
self.state = self.SENT
- self.save()
+ self.save(update_fiields=('state',))
def log(self, error):
result = SMTPLog.SUCCESS
diff --git a/orchestra/contrib/mailer/settings.py b/orchestra/contrib/mailer/settings.py
index 9e0d42e5..2711cbb5 100644
--- a/orchestra/contrib/mailer/settings.py
+++ b/orchestra/contrib/mailer/settings.py
@@ -13,7 +13,7 @@ MAILER_MESSAGES_CLEANUP_DAYS = Setting('MAILER_MESSAGES_CLEANUP_DAYS',
)
-MAILER_NON_QUEUED_MAILS_PER_REQUEST_THRESHOLD = Setting('MAILER_NON_QUEUED_MAILS_PER_REQUEST_THRESHOLD',
+MAILER_NON_QUEUED_PER_REQUEST_THRESHOLD = Setting('MAILER_NON_QUEUED_PER_REQUEST_THRESHOLD',
2,
- help_text=_("Number of emails that will be sent directly before starting to queue them."),
+ help_text=_("Number of emails that will be sent immediately before starting to queue them."),
)
diff --git a/orchestra/contrib/payments/actions.py b/orchestra/contrib/payments/actions.py
index d4dce049..78349c8d 100644
--- a/orchestra/contrib/payments/actions.py
+++ b/orchestra/contrib/payments/actions.py
@@ -1,6 +1,7 @@
from functools import partial
from django.contrib import messages
+from django.contrib.admin import actions
from django.db import transaction
from django.shortcuts import render
from django.utils.safestring import mark_safe
@@ -18,23 +19,25 @@ from .models import Transaction
def process_transactions(modeladmin, request, queryset):
processes = []
if queryset.exclude(state=Transaction.WAITTING_PROCESSING).exists():
- msg = _("Selected transactions must be on '{state}' state")
- messages.error(request, msg.format(state=Transaction.WAITTING_PROCESSING))
+ messages.error(request,
+ _("Selected transactions must be on '{state}' state").format(
+ state=Transaction.WAITTING_PROCESSING)
+ )
return
for method, transactions in queryset.group_by('source__method').items():
if method is not None:
method = PaymentMethod.get(method)
procs = method.process(transactions)
processes += procs
- for trans in transactions:
- modeladmin.log_change(request, trans, _("Processed"))
+ for transaction in transactions:
+ modeladmin.log_change(request, transaction, _("Processed"))
if not processes:
return
opts = modeladmin.model._meta
num = len(queryset)
context = {
'title': ungettext(
- _("Selected transaction has been processed."),
+ _("One selected transaction has been processed."),
_("%s Selected transactions have been processed.") % num,
num),
'content_message': ungettext(
@@ -54,9 +57,9 @@ def process_transactions(modeladmin, request, queryset):
@transaction.atomic
@action_with_confirmation()
def mark_as_executed(modeladmin, request, queryset):
- for trans in queryset:
- trans.mark_as_executed()
- modeladmin.log_change(request, trans, _("Executed"))
+ for transaction in queryset:
+ transaction.mark_as_executed()
+ modeladmin.log_change(request, transaction, _("Executed"))
num = len(queryset)
msg = ungettext(
_("One selected transaction has been marked as executed."),
@@ -70,9 +73,9 @@ mark_as_executed.verbose_name = _("Mark as executed")
@transaction.atomic
@action_with_confirmation()
def mark_as_secured(modeladmin, request, queryset):
- for trans in queryset:
- trans.mark_as_secured()
- modeladmin.log_change(request, trans, _("Secured"))
+ for transaction in queryset:
+ transaction.mark_as_secured()
+ modeladmin.log_change(request, transaction, _("Secured"))
num = len(queryset)
msg = ungettext(
_("One selected transaction has been marked as secured."),
@@ -86,9 +89,9 @@ mark_as_secured.verbose_name = _("Mark as secured")
@transaction.atomic
@action_with_confirmation()
def mark_as_rejected(modeladmin, request, queryset):
- for trans in queryset:
- trans.mark_as_rejected()
- modeladmin.log_change(request, trans, _("Rejected"))
+ for transaction in queryset:
+ transaction.mark_as_rejected()
+ modeladmin.log_change(request, transaction, _("Rejected"))
num = len(queryset)
msg = ungettext(
_("One selected transaction has been marked as rejected."),
@@ -157,9 +160,9 @@ abort.verbose_name = _("Abort")
@transaction.atomic
@action_with_confirmation(extra_context=_format_commit)
def commit(modeladmin, request, queryset):
- for trans in queryset:
- trans.mark_as_rejected()
- modeladmin.log_change(request, trans, _("Rejected"))
+ for transaction in queryset:
+ transaction.mark_as_rejected()
+ modeladmin.log_change(request, transaction, _("Rejected"))
num = len(queryset)
msg = ungettext(
_("One selected transaction has been marked as rejected."),
@@ -168,3 +171,29 @@ def commit(modeladmin, request, queryset):
modeladmin.message_user(request, msg)
commit.url_name = 'commit'
commit.verbose_name = _("Commit")
+
+
+def delete_selected(modeladmin, request, queryset):
+ """ Has to have same name as admin.actions.delete_selected """
+ if not queryset:
+ messages.warning(request, "No transaction process selected.")
+ return
+ if queryset.exclude(transactions__state=Transaction.WAITTING_EXECUTION).exists():
+ messages.error(request, "Done nothing. Not all related transactions in waitting execution.")
+ return
+ # Store before deleting
+ related_transactions = []
+ for process in queryset:
+ related_transactions.extend(process.transactions.filter(state=Transaction.WAITTING_EXECUTION))
+ response = actions.delete_selected(modeladmin, request, queryset)
+ if response is None:
+ # Confirmation
+ num = 0
+ for transaction in related_transactions:
+ transaction.state = Transaction.WAITTING_PROCESSING
+ transaction.save(update_fields=('state',))
+ num += 1
+ modeladmin.log_change(request, transaction, _("Unprocessed"))
+ messages.success(request, "%i related transactions marked as waitting for processing." % num)
+ return response
+delete_selected.short_description = actions.delete_selected.short_description
diff --git a/orchestra/contrib/payments/admin.py b/orchestra/contrib/payments/admin.py
index 6d0038df..3d52bdf9 100644
--- a/orchestra/contrib/payments/admin.py
+++ b/orchestra/contrib/payments/admin.py
@@ -122,8 +122,8 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
fields = ('data', 'file_url', 'created_at')
readonly_fields = ('data', 'file_url', 'display_transactions', 'created_at')
inlines = [TransactionInline]
- actions = (actions.mark_process_as_executed, actions.abort, actions.commit)
- change_view_actions = actions
+ change_view_actions = (actions.mark_process_as_executed, actions.abort, actions.commit)
+ actions = change_view_actions + (actions.delete_selected,)
def file_url(self, process):
if process.file:
@@ -138,7 +138,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
# Because of values_list this query doesn't benefit from prefetch_related
tx_ids = process.transactions.values_list('id', flat=True)
for tx_id in tx_ids:
- ids.append('#%i' % tx_id)
+ ids.append(str(tx_id))
counter += 1
if counter > 10:
counter = 0
diff --git a/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.mo b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..d130159a
Binary files /dev/null and b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.mo differ
diff --git a/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po
new file mode 100644
index 00000000..3d9c3bba
--- /dev/null
+++ b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po
@@ -0,0 +1,250 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR
, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-05-28 16:40+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: actions.py:21
+msgid "Selected transactions must be on '{state}' state"
+msgstr ""
+
+#: actions.py:30
+msgid "Processed"
+msgstr ""
+
+#: actions.py:37
+msgid "Selected transaction has been processed."
+msgstr ""
+
+#: actions.py:38
+#, python-format
+msgid "%s Selected transactions have been processed."
+msgstr ""
+
+#: actions.py:41
+msgid ""
+"The following transaction process has been generated, you may want to save "
+"it on your computer now."
+msgstr ""
+
+#: actions.py:43
+#, python-format
+msgid ""
+"The following %s transaction processes have been generated, you may want to "
+"save it on your computer now."
+msgstr ""
+
+#: actions.py:46
+msgid "Process"
+msgstr ""
+
+#: actions.py:59 actions.py:130 models.py:93 models.py:161
+msgid "Executed"
+msgstr ""
+
+#: actions.py:62
+msgid "One selected transaction has been marked as executed."
+msgstr ""
+
+#: actions.py:63
+#, python-format
+msgid "%s selected transactions have been marked as executed."
+msgstr ""
+
+#: actions.py:67 actions.py:138
+msgid "Mark as executed"
+msgstr ""
+
+#: actions.py:75 models.py:94
+msgid "Secured"
+msgstr ""
+
+#: actions.py:78
+msgid "One selected transaction has been marked as secured."
+msgstr ""
+
+#: actions.py:79
+#, python-format
+msgid "%s selected transactions have been marked as secured."
+msgstr ""
+
+#: actions.py:83
+msgid "Mark as secured"
+msgstr ""
+
+#: actions.py:91 actions.py:162 models.py:95
+msgid "Rejected"
+msgstr ""
+
+#: actions.py:94 actions.py:165
+msgid "One selected transaction has been marked as rejected."
+msgstr ""
+
+#: actions.py:95 actions.py:166
+#, python-format
+msgid "%s selected transactions have been marked as rejected."
+msgstr ""
+
+#: actions.py:99
+msgid "Mark as rejected"
+msgstr ""
+
+#: actions.py:133
+msgid "One selected process has been marked as executed."
+msgstr ""
+
+#: actions.py:134
+#, python-format
+msgid "%s selected processes have been marked as executed."
+msgstr ""
+
+#: actions.py:146 models.py:162
+msgid "Aborted"
+msgstr ""
+
+#: actions.py:149
+msgid "One selected process has been aborted."
+msgstr ""
+
+#: actions.py:150
+#, python-format
+msgid "%s selected processes have been aborted."
+msgstr ""
+
+#: actions.py:154
+msgid "Abort"
+msgstr ""
+
+#: actions.py:170
+msgid "Commit"
+msgstr ""
+
+#: admin.py:42
+msgid "ID"
+msgstr ""
+
+#: admin.py:101
+msgid "proc"
+msgstr ""
+
+#: admin.py:152
+msgid "Transactions"
+msgstr ""
+
+#: methods/creditcard.py:11
+msgid "Label"
+msgstr ""
+
+#: methods/creditcard.py:12
+msgid "Use a name such as \"Jo's Visa\" to remember which card it is."
+msgstr ""
+
+#: methods/creditcard.py:30
+msgid "Credit card"
+msgstr ""
+
+#: methods/sepadirectdebit.py:23 methods/sepadirectdebit.py:30
+msgid "Name"
+msgstr ""
+
+#: methods/sepadirectdebit.py:39
+msgid "SEPA Direct Debit"
+msgstr ""
+
+#: methods/sepadirectdebit.py:48
+msgid ""
+"This bill will be automatically charged to your bank account with IBAN "
+"number
%s."
+msgstr ""
+"Aquesta factura es cobrarà automaticament en el teu compte bancari amb IBAN "
+"
%s."
+
+#: models.py:19
+msgid "account"
+msgstr ""
+
+#: models.py:21
+msgid "method"
+msgstr ""
+
+#: models.py:23 models.py:166
+msgid "data"
+msgstr ""
+
+#: models.py:24
+msgid "active"
+msgstr ""
+
+#: models.py:91
+msgid "Waitting processing"
+msgstr ""
+
+#: models.py:92
+msgid "Waitting execution"
+msgstr ""
+
+#: models.py:98
+msgid "bill"
+msgstr ""
+
+#: models.py:101
+msgid "source"
+msgstr ""
+
+#: models.py:103
+msgid "process"
+msgstr ""
+
+#: models.py:104 models.py:168
+msgid "state"
+msgstr ""
+
+#: models.py:106
+msgid "amount"
+msgstr ""
+
+#: models.py:108 models.py:169
+msgid "created"
+msgstr ""
+
+#: models.py:109
+msgid "modified"
+msgstr ""
+
+#: models.py:124
+msgid "New transactions can not be allocated for this bill."
+msgstr ""
+
+#: models.py:160
+msgid "Created"
+msgstr ""
+
+#: models.py:163
+msgid "Commited"
+msgstr ""
+
+#: models.py:167
+msgid "file"
+msgstr ""
+
+#: models.py:170
+msgid "updated"
+msgstr ""
+
+#: models.py:173
+msgid "Transaction processes"
+msgstr ""
diff --git a/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.mo b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 00000000..ae58b2d0
Binary files /dev/null and b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.mo differ
diff --git a/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po
new file mode 100644
index 00000000..3088c22e
--- /dev/null
+++ b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po
@@ -0,0 +1,250 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-05-28 16:40+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: actions.py:21
+msgid "Selected transactions must be on '{state}' state"
+msgstr ""
+
+#: actions.py:30
+msgid "Processed"
+msgstr ""
+
+#: actions.py:37
+msgid "Selected transaction has been processed."
+msgstr ""
+
+#: actions.py:38
+#, python-format
+msgid "%s Selected transactions have been processed."
+msgstr ""
+
+#: actions.py:41
+msgid ""
+"The following transaction process has been generated, you may want to save "
+"it on your computer now."
+msgstr ""
+
+#: actions.py:43
+#, python-format
+msgid ""
+"The following %s transaction processes have been generated, you may want to "
+"save it on your computer now."
+msgstr ""
+
+#: actions.py:46
+msgid "Process"
+msgstr ""
+
+#: actions.py:59 actions.py:130 models.py:93 models.py:161
+msgid "Executed"
+msgstr ""
+
+#: actions.py:62
+msgid "One selected transaction has been marked as executed."
+msgstr ""
+
+#: actions.py:63
+#, python-format
+msgid "%s selected transactions have been marked as executed."
+msgstr ""
+
+#: actions.py:67 actions.py:138
+msgid "Mark as executed"
+msgstr ""
+
+#: actions.py:75 models.py:94
+msgid "Secured"
+msgstr ""
+
+#: actions.py:78
+msgid "One selected transaction has been marked as secured."
+msgstr ""
+
+#: actions.py:79
+#, python-format
+msgid "%s selected transactions have been marked as secured."
+msgstr ""
+
+#: actions.py:83
+msgid "Mark as secured"
+msgstr ""
+
+#: actions.py:91 actions.py:162 models.py:95
+msgid "Rejected"
+msgstr ""
+
+#: actions.py:94 actions.py:165
+msgid "One selected transaction has been marked as rejected."
+msgstr ""
+
+#: actions.py:95 actions.py:166
+#, python-format
+msgid "%s selected transactions have been marked as rejected."
+msgstr ""
+
+#: actions.py:99
+msgid "Mark as rejected"
+msgstr ""
+
+#: actions.py:133
+msgid "One selected process has been marked as executed."
+msgstr ""
+
+#: actions.py:134
+#, python-format
+msgid "%s selected processes have been marked as executed."
+msgstr ""
+
+#: actions.py:146 models.py:162
+msgid "Aborted"
+msgstr ""
+
+#: actions.py:149
+msgid "One selected process has been aborted."
+msgstr ""
+
+#: actions.py:150
+#, python-format
+msgid "%s selected processes have been aborted."
+msgstr ""
+
+#: actions.py:154
+msgid "Abort"
+msgstr ""
+
+#: actions.py:170
+msgid "Commit"
+msgstr ""
+
+#: admin.py:42
+msgid "ID"
+msgstr ""
+
+#: admin.py:101
+msgid "proc"
+msgstr ""
+
+#: admin.py:152
+msgid "Transactions"
+msgstr ""
+
+#: methods/creditcard.py:11
+msgid "Label"
+msgstr ""
+
+#: methods/creditcard.py:12
+msgid "Use a name such as \"Jo's Visa\" to remember which card it is."
+msgstr ""
+
+#: methods/creditcard.py:30
+msgid "Credit card"
+msgstr ""
+
+#: methods/sepadirectdebit.py:23 methods/sepadirectdebit.py:30
+msgid "Name"
+msgstr ""
+
+#: methods/sepadirectdebit.py:39
+msgid "SEPA Direct Debit"
+msgstr ""
+
+#: methods/sepadirectdebit.py:48
+msgid ""
+"This bill will be automatically charged to your bank account with IBAN "
+"number
%s."
+msgstr ""
+"Esta factura se cobrará automaticamente en tu cuenta bancaria con IBAN "
+"
%s."
+
+#: models.py:19
+msgid "account"
+msgstr ""
+
+#: models.py:21
+msgid "method"
+msgstr ""
+
+#: models.py:23 models.py:166
+msgid "data"
+msgstr ""
+
+#: models.py:24
+msgid "active"
+msgstr ""
+
+#: models.py:91
+msgid "Waitting processing"
+msgstr ""
+
+#: models.py:92
+msgid "Waitting execution"
+msgstr ""
+
+#: models.py:98
+msgid "bill"
+msgstr ""
+
+#: models.py:101
+msgid "source"
+msgstr ""
+
+#: models.py:103
+msgid "process"
+msgstr ""
+
+#: models.py:104 models.py:168
+msgid "state"
+msgstr ""
+
+#: models.py:106
+msgid "amount"
+msgstr ""
+
+#: models.py:108 models.py:169
+msgid "created"
+msgstr ""
+
+#: models.py:109
+msgid "modified"
+msgstr ""
+
+#: models.py:124
+msgid "New transactions can not be allocated for this bill."
+msgstr ""
+
+#: models.py:160
+msgid "Created"
+msgstr ""
+
+#: models.py:163
+msgid "Commited"
+msgstr ""
+
+#: models.py:167
+msgid "file"
+msgstr ""
+
+#: models.py:170
+msgid "updated"
+msgstr ""
+
+#: models.py:173
+msgid "Transaction processes"
+msgstr ""
diff --git a/orchestra/contrib/payments/methods/sepadirectdebit.py b/orchestra/contrib/payments/methods/sepadirectdebit.py
index 5d871926..52706ad0 100644
--- a/orchestra/contrib/payments/methods/sepadirectdebit.py
+++ b/orchestra/contrib/payments/methods/sepadirectdebit.py
@@ -45,7 +45,7 @@ class SEPADirectDebit(PaymentMethod):
due_delta = datetime.timedelta(days=5)
def get_bill_message(self):
- return _("This bill will been automatically charged to your bank account "
+ return _("This bill will be automatically charged to your bank account "
" with IBAN number
%s.") % self.instance.number
@classmethod
@@ -211,7 +211,7 @@ class SEPADirectDebit(PaymentMethod):
),
E.DbtrAcct( # Debtor Account
E.Id(
- E.IBAN(data['iban'])
+ E.IBAN(data['iban'].replace(' ', ''))
),
),
)
@@ -246,7 +246,7 @@ class SEPADirectDebit(PaymentMethod):
),
E.CdtrAcct( # Creditor Account
E.Id(
- E.IBAN(data['iban'])
+ E.IBAN(data['iban'].replace(' ', ''))
),
),
)
@@ -279,7 +279,8 @@ class SEPADirectDebit(PaymentMethod):
xsd_path = os.path.join(path, xsd)
schema_doc = etree.parse(xsd_path)
schema = etree.XMLSchema(schema_doc)
- sepa = etree.parse(StringIO(etree.tostring(sepa)))
+ sepa = StringIO(etree.tostring(sepa).decode('utf8'))
+ sepa = etree.parse(sepa)
schema.assertValid(sepa)
process.file = file_name
process.save(update_fields=['file'])
diff --git a/orchestra/contrib/payments/models.py b/orchestra/contrib/payments/models.py
index 79ce714a..aba33cfb 100644
--- a/orchestra/contrib/payments/models.py
+++ b/orchestra/contrib/payments/models.py
@@ -99,7 +99,7 @@ class Transaction(models.Model):
related_name='transactions')
source = models.ForeignKey(PaymentSource, null=True, blank=True,
verbose_name=_("source"), related_name='transactions')
- process = models.ForeignKey('payments.TransactionProcess', null=True,
+ process = models.ForeignKey('payments.TransactionProcess', null=True, on_delete=models.SET_NULL,
blank=True, verbose_name=_("process"), related_name='transactions')
state = models.CharField(_("state"), max_length=32, choices=STATES,
default=WAITTING_PROCESSING)
@@ -111,7 +111,7 @@ class Transaction(models.Model):
objects = TransactionQuerySet.as_manager()
def __str__(self):
- return "Transaction #{}".format(self.id)
+ return "#%i" % self.id
@property
def account(self):
diff --git a/orchestra/contrib/payments/templates/admin/payments/transaction/get_processes.html b/orchestra/contrib/payments/templates/admin/payments/transaction/get_processes.html
index 05c2f8a7..d3bb7b74 100644
--- a/orchestra/contrib/payments/templates/admin/payments/transaction/get_processes.html
+++ b/orchestra/contrib/payments/templates/admin/payments/transaction/get_processes.html
@@ -6,7 +6,7 @@
{{ content_message }}
{% for proc in processes %}
- - Process #{{ proc.id }}
+
- Process #{{ proc.id }}
{% if proc.file %}
{% endif %}
diff --git a/orchestra/contrib/systemusers/actions.py b/orchestra/contrib/systemusers/actions.py
index 51e03864..0fb90eb4 100644
--- a/orchestra/contrib/systemusers/actions.py
+++ b/orchestra/contrib/systemusers/actions.py
@@ -94,4 +94,3 @@ def delete_selected(modeladmin, request, queryset):
return
return admin.actions.delete_selected(modeladmin, request, queryset)
delete_selected.short_description = _("Delete selected %(verbose_name_plural)s")
-
diff --git a/orchestra/contrib/systemusers/admin.py b/orchestra/contrib/systemusers/admin.py
index c0fa6cf4..7e2957e6 100644
--- a/orchestra/contrib/systemusers/admin.py
+++ b/orchestra/contrib/systemusers/admin.py
@@ -41,8 +41,8 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende
add_form = SystemUserCreationForm
form = SystemUserChangeForm
ordering = ('-id',)
- actions = (delete_selected, set_permission, disable)
- change_view_actions = actions
+ change_view_actions = (set_permission, disable)
+ actions = (delete_selected,) + change_view_actions
def display_main(self, user):
return user.is_main
diff --git a/orchestra/contrib/systemusers/models.py b/orchestra/contrib/systemusers/models.py
index 29419b83..fb274a78 100644
--- a/orchestra/contrib/systemusers/models.py
+++ b/orchestra/contrib/systemusers/models.py
@@ -31,10 +31,10 @@ class SystemUser(models.Model):
"""
System users
- Username max_length determined by LINUX system user lentgh: 32
+ Username max_length determined by LINUX system user/group lentgh: 32
"""
username = models.CharField(_("username"), max_length=32, unique=True,
- help_text=_("Required. 64 characters or fewer. Letters, digits and ./-/_ only."),
+ help_text=_("Required. 32 characters or fewer. Letters, digits and ./-/_ only."),
validators=[validators.validate_username])
password = models.CharField(_("password"), max_length=128)
account = models.ForeignKey('accounts.Account', verbose_name=_("Account"),
diff --git a/orchestra/utils/html.py b/orchestra/utils/html.py
index 6d84a52d..6d6731d3 100644
--- a/orchestra/utils/html.py
+++ b/orchestra/utils/html.py
@@ -7,7 +7,6 @@ def html_to_pdf(html, pagination=False):
""" converts HTL to PDF using wkhtmltopdf """
context = {
'pagination': textwrap.dedent("""\
- --footer-center "Page [page] of [topage]"\\
--footer-center "Page [page] of [topage]" \\
--footer-font-name sans \\
--footer-font-size 7 \\
@@ -20,6 +19,6 @@ def html_to_pdf(html, pagination=False):
--use-xserver \\
%(pagination)s \\
--margin-bottom 22 \\
- --margin-top 20 - -\
+ --margin-top 20 - - \
""") % context
return run(cmd, stdin=html.encode('utf-8')).stdout
diff --git a/orchestra/utils/mail.py b/orchestra/utils/mail.py
index 21028fb9..e4c925a4 100644
--- a/orchestra/utils/mail.py
+++ b/orchestra/utils/mail.py
@@ -15,8 +15,6 @@ def render_email_template(template, context):
"""
if isinstance(context, dict):
context = Context(context)
- if isinstance(to, str):
- to = [to]
if not 'site' in context:
from orchestra import settings
@@ -32,6 +30,8 @@ def render_email_template(template, context):
def send_email_template(template, context, to, email_from=None, html=None, attachments=[]):
+ if isinstance(to, str):
+ to = [to]
subject, message = render_email_template(template, context)
msg = EmailMultiAlternatives(subject, message, email_from, to, attachments=attachments)
if html:
diff --git a/requirements.txt b/requirements.txt
index 7547422c..a3e0d8bc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@ celery==3.1.16
kombu==3.0.23
billiard==3.3.0.18
Markdown==2.4
-djangorestframework==3.1.1
+djangorestframework==3.1.2
ecdsa==0.11
Pygments==1.6
django-filter==0.7