source: remit/vouchers/views.py @ f7dd5e7

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

Let voucher recipients see requests as well

  • Property mode set to 100644
File size: 12.0 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            'amount',
28            'budget_area',
29            'expense_area',
30            'check_to_first_name',
31            'check_to_last_name',
32            'check_to_email',
33            'check_to_addr',
34        )
35
36class CommitteesField(ModelChoiceField):
37    def __init__(self, *args, **kargs):
38        base_area = BudgetArea.get_by_path(['Accounts', 'Assets', 'Budget', ])
39        self.strip_levels = base_area.depth
40        areas = (base_area.get_descendants()
41            .filter(depth__lte=base_area.depth+2)
42            .exclude(name='Holding')
43        )
44        ModelChoiceField.__init__(self, queryset=areas,
45            help_text='Select the appropriate committe or other budget area',
46            *args, **kargs)
47
48    def label_from_instance(self, obj,):
49        return obj.indented_name(strip_levels=self.strip_levels)
50
51class SelectRequestBasicsForm(Form):
52    area = CommitteesField()
53    term = ModelChoiceField(queryset = BudgetTerm.objects.all())
54
55class DocUploadForm(ModelForm):
56    class Meta:
57        model = Documentation
58        fields = (
59            'label',
60            'backing_file',
61        )
62
63
64@user_passes_test(lambda u: u.is_authenticated())
65def select_request_basics(http_request, ):
66    if http_request.method == 'POST': # If the form has been submitted...
67        form = SelectRequestBasicsForm(http_request.POST) # A form bound to the POST data
68        if form.is_valid(): # All validation rules pass
69            term = form.cleaned_data['term'].slug
70            area = form.cleaned_data['area'].id
71            return HttpResponseRedirect(reverse(submit_request, args=[term, area],)) # Redirect after POST
72    else:
73        form = SelectRequestBasicsForm() # An unbound form
74
75    context = {
76        'form':form,
77        'pagename':'request_reimbursement',
78    }
79    return render_to_response('vouchers/select.html', context, context_instance=RequestContext(http_request), )
80
81class CommitteeBudgetAreasField(ModelChoiceField):
82    def __init__(self, base_area, *args, **kargs):
83        self.strip_levels = base_area.depth
84        areas = base_area.get_descendants()
85        ModelChoiceField.__init__(self, queryset=areas,
86            help_text='In general, this should be a fully indented budget area, not one with children',
87            *args, **kargs)
88
89    def label_from_instance(self, obj,):
90        return obj.indented_name(strip_levels=self.strip_levels)
91
92class ExpenseAreasField(ModelChoiceField):
93    def __init__(self, *args, **kargs):
94        base_area = vouchers.models.BudgetArea.get_by_path(['Accounts', 'Expenses'])
95        self.strip_levels = base_area.depth
96        areas = base_area.get_descendants()
97        ModelChoiceField.__init__(self, queryset=areas,
98            help_text='In general, this should be a fully indented budget area, not one with children',
99            *args, **kargs)
100
101    def label_from_instance(self, obj,):
102        return obj.indented_name(strip_levels=self.strip_levels)
103
104@user_passes_test(lambda u: u.is_authenticated())
105def submit_request(http_request, term, committee):
106    term_obj = get_object_or_404(BudgetTerm, slug=term)
107    comm_obj = get_object_or_404(BudgetArea, pk=committee)
108
109    new_request = ReimbursementRequest()
110    new_request.submitter = http_request.user.username
111    new_request.budget_term = term_obj
112
113    # Prefill from user information (itself prefilled from LDAP now)
114    initial = {}
115    initial['check_to_first_name'] = http_request.user.first_name
116    initial['check_to_last_name']  = http_request.user.last_name
117    initial['check_to_email']      = http_request.user.email
118
119    if http_request.method == 'POST': # If the form has been submitted...
120        form = RequestForm(http_request.POST, instance=new_request) # A form bound to the POST data
121        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)
122        form.fields['expense_area'] = ExpenseAreasField()
123
124        if form.is_valid(): # All validation rules pass
125            request_obj = form.save()
126
127            # Send email
128            tmpl = get_template('vouchers/emails/request_submit_admin.txt')
129            ctx = Context({
130                'submitter': http_request.user,
131                'request': request_obj,
132            })
133            body = tmpl.render(ctx)
134            recipients = []
135            for name, addr in settings.ADMINS:
136                recipients.append(addr)
137            recipients.append(request_obj.budget_area.owner_address())
138            send_mail(
139                '%sRequest submittal: %s requested $%s' % (
140                    settings.EMAIL_SUBJECT_PREFIX,
141                    http_request.user,
142                    request_obj.amount,
143                ),
144                body,
145                settings.SERVER_EMAIL,
146                recipients,
147            )
148
149            return HttpResponseRedirect(reverse(review_request, args=[new_request.pk],) + '?new=true') # Redirect after POST
150    else:
151        form = RequestForm(instance=new_request, initial=initial, ) # An unbound form
152        form.fields['budget_area'] = CommitteeBudgetAreasField(comm_obj)
153        form.fields['expense_area'] = ExpenseAreasField()
154
155    context = {
156        'term':term_obj,
157        'comm':comm_obj,
158        'form':form,
159        'pagename':'request_reimbursement',
160    }
161    return render_to_response('vouchers/submit.html', context, context_instance=RequestContext(http_request), )
162
163class VoucherizeForm(Form):
164    name = django.forms.CharField(max_length=100, help_text='Signatory name for voucher',)
165    email = django.forms.EmailField(max_length=100, help_text='Signatory email for voucher')
166
167
168@user_passes_test(lambda u: u.is_authenticated())
169def review_request(http_request, object_id):
170    request_obj = get_object_or_404(ReimbursementRequest, pk=object_id)
171    user = http_request.user
172    pagename = 'request_reimbursement'
173    new = False
174    if 'new' in http_request.REQUEST:
175        if http_request.REQUEST['new'].upper() == 'TRUE':
176            new = True
177        else:
178            new = False
179
180    if (user.has_perm('vouchers.view_requests') or
181        user.username == request_obj.submitter or
182        user.email.upper() == request_obj.check_to_email.upper()
183        ):
184        pass
185    else:
186        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, )
187
188    # DOCUMENTATION #
189    if request_obj.documentation:
190        doc_upload_form = None
191    else:
192        new_docs = Documentation()
193        new_docs.submitter = http_request.user.username
194        if http_request.method == 'POST': # If the form has been submitted...
195            doc_upload_form = DocUploadForm(http_request.POST, http_request.FILES, instance=new_docs) # A form bound to the POST data
196
197            if doc_upload_form.is_valid(): # All validation rules pass
198                new_docs = doc_upload_form.save()
199                request_obj.documentation = new_docs
200                request_obj.save()
201
202                return HttpResponseRedirect(reverse(review_request, args=[object_id],)) # Redirect after POST
203        else:
204            doc_upload_form = DocUploadForm(instance=new_docs, ) # An unbound form
205
206    # SEND EMAILS
207    show_email = http_request.user.has_perm('vouchers.can_email')
208    if show_email:
209        email_message = ''
210        if http_request.method == 'POST' and 'send_email' in http_request.REQUEST:
211            mail = vouchers.models.stock_emails[http_request.REQUEST['email_name']]
212            assert mail.context == 'request'
213            mail.send_email_request(request_obj)
214            email_message = 'Sent email "%s".' % (mail.label, )
215        email_options = []
216        for mail in vouchers.models.stock_emails.values():
217            if mail.context == 'request':
218                email_options.append({
219                    'label': mail.label,
220                    'name' : mail.name,
221                })
222
223    # APPROVE VOUCHERS
224    show_approve = (http_request.user.has_perm('vouchers.can_approve')
225        and request_obj.approval_status == vouchers.models.APPROVAL_STATE_PENDING)
226    if show_approve:
227        # Voucherize form
228        # Prefill from certs / config
229        initial = {}
230        initial['name'] = '%s %s' % (http_request.user.first_name, http_request.user.last_name, )
231        if settings.SIGNATORY_EMAIL:
232            initial['email'] = settings.SIGNATORY_EMAIL
233        else:
234            initial['email'] = http_request.user.email
235
236        approve_message = ''
237        if http_request.method == 'POST' and 'approve' in http_request.REQUEST:
238            approve_form = VoucherizeForm(http_request.POST)
239            if approve_form.is_valid():
240                voucher = request_obj.convert(
241                    approve_form.cleaned_data['name'],
242                    signatory_email=approve_form.cleaned_data['email'],)
243                tmpl = get_template('vouchers/emails/request_approval_admin.txt')
244                ctx = Context({
245                    'approver': http_request.user,
246                    'request': request_obj,
247                })
248                body = tmpl.render(ctx)
249                mail_admins(
250                    'Request approval: %s approved $%s' % (
251                        http_request.user,
252                        request_obj.amount,
253                    ),
254                    body,
255                )
256                approve_message = 'Created new voucher from request'
257        else:
258            approve_form = VoucherizeForm(initial=initial)
259
260    context = {
261        'rr':request_obj,
262        'pagename':pagename,
263        'new': new,
264        'doc_form': doc_upload_form,
265    }
266    if show_approve:
267        context['approve_form'] = approve_form
268        context['approve_message'] = approve_message
269    if show_email:
270        context['email_options'] = email_options
271        context['email_message'] = email_message
272    return render_to_response('vouchers/ReimbursementRequest_review.html', context, context_instance=RequestContext(http_request), )
273
274@user_passes_test(lambda u: u.has_perm('vouchers.generate_vouchers'))
275def generate_vouchers(http_request, *args):
276    unprocessed = True
277    if 'unprocessed' in http_request.REQUEST:
278        if http_request.REQUEST['unprocessed'].upper() == 'TRUE':
279            unprocessed = True
280        else:
281            unprocessed = False
282    mark = True
283    if 'mark' in http_request.REQUEST:
284        if http_request.REQUEST['mark'].upper() == 'TRUE':
285            mark = True
286        else:
287            mark = False
288
289    lst = vouchers.models.Voucher.objects.all()
290    if unprocessed:
291        lst = lst.filter(processed=False)
292
293    context = {
294        'vouchers': lst,
295        'MEDIA_ROOT': settings.MEDIA_ROOT,
296    }
297    response = render_to_response('vouchers/vouchers.tex', context, context_instance=RequestContext(http_request), )
298
299    # Send mail
300    tmpl = get_template('vouchers/emails/vouchers_tex.txt')
301    ctx = Context({
302        'converter': http_request.user,
303        'vouchers': lst,
304        'mark': mark,
305        'unprocessed': unprocessed,
306    })
307    body = tmpl.render(ctx)
308    mail_admins(
309        'Voucher rendering: %d by %s' % (
310            len(lst),
311            http_request.user,
312        ),
313        body,
314    )
315
316    if mark:
317        for voucher in lst:
318            voucher.processed = True
319            voucher.save()
320
321    return response
Note: See TracBrowser for help on using the repository browser.