Skip to content
Snippets Groups Projects
ordernumber.php 10.33 KiB
<?php
/**
 * @package VirtueMart 2 OrderNumber plugin for Joomla! 2.5
 * @version $Id: mod_XYZ.php 599 2010-03-20 23:26:33Z you $
 * @author Reinhold Kainhofer, reinhold@kainhofer.com
 * @copyright (C) 2012 - Reinhold Kainhofer
 * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
**/

defined('_JEXEC') or     die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ) ;
if (!class_exists('vmShopperPlugin')) require(JPATH_VM_PLUGINS . DS . 'vmshopperplugin.php');

/* For php 5.2 we cannot use lambda functions or any other way to pass local variables to the callback.
   The only way to pass a local variable to a callback for preg_replace_callback is to 
   create a class instance... */
class ReplacementCallback {
    private $details;
    private $nrtype;

    function __construct($nrtype, $details) {
        $this->details = $details;
        $this->nrtype = $nrtype;
    }

    /* Return a random "string" of the given length taken from the given alphabet */
    static function randomString($alphabet, $len) {
        $alen = strlen($alphabet);
        $r = "";
        for ($n=0; $n<$len; $n++) {
            $r .= $alphabet[mt_rand(0, $alen-1)];
        }
        return $r;
    }

    function replace ($match) {
        $varname = strtolower($match[1]);
        switch ($varname) {
            case "year":  return date ("Y");
            case "year2": return date ("y");
            case "month": return date("m");
            case "day": return date("d");
            case "hour": return date("H");
            case "hour12": return date("h");
            case "ampm": return date("a");
            case "minute": return date("i");
            case "second": return date("s");
            case "random":
                /* the regexp matches (random)(Type)(Len) as match 1 to 3, Len is optional */
                $len = ($match[3]?$match[3]:1);
                // Fallback: If no Type is given, use Digit
                $alphabet = "0123456789";
                // Select the correct alphabet depending on Type
                switch (strtolower($match[2])) {
                    case "digit": $alphabet = "0123456789"; break;
                    case "hex": $alphabet = "0123456789abcdef"; break;
                    case "letter": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; break;
                    case "uletter": $alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; break;
                    case "lletter": $alphabet = "abcdefghijklmnopqrstuvwxyz"; break;
                    case "alphanum": $alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; break;
                }
                return self::randomString ($alphabet, $len);
            case "vendorid": return ($this->details->virtuemart_vendor_id);
            case "userid": return ($this->details->virtuemart_user_id);
            case "ipaddress": return ($this->details->ip_address);

            // Only for Invoice:
            case "ordernumber": return ($this->details->order_number);
            case "orderid": return ($this->details->virtuemart_order_id);
            case "lastname": return ($this->details->last_name);
            case "firstname": return ($this->details->first_name);
            case "company": return ($this->details->company);
            case "city": return ($this->details->city);
            case "zip": return ($this->details->zip);
            case "orderstatus": return ($this->details->order_status);
        }
        // No variable type matched, so don't replace, return the original string
        return $match[0];
    }
}


class plgVmShopperOrdernumber extends vmShopperPlugin {

    function __construct(& $subject, $config) {
        parent::__construct($subject, $config);
        /* Create the database table */
        $this->tableFields = array_keys ($this->getTableSQLFields ());
        $this->onStoreInstallPluginTable($this->_psType);
    }

    public function getVmPluginCreateTableSQL () {
        return $this->createTableSQL ('VM Shopper plugin: custom order and invoice numbers');
    }

    function getTableSQLFields () {
        $SQLfields = array(
            'id'             => 'int(11) UNSIGNED NOT NULL AUTO_INCREMENT',
            'number_type'    => 'int(1) UNSIGNED',
            'number_format'  => 'varchar(255)',
            'count'          => 'int(1)',
        );
        return $SQLfields;
    }

    // We don't need this function, but the parent class declares it abstract, so we need to overload
    function plgVmOnUpdateOrderBEShopper($_orderID) {}

    /* Replace the format variables, match[1] is the variable name, match[2] and match[3] are only used for random fields */
    /* Type 0 means order number, type 1 means invoice number, type 2 means customer number, 3 means order password */
    function replace_fields ($fmt, $nrtype, $details) {
        // Match variables for the form random[type][count] and everything else.
        // This makes it possible to handle "random" just like any other type!
        $patterns = array (
            '/\[(random)(.*?)([0-9]*?)\]/', // For randomTypeN, spit the three parts
            '/\[([^\]]+)\]/'   // Everything else matches whole variable name
        );
//         // TODO: in  php 5.3 and 5.4 we can easier pass $orderDetails to the callback
//         function my_callback ($match) {
//             return self::replacementCallback ($match, $details); 
//         };
//         return preg_replace_callback ($patterns, 
//             function ($match) use ($details) { 
//                 return self::replacementCallback ($match, $details); 
//             }, $fmt);

        /* php 5.2 does not allow lambda functions and there is no other way to
           pass a local variable to the callback than a class instance! */
        $callback = new ReplacementCallback ($nrtype, $details);
        return preg_replace_callback ($patterns, array($callback, 'replace'), $fmt);
    }

