Пример #1
0
    def accounting(self, request, tl, one, two, module, extra, prog):
        '''Lists accounting for a student.

        Defaults to the current user, but can take the user ID in the extra
        argument instead.'''

        if extra:
            user = ESPUser.objects.get(id=extra)
        else:
            user = request.user

        iac = IndividualAccountingController(prog, user)
        classified_transfers = [{
            'transfer': t,
            'type': iac.classify_transfer(t)
        } for t in iac.get_transfers().select_related('line_item')]
        context = {
            'target_user': user,
            'transfers': classified_transfers,
            'identifier': iac.get_identifier(),
            'grant': iac.latest_finaid_grant(),
        }
        if iac.transfers_to_program_exist():
            context['transfers_exist'] = True
            context['requested'] = iac.amount_requested(ensure_required=False)
            context['finaid'] = iac.amount_finaid()
            context['siblingdiscount'] = iac.amount_siblingdiscount()
            context['paid'] = iac.amount_paid()
            context['due'] = iac.amount_due()
        return render_to_response(self.baseDir() + 'accounting.html', request,
                                  context)
    def payonline(self, request, tl, one, two, module, extra, prog):

        user = ESPUser(request.user)

        iac = IndividualAccountingController(self.program, request.user)
        context = {}
        context['module'] = self
        context['one'] = one
        context['two'] = two
        context['tl'] = tl
        context['user'] = user
        context['invoice_id'] = iac.get_id()
        context['identifier'] = iac.get_identifier()
        payment_type = iac.default_payments_lineitemtype()
        sibling_type = iac.default_siblingdiscount_lineitemtype()
        grant_type = iac.default_finaid_lineitemtype()
        context['itemizedcosts'] = iac.get_transfers().exclude(
            line_item__in=[payment_type, sibling_type, grant_type]).order_by(
                '-line_item__required')
        context['itemizedcosttotal'] = iac.amount_due()
        context['subtotal'] = iac.amount_requested()
        context['financial_aid'] = iac.amount_finaid()
        context['sibling_discount'] = iac.amount_siblingdiscount()
        context['amount_paid'] = iac.amount_paid()

        if 'HTTP_HOST' in request.META:
            context['hostname'] = request.META['HTTP_HOST']
        else:
            context['hostname'] = Site.objects.get_current().domain
        context['institution'] = settings.INSTITUTION_NAME
        context['storename'] = self.store_id
        context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support']

        return render_to_response(self.baseDir() + 'cardpay.html', request,
                                  context)
    def paynow_cybersource(self, request, tl, one, two, module, extra, prog):

        # Force users to pay for non-optional stuffs
        user = ESPUser(request.user)

        iac = IndividualAccountingController(self.program, request.user)
        context = {}
        context['module'] = self
        context['one'] = one
        context['two'] = two
        context['tl'] = tl
        context['user'] = user
        context['invoice_id'] = iac.get_id()
        context['identifier'] = iac.get_identifier()
        payment_type = iac.default_payments_lineitemtype()
        sibling_type = iac.default_siblingdiscount_lineitemtype()
        grant_type = iac.default_finaid_lineitemtype()
        context['itemizedcosts'] = iac.get_transfers().exclude(
            line_item__in=[payment_type, sibling_type, grant_type]).order_by(
                '-line_item__required')
        context['itemizedcosttotal'] = iac.amount_due()
        context['subtotal'] = iac.amount_requested()
        context['financial_aid'] = iac.amount_finaid()
        context['sibling_discount'] = iac.amount_siblingdiscount()
        context['amount_paid'] = iac.amount_paid()

        return render_to_response(self.baseDir() + 'cardpay.html', request,
                                  context)
    def cybersource(self, request, tl, one, two, module, extra, prog):

        # Force users to pay for non-optional stuffs
        user = request.user

        iac = IndividualAccountingController(self.program, request.user)
        context = {}
        context['module'] = self
        context['one'] = one
        context['two'] = two
        context['tl'] = tl
        context['user'] = user
        context['contact_email'] = self.program.director_email
        context['invoice_id'] = iac.get_id()
        context['identifier'] = iac.get_identifier()
        payment_type = iac.default_payments_lineitemtype()
        sibling_type = iac.default_siblingdiscount_lineitemtype()
        grant_type = iac.default_finaid_lineitemtype()
        context['itemizedcosts'] = iac.get_transfers().exclude(
            line_item__in=[payment_type, sibling_type, grant_type]).order_by(
                '-line_item__required')
        context['itemizedcosttotal'] = iac.amount_due()
        context['subtotal'] = iac.amount_requested()
        context['financial_aid'] = iac.amount_finaid()
        context['sibling_discount'] = iac.amount_siblingdiscount()
        context['amount_paid'] = iac.amount_paid()
        context['result'] = request.GET.get("result")
        context['post_url'] = settings.CYBERSOURCE_CONFIG['post_url']
        context['merchant_id'] = settings.CYBERSOURCE_CONFIG['merchant_id']

        if (not context['post_url']) or (not context['merchant_id']):
            raise ESPError("The Cybersource module is not configured")

        return render_to_response(self.baseDir() + 'cardpay.html', request,
                                  context)
    def cybersource(self, request, tl, one, two, module, extra, prog):

        # Force users to pay for non-optional stuffs
        user = request.user

        iac = IndividualAccountingController(self.program, request.user)
        context = {}
        context['module'] = self
        context['one'] = one
        context['two'] = two
        context['tl']  = tl
        context['user'] = user
        context['contact_email'] = self.program.director_email
        context['invoice_id'] = iac.get_id()
        context['identifier'] = iac.get_identifier()
        payment_type = iac.default_payments_lineitemtype()
        sibling_type = iac.default_siblingdiscount_lineitemtype()
        grant_type = iac.default_finaid_lineitemtype()
        context['itemizedcosts'] = iac.get_transfers().exclude(line_item__in=[payment_type, sibling_type, grant_type]).order_by('-line_item__required')
        context['itemizedcosttotal'] = iac.amount_due()
        context['subtotal'] = iac.amount_requested()
        context['financial_aid'] = iac.amount_finaid()
        context['sibling_discount'] = iac.amount_siblingdiscount()
        context['amount_paid'] = iac.amount_paid()
        context['result'] = request.GET.get("result")
        context['post_url'] = settings.CYBERSOURCE_CONFIG['post_url']
        context['merchant_id'] = settings.CYBERSOURCE_CONFIG['merchant_id']

        if (not context['post_url']) or (not context['merchant_id']):
            raise ESPError("The Cybersource module is not configured")

        return render_to_response(self.baseDir() + 'cardpay.html', request, context)
    def payonline(self, request, tl, one, two, module, extra, prog):

        #   Check that the user has completed all required modules so that they
        #   are "finished" registering for the program.  (In other words, they
        #   should be registered for at least one class, and filled out other
        #   required forms, before paying by credit card.)
        modules = prog.getModules(request.user, tl)
        completedAll = True
        for module in modules:
            if not module.isCompleted() and module.required:
                completedAll = False
        if not completedAll and not request.user.isAdmin(prog):
            raise ESPError("Please go back and ensure that you have completed all required steps of registration before paying by credit card.", log=False)

        #   Check for setup of module.  This is also required to initialize settings.
        self.check_setup()

        user = request.user

        iac = IndividualAccountingController(self.program, request.user)
        context = {}
        context['module'] = self
        context['program'] = prog
        context['user'] = user
        context['invoice_id'] = iac.get_id()
        context['identifier'] = iac.get_identifier()
        payment_type = iac.default_payments_lineitemtype()
        sibling_type = iac.default_siblingdiscount_lineitemtype()
        grant_type = iac.default_finaid_lineitemtype()
        offer_donation = self.settings['offer_donation']
        donate_type = iac.get_lineitemtypes().get(text=self.settings['donation_text']) if offer_donation else None
        context['itemizedcosts'] = iac.get_transfers().exclude(line_item__in=filter(None, [payment_type, sibling_type, grant_type, donate_type])).order_by('-line_item__required')
        context['itemizedcosttotal'] = iac.amount_due()
        #   This amount should be formatted as an integer in order to be
        #   accepted by Stripe.
        context['totalcost_cents'] = int(context['itemizedcosttotal'] * 100)
        context['subtotal'] = iac.amount_requested()
        context['financial_aid'] = iac.amount_finaid()
        context['sibling_discount'] = iac.amount_siblingdiscount()
        context['amount_paid'] = iac.amount_paid()

        #   Load donation amount separately, since the client-side code needs to know about it separately.
        donation_prefs = iac.get_preferences([donate_type,]) if offer_donation else None
        if donation_prefs:
            context['amount_donation'] = Decimal(donation_prefs[0][2])
            context['has_donation'] = True
        else:
            context['amount_donation'] = Decimal('0.00')
            context['has_donation'] = False
        context['amount_without_donation'] = context['itemizedcosttotal'] - context['amount_donation']

        if 'HTTP_HOST' in request.META:
            context['hostname'] = request.META['HTTP_HOST']
        else:
            context['hostname'] = Site.objects.get_current().domain
        context['institution'] = settings.INSTITUTION_NAME
        context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support']

        return render_to_response(self.baseDir() + 'cardpay.html', request, context)
    def confirmreg_forreal(self, request, tl, one, two, module, extra, prog, new_reg):
        """ The page that is shown once the user saves their student reg,
            giving them the option of printing a confirmation            """
        self.request = request

        from esp.program.modules.module_ext import DBReceipt

        iac = IndividualAccountingController(prog, request.user)

        context = {}
        context['one'] = one
        context['two'] = two

        context['itemizedcosts'] = iac.get_transfers()

        user = request.user
        context['finaid'] = user.hasFinancialAid(prog)
        if user.appliedFinancialAid(prog):
            context['finaid_app'] = user.financialaidrequest_set.filter(program=prog).order_by('-id')[0]
        else:
            context['finaid_app'] = None
        context['balance'] = iac.amount_due()

        context['owe_money'] = ( context['balance'] != Decimal("0.0") )

        if not prog.user_can_join(user):
            raise ESPError("This program has filled!  It can't accept any more students.  Please try again next session.", log=False)

        modules = prog.getModules(request.user, tl)
        completedAll = True
        for module in modules:
            if hasattr(module, 'onConfirm'):
                module.onConfirm(request)
            if not module.isCompleted() and module.required:
                completedAll = False
            context = module.prepare(context)

        if completedAll:
            if new_reg:
                rec = Record.objects.create(user=user, event="reg_confirmed",
                                            program=prog)
        else:
            raise ESPError("You must finish all the necessary steps first, then click on the Save button to finish registration.", log=False)

        cfe = ConfirmationEmailController()
        cfe.send_confirmation_email(request.user, self.program)

        try:
            receipt_text = DBReceipt.objects.get(program=self.program, action='confirm').receipt
            context["request"] = request
            context["program"] = prog
            return HttpResponse( Template(receipt_text).render( Context(context, autoescape=False) ) )
        except DBReceipt.DoesNotExist:
            try:
                receipt = 'program/receipts/'+str(prog.id)+'_custom_receipt.html'
                return render_to_response(receipt, request, context)
            except:
                receipt = 'program/receipts/default.html'
                return render_to_response(receipt, request, context)
 def updatePaid(self, paid=True):
     """ Create an invoice for the student and, if paid is True, create a receipt showing
     that they have paid all of the money they owe for the program. """
     iac = IndividualAccountingController(self.program, self.student)
     if not iac.has_paid():
         iac.ensure_required_transfers()
         if paid:
             iac.submit_payment(iac.amount_due())
