Allow Same Discount/Access Code for Multiple Events in the Open Event Server

In this Blog-Post, I will show how to allow the system to create the same Discount/Access Code for multiple events in the Open Event Server.

What was the issue:

The main problem was that the server used to identify the discount code and access code based on the discount code/access code itself, which did not allow multiple events to have the same discount/access codes.

Can you think of a better solution to this?
Yes, we should have been searching for it based on the discount/access code as well as the event they are associated with.

Changing the endpoint:

Now to do so, we want to pass the id of the event as well as discount/access code itself with the endpoint so that we can search the database based on the event_id and the code itself.  

Changes in Discount/Access Code Endpoint:

'/event/<int:discount_event_id>/discount-code/<code>'
'/event/<int:access_event_id>/access-code/<code>'

Change logic for database search:

Now when searching for discount/access code in the database, we need to pass the event_id along with the discount/access code, so that we can get the column of discount/access code associated with that event, even if we have multiple discount/access code with the same name for a different event. 

Changes in Database search logic:

access = db.session.query(AccessCode).filter_by(code=kwargs.get('code')
event_id = kwargs.get('access_event_id')).first()

discount = db.session.query(DiscountCode).filter_by(code=kwargs.get('code'),
event_id = kwargs.get('discount_event_id')).first()

Change endpoint in API docs and update Dredd hooks:

Now that we have changed the endpoint to get a discount/access code, we need to change API docs as well as Dredd hooks to accommodate the change in API docs.

Changes in API docs:

## Get Discount Code Detail using the code [/v1/event/{event_id}/discount-code/{code}]
## Get Access Code Detail using the code [/v1/event/{event_id}/access-code/{code}]

Changes in Dredd Hooks:

In discount code hook:

discount_code.event_id = 1

In access code hook:

event = EventFactoryBasic()
db.session.add(event)
db.session.commit()

Resources:

Link to Issue: fossasia/open-event-server#6027
Link to PR: fossasia/open-event-server#6208

Continue ReadingAllow Same Discount/Access Code for Multiple Events in the Open Event Server

How to Make Promotional Codes Applicable on Tickets During Ordering in Open Event Frontend

This blog illustrate how to enable application of promotional codes on tickets during ordering tickets in Open Event Frontend to avail discounts and access to special tickets. Open event allows organizers to add some promotional codes on some tickets, which can be used by users to avail additional offers on tickets while ordering. Promotional codes can be of three types:

  1. Discount Codes: Allows customers to buy a ticket at discounted rates.
  2. Access Codes: Allows customers to access some hidden tickets which are accessible only to special customers.
  3. Discount + Access Code: Allows customer to access special tickets and avail discount at the same time.

Creating a discount/access code:

Organizers and admin can create an access code or a discount code from the event dashboard. They can specify the validity period of the code and can also specify the tickets on which the code will be applicable.

Validating promotional code after user enters the code:

User is allowed to enter the promotional code on events page upon selecting the tickets. IF promotional code is valid then suitable discount is provided on applicable tickets and if promotional code is an access code then hidden tickets for which the promotional code is valid are shown.

To check the validity of the promotional code we deal with the following APIs on the open event server:

  • GET             /v1/discount-codes/{Code}              (For Discount code)
  • GET             /v1/access-codes/{Code}                  (For Access code)

Code snippet to check the validity for access code is given below:

let promotionalCode = this.get('promotionalCode');
 let order = this.get('order');
   try {
     let accessCode = await this.get('store').findRecord('access-code', promotionalCode, {});
     order.set('accessCode', accessCode);
     let tickets = await accessCode.get('tickets');
     tickets.forEach(ticket => {
     ticket.set('isHidden', false);
     this.get('tickets').addObject(ticket);
     this.get('accessCodeTickets').addObject(ticket);
     this.set('invalidPromotionalCode', false);
  });
  } catch (e) {
     this.set('invalidPromotionalCode', true);
  }

 

Full code can be seen here https://github.com/fossasia/open-event-frontend/blob/development/app/components/public/ticket-list.js

Similarly for discount code we fetch the details of the discount code via the api and then validate the code. After the validation we apply the discount to the tickets applicable. Code snippet for the discount code part is given below:

try {
  let discountCode = await this.get('store').findRecord('discount-code', promotionalCode, { include: 'tickets' });
  let discountType = discountCode.get('type');
  let discountValue = discountCode.get('value');
  order.set('discountCode', discountCode);
  let tickets = await discountCode.get('tickets');
  tickets.forEach(ticket => {
     let ticketPrice = ticket.get('price');
     if (discountType === 'amount') {
       ticket.set('discount', Math.min(ticketPrice, discountValue));
       this.get('discountedTickets').addObject(ticket);
     } else {
       ticket.set('discount', ticketPrice * (discountValue / 100));
       this.get('discountedTickets').addObject(ticket);
     }
     this.set('invalidPromotionalCode', false);
  });
} catch (e) {
   if (this.get('invalidPromotionalCode')) {
      this.set('invalidPromotionalCode', true);
   }
}

 

Full code can be seen https://github.com/fossasia/open-event-frontend/blob/development/app/components/public/ticket-list.js

After promotional codes are verified we apply them to the selected tickets. In this way we apply the promotional codes to the tickets.

Resources

 

Continue ReadingHow to Make Promotional Codes Applicable on Tickets During Ordering in Open Event Frontend

Adding Multiple Select Checkboxes to Select Multiple Tickets for Discount Code in Open Event Frontend

This blog illustrates how we can add multiple select checkboxes to open event frontend using EmberJS.

Here we take an example of discount code creation in open event frontend. Since a discount code can be related to multiple tickets. Hence we should allow the organizer to choose multiple tickets from the event’s ticket list for which he/she wants the discount code to be applicable.

