diff --git a/alembic/versions/8d205370475_add_transferrable_until.py b/alembic/versions/8d205370475_add_transferrable_until.py new file mode 100644 index 00000000..472b7871 --- /dev/null +++ b/alembic/versions/8d205370475_add_transferrable_until.py @@ -0,0 +1,22 @@ +"""add_transferrable_until + +Revision ID: 8d205370475 +Revises: 4246213b032b +Create Date: 2016-11-08 14:12:26.061615 + +""" + +# revision identifiers, used by Alembic. +revision = '8d205370475' +down_revision = '4246213b032b' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('item', sa.Column('transferrable_until', sa.DateTime(), nullable=True)) + + +def downgrade(): + op.drop_column('item', 'transferrable_until') diff --git a/boxoffice/forms/__init__.py b/boxoffice/forms/__init__.py index 716b7785..5ec01f3b 100644 --- a/boxoffice/forms/__init__.py +++ b/boxoffice/forms/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from .order import * +from .assignee import * diff --git a/boxoffice/forms/assignee.py b/boxoffice/forms/assignee.py new file mode 100644 index 00000000..e2a5cfc0 --- /dev/null +++ b/boxoffice/forms/assignee.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from baseframe import __ +import baseframe.forms as forms + +__all__ = ['AssigneeForm'] + + +class AssigneeForm(forms.Form): + email = forms.EmailField(__("Email"), validators=[forms.validators.DataRequired(), forms.validators.Length(max=80)]) + fullname = forms.StringField(__("Full name"), validators=[forms.validators.DataRequired()]) + phone = forms.StringField(__("Phone number"), validators=[forms.validators.Length(max=16)]) diff --git a/boxoffice/mailclient.py b/boxoffice/mailclient.py index 72814c6a..01da2591 100644 --- a/boxoffice/mailclient.py +++ b/boxoffice/mailclient.py @@ -55,7 +55,7 @@ def send_line_item_cancellation_mail(line_item_id, subject="Ticket Cancellation" @job('boxoffice') -def send_ticket_assignment_mail(line_item_id): +def send_ticket_assignment_mail(line_item_id, recipient_list, cc_list=[]): """ Sends a confirmation email once details are filled and ticket has been assigned. """ @@ -63,7 +63,7 @@ def send_ticket_assignment_mail(line_item_id): line_item = LineItem.query.get(line_item_id) order = line_item.order subject = order.item_collection.title + ": Here's your ticket" - msg = Message(subject=subject, recipients=[line_item.current_assignee.email], bcc=[order.buyer_email]) + msg = Message(subject=subject, recipients=recipient_list, cc=cc_list) html = email_transform(render_template('ticket_assignment_mail.html', order=order, org=order.organization, line_item=line_item, base_url=app.config['BASE_URL'])) msg.html = html msg.body = html2text(html) diff --git a/boxoffice/models/item.py b/boxoffice/models/item.py index ad4cf9d0..bb1e5160 100644 --- a/boxoffice/models/item.py +++ b/boxoffice/models/item.py @@ -36,6 +36,7 @@ class Item(BaseScopedNameMixin, db.Model): assignee_details = db.Column(JsonDict, default={}, nullable=False) cancellable_until = db.Column(db.DateTime, nullable=True) + transferrable_until = db.Column(db.DateTime, nullable=True) def current_price(self): """ diff --git a/boxoffice/models/line_item.py b/boxoffice/models/line_item.py index 292d5559..9491d1c7 100644 --- a/boxoffice/models/line_item.py +++ b/boxoffice/models/line_item.py @@ -151,6 +151,10 @@ def fetch_all_details(cls, item_collection): line_item_query = db.select([cls.id, Order.invoice_no, Item.title, cls.base_amount, cls.discounted_amount, cls.final_amount, DiscountPolicy.title, DiscountCoupon.code, Order.buyer_fullname, Order.buyer_email, Order.buyer_phone, Assignee.fullname, Assignee.email, Assignee.phone, Assignee.details, OrderSession.utm_campaign, OrderSession.utm_source, OrderSession.utm_medium, OrderSession.utm_term, OrderSession.utm_content, OrderSession.utm_id, OrderSession.gclid, OrderSession.referrer]).select_from(line_item_join).where(cls.status == LINE_ITEM_STATUS.CONFIRMED).where(Order.item_collection == item_collection).order_by('created_at') return db.session.execute(line_item_query).fetchall() + def is_transferrable(self): + return self.is_confirmed and (datetime.datetime.now() < self.item.transferrable_until + if self.item.transferrable_until else True) + def get_availability(cls, item_ids): """Returns a dict -> {'item_id': ('item title', 'quantity_total', 'line_item_count')}""" diff --git a/boxoffice/static/css/app.css b/boxoffice/static/css/app.css index 76bbbada..b7a6b055 100644 --- a/boxoffice/static/css/app.css +++ b/boxoffice/static/css/app.css @@ -156,7 +156,8 @@ a.boxoffice-button:focus { -webkit-transition: 0.2s ease all; } -.group-input:disabled { +.group-input:disabled, +.group-input.disabled { position: relative; background-color: transparent; color: #CCC; @@ -521,7 +522,7 @@ a.boxoffice-button:focus { text-align: center; } -.attendee-form-title { +.assignee-form-title { font-size: 18px; font-weight: bold; padding: 15px 0; diff --git a/boxoffice/static/js/views/order.js b/boxoffice/static/js/views/order.js index 93298caa..2c41311d 100644 --- a/boxoffice/static/js/views/order.js +++ b/boxoffice/static/js/views/order.js @@ -57,7 +57,6 @@ window.Boxoffice.Order = { data: { order_id: data.order_id, access_token: data.access_token, - eventName: data.item_collection_name, line_items: data.line_items, buyer_name: data.buyer_name, buyer_email: data.buyer_email, @@ -65,8 +64,8 @@ window.Boxoffice.Order = { }, scrollTop: function(line_item_seq){ //Scroll to the corresponding line_item. - var domElem = order.ractive.nodes[ 'item-' + line_item_seq ]; - $('html,body').animate({ scrollTop: $(domElem).offset().top }, '300'); + var dom_elem = order.ractive.nodes[ 'item-' + line_item_seq ]; + $('html,body').animate({ scrollTop: $(dom_elem).offset().top }, '300'); }, viewTicket: function(event, line_item, line_item_seq) { event.original.preventDefault(); @@ -102,9 +101,9 @@ window.Boxoffice.Order = { } order.ractive.set(line_item + '.toAssign', true); }, - addAttendeeDetails: function(event, line_item, line_item_seq, line_item_id) { + addAssigneeDetails: function(event, line_item, line_item_seq, line_item_id) { - var validationConfig = [{ + var validation_config = [{ name: 'fullname', rules: 'required' }, @@ -118,25 +117,26 @@ window.Boxoffice.Order = { } ]; - var attendeeForm = 'attendee-form-' + line_item_seq; + var assignee_form = 'assignee-form-' + line_item_seq; - var formValidator = new FormValidator(attendeeForm, validationConfig, function(errors, event) { + var form_validator = new FormValidator(assignee_form, validation_config, function(errors, event) { event.preventDefault(); order.ractive.set(line_item + '.assignee.errormsg', ''); + order.ractive.set(line_item + '.errorMsg', ''); if (errors.length > 0) { order.ractive.set(line_item + '.assignee.errormsg.'+ errors[0].name, errors[0].message); order.ractive.scrollTop(line_item_seq); } else { order.ractive.set(line_item + '.assigningTicket', true); - order.ractive.sendAttendeeDetails(line_item, line_item_seq, line_item_id); + order.ractive.sendAssigneeDetails(line_item, line_item_seq, line_item_id); } }); - formValidator.setMessage('required', 'Please fill out the %s field'); - formValidator.setMessage('valid_email', 'Please enter a valid email'); + form_validator.setMessage('required', 'Please fill out the %s field'); + form_validator.setMessage('valid_email', 'Please enter a valid email'); - formValidator.registerCallback('validate_phone', function(phone) { + form_validator.registerCallback('validate_phone', function(phone) { //Remove all punctations (except +) and letters phone = phone.replace(/[^0-9+]/g,''); order.ractive.set(line_item + '.assignee.phone', phone); @@ -144,29 +144,30 @@ window.Boxoffice.Order = { var validPhone = /^\+[0-9]+$/; if (phone.length > 16) { - formValidator.setMessage('validate_phone', 'Please enter a valid mobile number'); + form_validator.setMessage('validate_phone', 'Please enter a valid mobile number'); return false; } else if (phone.match(validPhone)) { //Indian number starting with '+91' if (phone.indexOf('+91') === 0 && phone.length != 13) { - formValidator.setMessage('validate_phone', 'Please enter a valid Indian mobile number'); + form_validator.setMessage('validate_phone', 'Please enter a valid Indian mobile number'); return false; } } else { - formValidator.setMessage('validate_phone', "Please prefix your phone number with '+' and country code."); + form_validator.setMessage('validate_phone', "Please prefix your phone number with '+' and country code."); return false; } }); }, - sendAttendeeDetails: function(line_item, line_item_seq, line_item_id) { - var attendeeForm = 'attendee-details-' + line_item_seq; - var formElements = $('#'+ attendeeForm).serializeArray(); - var attendeeDetails ={}; - for (var formIndex=0; formIndex < formElements.length; formIndex++) { - if (formElements[formIndex].value) { - attendeeDetails[formElements[formIndex].name] = formElements[formIndex].value; + sendAssigneeDetails: function(line_item, line_item_seq, line_item_id) { + var assignee_form = 'assignee-details-' + line_item_seq; + var form_elements = $('#'+ assignee_form).serializeArray(); + var assignee_details ={}; + + for (var form_index=0; form_index < form_elements.length; form_index++) { + if (form_elements[form_index].value) { + assignee_details[form_elements[form_index].name] = form_elements[form_index].value; } } @@ -175,7 +176,7 @@ window.Boxoffice.Order = { type: Boxoffice.Order.config.assign.method, contentType: 'application/json', data: JSON.stringify({ - attendee: attendeeDetails, + assignee: assignee_details, line_item_id: line_item_id }), timeout: 30000, @@ -186,12 +187,20 @@ window.Boxoffice.Order = { order.ractive.set(line_item + '.toAssign', false); order.ractive.set(line_item + '.isTicketAssigned', true); order.ractive.scrollTop(line_item_seq); + order.ractive.set(line_item + '.assignee', data.result.assignee); }, error: function(response) { var ajaxLoad = this; ajaxLoad.retries -= 1; if (response.readyState === 4) { - order.ractive.set(line_item + '.errorMsg', 'Server error'); + var error_text; + if (response.status === 500) { + error_text = "Server Error"; + } + else { + error_text = JSON.parse(response.responseText).error_description; + } + order.ractive.set(line_item + '.errorMsg', error_text); order.ractive.set(line_item + '.assigningTicket', false); } else if (response.readyState === 0) { if (ajaxLoad.retries < 0) { diff --git a/boxoffice/static/sass/_layout.sass b/boxoffice/static/sass/_layout.sass index 4e21d261..43ee29fc 100644 --- a/boxoffice/static/sass/_layout.sass +++ b/boxoffice/static/sass/_layout.sass @@ -129,7 +129,8 @@ a.boxoffice-button:focus -moz-transition: 0.2s ease all -webkit-transition: 0.2s ease all -.group-input:disabled +.group-input:disabled, +.group-input.disabled position: relative background-color: transparent color: $color-form-disabled-label diff --git a/boxoffice/static/sass/_order.sass b/boxoffice/static/sass/_order.sass index 1a1718ca..63f32767 100644 --- a/boxoffice/static/sass/_order.sass +++ b/boxoffice/static/sass/_order.sass @@ -71,7 +71,7 @@ margin: 0 0 5px text-align: center -.attendee-form-title +.assignee-form-title font-size: 18px font-weight: bold padding: 15px 0 diff --git a/boxoffice/templates/order.html b/boxoffice/templates/order.html index c1e0e14b..acf70467 100644 --- a/boxoffice/templates/order.html +++ b/boxoffice/templates/order.html @@ -22,7 +22,7 @@

{{order.item_collection.title}} Ticket Assignment

-

Loading. If this takes too long, please mail {{order.item_collection.organization.contact_email}} with your receipt number.

+

Loading. If you are behind a firewall or using any script blocking extension, please ensure your browser can load static.hasgeek.co.in
For further queries, please write to us at {{order.item_collection.organization.contact_email}} with your receipt number.

{%- raw %}