courtly
courtly
courtly
courtly

Upcoming Events

San Francisco CiviCRM Meetup - February 8th, 2012
February 8th, 2012
Come meet others from the Bay Area who are interested in, using or developing (more...)

UK usergroup - London meetup
February 8th, 2012
Come and meet others from the UK that are using CiviCRM or are interested in (more...)

London user and administrator training
February 23rd, 2012
A comprehensive two day hands on training course covering the configuration, (more...)

CiviCRM London sprint Feb 2012
February 27th, 2012
Following the CiviCRM training here in London, we will have a CiviCRM code (more...)

Philadelphia - CiviCRM Meetup for Q1 2012
March 13th, 2012

UK South West - CiviCRM Meetup
March 20th, 2012
Come meet others from the Area who are interested in, using or developing for (more...)

[Bristol, UK] user and administrator training
March 21st, 2012
A comprehensive hands on training course covering the configuration, (more...)

San Francisco user and administrator training
March 29th, 2012
A comprehensive two day hands on training course covering the configuration, (more...)

CiviCRM Usability, Test and Code Sprint - San Francisco (March 2012)
March 29th, 2012
This usability, code and test sprint is targeted at CiviCRM users and (more...)

CiviCon 2012 San Francisco Bay Area - April 2nd 2012
April 2nd, 2012
CiviCon is THE annual event bringing together the people who use, develop, (more...)

CiviCRM Documentation, Test and Code Sprint - after CiviCon San Francisco (April 2012)
April 4th, 2012
This sprint is targeted at CiviCRM users and developers who want to work on (more...)

CiviCRM Components

Tools for engaging your supporters...

CiviContribute


CiviEvent


CiviMail


CiviMember


CiviReport


Event discounts / coupons for event registration

Not Just a Contact Database

These optional components give you more power to connect and engage your supporters.

  • civiCASE

  • Case management for clients and constituents.

  • civiEVENT

  • Online event registration and participant tracking.

  • civiMEMBER

  • Online signup and membership management.

  • civiMAIL

  • Personalized email blasts and newsletters.

  • civiREPORT

  • Report generation and template management.

May 21, 2009 - 22:47 — Deepak.Srivastava

A Make It Happen to move this into core for CiviCRM is currently running for v4.1. You can help make it happen by contributing here

Discounting events has been requested on the forums a few times. It also popped up both on IRC yesterday and was also needed on civicrm.org for our upcoming UK Training Camp. We decided to see if we could implement this via hooks and minimal / no changes to the core code. Turned out we can implement this this feature using three civicrm hooks and a helper fuction. We improved the core a wee bit by allowing the buildForm hook to inform the tpl system of any form elements added. The tpl then places these form elements at the top of the form. An event can have zero or more coupons. Coupons for an event are stored in the option group / value table. Each coupon code is a string (chosen by admin) and can be used a certain number of times. Coupons can also be used forever. A valid coupon gives the user a percentage discount on the event. Giving the coupon a 100% value basically gives the user a free registration. Coupons can be sent in via the URL or can be entered into the registration form.

Here is the recipe for making it work for an event sign-up page -

  • In a drupal module (lets call it civitest), Implement civicrm_buildAmount hook to add a form field to accept the coupon code. As the code checks for coupon value in both get and post request, this step is optional if coupon is being passed by url - e.g http://...civicrm/event/register?id=X&reset=1&discountCode=123456789.
    
    function civitest_civicrm_buildForm( $formName, &$form ) {
        if ( $formName == 'CRM_Event_Form_Registration_Register' &&
             $form->getVar( '_eventId' ) == 3 ) { //use event id here
            $form->addElement( 'text', 'discountCode', ts( 'Discount Code' ) );
    
            // also assign to template
            $template =& CRM_Core_Smarty::singleton( );
            $beginHookFormElements = $template->get_template_vars( 'beginHookFormElements' );
            if ( ! $beginHookFormElements ) { $beginHookFormElements = array( ); }
            $beginHookFormElements[] = 'discountCode';
            $form->assign( 'beginHookFormElements', $beginHookFormElements );
    
            $discountCode = CRM_Utils_Request::retrieve( 'discountCode', 'String', $form, false, null, $_REQUEST );
            if ( $discountCode ) {
                $defaults = array( 'discountCode' => $discountCode );
                $form->setDefaults( $defaults );
            }
        }
    }
    
    
  • Implement civicrm_buildAmount hook to apply the discount.
    
    function civitest_civicrm_buildAmount( $pageType, &$form, &$amount ) {
        $eventID = $form->getVar( '_eventId' );
        if ( $pageType != 'event' ||
             $eventID != 3 ) { // use event ID here
            return;
        }
        $discountCode = CRM_Utils_Request::retrieve( 'discountCode', 'String', $form, false, null, $_REQUEST );
        if ( ! $discountCode ) { return; }
    
        list( $discountID, $discountPercent, $discountNumber ) = _civitest_discountHelper( $eventID, $discountCode );
        if ( $discountNumber <= 0 ) { return; }
    
        foreach ( $amount as $amountId => $amountInfo ) {
            $amount[$amountId]['value'] = $amount[$amountId]['value'] -
                ceil($amount[$amountId]['value'] * $discountPercent / 100);
            $amount[$amountId]['label'] = $amount[$amountId]['label'] .
                "\t - with {$discountPercent}% discount";
        }
    }
    
  • Implement civicrm_postProcess hook to decrement the coupon usage counter by one. Ah! yes coupon code can be configured to be used for one / finite / infinite number of times :)
        
    function civitest_civicrm_postProcess( $class, &$form ) {
        $eventID = $form->getVar( '_eventId' );
        if ( ! is_a($form, 'CRM_Event_Form_Registration_Confirm') || $eventID != 3 ) { return; }
            
        $discountCode = CRM_Utils_Request::retrieve( 'discountCode', 'String', $form, false, null, $_REQUEST );
        if ( ! $discountCode ) { return; }
    
        list( $discountID, $discountPercent, $discountNumber ) = _civitest_discountHelper( $eventID, $discountCode );
        if ( ! $discountID || $discountNumber <= 0 || $discountNumber == 123456789 ) { return; }
    
        $query = "UPDATE civicrm_option_value v SET v.weight = v.weight - 1 WHERE  v.id = %1 AND v.weight > 0";
        $params = array( 1 => array( $discountID, 'Integer' ) );
        CRM_Core_DAO::executeQuery( $query, $params );
    }
    
  • Use following query to add a coupon (say '1234XYZ5678') to your database with a validity of two usage.
    
    INSERT INTO `civicrm_option_group` (`name`, `description`, `is_reserved`, `is_active`) 
    VALUES ('event_discount_3', 'Event Discount', 0, 1); # 3 is the event ID here
    
    SELECT @option_group_id_ed := max(id) from civicrm_option_group where name = 'event_discount_3';
    
    INSERT INTO `civicrm_option_value` (`option_group_id`, `label`, `value`, `name`, `weight`, `is_active`) 
    VALUES (@option_group_id_ed, 'Discount Code', 50, '1234XYZ5678', 2, 1); 
    

    weight=2 implies this coupon can't be used after second usage. value=50 indicates the discount percent. 50% for this coupon. name='1234XYZ5678' is the coupon code.

  • Helper function that was used while implmenting the hooks above -
    
    function _civitest_discountHelper( $eventID, $discountCode ) {
        $sql = "
    SELECT v.id as id, v.value as value, v.weight as weight
    FROM   civicrm_option_value v,
           civicrm_option_group g
    WHERE  v.option_group_id = g.id
    AND    v.name = %1
    AND    g.name = %2";
    
        $params = array( 1 => array( $discountCode              , 'String' ),
                         2 => array( "event_discount_{$eventID}", 'String' ) );
        $dao = CRM_Core_DAO::executeQuery( $sql, $params );
        if ( $dao->fetch( ) ) {
            // ensure discountPercent is a valid numeric number <= 100
            if ( $dao->value && is_numeric( $dao->value ) && $dao->value >= 0 &&
                 $dao->value <= 100 && is_numeric( $dao->weight ) ) {
                return array( $dao->id, $dao->value, $dao->weight );
            }
        }
        return array( null, null, null );
    }
    