    /* Type 0 means order number, type 1 means invoice number, type 2 means customer number */
    function format_number ($fmt, $details, $nrtype = 0, $global = 1, $padding = 1) {
        // First, replace all variables:
        $nr = $this->replace_fields ($fmt, $nrtype, $details);

        // Look up the current counter
        $db = JFactory::getDBO();
        /* prevent sql injection attacks by escaping the user-entered format! */
        $nr_escaped = $db->getEscaped ($nr);
        /* For global counting, simply read the empty number_format entries! */
        $q = 'SELECT `count` FROM `'.$this->_tablename.'` WHERE `number_type`='.(int)$nrtype.' AND `number_format`="'.($global?"":$nr_escaped).'"';
        $db->setQuery($q);
        $existing = $db->loadResult();
        $count = $existing?($existing+1):1;

        // Insert new counter value into the db
        if ($existing) {
            $q = 'UPDATE `'.$this->_tablename.'` SET `count`= "'.$count.'" WHERE `number_type`='.(int)$nrtype.' AND `number_format`="'.($global?"":$nr_escaped).'"';
        } else {
            $q = 'INSERT INTO `'.$this->_tablename.'` (`count`, `number_type`, `number_format`) VALUES ('.(int)$count.','.(int)$nrtype.', "'.($global?"":$nr_escaped).'")';
        }
        $db->setQuery( $q );
        $db->query();

        // return the format with the counter inserted
        
        return str_replace ("#", sprintf('%0' . $padding . 's', $count), $nr);
    }


    function plgVmOnUserOrder(&$orderDetails/*,&$data*/) {
        // Is order number customization enabled?
        if ($this->params->get('customize_order_number')) {
          $nrtype=0; /*order-nr*/
          $fmt = $this->params->get ('order_number_format', "#");
          $global = $this->params->get ('order_number_global', 1);
          $padding = $this->params->get ('order_number_padding', 1);
          $ordernr = $this->format_number ($fmt, $orderDetails, $nrtype, $global, $padding);
          $orderDetails->order_number = $ordernr;
        }
        // Is order password customization enabled?
        if ($this->params->get('customize_order_password')) {
          $fmt = $this->params->get ('order_password_format', "[randomHex8]");
          $passwd = $this->replace_fields ($fmt, 3, $orderDetails);
          $orderDetails->order_pass = $passwd;
        }
    }

    function plgVmOnUserInvoice($orderDetails,&$data) {
        // Is order number customization enabled?
        if ($this->params->get('customize_invoice_number')) {
            // check the default configuration
            $orderstatusForInvoice = VmConfig::get('inv_os',array());
            if(!is_array($orderstatusForInvoice)) $orderstatusForInvoice = array($orderstatusForInvoice); //for backward compatibility 2.0.8e
            $pdfInvoice = (int)VmConfig::get('pdf_invoice', 0); // backwards compatible
            $force_create_invoice=JRequest::getInt('create_invoice', 0);
            if ( in_array($orderDetails['order_status'],$orderstatusForInvoice)  or $pdfInvoice==1  or $force_create_invoice==1 ){
                $nrtype=1; /*invoice-nr*/
                $fmt = $this->params->get ('invoice_number_format', "#");
                $global = $this->params->get ('invoice_number_global', 1);
                $padding = $this->params->get ('invoice_number_padding', 1);
                $invoicenr = $this->format_number ($fmt, (object)$orderDetails, $nrtype, $global, $padding);
                $data['invoice_number'] = $invoicenr;
                return $data;
            }
        }
    }

    // Customizing the customer numbers requires VM >= 2.0.15b, earlier versions 
    // left out the & and thus didn't allow changing the user data
    function plgVmOnUserStore(&$data) {
        // Is order number customization enabled?
        if ($this->params->get('customize_customer_number') && isset($data['customer_number_bycore']) && $data['customer_number_bycore']==1) {
            $nrtype=2; /*customer-nr*/
            $fmt = $this->params->get ('customer_number_format', "#");
            $global = $this->params->get ('customer_number_global', 1);
            $padding = $this->params->get ('customer_number_padding', 1);
            $customernr = $this->format_number ($fmt, (object)$data, $nrtype, $global, $padding);
            $data['customer_number'] = $customernr;
            return $data;
        }
    }

}