FINAL suepr merge step : added all modules to this super repos
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Ubercart stock related tests
|
||||
*/
|
||||
|
||||
class UbercartStockTestCase extends UbercartTestHelper {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Stock',
|
||||
'description' => 'Ensure that stock control functions properly.',
|
||||
'group' => 'Ubercart',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides DrupalWebTestCase::setUp().
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp(array('uc_stock'), array('administer product stock'));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
}
|
||||
|
||||
public function testProductStock() {
|
||||
$this->drupalGet('node/' . $this->product->nid . '/edit/stock');
|
||||
$this->assertText($this->product->title);
|
||||
$this->assertText($this->product->model, 'Product SKU found.');
|
||||
|
||||
$this->assertNoFieldChecked('edit-stock-0-active', 'Stock tracking is not active.');
|
||||
$this->assertFieldByName('stock[0][stock]', '0', 'Default stock level found.');
|
||||
$this->assertFieldByName('stock[0][threshold]', '0', 'Default stock threshold found.');
|
||||
|
||||
$stock = rand(1, 1000);
|
||||
$edit = array(
|
||||
'stock[0][active]' => 1,
|
||||
'stock[0][stock]' => $stock,
|
||||
'stock[0][threshold]' => rand(1, 100),
|
||||
);
|
||||
$this->drupalPost(NULL, $edit, t('Save changes'));
|
||||
$this->assertText('Stock settings saved.');
|
||||
$this->assertTrue(uc_stock_is_active($this->product->model));
|
||||
$this->assertEqual($stock, uc_stock_level($this->product->model));
|
||||
|
||||
$stock = rand(1, 1000);
|
||||
uc_stock_set($this->product->model, $stock);
|
||||
$this->drupalGet('node/' . $this->product->nid . '/edit/stock');
|
||||
$this->assertFieldByName('stock[0][stock]', (string)$stock, 'Set stock level found.');
|
||||
}
|
||||
|
||||
public function testStockDecrement() {
|
||||
$stock = rand(100, 1000);
|
||||
$edit = array(
|
||||
'stock[0][active]' => 1,
|
||||
'stock[0][stock]' => $stock,
|
||||
);
|
||||
$this->drupalPost('node/' . $this->product->nid . '/edit/stock', $edit, t('Save changes'));
|
||||
$this->assertText('Stock settings saved.');
|
||||
|
||||
// Enable product quantity field.
|
||||
variable_set('uc_product_add_to_cart_qty', TRUE);
|
||||
|
||||
$qty = rand(1, 100);
|
||||
$edit = array('qty' => $qty);
|
||||
$this->drupalPost('node/' . $this->product->nid, $edit, t('Add to cart'));
|
||||
|
||||
$this->checkout();
|
||||
|
||||
$this->assertEqual($stock - $qty, uc_stock_level($this->product->model));
|
||||
}
|
||||
|
||||
public function testStockThresholdMail() {
|
||||
$edit = array('uc_stock_threshold_notification' => 1);
|
||||
$this->drupalPost('admin/store/settings/stock', $edit, 'Save configuration');
|
||||
|
||||
$qty = rand(10, 100);
|
||||
$edit = array(
|
||||
'stock[0][active]' => 1,
|
||||
'stock[0][stock]' => $qty + 1,
|
||||
'stock[0][threshold]' => $qty,
|
||||
);
|
||||
$this->drupalPost('node/' . $this->product->nid . '/edit/stock', $edit, 'Save changes');
|
||||
|
||||
$this->drupalPost('node/' . $this->product->nid, array(), 'Add to cart');
|
||||
$this->checkout();
|
||||
|
||||
$mail = $this->drupalGetMails(array('id' => 'uc_stock_threshold'));
|
||||
$mail = array_pop($mail);
|
||||
$this->assertTrue(strpos($mail['subject'], 'Stock threshold limit reached') !== FALSE, 'Threshold mail subject is correct.');
|
||||
$this->assertTrue(strpos($mail['body'], $this->product->title) !== FALSE, 'Mail body contains product title.');
|
||||
$this->assertTrue(strpos($mail['body'], $this->product->model) !== FALSE, 'Mail body contains SKU.');
|
||||
$this->assertTrue(strpos($mail['body'], 'has reached ' . $qty) !== FALSE, 'Mail body contains quantity.');
|
||||
}
|
||||
}
|
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Stock administration menu items.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Form builder for stock settings form.
|
||||
*
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_stock_settings_form($form, &$form_state) {
|
||||
$form['uc_stock_threshold_notification'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Send email notification when stock level reaches its threshold value'),
|
||||
'#default_value' => variable_get('uc_stock_threshold_notification', FALSE),
|
||||
);
|
||||
|
||||
$form['uc_stock_threshold_notification_recipients'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Notification recipients'),
|
||||
'#default_value' => variable_get('uc_stock_threshold_notification_recipients', uc_store_email()),
|
||||
'#description' => t('The list of comma-separated email addresses that will receive the notification.'),
|
||||
);
|
||||
|
||||
$form['uc_stock_threshold_notification_subject'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => t('Message subject'),
|
||||
'#default_value' => variable_get('uc_stock_threshold_notification_subject', uc_get_message('uc_stock_threshold_notification_subject')),
|
||||
);
|
||||
|
||||
$form['uc_stock_threshold_notification_message'] = array(
|
||||
'#type' => 'textarea',
|
||||
'#title' => t('Message text'),
|
||||
'#default_value' => variable_get('uc_stock_threshold_notification_message', uc_get_message('uc_stock_threshold_notification_message')),
|
||||
'#description' => t('The message the user receives when the stock level reaches its threshold value.'),
|
||||
'#rows' => 10,
|
||||
);
|
||||
|
||||
if (module_exists('token')) {
|
||||
$form['token_tree'] = array(
|
||||
'#markup' => theme('token_tree', array('token_types' => array('uc_order', 'uc_stock', 'node', 'site', 'store'))),
|
||||
);
|
||||
}
|
||||
|
||||
return system_settings_form($form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a stock report for products with stock tracking enabled.
|
||||
*/
|
||||
function uc_stock_report() {
|
||||
$page_size = (isset($_GET['nopage'])) ? UC_REPORTS_MAX_RECORDS : variable_get('uc_reports_table_size', 30);
|
||||
$csv_rows = array();
|
||||
$rows = array();
|
||||
|
||||
$header = array(
|
||||
array('data' => t('SKU'), 'field' => 'sku', 'sort' => 'asc'),
|
||||
array('data' => t('Product'), 'field' => 'title'),
|
||||
array('data' => t('Stock'), 'field' => 'stock'),
|
||||
array('data' => t('Threshold'), 'field' => 'threshold'),
|
||||
array('data' => t('Operations')),
|
||||
);
|
||||
|
||||
$csv_rows[] = array(t('SKU'), t('Product'), t('Stock'), t('Threshold'));
|
||||
|
||||
$query = db_select('uc_product_stock', 's')->extend('PagerDefault')->extend('TableSort')
|
||||
->orderByHeader($header)
|
||||
->limit($page_size)
|
||||
->fields('s', array(
|
||||
'nid',
|
||||
'sku',
|
||||
'stock',
|
||||
'threshold',
|
||||
));
|
||||
|
||||
$query->leftJoin('node', 'n', 's.nid = n.nid');
|
||||
$query->addField('n', 'title');
|
||||
$query->condition('active', 1)
|
||||
->condition('title', '', '<>');
|
||||
|
||||
|
||||
if (arg(4) == 'threshold') {
|
||||
$query->where('threshold >= stock');
|
||||
}
|
||||
|
||||
$result = $query->execute();
|
||||
foreach ($result as $stock) {
|
||||
$op = array();
|
||||
if (user_access('administer product stock')) {
|
||||
$op[] = l(t('edit'), 'node/' . $stock->nid . '/edit/stock', $options = array('query' => array('destination' => 'admin/store/reports/stock')));
|
||||
}
|
||||
|
||||
// Add the data to a table row for display.
|
||||
$rows[] = array(
|
||||
'data' => array(
|
||||
array('data' => $stock->sku),
|
||||
array('data' => l($stock->title, 'node/' . $stock->nid)),
|
||||
array('data' => $stock->stock),
|
||||
array('data' => $stock->threshold),
|
||||
array('data' => implode(' ', $op)),
|
||||
),
|
||||
'class' => array(($stock->threshold >= $stock->stock) ? 'uc-stock-below-threshold' : 'uc-stock-above-threshold'),
|
||||
);
|
||||
|
||||
// Add the data to the CSV contents for export.
|
||||
$csv_rows[] = array($stock->sku, $stock->title, $stock->stock, $stock->threshold);
|
||||
}
|
||||
|
||||
module_load_include('inc', 'uc_reports', 'uc_reports.admin');
|
||||
$csv_data = uc_reports_store_csv('uc_stock', $csv_rows);
|
||||
|
||||
$build['form'] = drupal_get_form('uc_stock_report_form');
|
||||
$build['report'] = array(
|
||||
'#theme' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#attributes' => array('width' => '100%', 'class' => array('uc-stock-table')),
|
||||
);
|
||||
$build['pager'] = array(
|
||||
'#theme' => 'pager',
|
||||
);
|
||||
|
||||
$build['links'] = array(
|
||||
'#prefix' => '<div class="uc-reports-links">',
|
||||
'#suffix' => '</div>',
|
||||
);
|
||||
$build['links']['export_csv'] = array(
|
||||
'#markup' => l(t('Export to CSV file'), 'admin/store/reports/getcsv/' . $csv_data['report'] . '/' . $csv_data['user']),
|
||||
'#suffix' => ' ',
|
||||
);
|
||||
|
||||
if (isset($_GET['nopage'])) {
|
||||
$build['links']['toggle_pager'] = array(
|
||||
'#markup' => l(t('Show paged records'), 'admin/store/reports/stock'),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$build['links']['toggle_pager'] = array(
|
||||
'#markup' => l(t('Show all records'), 'admin/store/reports/stock', array('query' => array('nopage' => '1'))),
|
||||
);
|
||||
}
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder for stock report threshold filter.
|
||||
*
|
||||
* @see uc_stock_report_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_stock_report_form($form, &$form_state) {
|
||||
$form['threshold'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#title' => t('Only show SKUs that are below their threshold.'),
|
||||
'#default_value' => arg(4) == 'threshold' ? TRUE : FALSE,
|
||||
'#attributes' => array('onchange' => 'this.form.submit();'),
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['submit'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Update'),
|
||||
'#attributes' => array('style' => "display:none;"),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_stock_report_form().
|
||||
*
|
||||
* @see uc_stock_report_form()
|
||||
*/
|
||||
function uc_stock_report_form_submit($form, &$form_state) {
|
||||
if ($form_state['values']['threshold']) {
|
||||
drupal_goto('admin/store/reports/stock/threshold');
|
||||
}
|
||||
else {
|
||||
drupal_goto('admin/store/reports/stock');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form builder for product stock edit form.
|
||||
*
|
||||
* @see uc_stock_edit_form_submit()
|
||||
* @see theme_uc_stock_edit_form()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function uc_stock_edit_form($form, &$form_state, $node) {
|
||||
drupal_set_title($node->title);
|
||||
|
||||
$form['stock'] = array('#tree' => TRUE);
|
||||
|
||||
$skus = uc_product_get_models($node->nid);
|
||||
|
||||
// Remove 'Any'.
|
||||
unset($skus[NULL]);
|
||||
|
||||
if (!$skus) {
|
||||
drupal_set_message(t('No SKU found.'), 'error');
|
||||
}
|
||||
else {
|
||||
foreach (array_values($skus) as $id => $sku) {
|
||||
$stock = db_query("SELECT * FROM {uc_product_stock} WHERE sku = :sku", array(':sku' => $sku))->fetchAssoc();
|
||||
|
||||
$form['stock'][$id]['sku'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $sku,
|
||||
);
|
||||
|
||||
// Checkbox to mark this as active.
|
||||
$form['stock'][$id]['active'] = array(
|
||||
'#type' => 'checkbox',
|
||||
'#default_value' => !empty($stock['active']) ? $stock['active'] : 0,
|
||||
);
|
||||
|
||||
// Sanitized version of the SKU for display.
|
||||
$form['stock'][$id]['display_sku'] = array(
|
||||
'#markup' => check_plain($sku),
|
||||
);
|
||||
|
||||
// Textfield for entering the stock level.
|
||||
$form['stock'][$id]['stock'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => !empty($stock['stock']) ? $stock['stock'] : 0,
|
||||
'#maxlength' => 9,
|
||||
'#size' => 9,
|
||||
);
|
||||
|
||||
// Textfield for entering the threshold level.
|
||||
$form['stock'][$id]['threshold'] = array(
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => !empty($stock['threshold']) ? $stock['threshold'] : 0,
|
||||
'#maxlength' => 9,
|
||||
'#size' => 9,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$form['nid'] = array(
|
||||
'#type' => 'value',
|
||||
'#value' => $node->nid,
|
||||
);
|
||||
|
||||
$form['actions'] = array('#type' => 'actions');
|
||||
$form['actions']['save'] = array(
|
||||
'#type' => 'submit',
|
||||
'#value' => t('Save changes'),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for uc_stock_edit_form().
|
||||
*
|
||||
* @see uc_stock_edit_form()
|
||||
* @see theme_uc_stock_edit_form()
|
||||
*/
|
||||
function uc_stock_edit_form_submit($form, &$form_state) {
|
||||
foreach (element_children($form_state['values']['stock']) as $id) {
|
||||
$stock = $form_state['values']['stock'][$id];
|
||||
|
||||
db_merge('uc_product_stock')
|
||||
->key(array('sku' => $stock['sku']))
|
||||
->updateFields(array(
|
||||
'active' => $stock['active'],
|
||||
'stock' => $stock['stock'],
|
||||
'threshold' => $stock['threshold'],
|
||||
))
|
||||
->insertFields(array(
|
||||
'sku' => $stock['sku'],
|
||||
'active' => $stock['active'],
|
||||
'stock' => $stock['stock'],
|
||||
'threshold' => $stock['threshold'],
|
||||
'nid' => $form_state['values']['nid'],
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
drupal_set_message(t('Stock settings saved.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for uc_stock_edit_form().
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - form: A render element representing the form.
|
||||
*
|
||||
* @see uc_stock_edit_form()
|
||||
* @see uc_stock_edit_form_submit()
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_uc_stock_edit_form($variables) {
|
||||
$form = $variables['form'];
|
||||
drupal_add_js('misc/tableselect.js');
|
||||
|
||||
$header = array(
|
||||
array('data' => ' ' . t('Active'), 'class' => array('select-all')),
|
||||
array('data' => t('SKU')),
|
||||
array('data' => t('Stock')),
|
||||
array('data' => t('Threshold')),
|
||||
);
|
||||
$rows = array();
|
||||
|
||||
foreach (element_children($form['stock']) as $id) {
|
||||
$rows[] = array(
|
||||
array('data' => drupal_render($form['stock'][$id]['active'])),
|
||||
array('data' => drupal_render($form['stock'][$id]['display_sku'])),
|
||||
array('data' => drupal_render($form['stock'][$id]['stock'])),
|
||||
array('data' => drupal_render($form['stock'][$id]['threshold'])),
|
||||
);
|
||||
}
|
||||
|
||||
return theme('table', array('header' => $header, 'rows' => $rows)) . drupal_render_children($form);
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Hooks provided by the Stock module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup hooks
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows modules to take action when a stock level is changed.
|
||||
*
|
||||
* @param $sku
|
||||
* The SKU whose stock level is being changed.
|
||||
* @param $stock
|
||||
* The stock level before the adjustment.
|
||||
* @param $qty
|
||||
* The amount by which the stock level was changed.
|
||||
*/
|
||||
function hook_uc_stock_adjusted($sku, $stock, $qty) {
|
||||
$params = array(
|
||||
'sku' => $sku,
|
||||
'stock' => $stock,
|
||||
'qty' => $qty,
|
||||
);
|
||||
|
||||
drupal_mail('uc_stock_notify', 'stock-adjusted', uc_store_email_from(), language_default(), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup hooks".
|
||||
*/
|
@@ -0,0 +1,20 @@
|
||||
name = Stock
|
||||
description = Manages stock levels of your products.
|
||||
dependencies[] = uc_product
|
||||
package = Ubercart - extra
|
||||
core = 7.x
|
||||
|
||||
; Test cases
|
||||
files[] = tests/uc_stock.test
|
||||
|
||||
; Views handlers
|
||||
files[] = views/uc_stock_handler_filter_below_threshold.inc
|
||||
|
||||
configure = admin/store/settings/stock
|
||||
|
||||
; Information added by Drupal.org packaging script on 2013-12-17
|
||||
version = "7.x-3.6"
|
||||
core = "7.x"
|
||||
project = "ubercart"
|
||||
datestamp = "1387304010"
|
||||
|
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the uc_stock module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_schema().
|
||||
*/
|
||||
function uc_stock_schema() {
|
||||
$schema = array();
|
||||
|
||||
$schema['uc_product_stock'] = array(
|
||||
'description' => 'Stock levels for Ubercart products.',
|
||||
'fields' => array(
|
||||
'sku' => array(
|
||||
'description' => 'SKU (Stock Keeping Unit) of a product.',
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'nid' => array(
|
||||
'description' => 'Node ID of a product.',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'active' => array(
|
||||
'description' => 'Boolean flag indicating whether stock is being tracked for this product. 1 => Yes. 0 => No.',
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'stock' => array(
|
||||
'description' => 'Quantity in stock.',
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'threshold' => array(
|
||||
'description' => 'Minimum quantity threshold level.',
|
||||
'type' => 'int',
|
||||
'size' => 'medium',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'nid' => array('nid'),
|
||||
),
|
||||
'primary key' => array('sku'),
|
||||
'foreign keys' => array(
|
||||
'uc_products' => array(
|
||||
'table' => 'uc_products',
|
||||
'columns' => array('nid' => 'nid'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uninstall().
|
||||
*/
|
||||
function uc_stock_uninstall() {
|
||||
db_delete('variable')
|
||||
->condition('name', 'uc_stock_%', 'LIKE')
|
||||
->execute();
|
||||
|
||||
variable_del('uc_stock_threshold_notification');
|
||||
variable_del('uc_stock_threshold_notification_recipients');
|
||||
variable_del('uc_stock_threshold_notification_subject');
|
||||
variable_del('uc_stock_threshold_notification_message');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_update_last_removed().
|
||||
*/
|
||||
function uc_stock_update_last_removed() {
|
||||
return 6000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unused message format variable.
|
||||
*/
|
||||
function uc_stock_update_7000() {
|
||||
variable_del('uc_stock_threshold_notification_format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unused message format variable.
|
||||
*/
|
||||
function uc_stock_update_7001() {
|
||||
variable_del('uc_stock_threshold_notification_message_format');
|
||||
}
|
@@ -0,0 +1,297 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Allow ubercart products to have stock levels associated with their SKU
|
||||
*
|
||||
* uc_stock enables ubercart to manage stock for products. Store admins can set
|
||||
* the stock levels on a product edit page and a threshold for each SKU value
|
||||
* When that threshold is reached admins can be optionally notified about the
|
||||
* current stock level. Store admins can view all stock levels in the reports
|
||||
* section of Ubercart.
|
||||
*
|
||||
* Development sponsored by the Ubercart project. http://www.ubercart.org
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function uc_stock_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'node/%/edit/stock':
|
||||
return '<p>' . t('To keep track of stock for a particular product SKU, make sure it is marked as active and enter a stock value. When the stock level drops below the threshold value, you can be notified based on your stock settings.') . '</p>';
|
||||
case 'admin/store/reports/stock':
|
||||
case 'admin/store/reports/stock/threshold':
|
||||
return '<p>' . t('This is the list of product SKUs that are currently active. Stock levels below their threshold have highlighted rows. Toggle the checkbox below to alter which stock levels are shown.') . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_menu().
|
||||
*/
|
||||
function uc_stock_menu() {
|
||||
$items = array();
|
||||
|
||||
$items['admin/store/settings/stock'] = array(
|
||||
'title' => 'Stock notifications',
|
||||
'description' => 'Enable or disable stock level notifications.',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_stock_settings_form'),
|
||||
'access arguments' => array('administer product stock'),
|
||||
'file' => 'uc_stock.admin.inc',
|
||||
);
|
||||
if (module_exists('uc_reports')) {
|
||||
$items['admin/store/reports/stock'] = array(
|
||||
'title' => 'Stock reports',
|
||||
'description' => 'View reports for product stock.',
|
||||
'page callback' => 'uc_stock_report',
|
||||
'access arguments' => array('view reports'),
|
||||
'file' => 'uc_stock.admin.inc',
|
||||
);
|
||||
}
|
||||
$items['node/%node/edit/stock'] = array(
|
||||
'title' => 'Stock',
|
||||
'page callback' => 'drupal_get_form',
|
||||
'page arguments' => array('uc_stock_edit_form', 1),
|
||||
'access callback' => 'uc_stock_product_access',
|
||||
'access arguments' => array(1),
|
||||
'weight' => 10,
|
||||
'type' => MENU_LOCAL_TASK,
|
||||
'file' => 'uc_stock.admin.inc',
|
||||
);
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_admin_paths().
|
||||
*/
|
||||
function uc_stock_admin_paths() {
|
||||
$paths = array(
|
||||
'node/*/edit/stock' => TRUE,
|
||||
);
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access callback for node/%node/edit/stock.
|
||||
*/
|
||||
function uc_stock_product_access($node) {
|
||||
if ($node->type == 'product_kit') {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return uc_product_is_product($node) && node_access('update', $node) && user_access('administer product stock');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_permission().
|
||||
*/
|
||||
function uc_stock_permission() {
|
||||
return array(
|
||||
'administer product stock' => array(
|
||||
'title' => t('Administer product stock'),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function uc_stock_theme() {
|
||||
return array(
|
||||
'uc_stock_edit_form' => array(
|
||||
'render element' => 'form',
|
||||
'file' => 'uc_stock.admin.inc',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_mail().
|
||||
*/
|
||||
function uc_stock_mail($key, &$message, $params) {
|
||||
switch ($key) {
|
||||
case 'threshold':
|
||||
$message['subject'] = $params['subject'];
|
||||
$message['body'][] = $params['body'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_uc_message().
|
||||
*/
|
||||
function uc_stock_uc_message() {
|
||||
$messages['uc_stock_threshold_notification_subject'] = t('[store:name]: Stock threshold limit reached');
|
||||
$messages['uc_stock_threshold_notification_message'] = t('This message has been sent to let you know that the stock level for "[node:title]" with SKU [uc_stock:model] has reached [uc_stock:level]. There may not be enough units in stock to fulfill order #[uc_order:link].');
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the product stock level by a set amount.
|
||||
*
|
||||
* @param $sku
|
||||
* The product SKU of the stock level to adjust.
|
||||
* @param $qty
|
||||
* The amount to add to or subtract from the stock level.
|
||||
* @param $check_active
|
||||
* If FALSE, don't check if stock tracking is active for this SKU.
|
||||
*/
|
||||
function uc_stock_adjust($sku, $qty, $check_active = TRUE) {
|
||||
$stock = db_query("SELECT active, stock FROM {uc_product_stock} WHERE sku = :sku", array(':sku' => $sku))->fetchObject();
|
||||
|
||||
if ($check_active) {
|
||||
if (!$stock || !$stock->active) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
db_update('uc_product_stock')
|
||||
->expression('stock', 'stock + :qty', array(':qty' => $qty))
|
||||
->condition('sku', $sku)
|
||||
->execute();
|
||||
|
||||
module_invoke_all('uc_stock_adjusted', $sku, $stock->stock, $qty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the product stock level.
|
||||
*
|
||||
* @param $sku
|
||||
* The product SKU of the stock level to set.
|
||||
* @param $qty
|
||||
* The number of items in stock.
|
||||
*/
|
||||
function uc_stock_set($sku, $qty) {
|
||||
db_update('uc_product_stock')
|
||||
->fields(array('stock' => $qty))
|
||||
->condition('sku', $sku)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stock level of a particular product SKU.
|
||||
*
|
||||
* @param $sku
|
||||
* The Ubercart product SKU of the stock level to return.
|
||||
*
|
||||
* @return
|
||||
* The SKU's stock level, or FALSE if not active.
|
||||
*/
|
||||
function uc_stock_level($sku) {
|
||||
$stock = db_query("SELECT active, stock FROM {uc_product_stock} WHERE sku = :sku", array(':sku' => $sku))->fetchObject();
|
||||
|
||||
if ($stock && $stock->active) {
|
||||
return $stock->stock;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a SKU has an active stock record.
|
||||
*
|
||||
* @param $sku
|
||||
* The Ubercart product SKU to check
|
||||
*
|
||||
* @return
|
||||
* Boolean indicating whether or not the sku has an active stock record.
|
||||
*/
|
||||
function uc_stock_is_active($sku) {
|
||||
return (bool) db_query("SELECT active FROM {uc_product_stock} WHERE sku = :sku", array(':sku' => $sku))->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emails administrator regarding any stock level thresholds hit.
|
||||
*
|
||||
* @param $order
|
||||
* The order object that tripped the threshold limit.
|
||||
* @param $node
|
||||
* The node object that is associated with the SKU/model.
|
||||
* @param $stock
|
||||
* The stock level object that contains the stock level and SKU.
|
||||
*
|
||||
* @return
|
||||
* The result of drupal_mail().
|
||||
*/
|
||||
function _uc_stock_send_mail($order, $node, $stock) {
|
||||
$account = user_load($order->uid);
|
||||
$token_filters = array('uc_order' => $order, 'user' => $account, 'uc_stock' => $stock, 'node' => $node);
|
||||
|
||||
$to = variable_get('uc_stock_threshold_notification_recipients', uc_store_email());
|
||||
$to = explode(',', $to);
|
||||
|
||||
$from = uc_store_email_from();
|
||||
|
||||
$subject = variable_get('uc_stock_threshold_notification_subject', uc_get_message('uc_stock_threshold_notification_subject'));
|
||||
$subject = token_replace($subject, $token_filters);
|
||||
|
||||
$body = variable_get('uc_stock_threshold_notification_message', uc_get_message('uc_stock_threshold_notification_message'));
|
||||
$body = token_replace($body, $token_filters);
|
||||
|
||||
// Send to each recipient.
|
||||
foreach ($to as $email) {
|
||||
$sent = drupal_mail('uc_stock', 'threshold', $email, uc_store_mail_recipient_language($email), array('body' => $body, 'subject' => $subject, 'order' => $order, 'stock' => $stock), $from);
|
||||
|
||||
if (!$sent['result']) {
|
||||
watchdog('uc_stock', 'Attempt to e-mail @email concerning stock level on sku @sku failed.', array('@email' => $email, '@sku' => $stock->sku), WATCHDOG_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_api().
|
||||
*/
|
||||
function uc_stock_views_api() {
|
||||
return array(
|
||||
'api' => '2.0',
|
||||
'path' => drupal_get_path('module', 'uc_stock') . '/views',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement a product's stock.
|
||||
*
|
||||
* @param $product
|
||||
* The product whose stock is being adjusted.
|
||||
* @param $key
|
||||
* Internal, so this function can be used as a callback of array_walk().
|
||||
* @param $order
|
||||
* Order object.
|
||||
*/
|
||||
function uc_stock_adjust_product_stock($product, $key, $order) {
|
||||
// Product has an active stock?
|
||||
if (!uc_stock_is_active($product->model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do nothing if decrement quantity is 0.
|
||||
if ($product->qty == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the product's stock.
|
||||
uc_stock_adjust($product->model, -$product->qty);
|
||||
|
||||
// Load the new stock record.
|
||||
$stock = db_query("SELECT * FROM {uc_product_stock} WHERE sku = :sku", array(':sku' => $product->model))->fetchObject();
|
||||
|
||||
// Should we notify?
|
||||
if (variable_get('uc_stock_threshold_notification', FALSE) && $stock->stock <= $stock->threshold) {
|
||||
$node = node_load($product->nid);
|
||||
_uc_stock_send_mail($order, $node, $stock);
|
||||
}
|
||||
|
||||
// Save a comment about the stock level.
|
||||
uc_order_comment_save($order->order_id, 0, t('The stock level for %model_name has been !action to !qty.', array('%model_name' => $product->model, '!qty' => $stock->stock, '!action' => (-$product->qty <= 0) ? t('decreased') : t('increased') )));
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment a product's stock.
|
||||
*/
|
||||
function uc_stock_increment_product_stock($product, $key, $order) {
|
||||
$product->qty = -$product->qty;
|
||||
return uc_stock_adjust_product_stock($product, $key, $order);
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules hooks for uc_stock.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_rules_action_info().
|
||||
*/
|
||||
function uc_stock_rules_action_info() {
|
||||
$actions['uc_stock_action_decrement_stock'] = array(
|
||||
'label' => t('Decrement stock of products on the order with tracking activated.'),
|
||||
'group' => t('Stock'),
|
||||
'base' => 'uc_stock_action_decrement_stock',
|
||||
'parameter' => array(
|
||||
'order' => array(
|
||||
'type' => 'uc_order',
|
||||
'label' => t('Order'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$actions['uc_stock_action_increment_stock'] = array(
|
||||
'label' => t('Increment stock of products on the order with tracking activated.'),
|
||||
'group' => t('Stock'),
|
||||
'base' => 'uc_stock_action_increment_stock',
|
||||
'parameter' => array(
|
||||
'order' => array(
|
||||
'type' => 'uc_order',
|
||||
'label' => t('Order'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the stock of ordered products.
|
||||
*/
|
||||
function uc_stock_action_decrement_stock($order) {
|
||||
if (is_array($order->products)) {
|
||||
array_walk($order->products, 'uc_stock_adjust_product_stock', $order);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the stock of ordered products.
|
||||
*/
|
||||
function uc_stock_action_increment_stock($order, $settings) {
|
||||
if (is_array($order->products)) {
|
||||
array_walk($order->products, 'uc_stock_increment_product_stock', $order);
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Rules default configurations for uc_stock.module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_default_rules_configuration().
|
||||
*/
|
||||
function uc_stock_default_rules_configuration() {
|
||||
$configs = array();
|
||||
|
||||
$rule = rules_reaction_rule();
|
||||
$rule->label = t('Decrement stock upon order submission');
|
||||
$rule->active = TRUE;
|
||||
$rule->event('uc_checkout_complete')
|
||||
->action('uc_stock_action_decrement_stock', array('order:select' => 'order'));
|
||||
$configs['uc_stock_decrement_on_order'] = $rule;
|
||||
|
||||
$rule = rules_reaction_rule();
|
||||
$rule->label = t('Increment stock on cancelling order');
|
||||
$rule->active = FALSE;
|
||||
$rule->event('uc_order_status_update')
|
||||
->condition(rules_condition('data_is', array('data:select' => 'updated_order:order-status', 'value' => 'canceled')))
|
||||
->condition(rules_condition('data_is', array('data:select' => 'order:order-status', 'value' => 'canceled'))->negate())
|
||||
->condition(rules_condition('data_is', array('data:select' => 'order:order-status', 'value' => 'in_checkout'))->negate())
|
||||
->action('uc_stock_action_increment_stock', array('order:select' => 'order'));
|
||||
$configs['uc_stock_increment_on_cancel'] = $rule;
|
||||
|
||||
$rule = rules_reaction_rule();
|
||||
$rule->label = t('Increment stock on deleting an order');
|
||||
$rule->active = FALSE;
|
||||
$rule->event('uc_order_delete')
|
||||
->condition(rules_condition('data_is', array('data:select' => 'order:order-status', 'value' => 'canceled'))->negate())
|
||||
->condition(rules_condition('data_is', array('data:select' => 'order:order-status', 'value' => 'in_checkout'))->negate())
|
||||
->action('uc_stock_action_increment_stock', array('order:select' => 'order'));
|
||||
$configs['uc_stock_increment_on_delete'] = $rule;
|
||||
|
||||
$rule = rules_reaction_rule();
|
||||
$rule->label = t('Decrement stock when order cancellation is being undone');
|
||||
$rule->active = FALSE;
|
||||
$rule->event('uc_order_status_update')
|
||||
->condition(rules_condition('data_is', array('data:select' => 'order:order-status', 'value' => 'canceled')))
|
||||
->condition(rules_condition('data_is', array('data:select' => 'updated_order:order-status', 'value' => 'canceled'))->negate())
|
||||
->condition(rules_condition('data_is', array('data:select' => 'updated_order:order-status', 'value' => 'in_checkout'))->negate())
|
||||
->action('uc_stock_action_decrement_stock', array('order:select' => 'order'));
|
||||
$configs['uc_stock_decrement_on_uncancel'] = $rule;
|
||||
|
||||
return $configs;
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Token hooks for the uc_stock module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_token_info().
|
||||
*/
|
||||
function uc_stock_token_info() {
|
||||
$type = array(
|
||||
'name' => t('Stock level'),
|
||||
'description' => t('Tokens for the stock levels of products.'),
|
||||
'needs-data' => 'uc_stock',
|
||||
);
|
||||
|
||||
$tokens['level'] = array(
|
||||
'name' => t('Level'),
|
||||
'description' => t('The current stock level.'),
|
||||
);
|
||||
$tokens['model'] = array(
|
||||
'name' => t('Model'),
|
||||
'description' => t('The model or SKU of the stock level.'),
|
||||
);
|
||||
$tokens['threshold'] = array(
|
||||
'name' => t('Threshold'),
|
||||
'description' => t('The threshold or warning limit of the stock level.'),
|
||||
);
|
||||
|
||||
return array(
|
||||
'types' => array('uc_stock' => $type),
|
||||
'tokens' => array('uc_stock' => $tokens),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_tokens().
|
||||
*/
|
||||
function uc_stock_tokens($type, $tokens, $data = array(), $options = array()) {
|
||||
$sanitize = !empty($options['sanitize']);
|
||||
|
||||
$replacements = array();
|
||||
|
||||
if ($type == 'uc_stock' && !empty($data['uc_stock'])) {
|
||||
$object = $data['uc_stock'];
|
||||
|
||||
foreach ($tokens as $name => $original) {
|
||||
switch ($name) {
|
||||
case 'level':
|
||||
$replacements[$original] = $object->stock;
|
||||
break;
|
||||
|
||||
case 'model':
|
||||
$replacements[$original] = $sanitize ? check_plain($object->sku) : $object->sku;
|
||||
break;
|
||||
|
||||
case 'threshold':
|
||||
$replacements[$original] = $object->threshold;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $replacements;
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Variable module hook implementations.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Implements hook_variable_group_info().
|
||||
*/
|
||||
function uc_stock_variable_group_info() {
|
||||
$groups['uc_stock'] = array(
|
||||
'title' => t('Ubercart stock settings'),
|
||||
'access' => 'administer store',
|
||||
'path' => array('admin/store/settings/stock'),
|
||||
);
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_variable_info().
|
||||
*/
|
||||
function uc_stock_variable_info($options) {
|
||||
$variables['uc_stock_threshold_notification_message'] = array(
|
||||
'type' => 'text',
|
||||
'title' => t('Message text', array(), $options),
|
||||
'description' => t('The message the user receives when the stock level reaches its threshold value.', array(), $options),
|
||||
'group' => 'uc_stock',
|
||||
'default' => t('[store:name]: Stock threshold limit reached', array(), $options),
|
||||
);
|
||||
$variables['uc_stock_threshold_notification_subject'] = array(
|
||||
'type' => 'text',
|
||||
'title' => t('Message subject', array(), $options),
|
||||
'description' => t('The subject line of the message the user receives when the stock level reaches its threshold value.', array(), $options),
|
||||
'group' => 'uc_stock',
|
||||
'default' => t('This message has been sent to let you know that the stock level for "[node:title]" with SKU [uc_stock:model] has reached [uc_stock:level]. There may not be enough units in stock to fulfill order #[uc_order:link].', array(), $options),
|
||||
);
|
||||
return $variables;
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Views hooks and callback registries.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function uc_stock_views_data() {
|
||||
$data['uc_product_stock']['table']['group'] = t('Stock');
|
||||
|
||||
// Attach stock data to nodes.
|
||||
$data['uc_product_stock']['table']['join']['node'] = array(
|
||||
'left_field' => 'nid',
|
||||
'field' => 'nid',
|
||||
);
|
||||
|
||||
$data['uc_product_stock']['sku'] = array(
|
||||
'title' => t('SKU'),
|
||||
'help' => t('The model or SKU of the stock level.'),
|
||||
'field' => array(
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_string',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
'argument' => array(
|
||||
'handler' => 'views_handler_argument_string',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_product_stock']['active'] = array(
|
||||
'title' => t('Active'),
|
||||
'help' => t('Whether or not stock is currently being tracked.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_boolean',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_boolean_operator',
|
||||
'label' => t('Active'),
|
||||
'type' => 'yes-no',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_product_stock']['stock'] = array(
|
||||
'title' => t('Level'),
|
||||
'help' => t('The current stock level.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_numeric',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_product_stock']['threshold'] = array(
|
||||
'title' => t('Threshold'),
|
||||
'help' => t('The level at which a stock warning can be sent.'),
|
||||
'field' => array(
|
||||
'handler' => 'views_handler_field_numeric',
|
||||
'click sortable' => TRUE,
|
||||
),
|
||||
'filter' => array(
|
||||
'handler' => 'views_handler_filter_numeric',
|
||||
),
|
||||
'sort' => array(
|
||||
'handler' => 'views_handler_sort',
|
||||
),
|
||||
);
|
||||
|
||||
$data['uc_product_stock']['below_threshold'] = array(
|
||||
'title' => t('Is below threshold'),
|
||||
'help' => t('Filter the node based on whether its stock level is below the threshold for the SKU.'),
|
||||
'filter' => array(
|
||||
'handler' => 'uc_stock_handler_filter_below_threshold',
|
||||
'label' => t('Is below threshold'),
|
||||
'type' => 'yes-no',
|
||||
),
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Views handler: Filter on comparison of stock level to threshold.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filters nodes based on comparison of stock value to stock threshold.
|
||||
*/
|
||||
class uc_stock_handler_filter_below_threshold extends views_handler_filter_boolean_operator {
|
||||
|
||||
/**
|
||||
* Overrides views_handler_field::query().
|
||||
*/
|
||||
function query() {
|
||||
$this->ensure_my_table();
|
||||
$this->query->add_where_expression($this->options['group'], "$this->table_alias.stock " . (empty($this->value) ? '>=' : '<') . " $this->table_alias.threshold");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user