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)
Пример #2
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 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)
Пример #4
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)
Пример #5
0
    def finalize(self):
        #   Create a transfer for the amount of this grant
        if self.finalized:
            return

        from esp.accounting.controllers import IndividualAccountingController
        iac = IndividualAccountingController(self.program, self.user)
        source_account = iac.default_finaid_account()
        dest_account = iac.default_source_account()
        line_item_type = iac.default_finaid_lineitemtype()

        (transfer, created) = Transfer.objects.get_or_create(source=source_account, destination=dest_account, user=self.user, line_item=line_item_type, amount_dec=iac.amount_finaid())
        self.finalized = True
        self.save()
        return transfer
Пример #6
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())
 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())
Пример #8
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)
Пример #9
0
    def register_student(self, request, tl, one, two, module, extra, prog):
        resp = HttpResponse(content_type='application/json')
        program = self.program
        success = False
        student = get_object_or_404(ESPUser,pk=request.POST.get("student_id"))

        registration_profile = RegistrationProfile.getLastForProgram(student,
                                                                program)
        success = registration_profile.student_info is not None

        if success:
            registration_profile.save()

            for extension in ['paid','Attended','medical','liability','OnSite']:
                Record.createBit(extension, program, student)

            IndividualAccountingController.updatePaid(self.program, student, paid=True)

        json.dump({'status':success}, resp)
        return resp
    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)