Пример #9
0
 def updatePaid(self, paid=True):
     """ Create an invoice for the student and, if paid is True, create a receipt showing
     that they have paid all of the money they owe for the program. """
     iac = IndividualAccountingController(self.program, self.student)
     if not iac.has_paid():
         iac.ensure_required_transfers()
         if paid:
             iac.submit_payment(iac.amount_due())
Пример #10
0
    def test_extracosts(self):
        """ Verify that the "Student Extra Costs" module behaves as specified. """

        program_cost = 25.0

        #   Set up some extra costs options: Item1 (max-qty 1) for $10, Item2 (max-qty 10) for $5, and selectable food
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost, [('Item1', 10, 1), ('Item2', 5, 10)], [('Food', [('Small', 3), ('Large', 7)])])

        #   Choose a random student and check that the extracosts page loads
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get('/learn/%s/extracosts' % self.program.url)
        self.assertEqual(response.status_code, 200)

        #   Check that they are being charged the program admission fee
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that selecting one of the "buy-one" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item1')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 10)

        #   Check that selecting one or more than one of the "buy many" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item2')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '1'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 5)

        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '3'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 15)

        #   Check that selecting an option for a "multiple choice" extra item works
        lit = LineItemType.objects.get(program=self.program, text='Food')
        lio = filter(lambda x: x[2] == 'Large', lit.options)[0]
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'multi%d-option' % lit.id: str(lio[0])})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 7)

        #   Check that financial aid applies to the "full" cost including the extra items
        #   (e.g. we are not forcing financial aid students to pay for food)
        request = FinancialAidRequest.objects.create(user=student, program=self.program)
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)
        fg.delete()

        #   Check that removing items on the form removes their cost for the student
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost)
Пример #11
0
    def test_extracosts(self):
        """ Verify that the "Student Extra Costs" module behaves as specified. """

        program_cost = 25.0

        #   Set up some extra costs options: Item1 (max-qty 1) for $10, Item2 (max-qty 10) for $5, and selectable food
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost, [('Item1', 10, 1), ('Item2', 5, 10)], [('Food', [('Small', 3), ('Large', 7)])])

        #   Choose a random student and check that the extracosts page loads
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get('/learn/%s/extracosts' % self.program.url)
        self.assertEqual(response.status_code, 200)

        #   Check that they are being charged the program admission fee
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that selecting one of the "buy-one" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item1')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 10)

        #   Check that selecting one or more than one of the "buy many" extra items works
        lit = LineItemType.objects.get(program=self.program, text='Item2')
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '1'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 5)

        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '3'})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 15)

        #   Check that selecting an option for a "multiple choice" extra item works
        lit = LineItemType.objects.get(program=self.program, text='Food')
        lio = filter(lambda x: x[2] == 'Large', lit.options)[0]
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'multi%d-option' % lit.id: str(lio[0])})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost + 7)

        #   Check that financial aid applies to the "full" cost including the extra items
        #   (e.g. we are not forcing financial aid students to pay for food)
        request = FinancialAidRequest.objects.create(user=student, program=self.program)
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)
        fg.delete()

        #   Check that removing items on the form removes their cost for the student
        response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {})
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), program_cost)
    def payment_success(self, request, tl, one, two, module, extra, prog):
        """ Receive payment from First Data Global Gateway """

        if request.method == 'GET' or request.POST.get('status',
                                                       '') != 'APPROVED':
            return self.payment_failure(request, tl, one, two, module, extra,
                                        prog)

        #   We should already know what user/program this is for, but it should also be stored.
        iac = IndividualAccountingController(self.program, request.user)
        post_locator = request.POST.get('ponumber', '')
        assert (post_locator == iac.get_id())

        post_identifier = request.POST.get('invoice_number', '')
        #   Optional: The line of code below would check consistency of the user's
        #   invoice items against the ones associated with the payment.
        #   assert(post_identifier == iac.get_identifier())

        post_amount = Decimal(request.POST.get('total', '0.0'))

        #   Warn for possible duplicate payments
        prev_payments = iac.get_transfers().filter(
            line_item=iac.default_payments_lineitemtype())
        if prev_payments.count() > 0 and iac.amount_due() <= 0:
            from django.conf import settings
            recipient_list = [contact[1] for contact in settings.ADMINS]

            subject = 'Possible Duplicate Postback/Payment'
            refs = 'User: %s (%d); Program: %s (%d)' % (request.user.name(
            ), request.user.id, self.program.niceName(), self.program.id)
            refs += '\n\nPrevious payments\' Transfer IDs: ' + (u', '.join(
                [x.id for x in prev_payments]))

            # Send mail!
            send_mail('[ ESP CC ] ' + subject + ' by ' + invoice.user.first_name + ' ' + invoice.user.last_name, \
                  """%s Notification\n--------------------------------- \n\n%s\n\nUser: %s %s (%s)\n\nCardholder: %s\n\nRequest: %s\n\n""" % \
                  (subject, refs, request.user.first_name, request.user.last_name, request.user.id, request.POST.get('bname', '--'), request) , \
                  settings.SERVER_EMAIL, recipient_list, True)

        #   Save the payment as a transfer in the database
        iac.submit_payment(post_amount)

        context = {}
        context['postdata'] = request.POST.copy()
        context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support']
        context['prog'] = prog

        #   Don't redirect to receipt just yet, in case they haven't finished all steps of registration
        #   return HttpResponseRedirect("http://%s/learn/%s/%s/confirmreg" % (request.META['HTTP_HOST'], one, two))
        return render_to_response(self.baseDir() + 'success.html', request,
                                  context)
    def viewpay_cybersource(self, request, tl, one, two, module, extra, prog):
        pac = ProgramAccountingController(prog)
        student_list = list(pac.all_students())
        payment_table = []

        for student in student_list:
            iac = IndividualAccountingController(prog, student)
            payment_table.append((student, iac.get_transfers(),
                                  iac.amount_requested(), iac.amount_due()))

        context = {'program': prog, 'payment_table': payment_table}

        return render_to_response(self.baseDir() + 'viewpay_cybersource.html',
                                  request, context)
    def set_donation_amount(self, request, tl, one, two, module, extra, prog):
        """ Set the student's desired donation amount.
            Creates a line item type for donations if it does not exist. """

        amount_donation = Decimal(request.POST.get('amount', '0'))
        iac = IndividualAccountingController(prog, request.user)
        #   Clear the Transfers by specifying quantity 0
        iac.set_preference('Donation to Learning Unlimited', 0)
        if amount_donation != Decimal('0'):
            #   Specify quantity 1 and the desired amount
            iac.set_preference('Donation to Learning Unlimited', 1, amount=amount_donation)

        data = {'amount_donation': amount_donation, 'amount_due': iac.amount_due()}
        return HttpResponse(json.dumps(data), content_type='application/json')
