source: remit/vouchers/views.py @ 70a9bbd

client
Last change on this file since 70a9bbd was 70a9bbd, checked in by Alex Dehnert <adehnert@…>, 15 years ago

Serve LaTeX as LATEX_MIMETYPE (Trac: #29)

Set the default value of LATEX_MIMETYPE to application/x-latex. Note that
text/plain might be preferable, since most browsers will display it inline.
Also, this fixes the issue with the client erroring out.

  • Property mode set to 100644
File size: 12.7 KB
RevLine 
[269db50]1import vouchers.models
[dc17b01]2from vouchers.models import ReimbursementRequest, Documentation
[3e79308]3from finance_core.models import BudgetTerm, BudgetArea
[f7dd5e7]4from util.shortcuts import get_403_response
[269db50]5
[6b8d891]6from django.contrib.auth.decorators import user_passes_test
7from django.shortcuts import render_to_response, get_object_or_404
[fedcbcf]8from django.template import RequestContext
[6b8d891]9from django.http import Http404, HttpResponseRedirect
[587bb95]10import django.forms
[70ce03a]11from django.forms import Form
[6b8d891]12from django.forms import ModelForm
13from django.forms import ModelChoiceField
14from django.core.urlresolvers import reverse
[0e58ad0]15from django.core.mail import send_mail, mail_admins
[a9d44e0]16from django.template import Context, Template
17from django.template.loader import get_template
[269db50]18
[587bb95]19import settings
20
[6b8d891]21class RequestForm(ModelForm):
22    class Meta:
23        model = ReimbursementRequest
24        fields = (
25            'name',
26            'description',
[82211ea]27            'incurred_time',
[6b8d891]28            'amount',
29            'budget_area',
[f6c7295]30            'expense_area',
[248b30b]31            'check_to_first_name',
32            'check_to_last_name',
[6b8d891]33            'check_to_email',
34            'check_to_addr',
35        )
36
[82211ea]37
[70ce03a]38class CommitteesField(ModelChoiceField):
39    def __init__(self, *args, **kargs):
[6f24604]40        base_area = BudgetArea.get_by_path(settings.BASE_COMMITTEE_PATH)
[70ce03a]41        self.strip_levels = base_area.depth
42        areas = (base_area.get_descendants()
[857256d]43            .filter(depth__lte=base_area.depth+settings.COMMITTEE_HIERARCHY_LEVELS)
[70ce03a]44            .exclude(name='Holding')
45        )
46        ModelChoiceField.__init__(self, queryset=areas,
47            help_text='Select the appropriate committe or other budget area',
48            *args, **kargs)
49
50    def label_from_instance(self, obj,):
51        return obj.indented_name(strip_levels=self.strip_levels)
52
53class SelectRequestBasicsForm(Form):
[17193ee]54    area = CommitteesField()
[70ce03a]55    term = ModelChoiceField(queryset = BudgetTerm.objects.all())
56
[dc17b01]57class DocUploadForm(ModelForm):
[0a5a003]58    def clean_backing_file(self, ):
59        f = self.cleaned_data['backing_file']
60        ext = f.name.rsplit('.')[-1]
61        contenttype = f.content_type
62        if ext != 'pdf':
63            raise django.forms.ValidationError("Only PDF files are accepted --- you submitted a .%s file" % (ext, ))
64        elif contenttype != 'application/pdf':
65            raise django.forms.ValidationError("Only PDF files are accepted --- you submitted a %s file" % (contenttype, ))
66        else:
67            return f
68
[dc17b01]69    class Meta:
70        model = Documentation
71        fields = (
72            'label',
73            'backing_file',
74        )
75
76
[70ce03a]77@user_passes_test(lambda u: u.is_authenticated())
78def select_request_basics(http_request, ):
79    if http_request.method == 'POST': # If the form has been submitted...
80        form = SelectRequestBasicsForm(http_request.POST) # A form bound to the POST data
81        if form.is_valid(): # All validation rules pass
82            term = form.cleaned_data['term'].slug
83            area = form.cleaned_data['area'].id
84            return HttpResponseRedirect(reverse(submit_request, args=[term, area],)) # Redirect after POST
85    else:
86        form = SelectRequestBasicsForm() # An unbound form
87
88    context = {
89        'form':form,
[3a0c51b]90        'pagename':'request_reimbursement',
[70ce03a]91    }
[fedcbcf]92    return render_to_response('vouchers/select.html', context, context_instance=RequestContext(http_request), )
[70ce03a]93
94class CommitteeBudgetAreasField(ModelChoiceField):
[6b8d891]95    def __init__(self, base_area, *args, **kargs):
96        self.strip_levels = base_area.depth
97        areas = base_area.get_descendants()
98        ModelChoiceField.__init__(self, queryset=areas,
99            help_text='In general, this should be a fully indented budget area, not one with children',
100            *args, **kargs)
101
102    def label_from_instance(self, obj,):
103        return obj.indented_name(strip_levels=self.strip_levels)
104
[f6c7295]105class ExpenseAreasField(ModelChoiceField):
106    def __init__(self, *args, **kargs):
107        base_area = vouchers.models.BudgetArea.get_by_path(['Accounts', 'Expenses'])
108        self.strip_levels = base_area.depth
109        areas = base_area.get_descendants()
110        ModelChoiceField.__init__(self, queryset=areas,
111            help_text='In general, this should be a fully indented budget area, not one with children',
112            *args, **kargs)
113
114    def label_from_instance(self, obj,):
115        return obj.indented_name(strip_levels=self.strip_levels)
116
[6b8d891]117@user_passes_test(lambda u: u.is_authenticated())
118def submit_request(http_request, term, committee):
119    term_obj = get_object_or_404(BudgetTerm, slug=term)
120    comm_obj = get_object_or_404(BudgetArea, pk=committee)
121
122    new_request = ReimbursementRequest()
123    new_request.submitter = http_request.user.username
124    new_request.budget_term = term_obj
125
[37c15c4]126    # Prefill from user information (itself prefilled from LDAP now)
[e2f2aa9]127    initial = {}
[37c15c4]128    initial['check_to_first_name'] = http_request.user.first_name
129    initial['check_to_last_name']  = http_request.user.last_name
130    initial['check_to_email']      = http_request.user.email
[e2f2aa9]131
[6b8d891]132    if http_request.method == 'POST': # If the form has been submitted...
133        form = RequestForm(http_request.POST, instance=new_request) # A form bound to the POST data
[70ce03a]134        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)
[f6c7295]135        form.fields['expense_area'] = ExpenseAreasField()
[dc17b01]136
[6b8d891]137        if form.is_valid(): # All validation rules pass
[0e58ad0]138            request_obj = form.save()
139
140            # Send email
[3bf063c]141            tmpl = get_template('vouchers/emails/request_submit_admin.txt')
[0e58ad0]142            ctx = Context({
143                'submitter': http_request.user,
144                'request': request_obj,
145            })
146            body = tmpl.render(ctx)
147            recipients = []
148            for name, addr in settings.ADMINS:
149                recipients.append(addr)
150            recipients.append(request_obj.budget_area.owner_address())
151            send_mail(
[14adb6b]152                '%sRequest submittal: %s requested $%s' % (
153                    settings.EMAIL_SUBJECT_PREFIX,
[0e58ad0]154                    http_request.user,
155                    request_obj.amount,
156                ),
157                body,
158                settings.SERVER_EMAIL,
159                recipients,
160            )
161
[856aac8]162            return HttpResponseRedirect(reverse(review_request, args=[new_request.pk],) + '?new=true') # Redirect after POST
[6b8d891]163    else:
[e2f2aa9]164        form = RequestForm(instance=new_request, initial=initial, ) # An unbound form
[70ce03a]165        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)
[f6c7295]166        form.fields['expense_area'] = ExpenseAreasField()
[269db50]167
168    context = {
169        'term':term_obj,
170        'comm':comm_obj,
[6b8d891]171        'form':form,
[3a0c51b]172        'pagename':'request_reimbursement',
[269db50]173    }
[fedcbcf]174    return render_to_response('vouchers/submit.html', context, context_instance=RequestContext(http_request), )
[6b8d891]175
[587bb95]176class VoucherizeForm(Form):
[e601d3b]177    name = django.forms.CharField(max_length=100, help_text='Signatory name for voucher',)
178    email = django.forms.EmailField(max_length=100, help_text='Signatory email for voucher')
[587bb95]179
180
[6b8d891]181@user_passes_test(lambda u: u.is_authenticated())
182def review_request(http_request, object_id):
183    request_obj = get_object_or_404(ReimbursementRequest, pk=object_id)
[f7dd5e7]184    user = http_request.user
185    pagename = 'request_reimbursement'
[856aac8]186    new = False
187    if 'new' in http_request.REQUEST:
188        if http_request.REQUEST['new'].upper() == 'TRUE':
189            new = True
190        else:
191            new = False
[587bb95]192
[f7dd5e7]193    if (user.has_perm('vouchers.view_requests') or
194        user.username == request_obj.submitter or
195        user.email.upper() == request_obj.check_to_email.upper()
196        ):
197        pass
198    else:
199        return get_403_response(http_request, errmsg="You do not have permission to access this reimbursement request. You can only view requests you submitted or are the recipient for, unless you have general viewing permissions.", pagename=pagename, )
200
[dc17b01]201    # DOCUMENTATION #
202    if request_obj.documentation:
203        doc_upload_form = None
204    else:
205        new_docs = Documentation()
206        new_docs.submitter = http_request.user.username
[b34d568]207        if http_request.method == 'POST' and 'upload_documentation' in http_request.REQUEST: # If the form has been submitted...
[dc17b01]208            doc_upload_form = DocUploadForm(http_request.POST, http_request.FILES, instance=new_docs) # A form bound to the POST data
209
210            if doc_upload_form.is_valid(): # All validation rules pass
211                new_docs = doc_upload_form.save()
[a75ed9b]212                request_obj.documentation = new_docs
213                request_obj.save()
[dc17b01]214
215                return HttpResponseRedirect(reverse(review_request, args=[object_id],)) # Redirect after POST
216        else:
217            doc_upload_form = DocUploadForm(instance=new_docs, ) # An unbound form
218
219    # SEND EMAILS
[e8550be]220    show_email = http_request.user.has_perm('vouchers.can_email')
221    if show_email:
222        email_message = ''
223        if http_request.method == 'POST' and 'send_email' in http_request.REQUEST:
224            mail = vouchers.models.stock_emails[http_request.REQUEST['email_name']]
225            assert mail.context == 'request'
226            mail.send_email_request(request_obj)
227            email_message = 'Sent email "%s".' % (mail.label, )
228        email_options = []
229        for mail in vouchers.models.stock_emails.values():
230            if mail.context == 'request':
231                email_options.append({
232                    'label': mail.label,
233                    'name' : mail.name,
234                })
235
[dc17b01]236    # APPROVE VOUCHERS
[3e79308]237    show_approve = (http_request.user.has_perm('vouchers.can_approve')
238        and request_obj.approval_status == vouchers.models.APPROVAL_STATE_PENDING)
239    if show_approve:
[587bb95]240        # Voucherize form
241        # Prefill from certs / config
242        initial = {}
[37c15c4]243        initial['name'] = '%s %s' % (http_request.user.first_name, http_request.user.last_name, )
[587bb95]244        if settings.SIGNATORY_EMAIL:
245            initial['email'] = settings.SIGNATORY_EMAIL
246        else:
[37c15c4]247            initial['email'] = http_request.user.email
[587bb95]248
249        approve_message = ''
250        if http_request.method == 'POST' and 'approve' in http_request.REQUEST:
251            approve_form = VoucherizeForm(http_request.POST)
252            if approve_form.is_valid():
253                voucher = request_obj.convert(
254                    approve_form.cleaned_data['name'],
255                    signatory_email=approve_form.cleaned_data['email'],)
[3bf063c]256                tmpl = get_template('vouchers/emails/request_approval_admin.txt')
[a9d44e0]257                ctx = Context({
258                    'approver': http_request.user,
259                    'request': request_obj,
260                })
261                body = tmpl.render(ctx)
262                mail_admins(
263                    'Request approval: %s approved $%s' % (
264                        http_request.user,
265                        request_obj.amount,
266                    ),
267                    body,
268                )
[587bb95]269                approve_message = 'Created new voucher from request'
270        else:
271            approve_form = VoucherizeForm(initial=initial)
272
[6b8d891]273    context = {
274        'rr':request_obj,
[f7dd5e7]275        'pagename':pagename,
[856aac8]276        'new': new,
[dc17b01]277        'doc_form': doc_upload_form,
[6b8d891]278    }
[3e79308]279    if show_approve:
[587bb95]280        context['approve_form'] = approve_form
281        context['approve_message'] = approve_message
[e8550be]282    if show_email:
283        context['email_options'] = email_options
284        context['email_message'] = email_message
[fedcbcf]285    return render_to_response('vouchers/ReimbursementRequest_review.html', context, context_instance=RequestContext(http_request), )
[6b8d891]286
[6054f18]287@user_passes_test(lambda u: u.has_perm('vouchers.generate_vouchers'))
[dcaa9c0]288def generate_vouchers(http_request, *args):
289    unprocessed = True
290    if 'unprocessed' in http_request.REQUEST:
291        if http_request.REQUEST['unprocessed'].upper() == 'TRUE':
292            unprocessed = True
293        else:
294            unprocessed = False
295    mark = True
296    if 'mark' in http_request.REQUEST:
297        if http_request.REQUEST['mark'].upper() == 'TRUE':
298            mark = True
299        else:
300            mark = False
301
302    lst = vouchers.models.Voucher.objects.all()
303    if unprocessed:
304        lst = lst.filter(processed=False)
305
[a75ed9b]306    context = {
307        'vouchers': lst,
308        'MEDIA_ROOT': settings.MEDIA_ROOT,
309    }
[70a9bbd]310    response = render_to_response(
311        'vouchers/vouchers.tex',
312        context, context_instance=RequestContext(http_request),
313        mimetype=settings.LATEX_MIMETYPE,
314    )
[dcaa9c0]315
[524b81e]316    # Send mail
[3bf063c]317    tmpl = get_template('vouchers/emails/vouchers_tex.txt')
[524b81e]318    ctx = Context({
319        'converter': http_request.user,
320        'vouchers': lst,
321        'mark': mark,
322        'unprocessed': unprocessed,
323    })
324    body = tmpl.render(ctx)
325    mail_admins(
326        'Voucher rendering: %d by %s' % (
327            len(lst),
328            http_request.user,
329        ),
330        body,
331    )
332
[dcaa9c0]333    if mark:
334        for voucher in lst:
[82211ea]335            voucher.mark_processed()
[dcaa9c0]336
337    return response
Note: See TracBrowser for help on using the repository browser.