Пример #11
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)
Пример #12
0
def submit_transaction(request):
    #   We might also need to forward post variables to http://shopmitprd.mit.edu/controller/index.php?action=log_transaction

    if request.POST.has_key(
            "decision") and request.POST["decision"] != "REJECT":

        #   Figure out which user and program the payment are for.
        post_identifier = request.POST.get('merchantDefinedData1', '')
        iac = IndividualAccountingController.from_identifier(post_identifier)
        post_amount = Decimal(request.POST.get('orderAmount', '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]
            recipient_list.append(settings.DEFAULT_EMAIL_ADDRESSES['treasury'])
            refs = 'Cybersource request ID: %s' % post_identifier

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

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

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

        tl = 'learn'
        one, two = iac.program.url.split('/')
        destination = Tag.getProgramTag("cc_redirect",
                                        iac.program,
                                        default="confirmreg")

        if destination.startswith('/') or '//' in destination:
            pass
        else:
            # simple urls like 'confirmreg' are relative to the program
            destination = "/%s/%s/%s/%s" % (tl, one, two, destination)

        return HttpResponseRedirect(destination)

    return render_to_response('accounting_docs/credit_rejected.html', request,
                              {})
    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')
Пример #14
0
def _submit_transaction(request, log_record):
    decision = request.POST['decision']
    if decision == "ACCEPT":
        # Handle payment
        identifier = request.POST['req_merchant_defined_data1']
        amount_paid = Decimal(request.POST['req_amount'])
        transaction_id = request.POST['transaction_id']

        payment = IndividualAccountingController.record_payment_from_identifier(
            identifier, amount_paid, transaction_id)

        # Link payment to log record
        log_record.transfer = payment
        log_record.save()

        return _redirect_from_identifier(identifier, "success")
    elif decision == "DECLINE":
        identifier = request.POST['req_merchant_defined_data1']
        return _redirect_from_identifier(identifier, "declined")
    else:
        raise NotImplementedError("Can't handle decision: %s" % decision)
Пример #15
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')
Пример #16
0
def _submit_transaction(request, log_record):
    decision = request.POST['decision']
    if decision == "ACCEPT":
        # Handle payment
        identifier = request.POST['req_merchant_defined_data1']
        amount_paid = Decimal(request.POST['req_amount'])
        transaction_id = request.POST['transaction_id']

        payment = IndividualAccountingController.record_payment_from_identifier(
            identifier, amount_paid, transaction_id)

        # Link payment to log record
        log_record.transfer = payment
        log_record.save()

        return _redirect_from_identifier(identifier, "success")
    elif decision == "DECLINE":
        identifier = request.POST['req_merchant_defined_data1']
        return _redirect_from_identifier(identifier, "declined")
    else:
        raise NotImplementedError("Can't handle decision: %s" % decision)
Пример #17
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)
Пример #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)
    def extracosts(self,request, tl, one, two, module, extra, prog):
        """
        Query the user for any extra items they may wish to purchase for this program

        This module should ultimately deal with things like optional lab fees, etc.
        Right now it doesn't.
        """
        if self.have_paid():
            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)

        #   Determine which line item types we will be asking about
        iac = IndividualAccountingController(self.program, get_current_request().user)
        costs_list = iac.get_lineitemtypes(optional_only=True).filter(max_quantity__lte=1, lineitemoptions__isnull=True)
        multicosts_list = iac.get_lineitemtypes(optional_only=True).filter(max_quantity__gt=1, lineitemoptions__isnull=True)
        multiselect_list = iac.get_lineitemtypes(optional_only=True).filter(lineitemoptions__isnull=False)

        #   Fetch the user's current preferences
        prefs = iac.get_preferences()

        forms_all_valid = True

        ## Another dirty hack, left as an exercise to the reader
        if request.method == 'POST':

            #   Initialize a list of forms using the POST data
            costs_db = [ { 'LineItemType': x,
                           'CostChoice': CostItem(request.POST, prefix="%s" % x.id) }
                         for x in costs_list ] + \
                         [ x for x in \
                           [ { 'LineItemType': x,
                               'CostChoice': MultiCostItem(request.POST, prefix="%s" % x.id) }
                             for x in multicosts_list ] \
                           if x['CostChoice'].is_valid() and 'cost' in x['CostChoice'].cleaned_data ] + \
                           [ { 'LineItemType': x,
                               'CostChoice': MultiSelectCostItem(request.POST, prefix="multi%s" % x.id,
                                                     choices=x.option_choices,
                                                     required=(x.required)) }
                             for x in multiselect_list ]

            #   Get a list of the (line item, quantity) pairs stored in the forms
            #   as well as a list of line items which had invalid forms
            form_prefs = []
            preserve_items = []
            for item in costs_db:
                form = item['CostChoice']
                lineitem_type = item['LineItemType']
                if form.is_valid():
                    if isinstance(form, CostItem):
                        if form.cleaned_data['cost'] is True:
                            form_prefs.append((lineitem_type.text, 1, lineitem_type.amount, None))
                    elif isinstance(form, MultiCostItem):
                        if form.cleaned_data['cost'] is True:
                            form_prefs.append((lineitem_type.text, form.cleaned_data['count'], lineitem_type.amount, None))
                    elif isinstance(form, MultiSelectCostItem):
                        if form.cleaned_data['option']:
                            form_prefs.append((lineitem_type.text, 1, None, int(form.cleaned_data['option'])))
                else:
                    #   Preserve selected quantity for any items that we don't have a valid form for
                    preserve_items.append(lineitem_type.text)
                    forms_all_valid = False

            #   Merge previous and new preferences (update only if the form was valid)
            new_prefs = []
            for lineitem_name in preserve_items:
                if lineitem_name in map(lambda x: x[0], prefs):
                    new_prefs.append(prefs[map(lambda x: x[0], prefs).index(lineitem_name)])
            new_prefs += form_prefs

            iac.apply_preferences(new_prefs)

            #   Redirect to main student reg page if all data was recorded properly
            #   (otherwise, the code below will reload the page)
            if forms_all_valid:
                bit, created = Record.objects.get_or_create(user=request.user, program=self.program, event=self.event)
                return self.goToCore(tl)

        count_map = {}
        for lineitem_type in iac.get_lineitemtypes(optional_only=True):
            count_map[lineitem_type.text] = [lineitem_type.id, 0, None, None]
        for item in iac.get_preferences():
            for i in range(1, 4):
                count_map[item[0]][i] = item[i]
        forms = [ { 'form': CostItem( prefix="%s" % x.id, initial={'cost': (count_map[x.text][1] > 0) } ),
                    'LineItem': x }
                  for x in costs_list ] + \
                  [ { 'form': MultiCostItem( prefix="%s" % x.id, initial={'cost': (count_map[x.text][1] > 0), 'count': count_map[x.text][1] } ),
                      'LineItem': x }
                    for x in multicosts_list ] + \
                    [ { 'form': MultiSelectCostItem( prefix="multi%s" % x.id,
                                                     initial={'option': count_map[x.text][3]},
                                                     choices=x.option_choices,
                                                     required=(x.required)),
                        'LineItem': x }
                      for x in multiselect_list ]

        return render_to_response(self.baseDir()+'extracosts.html',
                                  request,
                                  { 'errors': not forms_all_valid, 'forms': forms, 'financial_aid': request.user.hasFinancialAid(prog), 'select_qty': len(multicosts_list) > 0 })