Пример #15
0
    def paiditems(self, request, tl, one, two, module, extra, prog):

        #   Get a user
        user, found = search_for_user(request)
        if not found:
            return user

        #   Get the optional purchases for that user
        iac = IndividualAccountingController(prog, user)
        context = {}
        context['student'] = user
        context['requireditems'] = iac.get_transfers(required_only=True)
        context['reserveditems'] = iac.get_transfers(optional_only=True)
        context['amount_requested'] = iac.amount_requested()
        context['amount_finaid'] = iac.amount_finaid()
        context['amount_due'] = iac.amount_due()

        return render_to_response(self.baseDir()+'paiditems.html', request, context)
Пример #16
0
    def set_donation_amount(self, request, tl, one, two, module, extra, prog):
        """ Set the student's desired donation amount.
            Creates a line item type for donations if it does not exist. """

        amount_donation = Decimal(request.POST.get('amount', '0'))
        iac = IndividualAccountingController(prog, request.user)
        #   Clear the Transfers by specifying quantity 0
        iac.set_preference('Donation to Learning Unlimited', 0)
        if amount_donation != Decimal('0'):
            #   Specify quantity 1 and the desired amount
            iac.set_preference('Donation to Learning Unlimited',
                               1,
                               amount=amount_donation)

        data = {
            'amount_donation': amount_donation,
            'amount_due': iac.amount_due()
        }
        return HttpResponse(json.dumps(data), content_type='application/json')