As the whole functionality looks powerful, team is on a discussion to add this feature to core in a future release

( categories: )

Comments

Drupal module available

Drupal module available here http://drupal.org/project/civievent_discount

modifying pricesets

BTW, as of 3.3.x the buildAmount hook supports manipulating price sets. See the civitest.module.sample for example code.

Thanks and a question

Thanks very much for the code. I have it working nicely in a joomla instance with civicrm 3.1.3.

Forgive my ignorance here, I'm new with civicrm, but I wonder why the author chose to add a form field via code. Can't we add a custom field in the admin panel and reference it in code?

I was about to try doing that so the discount field is the last form field in my registration instead of the first.

not sure i understand the question ..

might be better to discuss on the forums. The form field is ignored if the discount code is sent in via the GET parameter

lobo

This Is What I Need, but I Don't Know How to Do it

I'm looking at this and this is exactly what I need for a site I'm building but, not being too versed in coding, I can't figure out from these instructions how to implement. Silly question: Since it says "In a drupal module (lets call it civitest), Implement civicrm_buildAmount hook to add a form field to accept the coupon code," couldn't some kind heart provide a Drupal module for this and add it to Drupal's module repository? It looks to me as if there are other steps required other than just setting up a module, so, if provided, perhaps the module I am requesting could provide, in its README file, a step-by-step (geared for us proverbial "dummies") so that even I could implement this type of discount?

Please hire a developer from ..

http://civicrm.org/professional/

and create a flexible discounting module that implements a lot of the UI and allows users to pick the event and discounts. That will help your ORG and the community

lobo

Any plans to update this for 3.0?

We have been using this little gem for a while now, upgraded a staging site to 3.0 (great work by the way!), however we are unable to get this working in 3.0.1 (field won't even display on the form now). Are major changes required to make this compatible?

Administrator Thinks This Should Work in 3.0 and 3.1

David Greenberg on forum post http://forum.civicrm.org/index.php/topic,10194.0.html says

"Took a quick look at the hooks used in that example - and they're certainly all available for 3.0 and 3.1. Not sure why the one poster reported a problem - but might have been a coding issue ..."

Hola

This is cool, But was wondering if it can be implemented in conjunction with other payment methods.. meaning i'd like to offer vouchers for free/prepaid entry.

So that the event manager can issue vouchers to groups, And regular people outside of them groups can register at full price.

Thanks again for this, It's awesome!

Thank You

Thanks for sharing this solution, we look forward to future releases that include this in UI, but will happily use this for now. Keep up the great work.

template access through hooks is cool

nice feature :)

especially like template access through hooks - very cool. Reckon I can use this template to move lots of my customiz/sations out of core and into hooks. supercool.

Fantastic - what about price sets?

Just wondering how / if that could work with a price set? Perhaps each price element needs an attribute to indicate that it is "discountable"?

No support for price-sets at this stage

price sets requires a redesign and some thinking about how to structure it better for this and other requests. We expect this to happen in a 3.x release. If important to you / your org and you want it ina earlier release consider sponsoring the work / retaining a developer

How to check discount for

How to check discount for additional particpants. Any idea. If we set the discount code for main participant the additional participants get the discounted amount instead of original amount.