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