Пример #17
0
    def donation(self, request, tl, one, two, module, extra, prog):

        user = request.user

        iac = IndividualAccountingController(self.program, user)

        # It's unclear if we support changing line item preferences after
        # credit card payment has occured. For now, just do the same thing we
        # do in other accounting modules, and don't allow changes after payment
        # has occured.
        if iac.amount_due() <= 0:
            raise ESPError("You've already paid for this program.  Please make any further changes on-site so that we can charge or refund you properly.", log=False)

        # Donations and non-donations go through different code paths. If a
        # user chooses to make a donation, set_donation_amount() is called via
        # an AJAX request. If a user chooses not to make a donation, their
        # browser makes a request to the studentreg main page. Therefore, it is
        # impossible set the donation_done Record after the user is actually
        # done with the module. So our only option is to mark them done when
        # they first visit the page. This should be fine, since donations are
        # always optional. If we really care about being correct, if we switch
        # this page to not use AJAX but instead use a normal form submission,
        # we can then switch to granting the Record after the user is done with
        # the page.
        Record.objects.get_or_create(user=user, program=self.program, event=self.event)

        context = {}
        context['module'] = self
        context['program'] = prog
        context['user'] = user

        #   Load donation amount separately, since the client-side code needs to know about it separately.
        donation_prefs = iac.get_preferences([self.line_item_type(),])
        if donation_prefs:
            context['amount_donation'] = Decimal(donation_prefs[0][2])
            context['has_donation'] = True
        else:
            context['amount_donation'] = Decimal('0.00')
            context['has_donation'] = False

        context['institution'] = settings.INSTITUTION_NAME
        return render_to_response(self.baseDir() + 'donation.html', request, context)
