I am working on a site that will be selling courses on and off-line and am in the process of designing a module to handle this. I would be interested in people's opinions on the best way to go about doing this.
The requirements for courses are as follows:
- A course is delivered on one or more dates/times in one or more locations
- A course has a start date and an end date, in line with the above dates
- A course has one or more tutors. A tutor is represented by a tutor content type so they need to be linked to the relevant course nodes
- Each course usually has a maximum number of places that are available
- Each course also has a minimum number of places that need to be sold in order for the course to run
- There can be multiple instances of any given course as the same course will be offered on different dates, so it would be nice not to have to re-enter all the details every time
- Once a course has started or ended (as set by options) then it is no longer available to be bought, even if there are places left
- Courses can be flagged as being available to be sold online or not. If not then instead of adding to cart, there is a link to an application form for the course or a link to contact details
- Various combinations of courses and multiples of the same course attract various discounts
Initially, I feel that it is not an option to create courses as an ecommerce product sub-type – that doesn't allow access to the benefits of a first-rate content type. Instead I plan to create courses as stand-alone nodes that integrate with ecommerce.
It makes sense to hook into the Event API for the course dates and repeating session dates for each course. The course module will then have to use this information to hook back into ecommerce to control availability of the course.
Is it possible for 'add cart' to be replaced with 'add to waiting list' until the minimum number of places have been sold, at which point the customer is informed that they can now complete the purchase? The alternative would be to sell the courses as normal and then have a refund process if minimum numbers are not achieved. Can ecommerce handle refunds currently?
Should I use CCK to construct the fields for courses or roll my own? Should I use a node relationship module to handle tutors or roll that into the module as well? Should I use Views to display the available courses – Views would allow me to filter the courses shown to those available to sell based on dates – or should I display courses through the normal ecommerce mechanism and write code in the course module to tell ecommerce when to display a course?
I am very fresh to ecommerce having only just started to use it so I am currently unfamiliar with the hooks it provides and how they can be used to achieve what I want. I have created a simple product type module as a proof of concept and that has convinced me that I need a solution that works with proper nodes.
I would be most interested in anyone's thoughts, ideas of suggestions on this as it is certainly possible that there will be other people who want to do similar things at some point.
Comments
Similar Project
I'm working on a way to sell places in a competition. My needs are similar except that
You may want to add this additional requirement:
Did anyone ever do this?
Did anyone ever locate a module for doing this (selling courses online)?
Sell seminars online.
Hello Nasi,
I've build a drupal ecommerce site wellesleyinstitute.com, the part of the site that was selling courses had identical requirements as you have with the exception that tutors are users in a special group and we didn't have minimum number of people requirement, but it should be trivial to implement.
There's no module that does this everything you want, the following modules drive the site
event, ecommerce modules, I wrote a seminar module based on ecommerce tangible module. Essentially replace the strings as you need and add logic that suits your workflow.
Signup module was hacked so that it doesn't insert any UI elements, we use signup_sign_up_user method
Paste this code into into your seminar module in seminar_productapi function
case 'on payment completion':
//$node->uid is valid because ecommerce store_payment_status_complete_callback inserts it into $node array passed here.
if ($node->manage_stock) {
db_query('UPDATE {ec_product_seminar} SET stock = %d WHERE nid = %d', $node->stock - $node->qty, $node->nid);
}
global $user;
$signup_values = array('nid'=>$node->nid,'uid'=>$node->uid);
signup_sign_up_user($signup_values);
attendance_signup($node->nid,$node->uid);
break;
case 'insert':
$test_num_rows_q = db_fetch_array(db_query("SELECT nid from {signup} WHERE nid = %d",$node->nid));
if (!$test_num_rows_q['nid']){
//need to insert into signup
$defaults = db_fetch_array(db_query("SELECT * from {signup} WHERE nid = 0"));
$values = array(
$node->nid,
$defaults['forwarding_email'],
$defaults['send_confirmation'],
$defaults['confirmation_email'],
$defaults['send_reminder'],
$defaults['reminder_days_before'],
$defaults['reminder_email'],
$defaults['close_signup_limit'],
);
if (isset($values)) {
db_query("INSERT INTO {signup} (nid, forwarding_email, send_confirmation, confirmation_email, send_reminder, reminder_days_before, reminder_email, close_signup_limit) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', %d)", $values);
}
}
return db_query("INSERT INTO {ec_product_seminar} (nid, vid, stock, manage_stock) VALUES ('%d', '%d', '%d', '%d')", $node->nid, $node->vid, $node->stock, $node->manage_stock);
break;
case 'update':
$test_num_rows_q = db_fetch_array(db_query("SELECT nid from {signup} WHERE nid = %d",$node->nid));
if (!$test_num_rows_q['nid']){
$defaults = db_fetch_array(db_query("SELECT * from {signup} WHERE nid = 0"));
$values = array(
$node->nid,
$defaults['forwarding_email'],
$defaults['send_confirmation'],
$defaults['confirmation_email'],
$defaults['send_reminder'],
$defaults['reminder_days_before'],
$defaults['reminder_email'],
$defaults['close_signup_limit'],
);
if (isset($values)) {
db_query("INSERT INTO {signup} (nid, forwarding_email, send_confirmation, confirmation_email, send_reminder, reminder_days_before, reminder_email, close_signup_limit) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', %d)", $values);
}
}
return db_query("UPDATE {ec_product_seminar} SET stock = '%d', manage_stock = '%d', availability = '%s' WHERE vid = '%d'", $node->stock, $node->manage_stock, $node->availability, $node->vid);
break;
case 'delete':
db_query('DELETE FROM {ec_product_seminar} WHERE nid = %d', $node->nid);
db_query("DELETE FROM {signup} WHERE nid = %d", $node->nid);
db_query("DELETE FROM {signup_log} WHERE nid = %d", $node->nid);
break;
Attendance is another module that we wrote to track who has signed up, let me know if you want it.
You probably want to change (add logic that represents your workflow) how 'add to cart link' is displayed, look at product.module
Install CCK and CCK modules that you want, create a content type for your courses.
When you do Create Content>>MyContentType, scroll down to the 'Product' foldout, and select 'seminar' or whatever you named your tangible-like module.
You will have price and SKU. Sku is how we identify people who took the course. Many nodes can be created representing seminars that happen at different dates, but they can have the same SKU. You will find node_clone module useful.
I'll follow up on this thread if I'll remember something that I've missed.
Feel free to get in touch if you are unsure about something.
Best regards,
Oleg Mitsura
omitsura@gmail.com
Do you mind providing your modules?
From your message, it looks like you're saying to take the tangible module, rename it to "seminar", and insert the above changes.
Do you mind just supplying the seminar, signup and attendance modules you created?
Thanks!