diff --git a/CHANGES.rst b/CHANGES.rst index 1e4b18809a..b34dc9e9ef 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,7 @@ Changelog **Added** +- #1553 Allow to modify the email template for rejection notification - #1549 Added registry profile for jQuery UI settings - #1544 Progress indicator for Batch listing - #1536 Integrated Setup and Profiles from senaite.lims diff --git a/bika/lims/api/mail.py b/bika/lims/api/mail.py index e8539c3ddf..891edb4ed3 100644 --- a/bika/lims/api/mail.py +++ b/bika/lims/api/mail.py @@ -22,6 +22,7 @@ import mimetypes import os import re +import six import socket from email import encoders from email.header import Header diff --git a/bika/lims/browser/analysisrequest/reject.py b/bika/lims/browser/analysisrequest/reject.py index 8000ec13db..26dc2c6be1 100644 --- a/bika/lims/browser/analysisrequest/reject.py +++ b/bika/lims/browser/analysisrequest/reject.py @@ -22,10 +22,16 @@ from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -class AnalysisRequestRejectBase(object): +class AnalysisRequestRejectPdfView(BrowserView): """ - Provides helper methods that ease the work with rejection reasons + View that renders the template to be used for the generation of a pdf to + be attached in the email for the notification of an Analysis Request + rejection action. """ + template = ViewPageTemplateFile("templates/analysisrequest_retract_pdf.pt") + + def __call__(self): + return self.template() def get_rejection_reasons(self, keyword=None): """ @@ -49,33 +55,3 @@ def get_rejection_reasons(self, keyword=None): return rejection_reasons.get(keyword, '') and [rejection_reasons.get(keyword, '')] or [] return rejection_reasons.get(keyword, []) - -class AnalysisRequestRejectEmailView(BrowserView, AnalysisRequestRejectBase): - """ - View that renders the template to be attached in the body of the email - for the notification of an Analysis Request rejection action. - """ - - template = ViewPageTemplateFile("templates/analysisrequest_retract_mail.pt") - - def __init__(self, context, request): - super(AnalysisRequestRejectEmailView, self).__init__(context, request) - - def __call__(self): - return self.template() - - -class AnalysisRequestRejectPdfView(BrowserView, AnalysisRequestRejectBase): - """ - View that renders the template to be used for the generation of a pdf to - be attached in the email for the notification of an Analysis Request - rejection action. - """ - - template = ViewPageTemplateFile("templates/analysisrequest_retract_pdf.pt") - - def __init__(self, context, request): - super(AnalysisRequestRejectPdfView, self).__init__(context, request) - - def __call__(self): - return self.template() diff --git a/bika/lims/browser/analysisrequest/templates/analysisrequest_retract_mail.pt b/bika/lims/browser/analysisrequest/templates/analysisrequest_retract_mail.pt deleted file mode 100644 index bc98b0ac51..0000000000 --- a/bika/lims/browser/analysisrequest/templates/analysisrequest_retract_mail.pt +++ /dev/null @@ -1,40 +0,0 @@ - - - - -

- - -
-
-
- - -

-
- -

- For further information please contact us under the following address. -

-
- -

- - -
-
- - -
-
- Phone:
- Fax:
-
-

-
diff --git a/bika/lims/content/bikasetup.py b/bika/lims/content/bikasetup.py index 46ea7ca27b..f8baa7648b 100644 --- a/bika/lims/content/bikasetup.py +++ b/bika/lims/content/bikasetup.py @@ -601,6 +601,31 @@ def getCounterTypes(self, instance=None): "via email to the Client when a Sample is rejected.") ), ), + TextField( + "EmailBodySampleRejection", + default_content_type='text/html', + default_output_type='text/x-html-safe', + schemata="Notifications", + label=_("Email body for Sample Rejection notifications"), + default="The sample $sample_link has been rejected because of the " + "following reasons:" + "

$reasons

" + "For further information, please contact us under the " + "following address.

" + "$lab_address", + widget=RichWidget( + label=_("Email body for Sample Rejection notifications"), + description=_( + "Set the text for the body of the email to be sent to the " + "Sample's client contact if the option 'Email notification on " + "Sample rejection' is enabled. You can use reserved keywords: " + "$sample_id, $sample_link, $reasons, $lab_address"), + default_mime_type='text/x-rst', + output_mime_type='text/x-html', + allow_file_upload=False, + rows=15, + ), + ), BooleanField( 'NotifyOnSampleInvalidation', schemata="Notifications", @@ -640,7 +665,7 @@ def getCounterTypes(self, instance=None): default_mime_type='text/x-rst', output_mime_type='text/x-html', allow_file_upload=False, - rows=10, + rows=15, ), ), StringField( diff --git a/bika/lims/utils/analysisrequest.py b/bika/lims/utils/analysisrequest.py index f31b1c1b9c..34775b81c6 100644 --- a/bika/lims/utils/analysisrequest.py +++ b/bika/lims/utils/analysisrequest.py @@ -19,6 +19,7 @@ # Some rights reserved, see README and LICENSE. import itertools +from string import Template import six from Products.Archetypes.config import UID_CATALOG @@ -45,6 +46,7 @@ from bika.lims.utils import changeWorkflowState from bika.lims.utils import copy_field_values from bika.lims.utils import createPdf +from bika.lims.utils import get_link from bika.lims.utils import tmpID from bika.lims.workflow import ActionHandlerPool from bika.lims.workflow import doActionFor @@ -520,13 +522,23 @@ def get_rejection_pdf(sample): def get_rejection_mail(sample, rejection_pdf=None): """Generates an email to sample contacts with rejection reasons """ - # Avoid circular dependencies - from bika.lims.browser.analysisrequest.reject import \ - AnalysisRequestRejectEmailView + # Get the reasons + reasons = sample.getRejectionReasons() + reasons = reasons and reasons[0] or {} + reasons = reasons.get("selected", []) + [reasons.get("other")] + reasons = filter(None, reasons) + reasons = "
- ".join(reasons) # Render the email body - tpl = AnalysisRequestRejectEmailView(sample, api.get_request()) - email_body = tpl.template() + setup = api.get_setup() + lab_address = setup.laboratory.getPrintAddress() + email_body = Template(setup.getEmailBodySampleRejection()) + email_body = email_body.safe_substitute({ + "lab_address": "
".join(lab_address), + "reasons": reasons and "
-{}".format(reasons) or "", + "sample_id": api.get_id(sample), + "sample_link": get_link(api.get_url(sample), api.get_id(sample)) + }) def to_valid_email_address(contact): if not contact: