commerce_recurring.module in Commerce Recurring Framework 7.2
Same filename and directory in other branches
Commerce recurring module file.
File
commerce_recurring.moduleView source
<?php
/**
* @file
* Commerce recurring module file.
*/
/**
* Implements hook_entity_info().
*/
function commerce_recurring_entity_info() {
$return = array(
'commerce_recurring' => array(
'label' => t('Commerce recurring entity'),
'plural label' => t('Commerce recurring entities'),
'entity class' => 'Entity',
'controller class' => 'EntityAPIController',
'views controller class' => 'EntityDefaultViewsController',
'base table' => 'commerce_recurring',
'uri callback' => 'commerce_recurring_uri',
'fieldable' => TRUE,
'entity keys' => array(
'id' => 'id',
'bundle' => 'type',
),
'bundle keys' => array(
'bundle' => 'type',
),
'bundles' => array(
'product' => array(
'label' => t('Commerce recurring product'),
),
'order' => array(
'label' => t('Commerce recurring order'),
),
),
'view modes' => array(
'full' => array(
'label' => t('Admin display'),
'custom settings' => FALSE,
),
),
'module' => 'commerce_recurring',
'access arguments' => array(
'user key' => 'uid',
'access tag' => 'commerce_recurring_access',
),
'permission labels' => array(
'singular' => t('recurring entity'),
'plural' => t('recurring entities'),
),
// Prevent Redirect from altering the line item form.
'redirect' => FALSE,
),
);
return $return;
}
/**
* Bundles of the recurring entity.
*/
function commerce_recurring_types() {
return array(
'product',
'order',
);
}
/**
* Ensures that the price and interval fields are created.
*/
function commerce_recurring_configure_product_type() {
// Create the price instances for the recurring product type.
commerce_recurring_price_create_instance('commerce_price', 'commerce_product', 'recurring', t('Price'), 0, 'calculated_sell_price');
// Set the formatter display as hidden by default for the additional price
// fields.
commerce_recurring_price_create_instance('commerce_recurring_ini_price', 'commerce_product', 'recurring', t('Initial price'), 0, 'calculated_sell_price', array(
'type' => 'hidden',
));
commerce_recurring_price_create_instance('commerce_recurring_rec_price', 'commerce_product', 'recurring', t('Recurring price'), 0, 'calculated_sell_price', array(
'type' => 'hidden',
));
// Create interval fields.
$fields = array(
'commerce_recurring_ini_period' => array(
'field_name' => 'commerce_recurring_ini_period',
'cardinality' => 1,
'type' => 'interval',
),
'commerce_recurring_rec_period' => array(
'field_name' => 'commerce_recurring_rec_period',
'cardinality' => 1,
'type' => 'interval',
),
'commerce_recurring_end_period' => array(
'field_name' => 'commerce_recurring_end_period',
'cardinality' => 1,
'type' => 'interval',
),
);
// Set instance settings for the interval fields.
$basic_interval_instance = array(
'entity_type' => 'commerce_product',
'bundle' => 'recurring',
'widget' => array(
'type' => 'interval_default',
),
'display' => array(
'default' => array(
'label' => 'hidden',
'type' => 'interval_default',
),
),
'settings' => array(
'allowed_periods' => array(
'day' => 'day',
'week' => 'week',
'month' => 'month',
'year' => 'year',
),
),
'weight' => 0,
);
$instances = array(
'commerce_recurring_ini_period' => $basic_interval_instance,
'commerce_recurring_rec_period' => $basic_interval_instance,
'commerce_recurring_end_period' => $basic_interval_instance,
);
$instances['commerce_recurring_ini_period']['label'] = t('Initial period');
$instances['commerce_recurring_rec_period']['label'] = t('Recurring period');
$instances['commerce_recurring_end_period']['label'] = t('End period');
// Create fields & instances if necessary.
foreach ($fields as $field_name => $field) {
$info_field = field_info_field($field_name);
$instance = field_info_instance('commerce_product', $field_name, 'recurring');
if (empty($info_field)) {
field_create_field($field);
}
if (empty($instance)) {
$instance = $instances[$field_name];
$instance['field_name'] = $field_name;
field_create_instance($instance);
}
}
}
/**
* Ensures that the fields for the recurring entity are created.
*/
function commerce_recurring_configure_recurring_entity_type() {
// Product bundle.
// Create a price field to save the price of the product at the moment of
// the entity creation.
commerce_recurring_price_create_instance('commerce_recurring_fixed_price', 'commerce_recurring', 'product', t('Fixed price'), 0, 'calculated_sell_price');
// Entity reference fields for relating the recurring entity to the product
// that is recurring and also with the orders.
$fields = array(
'commerce_recurring_ref_product' => array(
'field_name' => 'commerce_recurring_ref_product',
'type' => 'entityreference',
'cardinality' => 1,
'entity_types' => array(
'commerce_recurring',
),
'module' => 'commerce_recurring',
'translatable' => FALSE,
'settings' => array(
'target_type' => 'commerce_product',
'handler' => 'base',
'handler_settings' => array(),
),
),
'commerce_recurring_order' => array(
'field_name' => 'commerce_recurring_order',
'type' => 'entityreference',
'cardinality' => FIELD_CARDINALITY_UNLIMITED,
'entity_types' => array(
'commerce_recurring',
),
'module' => 'commerce_recurring',
'translatable' => FALSE,
'settings' => array(
'target_type' => 'commerce_order',
'handler' => 'base',
'handler_settings' => array(),
),
),
);
// ER instances.
$basic_instance = array(
'entity_type' => 'commerce_recurring',
'bundle' => 'product',
'required' => TRUE,
'settings' => array(),
'display' => array(),
'widget' => array(
'type' => 'entityreference_autocomplete',
'module' => 'entityreference',
),
);
$instances = array(
'commerce_recurring_ref_product' => $basic_instance,
'commerce_recurring_order' => $basic_instance,
);
$instances['commerce_recurring_ref_product']['label'] = t('Referenced product');
$instances['commerce_recurring_order']['label'] = t('Referenced orders');
// Create fields & instances if necessary.
foreach ($fields as $field_name => $field) {
$info_field = field_info_field($field_name);
$instance = field_info_instance('commerce_recurring', $field_name, 'product');
if (empty($info_field)) {
field_create_field($field);
}
if (empty($instance)) {
$instance = $instances[$field_name];
$instance['field_name'] = $field_name;
field_create_instance($instance);
}
}
// @TODO Order bundle.
}
/**
* Creates a instance of a price field on the specified bundle.
*
* @param string $field_name
* The name of the field; if it already exists, a new instance of the existing
* field will be created. For fields governed by the Commerce modules, this
* should begin with commerce_.
* @param string $entity_type
* The type of entity the field instance will be attached to.
* @param string $bundle
* The bundle name of the entity the field instance will be attached to.
* @param string $label
* The label of the field instance.
* @param int $weight
* The default weight of the field instance widget and display.
* @param bool $calculation
* A string indicating the default value of the display formatter's
* calculation setting.
* @param array $display
* An array of default display data used for the entity's current view modes.
* @param bool $required
* Determines if the field is required.
*/
function commerce_recurring_price_create_instance($field_name, $entity_type, $bundle, $label, $weight = 0, $calculation = FALSE, $display = array(), $required = FALSE) {
// If a field type we know should exist isn't found, clear the Field cache.
if (!field_info_field_types('commerce_price')) {
field_cache_clear();
}
// Look for or add the specified price field to the requested entity bundle.
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
if (empty($field)) {
$field = array(
'field_name' => $field_name,
'type' => 'commerce_price',
'cardinality' => 1,
'entity_types' => array(
$entity_type,
),
'translatable' => FALSE,
);
field_create_field($field);
}
if (empty($instance)) {
$instance = array(
'field_name' => $field_name,
'entity_type' => $entity_type,
'bundle' => $bundle,
'label' => $label,
'required' => $required,
'settings' => array(),
// Because this widget is locked, we need it to use the full price widget
// since the currency option can't be adjusted at the moment.
'widget' => array(
'type' => 'commerce_price_full',
'weight' => $weight,
'settings' => array(
'currency_code' => 'default',
),
),
'display' => array(),
);
$entity_info = entity_get_info($entity_type);
// Spoof the default view mode and node teaser so its display type is set.
$entity_info['view modes'] += array(
'default' => array(),
'node_teaser' => array(),
);
foreach ($entity_info['view modes'] as $view_mode => $data) {
$instance['display'][$view_mode] = $display + array(
'label' => 'hidden',
'type' => 'commerce_price_formatted_amount',
'settings' => array(
'calculation' => $calculation,
),
'weight' => $weight,
);
}
field_create_instance($instance);
}
}
/**
* Returns an initialized Commerce Recurring entity.
*
* Initializes all the properties defined plus the two entity reference fields.
* - product_id: Commerce product id, populates commerce_recurring_ref_product.
* - order_ids: Array of Commerce order ids, populates commerce_recurring_order.
*
* @see commerce_recurring_entity_property_info()
* @see commerce_recurring_configure_recurring_entity_type()
*
*/
function commerce_recurring_new(array $values = array()) {
$values += array(
'id' => NULL,
'type' => isset($values['type']) ? $values['type'] : 'product',
'status' => 1,
'start_date' => '',
'due_date' => '',
'end_date' => '',
'uid' => '',
'quantity' => 1,
'data' => array(),
);
// Retrieve the properties to get from fields.
if (isset($values['product_id'])) {
$values['commerce_recurring_ref_product'] = array(
LANGUAGE_NONE => array(
0 => array(
'target_id' => $values['product_id'],
'taget_type' => 'commerce_product',
),
),
);
unset($values['product_id']);
}
if (isset($values['order_ids']) && is_array($values['order_ids'])) {
foreach ($values['order_ids'] as $delta => $order_id) {
$values['commerce_recurring_order'] = array(
LANGUAGE_NONE => array(
$delta => array(
'target_id' => $order_id,
'taget_type' => 'commerce_order',
),
),
);
}
unset($values['order_ids']);
}
return entity_create('commerce_recurring', $values);
}
/**
* Generate a new recurring entity from a product.
* We set the price to the initial price from the line item if available.
* The start date for the recurring is the current date unless a set one is
* specified.
* Due date is start date + initial date.
* End date is optional, start date + end period.
*
* @param array $order
* Commerce order to associate the recurring entity with.
* @param array $product
* Commerce product associated with the recurring entity.
* @param int $fixed_price
* @param int $quantity
*
* @return entity $recurring_entity
*/
function commerce_recurring_new_from_product($order, $product, $fixed_price, $quantity) {
if (empty($product)) {
return;
}
$product_wrapper = entity_metadata_wrapper('commerce_product', $product);
$date = new DateObject();
$start_date = $date
->getTimestamp();
$due_date = clone $date;
if (!empty($product->commerce_recurring_ini_period)) {
$initial_interval = $product_wrapper->commerce_recurring_ini_period
->value();
}
if (empty($initial_interval)) {
if (!empty($product->commerce_recurring_rec_period)) {
$initial_interval = $product_wrapper->commerce_recurring_rec_period
->value();
}
}
if (!empty($initial_interval)) {
interval_apply_interval($due_date, $initial_interval, TRUE);
}
$due_date = $due_date
->getTimestamp();
if (!empty($product->commerce_recurring_end_period)) {
$end_interval = $product_wrapper->commerce_recurring_end_period
->value();
interval_apply_interval($date, $end_interval, TRUE);
$end_date = $date
->getTimestamp();
}
$values = array(
'product_id' => $product->product_id,
'order_ids' => array(
$order->order_id,
),
'uid' => $order->uid,
'start_date' => $start_date,
'due_date' => $due_date,
'end_date' => isset($end_date) ? $end_date : NULL,
);
$recurring_entity = commerce_recurring_new($values);
// Add the price and quantity at the moment of purchase.
if (!empty($fixed_price)) {
$recurring_entity->commerce_recurring_fixed_price[LANGUAGE_NONE][0] = $fixed_price;
$recurring_entity->quantity = empty($quantity) ? 1 : $quantity;
}
entity_save('commerce_recurring', $recurring_entity);
return $recurring_entity;
}
/**
* Loads a recurring entity by ID.
*/
function commerce_recurring_load($recurring_id) {
$recurring_entities = commerce_recurring_load_multiple(array(
$recurring_id,
), array());
return $recurring_entities ? reset($recurring_entities) : FALSE;
}
/**
* Loads multiple recurring entities by ID or based on a set of matching
* conditions.
*
* @see entity_load()
*
* @param $recurring_ids
* An array of commerce recurring IDs.
* @param $conditions
* An array of conditions to filter loaded orders by on the
* {commerce_recurring} table in the form 'field' => $value.
* @param $reset
* Whether to reset the internal commerce recurring loading cache.
*
* @return
* An array of order objects indexed by id.
*/
function commerce_recurring_load_multiple(array $recurring_ids = array(), array $conditions = array(), $reset = FALSE) {
return entity_load('commerce_recurring', $recurring_ids, $conditions, $reset);
}
/**
* Determine if a product has recurring properties.
*
* @param $product
* Commerce product.
* @return bool
*/
function commerce_recurring_product_is_recurring($product) {
// If the product is of type recurring, we don't need to look anything else.
if ($product->type == 'recurring') {
return TRUE;
}
// In case of other product types, they still might be recurring ones, check
// for the presence of any of the recurring fields.
$fields = array(
'commerce_recurring_ini_price',
'commerce_recurring_rec_price',
'commerce_recurring_ini_period',
'commerce_recurring_rec_period',
'commerce_recurring_end_period',
);
foreach ($fields as $field_name) {
if ($items = field_get_items('commerce_product', $product, $field_name)) {
return TRUE;
}
}
// @TODO: Add a hook to be able to set recurring product types otherwise.
return FALSE;
}
/**
* Return all the line items that contain a recurring product in an order
* context.
*
* @param $order
* Commerce order.
*
* @return array of commerce line items that have recurring capabilities or
* empty array if none of the products from the order is recurring.
*/
function commerce_recurring_order_load_recurring_line_items($order) {
if (empty($order->order_id)) {
return array();
}
$recurring_entities = array();
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
foreach ($order_wrapper->commerce_line_items as $line_item_wrapper) {
if (in_array($line_item_wrapper->type
->value(), commerce_product_line_item_types())) {
$product = $line_item_wrapper->commerce_product
->value();
if (commerce_recurring_product_is_recurring($product)) {
$recurring_entities[$order->order_id][] = $line_item_wrapper
->value();
}
}
}
return $recurring_entities;
}
/**
* Load all recurring entities in an order context.
*
* @param $order
* Commerce order.
* @return array
*/
function commerce_recurring_load_by_order($order) {
$return = array();
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'commerce_recurring')
->propertyCondition('status', TRUE)
->fieldCondition('commerce_recurring_order', 'target_id', $order->order_id);
$result = $query
->execute();
if (!empty($result['commerce_recurring'])) {
foreach ($result['commerce_recurring'] as $recurring) {
$return[] = entity_load_single('commerce_recurring', $recurring->id);
}
}
return $return;
}
/**
* Mark a recurring entity as inactive.
*
* @param $recurring_entity
* A commerce recurring entity.
*/
function commerce_recurring_stop_recurring($recurring_entity) {
$recurring_entity->status = 0;
rules_invoke_event('commerce_recurring_stop_recurring', $recurring_entity);
entity_save('commerce_recurring', $recurring_entity);
}
/**
* Implements hook_commerce_order_status_info().
*/
function commerce_recurring_commerce_order_status_info() {
$order_statuses = array();
// Create a new status before Pending so we can generate orders in recurring
// processes without intefering with the current cart for the user.
$order_statuses['recurring_pending'] = array(
'name' => 'recurring_pending',
'title' => t('Pending (Recurring order)'),
'state' => 'pending',
'weight' => -1,
);
return $order_statuses;
}
/**
* Implements hook_cron().
*/
function commerce_recurring_cron() {
// Invoke our cron Rules event.
rules_invoke_all('commerce_recurring_cron');
}
/**
* Implements hook_permission().
*/
function commerce_recurring_permission() {
$permissions = array();
$permissions += commerce_entity_access_permissions('commerce_recurring');
return $permissions;
}
/**
* Access callback for the entity API.
*
* @param $op
* The operation being performed. One of 'view', 'update', 'create', 'delete'
* or just 'edit' (being the same as 'create' or 'update').
* @param null $type
* @param $account
* The user to check for. Leave it to NULL to check for the global user.
* @return boolean
* Whether access is allowed or not.
*/
function commerce_recurring_access($op, $type = NULL, $account = NULL) {
return user_access('administer commerce_recurring entities', $account);
}
/**
* Entity uri callback: gives modules a chance to specify a path for a recurring
* entity.
* @param $recurring_entity
* @return null or $uri
*/
function commerce_recurring_uri($recurring_entity) {
// Allow modules to specify a path, returning the first one found.
foreach (module_implements('commerce_recurring_uri') as $module) {
$uri = module_invoke($module, 'commerce_recurring_uri', $recurring_entity);
// If the implementation returned data, use that now.
if (!empty($uri)) {
return $uri;
}
}
return NULL;
}
/**
* Implements hook_commerce_checkout_router().
* @param $order
* @param $checkout_page
*/
function commerce_recurring_commerce_checkout_router($order, $checkout_page) {
// Just in the last page of the checkout if the order is not anonymous,
// we get the recurring entities belonging to the order and save them so
// they don't end up as anonymous.
if ($checkout_page['page_id'] == 'complete' && !empty($order->uid)) {
$recurring_entities = commerce_recurring_load_by_order($order);
foreach ($recurring_entities as $recurring_entity) {
if (empty($recurring_entity->uid)) {
$recurring_entity->uid = $order->uid;
entity_save('commerce_recurring', $recurring_entity);
}
}
}
}
Functions
Name![]() |
Description |
---|---|
commerce_recurring_access | Access callback for the entity API. |
commerce_recurring_commerce_checkout_router | Implements hook_commerce_checkout_router(). |
commerce_recurring_commerce_order_status_info | Implements hook_commerce_order_status_info(). |
commerce_recurring_configure_product_type | Ensures that the price and interval fields are created. |
commerce_recurring_configure_recurring_entity_type | Ensures that the fields for the recurring entity are created. |
commerce_recurring_cron | Implements hook_cron(). |
commerce_recurring_entity_info | Implements hook_entity_info(). |
commerce_recurring_load | Loads a recurring entity by ID. |
commerce_recurring_load_by_order | Load all recurring entities in an order context. |
commerce_recurring_load_multiple | Loads multiple recurring entities by ID or based on a set of matching conditions. |
commerce_recurring_new | Returns an initialized Commerce Recurring entity. |
commerce_recurring_new_from_product | Generate a new recurring entity from a product. We set the price to the initial price from the line item if available. The start date for the recurring is the current date unless a set one is specified. Due date is start date + initial date. End date… |
commerce_recurring_order_load_recurring_line_items | Return all the line items that contain a recurring product in an order context. |
commerce_recurring_permission | Implements hook_permission(). |
commerce_recurring_price_create_instance | Creates a instance of a price field on the specified bundle. |
commerce_recurring_product_is_recurring | Determine if a product has recurring properties. |
commerce_recurring_stop_recurring | Mark a recurring entity as inactive. |
commerce_recurring_types | Bundles of the recurring entity. |
commerce_recurring_uri | Entity uri callback: gives modules a chance to specify a path for a recurring entity. |