Пример #20
0
def _redirect_from_identifier(identifier, result):
    program = IndividualAccountingController.program_from_identifier(identifier)
    destination = "/learn/%s/cybersource?result=%s" % (program.getUrlBase(), result)
    return HttpResponseRedirect(destination)
Пример #21
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)
    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)
Пример #23
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'
Пример #24
0
    def extracosts(self, request, tl, one, two, module, extra, prog):
        """
        Query the user for any extra items they may wish to purchase for this program

        This module should ultimately deal with things like optional lab fees, etc.
        Right now it doesn't.
        """
        if self.have_paid():
            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)

        #   Determine which line item types we will be asking about
        iac = IndividualAccountingController(self.program,
                                             get_current_request().user)
        costs_list = iac.get_lineitemtypes(optional_only=True).filter(
            max_quantity__lte=1, lineitemoptions__isnull=True)
        multicosts_list = iac.get_lineitemtypes(optional_only=True).filter(
            max_quantity__gt=1, lineitemoptions__isnull=True)
        multiselect_list = iac.get_lineitemtypes(optional_only=True).filter(
            lineitemoptions__isnull=False)

        #   Fetch the user's current preferences
        prefs = iac.get_preferences()

        forms_all_valid = True

        ## Another dirty hack, left as an exercise to the reader
        if request.method == 'POST':

            #   Initialize a list of forms using the POST data
            costs_db = [ { 'LineItemType': x,
                           'CostChoice': CostItem(request.POST, prefix="%s" % x.id) }
                         for x in costs_list ] + \
                         [ x for x in \
                           [ { 'LineItemType': x,
                               'CostChoice': MultiCostItem(request.POST, prefix="%s" % x.id) }
                             for x in multicosts_list ] \
                           if x['CostChoice'].is_valid() and 'cost' in x['CostChoice'].cleaned_data ] + \
                           [ { 'LineItemType': x,
                               'CostChoice': MultiSelectCostItem(request.POST, prefix="multi%s" % x.id,
                                                     choices=x.option_choices,
                                                     required=(x.required)) }
                             for x in multiselect_list ]

            #   Get a list of the (line item, quantity) pairs stored in the forms
            #   as well as a list of line items which had invalid forms
            form_prefs = []
            preserve_items = []
            for item in costs_db:
                form = item['CostChoice']
                lineitem_type = item['LineItemType']
                if form.is_valid():
                    if isinstance(form, CostItem):
                        if form.cleaned_data['cost'] is True:
                            form_prefs.append((lineitem_type.text, 1,
                                               lineitem_type.amount, None))
                    elif isinstance(form, MultiCostItem):
                        if form.cleaned_data['cost'] is True:
                            form_prefs.append((lineitem_type.text,
                                               form.cleaned_data['count'],
                                               lineitem_type.amount, None))
                    elif isinstance(form, MultiSelectCostItem):
                        if form.cleaned_data['option']:
                            form_prefs.append(
                                (lineitem_type.text, 1, None,
                                 int(form.cleaned_data['option'])))
                else:
                    #   Preserve selected quantity for any items that we don't have a valid form for
                    preserve_items.append(lineitem_type.text)
                    forms_all_valid = False

            #   Merge previous and new preferences (update only if the form was valid)
            new_prefs = []
            for lineitem_name in preserve_items:
                if lineitem_name in map(lambda x: x[0], prefs):
                    new_prefs.append(prefs[map(lambda x: x[0],
                                               prefs).index(lineitem_name)])
            new_prefs += form_prefs

            iac.apply_preferences(new_prefs)

            #   Redirect to main student reg page if all data was recorded properly
            #   (otherwise, the code below will reload the page)
            if forms_all_valid:
                bit, created = Record.objects.get_or_create(
                    user=request.user, program=self.program, event=self.event)
                return self.goToCore(tl)

        count_map = {}
        for lineitem_type in iac.get_lineitemtypes(optional_only=True):
            count_map[lineitem_type.text] = [lineitem_type.id, 0, None, None]
        for item in iac.get_preferences():
            for i in range(1, 4):
                count_map[item[0]][i] = item[i]
        forms = [ { 'form': CostItem( prefix="%s" % x.id, initial={'cost': (count_map[x.text][1] > 0) } ),
                    'LineItem': x }
                  for x in costs_list ] + \
                  [ { 'form': MultiCostItem( prefix="%s" % x.id, initial={'cost': (count_map[x.text][1] > 0), 'count': count_map[x.text][1] } ),
                      'LineItem': x }
                    for x in multicosts_list ] + \
                    [ { 'form': MultiSelectCostItem( prefix="multi%s" % x.id,
                                                     initial={'option': count_map[x.text][3]},
                                                     choices=x.option_choices,
                                                     required=(x.required)),
                        'LineItem': x }
                      for x in multiselect_list ]

        return render_to_response(
            self.baseDir() + 'extracosts.html', request, {
                'errors': not forms_all_valid,
                'forms': forms,
                'financial_aid': request.user.hasFinancialAid(prog),
                'select_qty': len(multicosts_list) > 0
            })
 def _payment_table_row_cached(prog, student):
     iac = IndividualAccountingController(prog, student)
     return (student, iac.get_transfers(), iac.amount_requested(),
             iac.amount_due())
