Changeset 7250 for main/waeup.sirp/trunk/src/waeup/sirp/applicants
- Timestamp:
- 2 Dec 2011, 12:46:36 (13 years ago)
- Location:
- main/waeup.sirp/trunk/src/waeup/sirp/applicants
- Files:
-
- 2 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py
r7244 r7250 45 45 return u"xxx_1234" 46 46 47 class Applicant(grok. Model):47 class Applicant(grok.Container): 48 48 grok.implements(IApplicant,IApplicantEdit) 49 49 grok.provides(IApplicant) -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py
r7249 r7250 21 21 import sys 22 22 import grok 23 23 from time import time 24 24 from datetime import datetime, date 25 25 from zope.authentication.interfaces import ILogout, IAuthentication … … 58 58 from waeup.sirp.utils.helpers import string_from_bytes, file_size 59 59 from waeup.sirp.widgets.datewidget import ( 60 FriendlyDateWidget, FriendlyDateDisplayWidget) 60 FriendlyDateWidget, FriendlyDateDisplayWidget, 61 FriendlyDatetimeDisplayWidget) 61 62 from waeup.sirp.widgets.restwidget import ReSTDisplayWidget 62 63 from waeup.sirp.widgets.objectwidget import ( … … 66 67 IApplicant, IApplicantEdit, IApplicantsRoot, 67 68 IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab, 68 MAX_UPLOAD_SIZE, 69 MAX_UPLOAD_SIZE, IApplicantOnlinePayment, 69 70 ) 70 from waeup.sirp.applicants.workflow import INITIALIZED, STARTED 71 from waeup.sirp.applicants.workflow import INITIALIZED, STARTED, PAID 71 72 from waeup.sirp.students.viewlets import PrimaryStudentNavTab 73 from waeup.sirp.students.interfaces import IStudentsUtils 72 74 73 75 grok.context(IWAeUPObject) # Make IWAeUPObject the default context … … 228 230 return self.context.application_number 229 231 232 class OnlinePaymentBreadcrumb(Breadcrumb): 233 """A breadcrumb for payments. 234 """ 235 grok.context(IApplicantOnlinePayment) 236 237 @property 238 def title(self): 239 return self.context.p_id 240 230 241 class ApplicantsAuthTab(PrimaryNavTab): 231 242 """Applicants tab in primary navigation. … … 233 244 grok.context(IWAeUPObject) 234 245 grok.order(3) 235 grok.require('waeup.viewApplica tionsTab')246 grok.require('waeup.viewApplicantsTab') 236 247 pnav = 3 237 248 tab_title = u'Applicants' … … 478 489 return '<a href="%s">%s - %s</a>' %(url,code,title) 479 490 return '' 491 492 def formatDatetime(self,datetimeobj): 493 if isinstance(datetimeobj, datetime): 494 return datetimeobj.strftime("%Y-%m-%d %H:%M:%S") 495 else: 496 return None 497 498 class AcceptanceFeePaymentAddPage(grok.View): 499 """ Page to add an online payment ticket 500 """ 501 grok.context(IApplicant) 502 grok.name('addafp') 503 grok.require('waeup.payApplicant') 504 505 def update(self): 506 p_category = 'acceptance' 507 d = {} 508 session = str(self.context.__parent__.year) 509 try: 510 academic_session = grok.getSite()['configuration'][session] 511 except KeyError: 512 self.flash('Session configuration object is not available.') 513 return 514 timestamp = "%d" % int(time()*1000) 515 #order_id = "%s%s" % (student_id[1:],timestamp) 516 for key in self.context.keys(): 517 ticket = self.context[key] 518 if ticket.p_state == 'paid': 519 self.flash( 520 'This type of payment has already been made.') 521 self.redirect(self.url(self.context)) 522 return 523 payment = createObject(u'waeup.ApplicantOnlinePayment') 524 payment.p_id = "p%s" % timestamp 525 payment.p_item = self.context.__parent__.title 526 payment.p_year = self.context.__parent__.year 527 payment.p_category = p_category 528 payment.amount_auth = academic_session.acceptance_fee 529 payment.surcharge_1 = academic_session.surcharge_1 530 payment.surcharge_2 = academic_session.surcharge_2 531 payment.surcharge_3 = academic_session.surcharge_3 532 self.context[payment.p_id] = payment 533 #import pdb; pdb.set_trace() 534 self.flash('Payment ticket created.') 535 return 536 537 def render(self): 538 usertype = getattr(self.request.principal, 'user_type', None) 539 if usertype == 'applicant': 540 self.redirect(self.url(self.context, '@@edit')) 541 return 542 self.redirect(self.url(self.context, '@@manage')) 543 return 544 545 546 class OnlinePaymentDisplayFormPage(WAeUPDisplayFormPage): 547 """ Page to view an online payment ticket 548 """ 549 grok.context(IApplicantOnlinePayment) 550 grok.name('index') 551 grok.require('waeup.viewApplication') 552 form_fields = grok.AutoFields(IApplicantOnlinePayment) 553 form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') 554 form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') 555 pnav = 3 556 557 @property 558 def title(self): 559 return 'Online Payment Ticket %s' % self.context.p_id 560 561 @property 562 def label(self): 563 return '%s: Online Payment Ticket %s' % ( 564 self.context.__parent__.fullname,self.context.p_id) 565 566 class PaymentReceiptActionButton(ManageActionButton): 567 grok.order(1) 568 grok.context(IApplicantOnlinePayment) 569 grok.view(OnlinePaymentDisplayFormPage) 570 grok.require('waeup.viewApplication') 571 icon = 'actionicon_pdf.png' 572 text = 'Download payment receipt' 573 target = 'payment_receipt.pdf' 574 575 @property 576 def target_url(self): 577 if self.context.p_state != 'paid': 578 return '' 579 return self.view.url(self.view.context, self.target) 580 581 class RequestCallbackActionButton(ManageActionButton): 582 grok.order(2) 583 grok.context(IApplicantOnlinePayment) 584 grok.view(OnlinePaymentDisplayFormPage) 585 grok.require('waeup.payApplicant') 586 icon = 'actionicon_call.png' 587 text = 'Request callback' 588 target = 'callback' 589 590 @property 591 def target_url(self): 592 if self.context.p_state != 'unpaid': 593 return '' 594 return self.view.url(self.view.context, self.target) 595 596 class OnlinePaymentCallbackPage(grok.View): 597 """ Callback view 598 """ 599 grok.context(IApplicantOnlinePayment) 600 grok.name('callback') 601 grok.require('waeup.payApplicant') 602 603 # This update method simulates a valid callback und must be 604 # specified in the customization package. The parameters must be taken 605 # from the incoming request. 606 def update(self): 607 if self.context.p_state == 'paid': 608 self.flash('This ticket has already been paid.') 609 return 610 self.context.r_amount_approved = self.context.amount_auth 611 self.context.r_card_num = u'0000' 612 self.context.r_code = u'00' 613 self.context.p_state = 'paid' 614 self.context.payment_date = datetime.now() 615 ob_class = self.__implemented__.__name__.replace('waeup.sirp.','') 616 self.context.__parent__.loggerInfo( 617 ob_class, 'valid callback: %s' % self.context.p_id) 618 self.wf_info = IWorkflowInfo(self.context.__parent__) 619 self.wf_info.fireTransition('pay') 620 self.flash('Valid callback received.') 621 return 622 623 def render(self): 624 self.redirect(self.url(self.context, '@@index')) 625 return 626 627 class ExportPDFPaymentSlipPage(grok.View): 628 """Deliver a PDF slip of the context. 629 """ 630 grok.context(IApplicantOnlinePayment) 631 grok.name('payment_receipt.pdf') 632 grok.require('waeup.viewApplication') 633 form_fields = grok.AutoFields(IApplicantOnlinePayment) 634 form_fields['creation_date'].custom_widget = FriendlyDateDisplayWidget('le') 635 form_fields['payment_date'].custom_widget = FriendlyDateDisplayWidget('le') 636 prefix = 'form' 637 638 @property 639 def label(self): 640 return 'Online Payment Receipt %s' % self.context.p_id 641 642 def render(self): 643 if self.context.p_state != 'paid': 644 self.flash('Ticket not yet paid.') 645 self.redirect(self.url(self.context)) 646 return 647 applicantview = ApplicantDisplayFormPage(self.context.__parent__, 648 self.request) 649 students_utils = getUtility(IStudentsUtils) 650 return students_utils.renderPDF(self,'Payment', 'payment_receipt.pdf', 651 self.context.__parent__, applicantview) 480 652 481 653 class PDFActionButton(ManageActionButton): … … 646 818 manage_applications = True 647 819 pnav = 3 820 display_actions = [['Save', 'Final Submit'], 821 ['Add online payment ticket','Remove selected tickets']] 648 822 649 823 def update(self): … … 716 890 return 717 891 892 def formatDatetime(self,datetimeobj): 893 if isinstance(datetimeobj, datetime): 894 return datetimeobj.strftime("%Y-%m-%d %H:%M:%S") 895 else: 896 return None 897 898 def unremovable(self, ticket): 899 usertype = getattr(self.request.principal, 'user_type', None) 900 if not usertype: 901 return False 902 return self.request.principal.user_type == 'applicant' and ticket.r_code 903 904 # This method is also used by the ApplicantEditFormPage 905 def delPaymentTickets(self, **data): 906 form = self.request.form 907 if form.has_key('val_id'): 908 child_id = form['val_id'] 909 else: 910 self.flash('No payment selected.') 911 self.redirect(self.url(self.context)) 912 return 913 if not isinstance(child_id, list): 914 child_id = [child_id] 915 deleted = [] 916 for id in child_id: 917 # Applicants are not allowed to remove used payment tickets 918 if not self.unremovable(self.context[id]): 919 try: 920 del self.context[id] 921 deleted.append(id) 922 except: 923 self.flash('Could not delete %s: %s: %s' % ( 924 id, sys.exc_info()[0], sys.exc_info()[1])) 925 if len(deleted): 926 self.flash('Successfully removed: %s' % ', '.join(deleted)) 927 ob_class = self.__implemented__.__name__.replace('waeup.sirp.','') 928 self.context.loggerInfo(ob_class, 'removed: % s' % ', '.join(deleted)) 929 return 930 931 @grok.action('Add online payment ticket') 932 def addPaymentTicket(self, **data): 933 self.redirect(self.url(self.context, '@@addafp')) 934 935 @grok.action('Remove selected tickets') 936 def removePaymentTickets(self, **data): 937 self.delPaymentTickets(**data) 938 self.redirect(self.url(self.context) + '/@@manage') 939 return 940 718 941 class ApplicantEditFormPage(ApplicantManageFormPage): 719 942 """An applicant-centered edit view for applicant data. … … 731 954 title = u'Your Application Form' 732 955 956 @property 957 def display_actions(self): 958 state = IWorkflowState(self.context).getState() 959 if state == INITIALIZED: 960 actions = [[],[]] 961 elif state == STARTED: 962 actions = [['Save'], 963 ['Add online payment ticket','Remove selected tickets']] 964 elif state == PAID: 965 actions = [['Save', 'Final Submit'], 966 ['Remove selected tickets']] 967 elif state == SUBMITTED: 968 actions = [[],[]] 969 return actions 970 733 971 def emit_lock_message(self): 734 972 self.flash('The requested form is locked (read-only).') … … 748 986 return 'Passport confirmation box not ticked.' 749 987 return False 988 989 @grok.action('Add online payment ticket') 990 def addPaymentTicket(self, **data): 991 self.redirect(self.url(self.context, '@@addafp')) 992 993 @grok.action('Remove selected tickets') 994 def removePaymentTickets(self, **data): 995 self.delPaymentTickets(**data) 996 self.redirect(self.url(self.context) + '/@@edit') 997 return 750 998 751 999 @grok.action('Save') … … 754 1002 return # error during image upload. Ignore other values 755 1003 self.applyData(self.context, **data) 756 #self.context._p_changed = True757 1004 self.flash('Form has been saved.') 758 1005 return … … 770 1017 # This shouldn't happen, but the application officer 771 1018 # might have forgotten to lock the form after changing the state 772 if state != STARTED:1019 if state != PAID: 773 1020 self.flash('This form cannot be submitted. Wrong state!') 774 1021 return -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicantdisplaypage.pt
r7240 r7250 43 43 </tbody> 44 44 </table> 45 <h3 i18n:translate="">Acceptance Fee Payment Tickets</h3> 46 <table class="zebra"> 47 <thead> 48 <tr> 49 <th>Payment Id</th> 50 <th>Creation Date</th> 51 <th>Payment Date</th> 52 <th>Category</th> 53 <th>Item</th> 54 <th>State</th> 55 </tr> 56 </thead> 57 <tbody> 58 <tr tal:repeat="value context/values"> 59 <td> <a tal:attributes="href value/__name__"> 60 <span tal:content="value/p_id">PID</span></a></td> 61 <td tal:content="python: view.formatDatetime(value.creation_date)">CREATION DATE</td> 62 <td tal:content="python: view.formatDatetime(value.payment_date)">PAYMENT DATE</td> 63 <td tal:content ="value/category">CATEGORY</td> 64 <td tal:content ="value/p_item">ITEM</td> 65 <td tal:content ="value/state">STATE</td> 66 </tr> 67 </tbody> 68 </table> -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser_templates/applicanteditpage.pt
r7240 r7250 95 95 96 96 <div class="actionButtons" tal:condition="view/availableActions"> 97 <input tal:repeat="action view/actions" 98 tal:replace="structure action/render" 99 /> 97 <span tal:repeat="action view/actions" 98 tal:omit-tag=""> 99 <input tal:condition="python:action.label in view.display_actions[0]" 100 tal:replace="structure action/render"/> 101 </span> 102 </div> 103 104 <br /><br /> 105 <h3 i18n:translate="">Acceptance Fee Payment Tickets</h3> 106 107 <table class="display dataTableManage"> 108 <thead> 109 <tr> 110 <th> </th> 111 <th>Payment Id</th> 112 <th>Creation Date</th> 113 <th>Payment Date</th> 114 <th>Category</th> 115 <th>Item</th> 116 <th>State</th> 117 </tr> 118 </thead> 119 <tbody> 120 <tr tal:repeat="cl context/values"> 121 <td> 122 <input type="checkbox" 123 name="val_id" 124 tal:attributes="value cl/__name__" 125 tal:condition="python: not view.unremovable(cl)" /> 126 </td> 127 <td> <a tal:attributes="href cl/__name__"> 128 <span tal:content="cl/p_id">PID</span></a></td> 129 <td tal:content="python: view.formatDatetime(cl.creation_date)">CREATION DATE</td> 130 <td tal:content="python: view.formatDatetime(cl.payment_date)">PAYMENT DATE</td> 131 <td tal:content ="cl/category">CATEGORY</td> 132 <td tal:content ="cl/p_item">ITEM</td> 133 <td tal:content ="cl/state">STATE</td> 134 </tr> 135 </tbody> 136 </table> 137 138 <div class="actionButtons" tal:condition="view/availableActions"> 139 <span tal:repeat="action view/actions" 140 tal:omit-tag=""> 141 <input tal:condition="python:action.label in view.display_actions[1]" 142 tal:replace="structure action/render"/> 143 </span> 100 144 </div> 101 145 </form> -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py
r7240 r7250 29 29 from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal 30 30 from zc.sourcefactory.basic import BasicSourceFactory 31 from waeup.sirp.interfaces import IWAeUPObject, year_range, validate_email 31 from waeup.sirp.interfaces import ( 32 IWAeUPObject, year_range, validate_email, academic_sessions_vocab) 32 33 from waeup.sirp.university.vocabularies import application_categories 33 34 from waeup.sirp.students.vocabularies import ( … … 38 39 AppCatCertificateSource, 39 40 ) 41 from waeup.sirp.payments.interfaces import IOnlinePayment 40 42 41 43 #: Maximum upload size for applicant passport photographs (in bytes) … … 351 353 ) 352 354 student_id = schema.TextLine( 353 title = u'Student I D',355 title = u'Student Id', 354 356 required = False, 355 357 readonly = True, … … 398 400 readonly = True, 399 401 ) 402 403 class IApplicantOnlinePayment(IOnlinePayment): 404 """An applicant payment via payment gateways. 405 406 """ 407 p_year = schema.Choice( 408 title = u'Payment Session', 409 source = academic_sessions_vocab, 410 required = False, 411 ) 412 413 IApplicantOnlinePayment['p_year'].order = IApplicantOnlinePayment[ 414 'p_year'].order 400 415 401 416 class IApplicantsContainerProvider(Interface): -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/permissions.py
r7240 r7250 30 30 31 31 class ViewApplicationsTab(grok.Permission): 32 grok.name('waeup.viewApplica tionsTab')32 grok.name('waeup.viewApplicantsTab') 33 33 34 34 class ViewMyApplicationDataTab(grok.Permission): … … 38 38 grok.name('waeup.manageApplication') 39 39 40 class PayApplicant(grok.Permission): 41 grok.name('waeup.payApplicant') 42 40 43 # Local role 41 44 class ApplicationOwner(grok.Role): … … 43 46 grok.title(u'Application Owner') 44 47 grok.permissions('waeup.handleApplication', 'waeup.viewApplication', 45 'waeup.Authenticated' )48 'waeup.Authenticated', 'waeup.payApplicant') 46 49 47 50 # Site role … … 55 58 grok.title(u'Applications Officer') 56 59 grok.permissions('waeup.manageApplication', 'waeup.viewApplication' 57 'waeup.viewApplica tionsTab')60 'waeup.viewApplicantsTab', 'waeup.payApplicant') -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/workflow.py
r7192 r7250 28 28 INITIALIZED = 'initialized' 29 29 STARTED = 'started' 30 PAID = 'paid' 30 31 SUBMITTED = 'submitted' 31 32 ADMITTED = 'admitted' … … 50 51 51 52 Transition( 53 transition_id = 'pay', 54 title = 'Pay acceptance fee', 55 msg = 'Fee paid', 56 source = STARTED, 57 destination = PAID), 58 59 Transition( 52 60 transition_id = 'submit', 53 61 title = 'Submit application', 54 62 msg = 'Application submitted', 55 source = STARTED,63 source = PAID, 56 64 destination = SUBMITTED), 57 65 … … 86 94 Transition( 87 95 transition_id = 'reset1', 88 title = 'Reset application ',96 title = 'Reset application to started', 89 97 msg = 'Application reset', 90 98 source = SUBMITTED, … … 93 101 Transition( 94 102 transition_id = 'reset2', 95 title = 'Reset application ',103 title = 'Reset application to started', 96 104 msg = 'Application reset', 97 105 source = ADMITTED, … … 100 108 Transition( 101 109 transition_id = 'reset3', 102 title = 'Reset application ',110 title = 'Reset application to started', 103 111 msg = 'Application reset', 104 112 source = NOT_ADMITTED, … … 107 115 Transition( 108 116 transition_id = 'reset4', 109 title = 'Reset application ',117 title = 'Reset application to started', 110 118 msg = 'Application reset', 111 119 source = CREATED, 112 120 destination = STARTED), 121 Transition( 122 transition_id = 'reset5', 123 title = 'Reset application to paid', 124 msg = 'Application reset', 125 source = SUBMITTED, 126 destination = PAID), 113 127 ) 114 128
Note: See TracChangeset for help on using the changeset viewer.