We start by generating a create route where we create a record for the discount code and pass the model to the template.

// routes/events/view/tickets/discount-codes/create.js

import Route from '@ember/routing/route';

export default Route.extend({
 titleToken() {
   return this.get('l10n').t('Create');
 },

 model() {
   return this.get('store').createRecord('discount-code', {
     event    : this.modelFor('events.view'),
     tickets  : [],
     usedFor  : 'ticket',
     marketer : this.get('authManager.currentUser')
   });
 }
});

We can see that we have a tickets relationship for new record of discount code which can accept multiple ticket as an array. We access this model in our template and create checkboxes for all tickets related to the event. So that the organizer can select multiple tickets for which he/she wants the discount code to be applicable.

// templates/components/forms/events/view/create-discount-code.hbs

{{t 'Select Ticket(s) applied to the discount code'}}
   {{ui-checkbox label='Select all Ticket types' name='all_ticket_types' value='tickets' checked=allTicketTypesChecked onChange=(action 'toggleAllSelection')}}

   {{#each data.event.tickets as |ticket|}}
     {{ui-checkbox label=ticket.name checked=ticket.isChecked onChange=(action 'updateTicketsSelection' ticket)}}
     <br>
   {{/each}}

This is part of the code that contain checkboxes for tickets. Full code can be seen here. We can see that first div contains the checkbox that allows us to select all the tickets at once. Once checked, this calls the action toggleAllSelection. This action is defined like this.

// components/forms/events/view/create-discount-code.js

toggleAllSelection(allTicketTypesChecked) {
     this.toggleProperty('allTicketTypesChecked');
     let tickets = this.get('data.event.tickets');
     if (allTicketTypesChecked) {
       this.set('data.tickets', tickets.slice());
     } else {
       this.get('data.tickets').clear();
     }
     tickets.forEach(ticket => {
       ticket.set('isChecked', allTicketTypesChecked);
     });
   },

 

In the toggleAllSelection action we loop over all the tickets of an event and set their isCheck property to either true or false depending on whether Select All checkbox was checked or unchecked. This is done to make all individual tickets checkboxes as checked or unchecked depending on whether we have selected all or unselected all. Also we set or unset the data.tickets array which contains all the tickets of discount-code record that were selected using checkboxes.

Going back to template in second div we render all the tickets individually with their checkbox. Each time a ticket is checked or unchecked we call updateTicketSelection action. Let us have a look at updateTicketSelection action.

// components/forms/events/view/create-discount-code.js

updateTicketsSelection(ticket) {
     if (!ticket.get('isChecked')) {
       this.get('data.tickets').pushObject(ticket);
       ticket.set('isChecked', true);
       if (this.get('data.tickets').length === this.get('data.event.tickets').length) {
         this.set('allTicketTypesChecked', true);
       }
     } else {
       this.get('data.tickets').removeObject(ticket);
       ticket.set('isChecked', false);
       this.set('allTicketTypesChecked', false);
     }
   },

 

In this action, we check if the ticket is checked or not. If it is checked we add it to the data.tickets array and further check if we have selected all the tickets or not. In case we have selected all the tickets then we also mark select all checkbox as checked through allTicketTypesChecked property. If it is unchecked we remove that ticket from the array. You can see full code here.

In this way, we implement multiple select checkboxes to select more than one data of a particular type through Ember.

Resources:

Continue ReadingAdding Multiple Select Checkboxes to Select Multiple Tickets for Discount Code in Open Event Frontend

Semantic-UI Validations for Forms in Open Event Frontend

Open Event Frontend requires forms at several places like at the time of login, for creation of events, taking the details of the user, creating discount codes for tickets etc.. Validations for these forms is a must, like in the above picture, we can see that many fields like discount code, amount etc. have been left empty, these null values when stored at backend can induce errors.

Semantic-UI makes our life easier and provides us with it’s own validations. Its form validation behavior checks data against a set of criteria or rules before passing it along to the server.

Let’s now dive deeper into Semantic validations, we’ll take discount code form as our example. The discount code form has many input fields and we have put checks at all of them, these checks are called rules here. We’ll discuss all the rules used in this form one by one

  1. Empty

Here we check if the input box with the identifier discount_amount is empty or not, if it is empty, a prompt is shown with the given message.

         identifier : ‘discount_amount’,
         rules      : [
           {
             type   : ’empty’,
             prompt : this.l10n.t(‘Please enter the discount amount’)
           }
         ]

2. Checked
Here, we validate whether the checkbox is checked or not and if it is not, show corresponding message

rules      : [
   {
     type   : ‘checked’,
     prompt : this.l10n.t(‘Please select the appropriate choices’)
   }]

3. RegExp

These checks are very important in input fields requiring passwords and codes, they specify the allowed input characters

rules      : [{
   type  : ‘regExp’,
   value : ‘^[a-zA-Z0-9_-]*$’
}]

4.Custom rules

Many a times, we require some rules which are by default not given by semantic, here we can create custom rules.

Like here, we want to check whether the user has not set max value lower than min.

$.fn.form.settings.rules.checkMaxMin = () => {
     if (this.get(‘data.minQuantity’) > this.get(‘data.maxQuantity’)) {
       return false;
     }
     return true;
   };

Here, we are creating our own custom rule checkMaxMin which returns boolean value depending upon minQuantity and maxQuantity. Now, this can be directly used as a rule

identifier : ‘min_order’,
optional   : true,
rules      : [
 {
  type   : ‘checkMaxMin’,
  prompt : this.l10n.t(‘Minimum value should not be greater than maximum’)
 }]

You can find the above code here

Additional Resources

Continue ReadingSemantic-UI Validations for Forms in Open Event Frontend