Пример #26
0
    def finaid_app(self, request, tl, one, two, module, extra, prog):
        """
        A way for a student to apply for financial aid.
        """
        from datetime import datetime
        from esp.dbmail.models import send_mail

        app, created = FinancialAidRequest.objects.get_or_create(
            user=request.user, program=self.program)

        class Form(forms.ModelForm):
            class Meta:
                model = FinancialAidRequest
                tag_data = Tag.getTag('finaid_form_fields')
                if tag_data:
                    fields = tuple(tag_data.split(','))

        if request.method == 'POST':
            form = Form(request.POST, initial=app.__dict__)
            if form.is_valid():
                app.__dict__.update(form.cleaned_data)

                if not request.POST.has_key('submitform') or request.POST[
                        'submitform'].lower() == 'complete':
                    app.done = True
                elif request.POST['submitform'].lower(
                ) == 'mark as incomplete' or request.POST['submitform'].lower(
                ) == 'save progress':
                    app.done = False
                else:
                    raise ESPError(
                    ), "Our server lost track of whether or not you were finished filling out this form.  Please go back and click 'Complete' or 'Mark as Incomplete'."

                app.save()

                # Automatically accept apps for people with subsidized lunches
                # Send an e-mail announcing the application either way
                date_str = str(datetime.now())
                iac = IndividualAccountingController(self.program,
                                                     request.user)
                if app.reduced_lunch:
                    iac.grant_full_financial_aid()
                    subj_str = '%s %s received Financial Aid for %s' % (
                        request.user.first_name, request.user.last_name,
                        prog.niceName())
                    msg_str = "\n%s %s received Financial Aid for %s on %s, for stating that they receive a free or reduced-price lunch."
                else:
                    subj_str = '%s %s applied for Financial Aid for %s' % (
                        request.user.first_name, request.user.last_name,
                        prog.niceName())
                    msg_str = "\n%s %s applied for Financial Aid for %s on %s, but did not state that they receive a free or reduced-price lunch."
                send_mail(
                    subj_str,
                    (msg_str + """

Here is their form data:

========================================
Program:  %s
User:  %s %s <%s>
Approved:  %s
Has Reduced Lunch:  %s
Household Income:  $%s
Form Was Filled Out by Non-Student:  %s
Extra Explanation:
%s

========================================

This request can be (re)viewed at:
<http://%s/admin/program/financialaidrequest/%s/>


""") % (
                        request.user.first_name,
                        request.user.last_name,
                        prog.niceName(),
                        date_str,
                        str(app.program),
                        request.user.first_name,
                        request.user.last_name,
                        str(app.user),
                        date_str,
                        str(app.reduced_lunch),
                        str(app.household_income),
                        str(app.student_prepare),
                        app.extra_explaination,
                        settings.DEFAULT_HOST,  # server hostname
                        str(app.id)),
                    settings.SERVER_EMAIL,
                    [prog.getDirectorConfidentialEmail()])

                return self.goToCore(tl)

        else:
            form = Form(initial=app.__dict__)

        return render_to_response(self.baseDir() + 'application.html', request,
                                  {
                                      'form': form,
                                      'app': app
                                  })