Пример #18
0
    def paiditems(self, request, tl, one, two, module, extra, prog):

        #   Get a user
        filterObj, found = UserSearchController().create_filter(
            request, self.program)
        if not found:
            return filterObj
        user = filterObj.getList(ESPUser).distinct()[0]

        #   Get the optional purchases for that user
        iac = IndividualAccountingController(prog, user)
        context = {}
        context['student'] = user
        context['requireditems'] = iac.get_transfers(required_only=True)
        context['reserveditems'] = iac.get_transfers(optional_only=True)
        context['amount_requested'] = iac.amount_requested()
        context['amount_finaid'] = iac.amount_finaid()
        context['amount_due'] = iac.amount_due()

        return render_to_response(self.baseDir() + 'paiditems.html', request,
                                  context)
Пример #19
0
    def test_finaid(self):
        """ Verify that financial aid behaves as specified. """

        program_cost = 25.0

        #   Set the cost of the program
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost)

        #   Choose a random student and sign up for a class
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        sec = random.choice(self.program.sections())
        sec.preregister_student(student)

        #   Check that the student owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Apply for financial aid
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get(
                    '/learn/%s/finaid' % self.program.url,
                    **{'wsgi.url_scheme': 'https'})
        self.assertEqual(response.status_code, 200)

        form_settings = {
            'reduced_lunch': '',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])

        #   Check that the student still owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Have an admin approve the financial aid app and check a few different cases:
        request = FinancialAidRequest.objects.get(user=student, program=self.program)

        #   - 100 percent
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)

        #   - absolute discount amount
        fg.percent = None
        fg.amount_max_dec = Decimal('15.0')
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost - 15.0)

        #   - discount percentage
        fg.amount_max_dec = None
        fg.percent = 50
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost / 2)

        #   Check that deleting the financial aid grant restores original program cost
        fg.delete()
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that the 'free/reduced lunch' option on the finaid results in zero amount due
        form_settings = {
            'reduced_lunch': 'checked',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), 0)
