source: remit/vouchers/views.py @ 9102ac9

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

Add CC_SUBMITTER option (Trac: #25)

  • Property mode set to 100644
File size: 12.8 KB
Line 
1import vouchers.models
2from vouchers.models import ReimbursementRequest, Documentation
3from finance_core.models import BudgetTerm, BudgetArea
4from util.shortcuts import get_403_response
5
6from django.contrib.auth.decorators import user_passes_test
7from django.shortcuts import render_to_response, get_object_or_404
8from django.template import RequestContext
9from django.http import Http404, HttpResponseRedirect
10import django.forms
11from django.forms import Form
12from django.forms import ModelForm
13from django.forms import ModelChoiceField
14from django.core.urlresolvers import reverse
15from django.core.mail import send_mail, mail_admins
16from django.template import Context, Template
17from django.template.loader import get_template
18
19import settings
20
21class RequestForm(ModelForm):
22    class Meta:
23        model = ReimbursementRequest
24        fields = (
25            'name',
26            'description',
27            'incurred_time',
28            'amount',
29            'budget_area',
30            'expense_area',
31            'check_to_first_name',
32            'check_to_last_name',
33            'check_to_email',
34            'check_to_addr',
35        )
36
37
38class CommitteesField(ModelChoiceField):
39    def __init__(self, *args, **kargs):
40        base_area = BudgetArea.get_by_path(settings.BASE_COMMITTEE_PATH)
41        self.strip_levels = base_area.depth
42        areas = (base_area.get_descendants()
43            .filter(depth__lte=base_area.depth+settings.COMMITTEE_HIERARCHY_LEVELS)
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):
54    area = CommitteesField()
55    term = ModelChoiceField(queryset = BudgetTerm.objects.all())
56
57class DocUploadForm(ModelForm):
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
69    class Meta:
70        model = Documentation
71        fields = (
72            'label',
73            'backing_file',
74        )
75
76
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,
90        'pagename':'request_reimbursement',
91    }
92    return render_to_response('vouchers/select.html', context, context_instance=RequestContext(http_request), )
93
94class CommitteeBudgetAreasField(ModelChoiceField):
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
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
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
126    # Prefill from user information (itself prefilled from LDAP now)
127    initial = {}
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
131
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
134        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)
135        form.fields['expense_area'] = ExpenseAreasField()
136
137        if form.is_valid(): # All validation rules pass
138            request_obj = form.save()
139
140            # Send email
141            tmpl = get_template('vouchers/emails/request_submit_admin.txt')
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            if settings.CC_SUBMITTER:
152                recipients.append(http_request.user.email)
153            send_mail(
154                '%sRequest submittal: %s requested $%s' % (
155                    settings.EMAIL_SUBJECT_PREFIX,
156                    http_request.user,
157                    request_obj.amount,
158                ),
159                body,
160                settings.SERVER_EMAIL,
161                recipients,
162            )
163
164            return HttpResponseRedirect(reverse(review_request, args=[new_request.pk],) + '?new=true') # Redirect after POST
165    else:
166        form = RequestForm(instance=new_request, initial=initial, ) # An unbound form
167        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)
168        form.fields['expense_area'] = ExpenseAreasField()
169
170    context = {
171        'term':term_obj,
172        'comm':comm_obj,
173        'form':form,
174        'pagename':'request_reimbursement',
175    }
176    return render_to_response('vouchers/submit.html', context, context_instance=RequestContext(http_request), )
177
178class VoucherizeForm(Form):
179    name = django.forms.CharField(max_length=100, help_text='Signatory name for voucher',)
180    email = django.forms.EmailField(max_length=100, help_text='Signatory email for voucher')
181
182
183@user_passes_test(lambda u: u.is_authenticated())
184def review_request(http_request, object_id):
185    request_obj = get_object_or_404(ReimbursementRequest, pk=object_id)
186    user = http_request.user
187    pagename = 'request_reimbursement'
188    new = False
189    if 'new' in http_request.REQUEST:
190        if http_request.REQUEST['new'].upper() == 'TRUE':
191            new = True
192        else:
193            new = False
194
195    if (user.has_perm('vouchers.view_requests') or
196        user.username == request_obj.submitter or
197        user.email.upper() == request_obj.check_to_email.upper()
198        ):
199        pass
200    else:
201        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, )
202
203    # DOCUMENTATION #
204    if request_obj.documentation:
205        doc_upload_form = None
206    else:
207        new_docs = Documentation()
208        new_docs.submitter = http_request.user.username
209        if http_request.method == 'POST' and 'upload_documentation' in http_request.REQUEST: # If the form has been submitted...
210            doc_upload_form = DocUploadForm(http_request.POST, http_request.FILES, instance=new_docs) # A form bound to the POST data
211
212            if doc_upload_form.is_valid(): # All validation rules pass
213                new_docs = doc_upload_form.save()
214                request_obj.documentation = new_docs
215                request_obj.save()
216
217                return HttpResponseRedirect(reverse(review_request, args=[object_id],)) # Redirect after POST
218        else:
219            doc_upload_form = DocUploadForm(instance=new_docs, ) # An unbound form
220
221    # SEND EMAILS
222    show_email = http_request.user.has_perm('vouchers.can_email')
223    if show_email:
224        email_message = ''
225        if http_request.method == 'POST' and 'send_email' in http_request.REQUEST:
226            mail = vouchers.models.stock_emails[http_request.REQUEST['email_name']]
227            assert mail.context == 'request'
228            mail.send_email_request(request_obj)
229            email_message = 'Sent email "%s".' % (mail.label, )
230        email_options = []
231        for mail in vouchers.models.stock_emails.values():
232            if mail.context == 'request':
233                email_options.append({
234                    'label': mail.label,
235                    'name' : mail.name,
236                })
237
238    # APPROVE VOUCHERS
239    show_approve = (http_request.user.has_perm('vouchers.can_approve')
240        and request_obj.approval_status == vouchers.models.APPROVAL_STATE_PENDING)
241    if show_approve:
242        # Voucherize form
243        # Prefill from certs / config
244        initial = {}
245        initial['name'] = '%s %s' % (http_request.user.first_name, http_request.user.last_name, )
246        if settings.SIGNATORY_EMAIL:
247            initial['email'] = settings.SIGNATORY_EMAIL
248        else:
249            initial['email'] = http_request.user.email
250
251        approve_message = ''
252        if http_request.method == 'POST' and 'approve' in http_request.REQUEST:
253            approve_form = VoucherizeForm(http_request.POST)
254            if approve_form.is_valid():
255                voucher = request_obj.convert(
256                    approve_form.cleaned_data['name'],
257                    signatory_email=approve_form.cleaned_data['email'],)
258                tmpl = get_template('vouchers/emails/request_approval_admin.txt')
259                ctx = Context({
260                    'approver': http_request.user,
261                    'request': request_obj,
262                })
263                body = tmpl.render(ctx)
264                mail_admins(
265                    'Request approval: %s approved $%s' % (
266                        http_request.user,
267                        request_obj.amount,
268                    ),
269                    body,
270                )
271                approve_message = 'Created new voucher from request'
272        else:
273            approve_form = VoucherizeForm(initial=initial)
274
275    context = {
276        'rr':request_obj,
277        'pagename':pagename,
278        'new': new,
279        'doc_form': doc_upload_form,
280    }
281    if show_approve:
282        context['approve_form'] = approve_form
283        context['approve_message'] = approve_message
284    if show_email:
285        context['email_options'] = email_options
286        context['email_message'] = email_message
287    return render_to_response('vouchers/ReimbursementRequest_review.html', context, context_instance=RequestContext(http_request), )
288
289@user_passes_test(lambda u: u.has_perm('vouchers.generate_vouchers'))
290def generate_vouchers(http_request, *args):
291    unprocessed = True
292    if 'unprocessed' in http_request.REQUEST:
293        if http_request.REQUEST['unprocessed'].upper() == 'TRUE':
294            unprocessed = True
295        else:
296            unprocessed = False
297    mark = True
298    if 'mark' in http_request.REQUEST:
299        if http_request.REQUEST['mark'].upper() == 'TRUE':
300            mark = True
301        else:
302            mark = False
303
304    lst = vouchers.models.Voucher.objects.all()
305    if unprocessed:
306        lst = lst.filter(processed=False)
307
308    context = {
309        'vouchers': lst,
310        'MEDIA_ROOT': settings.MEDIA_ROOT,
311    }
312    response = render_to_response(
313        'vouchers/vouchers.tex',
314        context, context_instance=RequestContext(http_request),
315        mimetype=settings.LATEX_MIMETYPE,
316    )
317
318    # Send mail
319    tmpl = get_template('vouchers/emails/vouchers_tex.txt')
320    ctx = Context({
321        'converter': http_request.user,
322        'vouchers': lst,
323        'mark': mark,
324        'unprocessed': unprocessed,
325    })
326    body = tmpl.render(ctx)
327    mail_admins(
328        'Voucher rendering: %d by %s' % (
329            len(lst),
330            http_request.user,
331        ),
332        body,
333    )
334
335    if mark:
336        for voucher in lst:
337            voucher.mark_processed()
338
339    return response
Note: See TracBrowser for help on using the repository browser.