Пример #27
0
def _redirect_from_identifier(identifier, result):
    program = IndividualAccountingController.program_from_identifier(
        identifier)
    destination = "/learn/%s/cybersource?result=%s" % (program.getUrlBase(),
                                                       result)
    return HttpResponseRedirect(destination)
Пример #28
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)
Пример #29
0
 def have_paid(self, user):
     """ Whether the user has paid for this program.  """
     iac = IndividualAccountingController(self.program, user)
     return (iac.amount_due() <= 0)
Пример #30
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)
    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)
Пример #32
0
    def onsite_create(self, request, tl, one, two, module, extra, prog):
        if request.method == 'POST':
            form = OnSiteRegForm(request.POST)

            if form.is_valid():
                new_data = form.cleaned_data
                username = ESPUser.get_unused_username(new_data['first_name'], new_data['last_name'])
                new_user = ESPUser.objects.create_user(username = username,
                                first_name = new_data['first_name'],
                                last_name  = new_data['last_name'],
                                email      = new_data['email'])

                self.student = new_user

                regProf = RegistrationProfile.getLastForProgram(new_user,
                                                                self.program)
                contact_user = ContactInfo(first_name = new_user.first_name,
                                           last_name  = new_user.last_name,
                                           e_mail     = new_user.email,
                                           user       = new_user)
                contact_user.save()
                regProf.contact_user = contact_user

                student_info = StudentInfo(user = new_user, graduation_year = ESPUser.YOGFromGrade(new_data['grade'], ESPUser.program_schoolyear(self.program)))

                try:
                    if isinstance(new_data['k12school'], K12School):
                        student_info.k12school = new_data['k12school']
                    else:
                        if isinstance(new_data['k12school'], int):
                            student_info.k12school = K12School.objects.get(id=int(new_data['k12school']))
                        else:
                            student_info.k12school = K12School.objects.filter(name__icontains=new_data['k12school'])[0]
                except:
                    student_info.k12school = None
                student_info.school = new_data['school'] if not student_info.k12school else student_info.k12school.name

                student_info.save()
                regProf.student_info = student_info

                regProf.save()

                if new_data['paid']:
                    Record.createBit('paid', self.program, self.user)
                    IndividualAccountingController.updatePaid(True, self.program, self.user)
                else:
                    IndividualAccountingController.updatePaid(False, self.program, self.user)

                Record.createBit('Attended', self.program, self.user)

                if new_data['medical']:
                    Record.createBit('Med', self.program, self.user)

                if new_data['liability']:
                    Record.createBit('Liab', self.program, self.user)

                Record.createBit('OnSite', self.program, self.user)


                new_user.groups.add(Group.objects.get(name="Student"))

                new_user.recoverPassword()

                return render_to_response(self.baseDir()+'reg_success.html', request, {
                    'student': new_user,
                    'retUrl': '/onsite/%s/classchange_grid?student_id=%s' % (self.program.getUrlBase(), new_user.id)
                    })

        else:
            form = OnSiteRegForm()

        return render_to_response(self.baseDir()+'reg_info.html', request, {'form':form})
    def finaid(self,request, tl, one, two, module, extra, prog):
        """
        A way for a student to apply for financial aid.
        """
        from datetime import datetime
        from esp.dbmail.models import send_mail

        app, created = FinancialAidRequest.objects.get_or_create(user = request.user,
                                                                program = self.program)

        class Form(forms.ModelForm):
            class Meta:
                model = FinancialAidRequest
                tag_data = Tag.getTag('finaid_form_fields')
                if tag_data:
                    fields = tuple(tag_data.split(','))
                else:
                    fields = '__all__'

        if request.method == 'POST':
            form = Form(request.POST, initial = app.__dict__)
            if form.is_valid():
                app.__dict__.update(form.cleaned_data)

                if not 'submitform' in request.POST or request.POST['submitform'].lower() == 'complete':
                    app.done = True
                elif request.POST['submitform'].lower() == 'mark as incomplete' or request.POST['submitform'].lower() == 'save progress':
                    app.done = False
                else:
                    raise ESPError("Our server lost track of whether or not you were finished filling out this form.  Please go back and click 'Complete' or 'Mark as Incomplete'.")

                app.save()

                # Automatically accept apps for people with subsidized lunches
                # Send an e-mail announcing the application either way
                date_str = str(datetime.now())
                iac = IndividualAccountingController(self.program, request.user)
                if app.reduced_lunch:
                    iac.grant_full_financial_aid()
                    subj_str = '%s %s received Financial Aid for %s' % (request.user.first_name, request.user.last_name, prog.niceName())
                    msg_str = "\n%s %s received Financial Aid for %s on %s, for stating that they receive a free or reduced-price lunch."
                else:
                    subj_str = '%s %s applied for Financial Aid for %s' % (request.user.first_name, request.user.last_name, prog.niceName())
                    msg_str = "\n%s %s applied for Financial Aid for %s on %s, but did not state that they receive a free or reduced-price lunch."
                send_mail(subj_str, (msg_str +
                """

Here is their form data:

========================================
Program:  %s
User:  %s %s <%s>
Approved:  %s
Has Reduced Lunch:  %s
Household Income:  $%s
Form Was Filled Out by Non-Student:  %s
Extra Explanation:
%s

========================================

This request can be (re)viewed at:
<http://%s/admin/program/financialaidrequest/%s/>


""") % (request.user.first_name,
    request.user.last_name,
    prog.niceName(),
    date_str,
    str(app.program),
    request.user.first_name,
    request.user.last_name,
    str(app.user),
    date_str,
    str(app.reduced_lunch),
    str(app.household_income),
    str(app.student_prepare),
    app.extra_explaination,
    settings.DEFAULT_HOST, # server hostname
    str(app.id)),
                            settings.SERVER_EMAIL,
                            [ prog.getDirectorConfidentialEmail() ] )

                return self.goToCore(tl)

        else:
            form = Form(initial = app.__dict__)

        return render_to_response(self.baseDir()+'application.html',
                                  request,
                                  {'form': form, 'app': app})
