10 August 2012

Attribute Counts for Attributes Tab v1.1

Shows the count of attributes in each Group and how many times each individual attribute is linked to a product. Very useful when cleaning up the attributes list to know if attributes are linked to products.

#
#----------[ OPEN ]----------
#
admin/classes/AttributeGroup.php

#
#----------[ FIND ]----------
#
 public static function getAttributes($id_lang, $id_attribute_group)
 {
  return Db::getInstance()->ExecuteS('
  SELECT *
  FROM `'._DB_PREFIX_.'attribute` a
  LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)($id_lang).')
  WHERE a.`id_attribute_group` = '.(int)($id_attribute_group).'
  ORDER BY `name`');
 }

#
#----------[ REPLACE WITH ]----------
#
 public static function getAttributes($id_lang, $id_attribute_group)
 {
  return Db::getInstance()->ExecuteS('
  SELECT a.*, al.*, COUNT(pac.`id_attribute`) AS count
  FROM `'._DB_PREFIX_.'attribute` a
  LEFT JOIN `'._DB_PREFIX_.'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = '.(int)($id_lang).')
  LEFT JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON a.`id_attribute` = pac.`id_attribute`
  WHERE a.`id_attribute_group` = '.(int)($id_attribute_group).'
  GROUP BY a.`id_attribute`
  ORDER BY `name`');
 }
#
#----------[ OPEN ]----------
#
admin/tabs/AdminAttributesGroups.php

#
#----------[ FIND ]----------
#
   'name' => array('title' => $this->l('Name'), 'width' => 140),

#
#----------[ REPLACE WITH ]----------
#
   'name' => array('title' => $this->l('Name') .' ('.$this->l('Used').')', 'width' => 140),

#
#----------[ FIND ]----------
#
   $id = (int)($tr['id_'.$this->table]);

#
#----------[ REPLACE WITH ]----------
#
   $id = (int)($tr['id_'.$this->table]);
   $attributes = AttributeGroup::getAttributes((int)($cookie->id_lang), $id);

#
#----------[ FIND ]----------
#
    <td style="width: 140px; vertical-align: top; padding: 4px 0 4px 0; cursor: pointer" onclick="$(\'#attributes_'.$id.'\').slideToggle();">'.$tr['name'].' ('.count($attributes).')</td>

#
#----------[ REPLACE WITH ]----------
#
    <td style="width: 140px; vertical-align: top; padding: 4px 0 4px 0; cursor: pointer" onclick="$(\'#attributes_'.$id.'\').slideToggle();">'.$tr['name'].'</td>

#
#----------[ FIND ]----------
#
       <th width="100%">'.$this->l('Attribute').'</th>

#
#----------[ REPLACE WITH ]----------
#
       <th width="100%">'.$this->l('Attribute').' ('.$this->l('Used').')</th>

#
#----------[ FIND & DELETE ]----------
#
   $attributes = AttributeGroup::getAttributes((int)($cookie->id_lang), $id);

#
#----------[ FIND ]----------
#
        .$attribute['name'].'

#
#----------[ REPLACE WITH ]----------
#
        .$attribute['name'].' ('.$attribute['count'].')
CHANGELOG:
v1.1 (2012-08-10)
[+] Number of attributes next to Attribute Group.
v1.0 (2011-11-30)
[+] First release to the PrestaShop community.

4 August 2012

Customer Messages on BO Index v1.5

Adds a list of latest customer messages on the BO index page.
#
#----------[ OPEN ]----------
#
admin/tabs/AdminHome.php

#
#----------[ FIND ]----------
#
 echo '
   </tbody>
  </table>

#
#----------[ AFTER, ADD ]----------
#
  <br />

  <table cellpadding="0" cellspacing="0" id="table_customer">
   <thead>
    <tr>
     <th class="order_id"><span class="first">'.translate('ID').'</span></th>
     <th class="order_date"><span>'.translate('Date').'</span></th>
     <th class="order_customer"><span>'.translate('Customer').'</span></th>
     <th class="order_status"><span>'.translate('Message').'</span></th>
     <th class="order_action"><span class="last">'.translate('Action').'</span></th>
    <tr>
   </thead>
   <tbody>';
 
 $messages = Message::getLastMessages(10);
 $i = 0;
 foreach ($messages AS $message)
 {
  echo '
     <tr'.($i % 2 ? ' id="order_line1"' : '').($message['is_new_for_me'] ?' class="new_message"' : '').'>
     <td class="order_td_first order_id">'.(int)$message['message_id'].'</td>
     <td class="order_date">'.$message['added'].'</td>
     <td class="order_customer">'.Tools::htmlentitiesUTF8($message['firstname']).' '.Tools::htmlentitiesUTF8($message['lastname']).'</td>
     <td class="order_status" width="50%">'.nl2br2($message['message']).'</td>
     <td class="order_action">
     '.($message['is_new_for_me'] ? '<a class="new_message" title="'.$this->l('Mark this message as \'viewed\'').'" href="index.php?tab=AdminOrders&id_order='.(int)$message['id_order'].'&vieworder&token='.Tools::getAdminTokenLite('AdminOrders').'&messageReaded='.(int)($message['message_id']).'"><img src="../img/admin/enabled.gif" alt="'.$this->l('Mark this message as \'viewed\'').'" /></a>&nbsp;' : '').'
     <a href="index.php?tab=AdminOrders&id_order='.(int)$message['id_order'].'&vieworder&token='.Tools::getAdminTokenLite('AdminOrders').'" title="'.translate('Details').'"><img src="../img/admin/details.gif" alt="'.translate('Details').'" /></a>
     </td>
    </tr>
   ';
  $i++;
 }
  
 echo '</tbody>
 </table>
#
#----------[ OPEN ]----------
#
classes/Message.php

#
#----------[ FIND ]----------
#
  ('.(int)($id_message).', '.(int)($id_employee).', NOW());
  ');
  return $result;
 }

#
#----------[ AFTER, ADD ]----------
#
 /**
   * Return last messages
   */
public static function getLastMessages($limit = NULL)
 {
  global $cookie;

  return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
   SELECT *, m.`id_message` message_id, m.`date_add` added, c.`firstname`, c.`lastname`, (COUNT(mr.`id_message`) = 0 AND m.`id_customer` != 0) is_new_for_me
   FROM `'._DB_PREFIX_.'message` m
   LEFT JOIN `'._DB_PREFIX_.'customer` c ON m.`id_customer` = c.`id_customer`
   LEFT JOIN `'._DB_PREFIX_.'message_readed` mr ON (mr.`id_message` = m.`id_message` AND mr.`id_employee` = '.(int)$cookie->id_employee.')
   WHERE m.`private` = 0 
   AND m.`id_customer` > 0
   AND m.`id_order` != 0
   GROUP BY m.`id_message`
   ORDER BY m.`date_add` DESC
   '.((int)$limit ? 'LIMIT 0, '.(int)$limit : ''));
 }
CHANGELOG:
v1.5 (2012-08-04)
[*] Changed ID to Order ID (id_order).
[*] Updated table row class to match changes in PS 1.4.9.0.
v1.4 (2012-03-23)
[*] Only display messages from orders (not carts).
v1.3 (2012-01-20)
[+] Added highlighted unread message format.
[+] Added ability to mark message as read.
v1.2 (2011-05-22)
[*] Fixed message formatting with special characters.
[*] Removed table width changes.
v1.1 (2011-03-10)
[*] Updated for new PS 1.4 AdminHome file.
[*] Fixed action icon if message not attached to an order.
v1.0 (2011-01-30)
[+] First release to the PrestaShop community.

27 March 2012

Product Ratings Suite v1.6

A complete ratings package for your 1.4 PrestaShop store.

Allow customers/visitors to rate products. Add a BO Stats page and FO Block to show the best rated products.

- Ratings of Products in AJAX v0.97
- Best Rated Products BO Stats v1.2
- Best Rated Products FO Block v1.5
FREE Download
CHANGELOG:
v1.6 (2012-03-27)
[+] Added option to include Google Rich Snippets. (productrating)
v1.5 (2012-03-16)
[+] Added option to exclude products not available to order. (blockbestrated)
v1.4 (2011-07-14)
[*] Fixed missing quotes on database table name. (productrating)
v1.3 (2011-06-01)
[*] Moved included JS/CSS calls to header hook and added page recognition. (productrating)
v1.2 (2011-04-13)
[+] Added category related rated products when on product page. (blockbestrated)
v1.1 (2011-04-06)
[*] Fixed ratings for friendly urls. (productrating)
v1.0 (2011-03-27)
[+] First release of re-packaged 1.4 modules to the PrestaShop community.

4 January 2012

Search by Tracking Number v1.0

Adds a feature to the BO to search for tracking/shipping numbers.
#
#----------[ OPEN ]----------
#
admin/header.php

#
#----------[ FIND ]----------
#
      <option value="4" '.(Tools::getValue('bo_search_type') == 4 ? 'selected="selected"' : '').'>'.translate('invoices').'</option>


#
#----------[ BEFORE, ADD ]----------
#
      <option value="8" '.(Tools::getValue('bo_search_type') == 8 ? 'selected="selected"' : '').'>'.translate('tracking numbers').'</option>
#
#----------[ OPEN ]----------
#
admin/tabs/AdminSearch.php

#
#----------[ FIND ]----------
#
 function postProcess()

#
#----------[ BEFORE, ADD ]----------
#
 /**
 * Search a shipping number in the orders 
 *
 * @params string $query String to find in the catalog
 */
 public function searchShippingNumber($query)
 {
  $this->_list['shipping_numbers'] = Order::searchByShippingNumber($query);
 }

#
#----------[ FIND ]----------
#
   /* IP */
   // 6 - but it is included in the customer block

#
#----------[ AFTER, ADD ]----------
#
   /* Shipping Number */
   if (!$searchType OR $searchType == 8)
   {
    $this->fieldsDisplay['shipping_numbers'] = (array(
     'ID' => array('title' => $this->l('Order #')),
     'shipping_number' => array('title' => $this->l('Tracking #')),
     'customer' => array('title' => $this->l('Name')),
     'address1' => array('title' => $this->l('Address')),
     'city' => array('title' => $this->l('Suburb')),
     'state' => array('title' => $this->l('State')),
     'postcode' => array('title' => $this->l('Postcode')),
     'country' => array('title' => $this->l('Country')),
     'phone' => array('title' => $this->l('Phone')),
     'date' => array('title' => $this->l('Delivery Date')),
     'actions' => array('title' => $this->l('View'))
    ));

    /* Normal shipping number search */
    $this->searchShippingNumber($query);
   }

#
#----------[ FIND ]----------
#
  $nbCategories = $nbProducts = $nbCustomers = 0;

#
#----------[ REPLACE WITH ]----------
#
  $nbCategories = $nbProducts = $nbCustomers = $nbShippingNumbers = 0;

#
#----------[ FIND ]----------
#
  /* Display error if nothing has been matching */
  if (!$nbCategories AND !$nbProducts AND !$nbCustomers)

#
#----------[ REPLACE WITH ]----------
#
  /* Display shipping numbers if any has been matching */
  if (isset($this->_list['shipping_numbers']) AND !empty($this->_list['shipping_numbers']) AND $nbShippingNumbers = sizeof($this->_list['shipping_numbers']))
  {
   echo '<h3>'.$nbShippingNumbers.' '.($nbShippingNumbers > 1 ? $this->l('tracking numbers') : $this->l('tracking number')).' '.$this->l('found with').' <b>"'.Tools::htmlentitiesUTF8($query).'"</b></h3>
   <table cellspacing="0" cellpadding="0" class="table widthfull">
    <tr>';
   foreach ($this->fieldsDisplay['shipping_numbers'] AS $field)
    echo '<th'.(isset($field['width']) ? 'style="width: '.$field['width'].'"' : '').'>'.$field['title'].'</th>';
   echo '</tr>';
   $irow = 0;
   foreach ($this->_list['shipping_numbers'] AS $k => $shipping_number)
   {
    $carrier = new Carrier($shipping_number['id_carrier']);
    echo '
    <tr class="'.($irow++ % 2 ? 'alt_row' : '').'">
     <td>'.$shipping_number['id_order'].'</td>
     <td>'.$shipping_number['shipping_number'].'</td>
     <td>'.stripslashes($shipping_number['firstname']).' '.stripslashes($shipping_number['lastname']).'</td>
     <td>'.$shipping_number['address1'].'</td>
     <td>'.$shipping_number['city'].'</td>
     <td>'.$shipping_number['state'].'</td>
     <td class="center">'.$shipping_number['postcode'].'</td>
     <td>'.$shipping_number['country'].'</td>
     <td>'.($shipping_number['phone_mobile'] ? $shipping_number['phone_mobile'] : $shipping_number['phone']).'</td>
     <td align="center">'.Tools::displayDate($shipping_number['delivery_date'], (int)$cookie->id_lang, true).'</td>
     <td class="center" width="40px">
      <a href="'.str_replace('@', $shipping_number['shipping_number'], $carrier->url).'" target="_blank">
      <img src="../img/admin/supplier.gif" alt="'.$this->l('Track order').'" /></a>
      <a href="'.$currentIndex.'?tab=AdminOrders&id_order='.$shipping_number['id_order'].'&vieworder&token='.Tools::getAdminToken('AdminOrders'.(int)(Tab::getIdFromClassName('AdminOrders')).(int)($cookie->id_employee)).'">
      <img src="../img/admin/details.gif" alt="'.$this->l('View order').'" /></a>
     </td>
    </tr>';
   }
   echo '</table>
   <div class="clear">&nbsp;</div>';
  }
   
  /* Display error if nothing has been matching */
  if (!$nbCategories AND !$nbProducts AND !$nbCustomers AND !$nbShippingNumbers)
#
#----------[ OPEN ]----------
#
classes/Order.php

#
#----------[ FIND ]----------
#
 public function deleteAssociations()
 {
  return (Db::getInstance()->Execute('
    DELETE FROM `'._DB_PREFIX_.'order_detail`
    WHERE `id_order` = '.(int)($this->id)) !== false);
 }

#
#----------[ AFTER, ADD ]----------
#
 /**
   * Light back office search for shipping numbers
   *
   * @param string $query Searched string
   * @return array Corresponding shipping number
   */
 public static function searchByShippingNumber($query)
 {
  global $cookie;

  return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('
  SELECT o.`shipping_number`, o.`id_order`, o.`delivery_date`, o.`id_carrier`, a.`firstname`, a.`lastname`, a.`address1`, a.`city`, a.`postcode`, a.`phone_mobile`, a.`phone`, s.`name` as state, cl.`name` as country 
  FROM `'._DB_PREFIX_.'orders` o
  LEFT JOIN `'._DB_PREFIX_.'address` a ON (a.`id_address` = o.`id_address_delivery`)
  LEFT JOIN `'._DB_PREFIX_.'state` s ON (s.`id_state` = a.`id_state`)
  LEFT JOIN `'._DB_PREFIX_.'country_lang` cl ON (cl.`id_country` = a.`id_country` AND cl.`id_lang` = '.(int)($cookie->id_lang).')
  WHERE o.shipping_number LIKE \'%'.pSQL($query).'%\'
  ORDER BY o.`shipping_number` DESC');
 }

31 December 2011

No. Units Sold for Stats Forecast Category Distribution table v1.0

Adds the number of units sold to the Stats Forecast Category Distribution table.
#
#----------[ OPEN ]----------
#
modules/statsforecast/statsforecast.php

#
#----------[ FIND ]----------
#
    <tr><th style="width:50px">'.$this->l('Category').'</th><th>'.$this->l('Count').'</th><th>'.$this->l('Sales').'</th><th>'.$this->l('% Count').'</th><th>'.$this->l('% Sales').'</th><th>'.$this->l('Avg price').'</th></tr>';

#
#----------[ REPLACE WITH ]----------
#
    <tr><th style="width:50px">'.$this->l('Category').'</th><th>'.$this->l('Count').'</th><th>'.$this->l('Units').'</th><th>'.$this->l('Sales').'</th><th>'.$this->l('% Count').'</th><th>'.$this->l('% Sales').'</th><th>'.$this->l('Avg price').'</th></tr>';

#
#----------[ FIND ]----------
#
     <td align="right">'.$catrow['orderQty'].'</td>

#
#----------[ AFTER, ADD ]----------
#
     <td align="right">'.$catrow['unitsSold'].'</td>

#
#----------[ FIND ]----------
#
COUNT(*) AS orderQty, 

#
#----------[ AFTER, ADD ]----------
#
SUM(od.`product_quantity`) AS unitsSold,

26 November 2011

Back Link & Date for Stock Movement Tab v1.0

Add a "Back to list" link & date column to BO stock movement tab.
#
#----------[ OPEN ]----------
#
admin/tabs/AdminStockMvt.php

#
#----------[ FIND ]----------
#
'employee' => array('title' => $this->l('Employee'), 'width' => 100, 'havingFilter' => true),

#
#----------[ AFTER, ADD ]----------
#
'date_add' => array('title' => $this->l('Date'), 'width' => 60, 'type' => 'datetime', 'align' => 'right'),

#
#----------[ FIND ]----------
#
public function viewstock_mvt()
 {
  global $cookie;

#
#----------[ REPLACE WITH ]----------
#
public function viewstock_mvt()
 {
  global $currentIndex, $cookie;

#
#----------[ FIND ]----------
#
echo '</table>';
 }
 
 public function display()

#
#----------[ REPLACE WITH ]----------
#
echo '</table>';
   echo '<div class="clear" style="height:20px;">&nbsp;</div>
   <br /><br /><a href="'.$currentIndex.'&token='.$this->token.'"><img src="../img/admin/arrow2.gif" /> '.$this->l('Back to list').'</a><br />';
 }
 
 public function display()

Back Link for Carts Tab v1.0

Add a "Back to list" link on the bottom of the cart detail page.
#
#----------[ OPEN ]----------
#
admin/tabs/AdminCarts.php

#
#----------[ FIND ]----------
#
    // Cancel product
    echo '
   </fieldset>
  <div class="clear" style="height:20px;">&nbsp;</div>';

#
#----------[ AFTER, ADD ]----------
#
  echo '<br /><br /><a href="'.$currentIndex.'&token='.$this->token.'"><img src="../img/admin/arrow2.gif" alt="" /> '.$this->l('Back to cart list').'</a><br />';