Пример #20
0
    def viewpay_cybersource(self, request, tl, one, two, module, extra, prog):
        pac = ProgramAccountingController(prog)
        student_list = list(pac.all_students())
        payment_table = []

        #   Fetch detailed information for every student associated with the program
        for student in student_list:
            iac = IndividualAccountingController(prog, student)
            payment_table.append((student, iac.get_transfers(), iac.amount_requested(), iac.amount_due()))

        #   Also fetch summary information about the payments
        (num_payments, total_payment) = pac.payments_summary()

        context = {
            'program': prog,
            'payment_table': payment_table,
            'num_students': len(student_list),
            'num_payments': num_payments,
            'total_payment': total_payment,
        }

        return render_to_response(self.baseDir() + 'viewpay_cybersource.html', request, context)
 def _payment_table_row_cached(prog, student):
     iac = IndividualAccountingController(prog, student)
     return (student, iac.get_transfers(), iac.amount_requested(),
             iac.amount_due())
Пример #22
0
 def have_paid(self):
     iac = IndividualAccountingController(self.program,
                                          get_current_request().user)
     return (iac.amount_due() <= 0)
 def have_paid(self):
     iac = IndividualAccountingController(self.program, get_current_request().user)
     return (iac.amount_due() <= 0)
    def charge_payment(self, request, tl, one, two, module, extra, prog):
        #   Check for setup of module.  This is also required to initialize settings.
        self.check_setup()

        context = {'postdata': request.POST.copy()}

        group_name = Tag.getTag('full_group_name') or '%s %s' % (settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME)

        iac = IndividualAccountingController(self.program, request.user)

        #   Set Stripe key based on settings.  Also require the API version
        #   which our code is designed for.
        stripe.api_key = self.settings['secret_key']
        # We are using the 2014-03-13 version of the Stripe API, which is
        # v1.12.2.
        stripe.api_version = '2014-03-13'

        if request.POST.get('ponumber', '') != iac.get_id():
            #   If we received a payment for the wrong PO:
            #   This is not a Python exception, but an error nonetheless.
            context['error_type'] = 'inconsistent_po'
            context['error_info'] = {'request_po': request.POST.get('ponumber', ''), 'user_po': iac.get_id()}

        if 'error_type' not in context:
            #   Check the amount in the POST against the amount in our records.
            #   If they don't match, raise an error.
            amount_cents_post = Decimal(request.POST['totalcost_cents'])
            amount_cents_iac = Decimal(iac.amount_due()) * 100
            if amount_cents_post != amount_cents_iac:
                context['error_type'] = 'inconsistent_amount'
                context['error_info'] = {
                    'amount_cents_post': amount_cents_post,
                    'amount_cents_iac':  amount_cents_iac,
                }

        if 'error_type' not in context:
            try:
                with transaction.atomic():
                    # Save a record of the charge if we can uniquely identify the user/program.
                    # If this causes an error, the user will get a 500 error
                    # page, and the card will NOT be charged.
                    # If an exception is later raised by
                    # stripe.Charge.create(), then the transaction will be
                    # rolled back.
                    # Thus, we will never be in a state where the card has been
                    # charged without a record being created on the site, nor
                    # vice-versa.
                    totalcost_dollars = Decimal(request.POST['totalcost_cents']) / 100

                    #   Create a record of the transfer without the transaction ID.
                    transfer = iac.submit_payment(totalcost_dollars, 'TBD')

                    # Create the charge on Stripe's servers - this will charge
                    # the user's card.
                    charge = stripe.Charge.create(
                        amount=amount_cents_post,
                        currency="usd",
                        card=request.POST['stripeToken'],
                        description="Payment for %s %s - %s" % (group_name, prog.niceName(), request.user.name()),
                        statement_descriptor=group_name[0:22], #stripe limits statement descriptors to 22 characters
                        metadata={
                            'ponumber': request.POST['ponumber'],
                        },
                    )

                    #   Now that the charge has been performed by Stripe, save its
                    #   transaction ID for our records.
                    transfer.transaction_id = charge.id
                    transfer.save()
            except stripe.error.CardError, e:
                context['error_type'] = 'declined'
                context['error_info'] = e.json_body['error']
            except stripe.error.InvalidRequestError, e:
                #   While this is a generic error meaning invalid parameters were supplied
                #   to Stripe's API, we will usually see it because of a duplicate request.
                context['error_type'] = 'invalid'
 def have_paid(self, user):
     """ Whether the user has paid for this program.  """
     iac = IndividualAccountingController(self.program, user)
     return (iac.amount_due() <= 0)
Пример #26
0
 def have_paid(self, user):
     """ Whether the user has paid for this program.  """
     iac = IndividualAccountingController(self.program, user)
     return (iac.amount_due() <= 0)