Пример #34
0
 def hasPaid(self):
     iac = IndividualAccountingController(self.program, self.student)
     return Record.user_completed(self.student, "paid", self.program) or \
         iac.has_paid(in_full=True)
Пример #35
0
 def isCompleted(self):
     iac = IndividualAccountingController(self.program,
                                          get_current_request().user)
     return (len(iac.get_preferences()) > 0)
    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)
Пример #37
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 isCompleted(self):
     """ Whether the user has paid for this program or its parent program. """
     return IndividualAccountingController(
         self.program,
         get_current_request().user).has_paid()
Пример #41
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)
    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)
Пример #43
0
def migrate_program(program):

    docs = Document.objects.filter(anchor=program.anchor, doctype=2)
    num_uncategorized = 0
    num = 0
    found_for_program = False
    student_dict = {}

    #   Clear financial data for this program
    pac = ProgramAccountingController(program)
    pac.clear_all_data()
    lineitem_types = {'one': {}, 'multi': {}, 'select': []}
    lineitem_choices = {}

    #   Build a database of each student's financial transactions for the program
    for doc in docs:
        student_id = doc.user.id

        if student_id not in student_dict:
            student_dict[student_id] = {
                'admission': {},
                'finaid_amt': Decimal('0'),
                'items': [],
                'items_select': [],
                'payment': [],
            }
        lineitems = doc.txn.lineitem_set.all()
        """
		if lineitems.count() > 0:
			try:
				print '\nStudent: %s' % doc.user.name()
			except:
				print '\nStudent: %s' % doc.user.username
		"""
        for li in lineitems:
            found_for_program = True
            base_txt = '[%5d] %s: %.2f' % (li.id, li.text, li.amount)
            #   if li.anchor.uri == splash.anchor.uri + '/LineItemTypes/Required':
            if 'admission' in li.text.lower(
            ) or 'cost of attending' in li.text.lower():
                base_txt = '(Admission) ' + base_txt
                if li.text not in student_dict[student_id]['admission']:
                    student_dict[student_id]['admission'][
                        li.text] = li.amount.copy_abs()
            elif 'financial aid' in li.text.lower():
                base_txt = '(Finaid) ' + base_txt
                student_dict[student_id]['finaid_amt'] += li.amount
            elif 'payment received' in li.text.lower():
                base_txt = '(Payment) ' + base_txt
                student_dict[student_id]['payment'].append(
                    li.amount.copy_abs())
            elif 'expecting on-site payment' in li.text.lower():
                base_txt = '(Payment expected) ' + base_txt
                student_dict[student_id]['payment'].append(
                    li.amount.copy_abs())
            elif 'BuyMultiSelect' in li.anchor.uri:
                base_txt = '(Select: field "%s", choice "%s") ' % (
                    li.anchor.name, li.text) + base_txt
                student_dict[student_id]['items_select'].append(
                    (li.anchor.name, li.text, li.amount.copy_abs()))
                if li.anchor.name not in lineitem_types['select']:
                    lineitem_types['select'].append(li.anchor.name)
                    lineitem_choices[li.anchor.name] = []
                if (li.text, li.amount.copy_abs()
                    ) not in lineitem_choices[li.anchor.name]:
                    lineitem_choices[li.anchor.name].append(
                        (li.text, li.amount.copy_abs()))
            elif 'BuyMany' in li.anchor.uri or 'shirt' in li.text.lower():
                base_txt = '(Multi: field "%s") ' % (li.anchor.name) + base_txt
                student_dict[student_id]['items'].append(
                    (li.text, li.amount.copy_abs()))
                if li.text not in lineitem_types['multi']:
                    lineitem_types['multi'][li.text] = li.amount.copy_abs()
            elif 'BuyOne' in li.anchor.uri or 'lunch' in li.text.lower(
            ) or 'dinner' in li.text.lower() or 'photo' in li.text.lower():
                base_txt = '(Single: field "%s") ' % (
                    li.anchor.name) + base_txt
                student_dict[student_id]['items'].append(
                    (li.text, li.amount.copy_abs()))
                if li.text not in lineitem_types['one']:
                    lineitem_types['one'][li.text] = li.amount.copy_abs()
            else:
                num_uncategorized += 1
                print 'WARNING: Uncategorized line item: %s' % base_txt
                #   raise Exception('Uncategorized line item: %s' % base_txt)

            num += 1
            #   print '-- %s' % base_txt
        """
		if student_dict[student_id]['finaid_amt'] > 0:
			print student_dict[student_id]
		elif len(student_dict[student_id]['items_multi']) > 0:
			print student_dict[student_id]
		"""

    if found_for_program:
        num_programs = 1
    else:
        num_programs = 0

    #   Populate line item types for the program
    optional_items = []
    for item in lineitem_types['one']:
        optional_items.append((item, lineitem_types['one'][item], 1))
    for item in lineitem_types['multi']:
        optional_items.append((item, lineitem_types['multi'][item], 10))
    select_items = []
    for item in lineitem_types['select']:
        select_items.append((item, lineitem_choices[item]))

    #   print optional_items
    #   print select_items
    pac.setup_accounts()
    pac.setup_lineitemtypes(0.0, optional_items, select_items)

    #   Create new transfer records for this student
    for student_id in student_dict:
        user = ESPUser.objects.get(id=student_id)
        iac = IndividualAccountingController(program, user)
        rec = student_dict[student_id]

        #   Admission fee
        admission_total_cost = 0
        for key in rec['admission']:
            admission_total_cost += rec['admission'][key]
        if admission_total_cost > 0:
            initial_transfer = iac.add_required_transfers()[0]
            initial_transfer.amount_dec = admission_total_cost
            initial_transfer.save()

        #   Financial aid
        if rec['finaid_amt'] > 0:
            if rec['finaid_amt'] >= admission_total_cost:
                iac.grant_full_financial_aid()
            else:
                iac.set_finaid_params(rec['finaid_amt'], None)

        #   Optional items
        prefs = []
        for item in rec['items']:
            prefs.append((item[0], 1, item[1]))
        for item in rec['items_select']:
            prefs.append((item[0], 1, item[2]))
        iac.apply_preferences(prefs)

        #   Payments
        for payment in rec['payment']:
            iac.submit_payment(payment)

    try:
        attended_ids = program.students()['attended'].values_list('id',
                                                                  flat=True)

        #   Execute transfers for the students that are marked as attended
        pac.execute_pending_transfers(
            ESPUser.objects.filter(id__in=attended_ids))

        #   Clear transfers for the students that are marked as not attended
        pac.remove_pending_transfers(
            ESPUser.objects.exclude(id__in=attended_ids))
    except:
        print 'Unable to determine which students attended the program; all transfers remain unexecuted'

    print 'Converted %d line items for %s; %d uncategorized' % (
        num, program.niceName(), num_uncategorized)
    return num_programs
 def have_paid(self, user):
     """ Whether the user has paid for this program.  """
     iac = IndividualAccountingController(self.program, user)
     return (iac.amount_due() <= 0)