Upcoming Events
NYC CiviCRM Meetup - September 7th
September 7th, 2010
This next NYC meetup will feature a case study or 2, a look at what's new in (more...)
Configuring, Customizing and Extending CiviCRM - New York
September 16th, 2010
This comprehensive two-day hands on training course is targeted at (more...)
CiviCRM User and Administrator Training - New York
September 16th, 2010
A comprehensive two day hands on training course covering the configuration, (more...)
CiviCRM Code and Test Sprint - New York
September 18th, 2010
This code and test sprint is targeted at experienced developers who want to (more...)
CiviCRM Toronto Meetup
September 21st, 2010
Come meet others from the Toronto Area who are interested in, using or (more...)
CiviCRM Philly Meetup – September 2010
September 23rd, 2010
Come meet others from the Philadelphia Area who are interested in, using or (more...)
CiviCRM Seminar - Dublin
September 28th, 2010
NfP Services are hosting a free seminar at The IBOA, Stephen St Upper, Dublin 8 (more...)
London developer and implementer training
September 30th, 2010
This comprehensive two-day hands on training course is targeted at implementers, (more...)
London user and administrator training
September 30th, 2010
A comprehensive two day hands on training course covering the configuration, (more...)
Berlin user and administrator training
October 6th, 2010
A comprehensive one day hands on training course covering the configuration, (more...)
Berlin developer and implementer training
October 7th, 2010
This comprehensive one-day hands on training course is targeted at implementers, (more...)
Benelux meetup in Brussels: Connect, communicate and activate your supporters and constituents
October 11th, 2010
Come meet others who are interested in, using or developing for CiviCRM. For (more...)
CiviCRM Toronto Meetup
October 19th, 2010
Come meet others from the Toronto Area who are interested in, using or (more...)
CiviCRM Toronto Meetup
November 16th, 2010
Come meet others from the Toronto Area who are interested in, using or (more...)
Event discounts / coupons for event registration
- Not Just a Contact Database
-
These optional components give you more power to connect and engage your supporters.

civiCONTRIBUTE
Online fundraising and donor management.

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.
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







Comments
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