Пример #27
0
    def confirmreg_forreal(self, request, tl, one, two, module, extra, prog,
                           new_reg):
        """ The page that is shown once the user saves their student reg,
            giving them the option of printing a confirmation            """
        self.request = request

        from esp.program.modules.module_ext import DBReceipt

        iac = IndividualAccountingController(prog, request.user)

        context = {}
        context['one'] = one
        context['two'] = two

        context['itemizedcosts'] = iac.get_transfers()

        user = ESPUser(request.user)
        context['finaid'] = user.hasFinancialAid(prog)
        if user.appliedFinancialAid(prog):
            context['finaid_app'] = user.financialaidrequest_set.filter(
                program=prog).order_by('-id')[0]
        else:
            context['finaid_app'] = None
        context['balance'] = iac.amount_due()

        context['owe_money'] = (context['balance'] != Decimal("0.0"))

        if prog.isFull() and not user.canRegToFullProgram(
                prog) and not self.program.isConfirmed(user):
            raise ESPError(
                log=False
            ), "This program has filled!  It can't accept any more students.  Please try again next session."

        modules = prog.getModules(request.user, tl)
        completedAll = True
        for module in modules:
            if hasattr(module, 'onConfirm'):
                module.onConfirm(request)
            if not module.isCompleted() and module.required:
                completedAll = False
            context = module.prepare(context)

        if completedAll:
            if new_reg:
                rec = Record.objects.create(user=user,
                                            event="reg_confirmed",
                                            program=prog)
        else:
            raise ESPError(
                False
            ), "You must finish all the necessary steps first, then click on the Save button to finish registration."

        cfe = ConfirmationEmailController()
        cfe.send_confirmation_email(request.user, self.program)

        try:
            receipt_text = DBReceipt.objects.get(program=self.program,
                                                 action='confirm').receipt
            context["request"] = request
            context["program"] = prog
            return HttpResponse(
                Template(receipt_text).render(
                    Context(context, autoescape=False)))
        except DBReceipt.DoesNotExist:
            try:
                receipt = 'program/receipts/' + str(
                    prog.id) + '_custom_receipt.html'
                return render_to_response(receipt, request, context)
            except:
                receipt = 'program/receipts/default.html'
                return render_to_response(receipt, request, context)
Пример #28
0
    def charge_payment(self, request, tl, one, two, module, extra, prog):
        #   Check for setup of module.  This is also required to initialize settings.
        self.check_setup()

        context = {'postdata': request.POST.copy()}

        group_name = Tag.getTag('full_group_name') or '%s %s' % (
            settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME)

        iac = IndividualAccountingController(self.program, request.user)

        #   Set Stripe key based on settings.  Also require the API version
        #   which our code is designed for.
        stripe.api_key = self.settings['secret_key']
        # We are using the 2014-03-13 version of the Stripe API, which is
        # v1.12.2.
        stripe.api_version = '2014-03-13'

        if request.POST.get('ponumber', '') != iac.get_id():
            #   If we received a payment for the wrong PO:
            #   This is not a Python exception, but an error nonetheless.
            context['error_type'] = 'inconsistent_po'
            context['error_info'] = {
                'request_po': request.POST.get('ponumber', ''),
                'user_po': iac.get_id()
            }

        if 'error_type' not in context:
            #   Check the amount in the POST against the amount in our records.
            #   If they don't match, raise an error.
            amount_cents_post = Decimal(request.POST['totalcost_cents'])
            amount_cents_iac = Decimal(iac.amount_due()) * 100
            if amount_cents_post != amount_cents_iac:
                context['error_type'] = 'inconsistent_amount'
                context['error_info'] = {
                    'amount_cents_post': amount_cents_post,
                    'amount_cents_iac': amount_cents_iac,
                }

        if 'error_type' not in context:
            try:
                with transaction.atomic():
                    # Save a record of the charge if we can uniquely identify the user/program.
                    # If this causes an error, the user will get a 500 error
                    # page, and the card will NOT be charged.
                    # If an exception is later raised by
                    # stripe.Charge.create(), then the transaction will be
                    # rolled back.
                    # Thus, we will never be in a state where the card has been
                    # charged without a record being created on the site, nor
                    # vice-versa.
                    totalcost_dollars = Decimal(
                        request.POST['totalcost_cents']) / 100

                    #   Create a record of the transfer without the transaction ID.
                    transfer = iac.submit_payment(totalcost_dollars, 'TBD')

                    # Create the charge on Stripe's servers - this will charge
                    # the user's card.
                    charge = stripe.Charge.create(
                        amount=amount_cents_post,
                        currency="usd",
                        card=request.POST['stripeToken'],
                        description="Payment for %s %s - %s" %
                        (group_name, prog.niceName(), request.user.name()),
                        statement_descriptor=group_name[
                            0:
                            22],  #stripe limits statement descriptors to 22 characters
                        metadata={
                            'ponumber': request.POST['ponumber'],
                        },
                    )

                    #   Now that the charge has been performed by Stripe, save its
                    #   transaction ID for our records.
                    transfer.transaction_id = charge.id
                    transfer.save()
            except stripe.error.CardError, e:
                context['error_type'] = 'declined'
                context['error_info'] = e.json_body['error']
            except stripe.error.InvalidRequestError, e:
                #   While this is a generic error meaning invalid parameters were supplied
                #   to Stripe's API, we will usually see it because of a duplicate request.
                context['error_type'] = 'invalid'
