Index: remit/vouchers/models.py
===================================================================
--- remit/vouchers/models.py	(revision 6054f181b43fc9d23e9e86700f9d3eb4fee8a3c4)
+++ remit/vouchers/models.py	(revision e8550be3fa19b22cb087aaba216fdad79c43a025)
@@ -36,4 +36,5 @@
             ('can_list', 'Can list requests',),
             ('can_approve', 'Can approve requests',),
+            ('can_email', 'Can send mail about requests',),
         )
 
@@ -107,2 +108,84 @@
         )
 
+
+class StockEmail:
+    def __init__(self, name, label, recipients, template, subject_template, context, ):
+        """
+        Initialize a stock email object.
+        
+        Each argument is required.
+        
+        name:       Short name. Letters, numbers, and hyphens only.
+        label:      User-readable label. Briefly describe what the email says
+        recipients: Who receives the email. List of "recipient" (check recipient), "area" (area owner), "admins" (site admins)
+        template:   Django template filename with the actual text
+        subject_template: Django template string with the subject
+        context:    Type of context the email needs. Must be 'request' currently.
+        """
+
+        self.name       = name
+        self.label      = label
+        self.recipients = recipients
+        self.template   = template
+        self.subject_template = subject_template
+        self.context    = context
+
+    def send_email_request(self, request,):
+        """
+        Send an email that requires context "request".
+        """
+
+        assert self.context == 'request'
+
+        # Generate text
+        from django.template import Context, Template
+        from django.template.loader import get_template
+        ctx = Context({
+            'prefix': settings.EMAIL_SUBJECT_PREFIX,
+            'request': request,
+            'sender': settings.USER_EMAIL_SIGNATURE,
+        })
+        tmpl = get_template(self.template)
+        body = tmpl.render(ctx)
+        subject_tmpl = Template(self.subject_template)
+        subject = subject_tmpl.render(ctx)
+
+        # Generate recipients
+        recipients = []
+        for rt in self.recipients:
+            if rt == 'recipient':
+                recipients.append(request.check_to_email)
+            elif rt == 'area':
+                recipients.append(request.budget_area.owner_address())
+            elif rt == 'admins':
+                pass # you don't *actually* have a choice...
+        for name, addr in settings.ADMINS:
+            recipients.append(addr)
+
+        # Send mail!
+        from django.core.mail import send_mail
+        send_mail(
+            subject,
+            body,
+            settings.SERVER_EMAIL,
+            recipients,
+        )
+
+stock_emails = {
+    'nodoc': StockEmail(
+        name='nodoc',
+        label='No documentation',
+        recipients=['recipient', 'area',],
+        template='vouchers/emails/no_docs_user.txt',
+        subject_template='{{prefix}}Missing documentation for reimbursement',
+        context = 'request',
+    ),
+    'voucher-sao': StockEmail(
+        name='voucher-sao',
+        label='Voucher submitted to SAO',
+        recipients=['recipient', ],
+        template='vouchers/emails/voucher_sao_user.txt',
+        subject_template='{{prefix}}Reimbursement sent to SAO for processing',
+        context = 'request',
+    ),
+}
Index: remit/vouchers/views.py
===================================================================
--- remit/vouchers/views.py	(revision 14adb6bccbb0c3cadb7838e9d822102b232d70ad)
+++ remit/vouchers/views.py	(revision e8550be3fa19b22cb087aaba216fdad79c43a025)
@@ -173,4 +173,20 @@
             new = False
 
+    show_email = http_request.user.has_perm('vouchers.can_email')
+    if show_email:
+        email_message = ''
+        if http_request.method == 'POST' and 'send_email' in http_request.REQUEST:
+            mail = vouchers.models.stock_emails[http_request.REQUEST['email_name']]
+            assert mail.context == 'request'
+            mail.send_email_request(request_obj)
+            email_message = 'Sent email "%s".' % (mail.label, )
+        email_options = []
+        for mail in vouchers.models.stock_emails.values():
+            if mail.context == 'request':
+                email_options.append({
+                    'label': mail.label,
+                    'name' : mail.name,
+                })
+
     show_approve = (http_request.user.has_perm('vouchers.can_approve')
         and request_obj.approval_status == vouchers.models.APPROVAL_STATE_PENDING)
@@ -232,4 +248,7 @@
         context['approve_form'] = approve_form
         context['approve_message'] = approve_message
+    if show_email:
+        context['email_options'] = email_options
+        context['email_message'] = email_message
     return render_to_response('vouchers/ReimbursementRequest_review.html', context, context_instance=RequestContext(http_request), )
 