Пример #29
0
    def payonline(self, request, tl, one, two, module, extra, prog):

        #   Check that the user has completed all required modules so that they
        #   are "finished" registering for the program.  (In other words, they
        #   should be registered for at least one class, and filled out other
        #   required forms, before paying by credit card.)
        modules = prog.getModules(request.user, tl)
        completedAll = True
        for module in modules:
            if not module.isCompleted() and module.required:
                completedAll = False
        if not completedAll and not request.user.isAdmin(prog):
            raise ESPError(
                "Please go back and ensure that you have completed all required steps of registration before paying by credit card.",
                log=False)

        #   Check for setup of module.  This is also required to initialize settings.
        self.check_setup()

        user = request.user

        iac = IndividualAccountingController(self.program, request.user)
        context = {}
        context['module'] = self
        context['program'] = prog
        context['user'] = user
        context['invoice_id'] = iac.get_id()
        context['identifier'] = iac.get_identifier()
        payment_type = iac.default_payments_lineitemtype()
        sibling_type = iac.default_siblingdiscount_lineitemtype()
        grant_type = iac.default_finaid_lineitemtype()
        offer_donation = self.settings['offer_donation']
        donate_type = iac.get_lineitemtypes().get(
            text=self.settings['donation_text']) if offer_donation else None
        context['itemizedcosts'] = iac.get_transfers().exclude(
            line_item__in=filter(
                None, [payment_type, sibling_type, grant_type, donate_type
                       ])).order_by('-line_item__required')
        context['itemizedcosttotal'] = iac.amount_due()
        #   This amount should be formatted as an integer in order to be
        #   accepted by Stripe.
        context['totalcost_cents'] = int(context['itemizedcosttotal'] * 100)
        context['subtotal'] = iac.amount_requested()
        context['financial_aid'] = iac.amount_finaid()
        context['sibling_discount'] = iac.amount_siblingdiscount()
        context['amount_paid'] = iac.amount_paid()

        #   Load donation amount separately, since the client-side code needs to know about it separately.
        donation_prefs = iac.get_preferences([
            donate_type,
        ]) if offer_donation else None
        if donation_prefs:
            context['amount_donation'] = Decimal(donation_prefs[0][2])
            context['has_donation'] = True
        else:
            context['amount_donation'] = Decimal('0.00')
            context['has_donation'] = False
        context['amount_without_donation'] = context[
            'itemizedcosttotal'] - context['amount_donation']

        if 'HTTP_HOST' in request.META:
            context['hostname'] = request.META['HTTP_HOST']
        else:
            context['hostname'] = Site.objects.get_current().domain
        context['institution'] = settings.INSTITUTION_NAME
        context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support']

        return render_to_response(self.baseDir() + 'cardpay.html', request,
                                  context)
Пример #30
0
    def test_finaid(self):
        """ Verify that financial aid behaves as specified. """

        program_cost = 25.0

        #   Set the cost of the program
        pac = ProgramAccountingController(self.program)
        pac.clear_all_data()
        pac.setup_accounts()
        pac.setup_lineitemtypes(program_cost)

        #   Choose a random student and sign up for a class
        student = random.choice(self.students)
        iac = IndividualAccountingController(self.program, student)
        sec = random.choice(self.program.sections())
        sec.preregister_student(student)

        #   Check that the student owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Apply for financial aid
        self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username )
        response = self.client.get(
                    '/learn/%s/finaid' % self.program.url,
                    **{'wsgi.url_scheme': 'https'})
        self.assertEqual(response.status_code, 200)

        form_settings = {
            'reduced_lunch': '',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])

        #   Check that the student still owes the cost of the program
        self.assertEqual(iac.amount_due(), program_cost)

        #   Have an admin approve the financial aid app and check a few different cases:
        request = FinancialAidRequest.objects.get(user=student, program=self.program)

        #   - 100 percent
        (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100)
        self.assertEqual(iac.amount_due(), 0.0)

        #   - absolute discount amount
        fg.percent = None
        fg.amount_max_dec = Decimal('15.0')
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost - 15.0)

        #   - discount percentage
        fg.amount_max_dec = None
        fg.percent = 50
        fg.save()
        self.assertEqual(iac.amount_due(), program_cost / 2)

        #   Check that deleting the financial aid grant restores original program cost
        fg.delete()
        self.assertEqual(iac.amount_due(), program_cost)

        #   Check that the 'free/reduced lunch' option on the finaid results in zero amount due
        form_settings = {
            'reduced_lunch': 'checked',
            'household_income': '12345',
            'extra_explaination': 'No',
            'student_prepare': '',
        }
        response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings)
        self.assertEqual(response.status_code, 302)
        self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location'])
        self.assertEqual(iac.amount_due(), 0)