From c337dceaeff2e98d27a49fefd911767b2002642b Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <reinhold@kainhofer.com>
Date: Tue, 9 Dec 2014 01:49:42 +0100
Subject: [PATCH] V0.1.0: First version that actually works. No DB editing in
 the BE implemented yet.

---
 OpenTools_Ordernumber.xml                     |   2 +-
 Ordernumber/Helper/Data.php                   | 137 +++++++++++++-
 Ordernumber/Model/Observer.php                | 174 ++++++++++--------
 Ordernumber/Model/Ordernumber.php             |  28 +++
 Ordernumber/Model/Resource/Ordernumber.php    |  28 +++
 .../Model/Resource/Ordernumber/Collection.php |   8 +
 Ordernumber/etc/config.xml                    |  92 +++++++--
 Ordernumber/etc/system.xml                    | 169 ++++++++++++++++-
 .../install-0.1.0.php                         |  34 ++++
 9 files changed, 567 insertions(+), 105 deletions(-)
 create mode 100644 Ordernumber/Model/Ordernumber.php
 create mode 100644 Ordernumber/Model/Resource/Ordernumber.php
 create mode 100644 Ordernumber/Model/Resource/Ordernumber/Collection.php
 create mode 100644 Ordernumber/sql/opentools_ordernumber_setup/install-0.1.0.php

diff --git a/OpenTools_Ordernumber.xml b/OpenTools_Ordernumber.xml
index 0eb4979..4d53e39 100644
--- a/OpenTools_Ordernumber.xml
+++ b/OpenTools_Ordernumber.xml
@@ -23,7 +23,7 @@
     <modules>
         <OpenTools_Ordernumber>
             <active>true</active>
-            <codePool>local</codePool>
+            <codePool>community</codePool>
             <depends>
                 <Mage_Sales />
             </depends>
diff --git a/Ordernumber/Helper/Data.php b/Ordernumber/Helper/Data.php
index 5c7f8fe..3d8e31a 100644
--- a/Ordernumber/Helper/Data.php
+++ b/Ordernumber/Helper/Data.php
@@ -1,5 +1,140 @@
 <?php
 class OpenTools_Ordernumber_Helper_Data extends Mage_Core_Helper_Abstract
 {
+function logitem($label, $item) {
+    Mage::Log($label . " " . get_class($item) . "\n", null, 'ordernumber.log');
+    Mage::Log(is_array($item)?$item:$item->debug(), null, 'ordernumber.log');
+    Mage::Log(get_class_methods(get_class($item)), null, 'ordernumber.log');
+}
 
-}
\ No newline at end of file
+    /* Return a random "string" of the given length taken from the given alphabet */
+    function randomString($alphabet, $len) {
+        $alen = strlen($alphabet);
+        $r = "";
+        for ($n=0; $n<$len; $n++) {
+            $r .= $alphabet[mt_rand(0, $alen-1)];
+        }
+        return $r;
+    }
+
+    function replaceRandom ($match) {
+        /* the regexp matches (random)(Type)(Len) as match, Type and 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);
+    }
+    
+    function setupDateTimeReplacements (&$reps, $nrtype) {
+        $reps["[year]"] = date ("Y");
+        $reps["[year2]"] = date ("y");
+        $reps["[month]"] = date("m");
+        $reps["[day]"] = date("d");
+        $reps["[hour]"] = date("H");
+        $reps["[hour12]"] = date("h");
+        $reps["[ampm]"] = date("a");
+        $reps["[minute]"] = date("i");
+        $reps["[second]"] = date("s");
+    }
+    function setupAddressReplacements(&$reps, $prefix, $address, $nrtype) {
+        if (!$address) return;
+        $reps["[".$prefix."addressid]"] = $address->getId();
+        
+        $reps["[".$prefix."firstname]"] = $address->firstname;
+        $reps["[".$prefix."lastname]"] = $address->lastname;
+        $reps["[".$prefix."company]"] = $address->company;
+        $reps["[".$prefix."city]"] = $address->city;
+        $reps["[".$prefix."zip]"] = $address->postcode;
+        $reps["[".$prefix."postcode]"] = $address->postcode;
+
+        $reps["[".$prefix."region]"] = $address->getRegion();
+        $reps["[".$prefix."regioncode]"] = $address->getRegionCode();
+        $reps["[".$prefix."regionid]"] = $address->getRegionId();
+
+        $country = $address->getCountryModel();
+        $reps["[".$prefix."country]"] = $country->getName();
+        $reps["[".$prefix."countrycode2]"] = $country->iso2_code;
+        $reps["[".$prefix."countrycode3]"] = $country->iso3_code;
+        $reps["[".$prefix."countryid]"] = $country->getId();
+        
+    }
+    function setupStoreReplacements (&$reps, $order, $nrtype) {
+        $store = $order->getStore();
+        $reps["[storeid]"] = $store->getStoreId();
+        $reps["[storecurrency]"] = $order->getStoreCurrency();
+    }
+    function setupOrderReplacements (&$reps, $order, $nrtype) {
+        $shippingAddress = $order->getShippingAddress();
+        $billingAddress = $order->getBillingAddress();
+        $address = $shippingAddress;
+        /* if ($nrtype == "invoice") {
+            // Invoices use the billing address for un-prefixed fields
+            $address = $billingAddress;
+        } */
+        $reps["[orderid]"] = $order->getId();
+        $reps["[ordernumber]"] = $order->getIncrementId();
+        $reps["[orderstatus]"] = $order->status;
+        $reps["[currency]"] = $order->getOrderCurrency()->getCurrencyCode();
+        $reps["[customerid]"] = $order->customer_id;
+        $this->setupAddressReplacements($reps, "", $address, $nrtype);
+        $this->setupAddressReplacements($reps, "shipping", $shippingAddress, $nrtype);
+        $this->setupAddressReplacements($reps, "billing", $billingAddress, $nrtype);
+        
+        $reps["[totalitems]"] = $order->total_item_count;
+        $reps["[totalquantity]"] = $order->total_qty_ordered;
+    }
+    function setupShippingReplacements(&$reps, $order, $nrtype) {
+        $reps["[shippingmethod]"] = $order->getShippingMethod();
+    }
+    
+    function setupShipmentReplacements (&$reps, $shipment, $order, $nrtype) {
+        // TODO
+    }
+    function setupInvoiceReplacements (&$reps, $invoice, $order, $nrtype) {
+        $reps["[invoiceid]"] = $invoice->getId();
+    }
+    function setupCreditMemoReplacements (&$reps, $creditmemo, $order, $nrtype) {
+        // TODO
+    }
+    function setupReplacements($nrtype, $info) {
+        $reps = array();
+        $order = $info['order'];
+        $this->setupDateTimeReplacements($reps, $nrtype);
+        $this->setupStoreReplacements($reps, $order, $nrtype);
+        $this->setupOrderReplacements($reps, $order, $nrtype);
+        $this->setupShippingReplacements($reps, $order, $nrtype);
+        if (isset($info['shipment'])) {
+            $this->setupShipmentReplacements($reps, $info['shipment'], $order, $nrtype);
+        }
+        if (isset($info['invoice'])) {
+            $this->setupInvoiceReplacements($reps, $info['invoice'], $order, $nrtype);
+        }
+        if (isset($info['creditmemo'])) {
+            $this->setupCreditMemoReplacements($reps, $info['creditmemo'], $order, $nrtype);
+        }
+        return $reps;
+    }
+    
+    function doReplacements ($fmt, $reps) {
+        // First, replace all randomXXX[n] fields. This needs to be done with a regexp and a callback:
+        $fmt = preg_replace_callback ('/\[(random)(.*?)([0-9]*?)\]/', array($this, 'replaceRandom'), $fmt);
+        return str_ireplace (array_keys($reps), array_values($reps), $fmt);
+    }
+    
+    /* Type 0 means order number, type 1 means invoice number, type 2 means customer number, 3 means order password */
+    function replace_fields ($fmt, $nrtype, $info) {
+        $reps = $this->setupReplacements ($nrtype, $info);
+        return $this->doReplacements($fmt, $reps);
+    }
+
+
+}
diff --git a/Ordernumber/Model/Observer.php b/Ordernumber/Model/Observer.php
index c76bd27..243853f 100644
--- a/Ordernumber/Model/Observer.php
+++ b/Ordernumber/Model/Observer.php
@@ -20,93 +20,105 @@
  * @copyright  Copyright (c) 2010 Fooman Limited (http://www.fooman.co.nz)
  * @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  */
-class OpenTools_Ordernumber_Model_Observer
+class OpenTools_Ordernumber_Model_Observer extends Mage_Core_Model_Abstract
 {
+     public function _construct()
+     {
+         parent::_construct();
+         $this->_init('ordernumber/ordernumber');
+     }
+ 
+    protected $_dbModel = null;
+    protected function _getModel() {
+        return Mage::getModel('opentools_ordernumber/ordernumber');
+    }
+    
+    public function getModel() {
+        if (is_null($this->_dbModel)) 
+            $this->_dbModel = $this->_getModel();
+        return $this->_dbModel;
+    }
 
-    public function sales_order_invoice_save_before ($observer)
-    {
-        $invoice = $observer->getInvoice();
-        if (!$invoice->getId()) {
-/*            $order = $invoice->getOrder();
-            $storeId = $order->getStore()->getStoreId();
-            $prefix = Mage::getStoreConfig('sameorderinvoicenumber/settings/invoiceprefix',
-                            $storeId);
-                $newInvoiceNr = 0;
-                $currentPostfix = 0;
-                while (!$newInvoiceNr) {
-                    if ($currentPostfix) {
-                        $newInvoiceNr = $prefix . $order->getIncrementId() . '-' . $currentPostfix;
-                    } else {
-                        $newInvoiceNr = $prefix . $order->getIncrementId();
-                    }
-                    $collection = Mage::getModel('sales/order_invoice')->getCollection()->addFieldToFilter('increment_id',
-                            $newInvoiceNr);
-                    if ($collection->getAllIds()) {
-                        //number already exists
-                        $newInvoiceNr = 0;
-                        $currentPostfix++;
-                    } else {
-                        $invoice->setIncrementId($newInvoiceNr);
-                    }
-                }*/
-        }
+    // This trigger is called directly after the increment ID is reserved for an order
+    // TODO: Ideally, we would overwrite the reserveOrderId function!
+    //       Then magento would not create/reserve an order in the first place
+    public function sales_model_service_quote_submit_before ($observer) {
+        $order = $observer->getEvent()->getOrder();
+        return $this->handle_new_number('order', $order, $order);
+    }
+    public function sales_order_save_before ($observer) {
+        $order = $observer->getEvent()->getOrder();
+        return $this->handle_new_number('order', $order, $order);
     }
 
-    public function sales_order_shipment_save_before ($observer)
-    {
-        $shipment = $observer->getShipment();
-        if (!$shipment->getId()) {
-/*            $order = $shipment->getOrder();
-            $storeId = $order->getStore()->getStoreId();
-            $prefix = Mage::getStoreConfig('sameorderinvoicenumber/settings/shipmentprefix',
-                            $storeId);
-                $newShipmentNr = 0;
-                $currentPostfix = 0;
-                while (!$newShipmentNr) {
-                    if ($currentPostfix) {
-                        $newShipmentNr = $prefix . $order->getIncrementId() . '-' . $currentPostfix;
-                    } else {
-                        $newShipmentNr = $prefix . $order->getIncrementId();
-                    }
-                    $collection = Mage::getModel('sales/order_shipment')->getCollection()->addFieldToFilter('increment_id',
-                            $newShipmentNr);
-                    if ($collection->getAllIds()) {
-                        //number already exists
-                        $newShipmentNr = 0;
-                        $currentPostfix++;
-                    } else {
-                        $shipment->setIncrementId($newShipmentNr);
-                    }
-                }*/
-        }
+    public function sales_order_invoice_save_before ($observer) {
+        $invoice = $observer->getEvent()->getInvoice();
+        return $this->handle_new_number('invoice', $invoice, $invoice->getOrder());
+    }
+
+    public function sales_order_shipment_save_before ($observer) {
+        $shipment = $observer->getEvent()->getShipment();
+        return $this->handle_new_number('shipment', $shipment, $shipment->getOrder());
     }
 
-    public function sales_order_creditmemo_save_before ($observer)
-    {
-        $creditmemo = $observer->getCreditmemo();
-        if (!$creditmemo->getId()) {
-/*            $order = $creditmemo->getOrder();
-            $storeId = $order->getStore()->getStoreId();
-            $prefix = Mage::getStoreConfig('sameorderinvoicenumber/settings/creditmemoprefix',
-                            $storeId);
-                $newCreditmemoNr = 0;
-                $currentPostfix = 0;
-                while (!$newCreditmemoNr) {
-                    if ($currentPostfix) {
-                        $newCreditmemoNr = $prefix . $order->getIncrementId() . '-' . $currentPostfix;
-                    } else {
-                        $newCreditmemoNr = $prefix . $order->getIncrementId();
-                    }
-                    $collection = Mage::getModel('sales/order_creditmemo')->getCollection()->addFieldToFilter('increment_id',
-                            $newCreditmemoNr);
-                    if ($collection->getAllIds()) {
-                        //number already exists
-                        $newCreditmemoNr = 0;
-                        $currentPostfix++;
-                    } else {
-                        $creditmemo->setIncrementId($newCreditmemoNr);
-                    }
-                }*/
+    public function sales_order_creditmemo_save_before ($observer) {
+        $creditmemo = $observer->getEvent()->getCreditmemo();
+        return $this->handle_new_number('creditmemo', $creditmemo, $creditmemo->getOrder());
+    }
+
+    public function handle_new_number ($nrtype, $object, $order) {
+        $storeId = $order->getStore()->getStoreId();
+        $cfgprefix = 'ordernumber/'.$nrtype.'numbers';
+        $enabled = Mage::getStoreConfig($cfgprefix.'/active', $storeId);
+
+        if ($enabled && !$object->getId() && !$object->getOrdernumberProcessed()) {
+            // This trigger might be called twice, so ignore it the second time!
+            $object->setOrdernumberProcessed(true);
+
+            $format = Mage::getStoreConfig($cfgprefix.'/format', $storeId);
+            $global = Mage::getStoreConfig($cfgprefix.'/global', $storeId);
+            $digits = Mage::getStoreConfig($cfgprefix.'/digits', $storeId);
+
+            // First, replace all variables:
+            $helper = Mage::helper('ordernumber');
+            $info = array('order'=>$order, $nrtype=>$object);
+            $nr = $helper->replace_fields ($format, $nrtype, $info);
+
+            // Split at a | to get the number format and a possibly different counter increment format
+            // If a separate counter format is given after the |, use it, otherwise reuse the number format itself as counter format
+            $parts = explode ("|", $nr);
+            $format = $parts[0];
+            $counterfmt = ($global==1)?"":$parts[(count($parts)>1)?1:0];
+            
+            // Now find the next counter that does not lead to duplicate 
+            $newnumber = null;
+            $model = $this->getModel();
+            
+            $count = 0;
+            $created = false;
+            // Make up to 150 attempts to create a number...
+            while (empty($newnumber) && (count<150)) { 
+                $count += 1;
+
+                // Find the next counter value
+                $count = $model->getCounterValueIncremented($nrtype, $counterfmt);
+                $newnumber = str_replace ("#", sprintf('%0' . (int)$padding . 's', $count), $format);
+                
+                // Check whether that number is already in use. If so, attempt to create the next number:
+                $modelname=($nrtype=='order') ? 'sales/order' : ('sales/order_'.$nrtype);
+                $collection = Mage::getModel($modelname)->getCollection()->addFieldToFilter('increment_id', $newnumber);
+                if ($collection->getAllIds()) {
+                    Mage::Log("$nrtype number $newnumber already in use, trying again", null, 'ordernumber.log');
+                    //number already exists => next attempt in the loop
+                    $newnumber = null;
+                } else {
+                    $object->setIncrementId($newnumber);
+                    $created = true;
+                }
+            }
+            if (!$created) {
+                Mage::Log("Unable to create $nrtype number for counter format $nr (name $counterfmt)...", null, 'ordernumber.log');
+            }
         }
     }
 
diff --git a/Ordernumber/Model/Ordernumber.php b/Ordernumber/Model/Ordernumber.php
new file mode 100644
index 0000000..b2073cf
--- /dev/null
+++ b/Ordernumber/Model/Ordernumber.php
@@ -0,0 +1,28 @@
+<?php
+class OpenTools_Ordernumber_Model_Ordernumber extends Mage_Core_Model_Abstract
+{
+     public function _construct()
+     {
+         parent::_construct();
+         $this->_init('opentools_ordernumber/ordernumber');
+     }
+     public function getCounterValueIncremented($nrtype, $format) {
+        $helper = Mage::helper('ordernumber');
+        $this->loadNumberCounter($nrtype, $format);
+
+        $this->setNumberType($nrtype);
+        $this->setNumberFormat($format);
+        $count = $this->getCount()+1;
+        $this->setCount($count);
+        $res = $this->save();
+        return $count;
+     }
+    public function loadNumberCounter($nrtype, $format)
+    {
+        $this->_getResource()->loadNumberCounter($this, $nrtype, $format);
+        $this->_afterLoad();
+        $this->setOrigData();
+        $this->_hasDataChanges = false;
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/Ordernumber/Model/Resource/Ordernumber.php b/Ordernumber/Model/Resource/Ordernumber.php
new file mode 100644
index 0000000..784bf50
--- /dev/null
+++ b/Ordernumber/Model/Resource/Ordernumber.php
@@ -0,0 +1,28 @@
+<?php
+class OpenTools_Ordernumber_Model_Resource_Ordernumber extends Mage_Core_Model_Resource_Db_Abstract
+{
+     public function _construct() {
+         $this->_init('opentools_ordernumber/ordernumber', 'ordernumber_id');
+     }
+
+    public function loadNumberCounter(Mage_Core_Model_Abstract $object, $nrtype, $format) {
+        $read = $this->_getWriteAdapter();
+        if ($read && !is_null($nrtype)) {
+            $typefield = $read->quoteIdentifier(sprintf('%s.%s', $this->getMainTable(), 'number_type'));
+            $formatfield = $read->quoteIdentifier(sprintf('%s.%s', $this->getMainTable(), 'number_format'));
+            $select = $read->select()
+                ->from($this->getMainTable())
+                ->where($typefield . '=?', $nrtype)
+                ->where($formatfield .'=?', $format);
+            $data = $read->fetchRow($select);
+            if ($data) {
+                $object->setData($data);
+            }
+        }
+
+        $this->unserializeFields($object);
+        $this->_afterLoad($object);
+
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/Ordernumber/Model/Resource/Ordernumber/Collection.php b/Ordernumber/Model/Resource/Ordernumber/Collection.php
new file mode 100644
index 0000000..f12f1ff
--- /dev/null
+++ b/Ordernumber/Model/Resource/Ordernumber/Collection.php
@@ -0,0 +1,8 @@
+<?php
+class OpenTools_Ordernumber_Model_Resource_Ordernumber_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
+ {
+     public function _construct() {
+         parent::_construct();
+         $this->_init('opentools_ordernumber/ordernumber');
+     }
+}
\ No newline at end of file
diff --git a/Ordernumber/etc/config.xml b/Ordernumber/etc/config.xml
index ae10ee4..2030f4a 100644
--- a/Ordernumber/etc/config.xml
+++ b/Ordernumber/etc/config.xml
@@ -12,45 +12,87 @@
     </modules>
  
     <global>
-<!-- declare model group for new module -->
         <models>
-<!-- model group alias to be used in Mage::getModel() -->
-            <ordernumber>
-<!-- base class name for the model group -->
+            <opentools_ordernumber>
                 <class>OpenTools_Ordernumber_Model</class>
-            </ordernumber>
+                <resourceModel>ordernumber_resource</resourceModel>
+            </opentools_ordernumber>
+            <ordernumber_resource>
+                <class>OpenTools_Ordernumber_Model_Resource</class>
+                <entities>
+                    <ordernumber>
+                        <table>opentools_ordernumber</table>
+                    </ordernumber>
+                </entities>
+            </ordernumber_resource>
         </models>
         <helpers>
             <ordernumber>
                 <class>OpenTools_Ordernumber_Helper</class>
             </ordernumber>
         </helpers>
+        <resources>
+            <opentools_ordernumber_setup>
+                <setup>
+                    <module>OpenTools_Ordernumber</module>
+                    <class>Mage_Core_Model_Resource_Setup</class>
+                </setup>
+            </opentools_ordernumber_setup>
+            <opentools_ordernumber_write>
+                <connection>
+                    <use>core_write</use>
+                </connection>
+            </opentools_ordernumber_write>
+            <opentools_ordernumber_read>
+                <connection>
+                    <use>core_read</use>
+                </connection>
+            </opentools_ordernumber_read>
+        </resources>
         <events>
+            <sales_model_service_quote_submit_before>
+                <observers>
+                    <opentools_ordernumber>
+                        <type>singleton</type>
+                        <class>OpenTools_Ordernumber_Model_Observer</class>
+                        <method>sales_model_service_quote_submit_before</method>
+                    </opentools_ordernumber>
+                </observers>
+            </sales_model_service_quote_submit_before>
+            <sales_order_save_before>
+                <observers>
+                    <opentools_ordernumber>
+                        <type>singleton</type>
+                        <class>OpenTools_Ordernumber_Model_Observer</class>
+                        <method>sales_order_save_before</method>
+                    </opentools_ordernumber>
+                </observers>
+            </sales_order_save_before>
             <sales_order_invoice_save_before>
                 <observers>
-                    <sales_order_invoice_save_before>
+                    <opentools_ordernumber>
                         <type>singleton</type>
                         <class>OpenTools_Ordernumber_Model_Observer</class>
                         <method>sales_order_invoice_save_before</method>
-                    </sales_order_invoice_save_before>
+                    </opentools_ordernumber>
                 </observers>
             </sales_order_invoice_save_before>
             <sales_order_shipment_save_before>
                 <observers>
-                    <sales_order_shipment_save_before>
+                    <opentools_ordernumber>
                         <type>singleton</type>
                         <class>OpenTools_Ordernumber_Model_Observer</class>
                         <method>sales_order_shipment_save_before</method>
-                    </sales_order_shipment_save_before>
+                    </opentools_ordernumber>
                 </observers>
             </sales_order_shipment_save_before>
             <sales_order_creditmemo_save_before>
                 <observers>
-                    <sales_order_creditmemo_save_before>
+                    <opentools_ordernumber>
                         <type>singleton</type>
                         <class>OpenTools_Ordernumber_Model_Observer</class>
                         <method>sales_order_creditmemo_save_before</method>
-                    </sales_order_creditmemo_save_before>
+                    </opentools_ordernumber>
                 </observers>
             </sales_order_creditmemo_save_before>
         </events>
@@ -77,12 +119,34 @@
         </acl>
     </adminhtml>
     <default>
-         <ordernumber>
+        <ordernumber>
             <settings>
                 <active>0</active>
             </settings>
+            <ordernumbers>
+                <active>0</active>
+                <format>Order-[year]-#</format>
+                <digits>0</digits>
+                <global>0</global>
+            </ordernumbers>
+            <invoicenumbers>
+                <active>0</active>
+                <format>Invoice-[year]-#</format>
+                <digits>0</digits>
+                <global>0</global>
+            </invoicenumbers>
+            <shipmentnumbers>
+                <active>0</active>
+                <format>Shipment-[year]-#</format>
+                <digits>0</digits>
+                <global>0</global>
+            </shipmentnumbers>
+            <creditmemonumbers>
+                <active>0</active>
+                <format>CreditMemo-[year]-#</format>
+                <digits>0</digits>
+                <global>0</global>
+            </creditmemonumbers>
         </ordernumber>
     </default>
-
- 
 </config>
\ No newline at end of file
diff --git a/Ordernumber/etc/system.xml b/Ordernumber/etc/system.xml
index 9fb31bd..4bc6b59 100644
--- a/Ordernumber/etc/system.xml
+++ b/Ordernumber/etc/system.xml
@@ -5,7 +5,7 @@
             <label>Customize Order and Invoice Numbers</label>
             <tab>sales</tab>
             <frontend_type>text</frontend_type>
-            <sort_order>2</sort_order>
+            <sort_order>300</sort_order>
             <show_in_default>1</show_in_default>
             <show_in_website>1</show_in_website>
             <show_in_store>1</show_in_store>
@@ -14,12 +14,13 @@
                     <label>Order Numbers</label>
                     <frontend_type>text</frontend_type>
                     <sort_order>10</sort_order>
+                    <expanded>1</expanded>
                     <show_in_default>1</show_in_default>
                     <show_in_website>1</show_in_website>
                     <show_in_store>1</show_in_store>
                     <fields>
                         <active translate="label">
-                            <label>Customize Order Numbers</label>
+                            <label>Customize order numbers</label>
                             <frontend_type>select</frontend_type>
                             <source_model>adminhtml/system_config_source_yesno</source_model>
                             <sort_order>1</sort_order>
@@ -27,26 +28,100 @@
                             <show_in_website>1</show_in_website>
                             <show_in_store>1</show_in_store>
                         </active>
+                        <format translate="label">
+                            <label>Format of the order numbers</label>
+                            <tooltip><![CDATA[A string, where [...] indicates a variable and # is replaced with the counter. Possible (case-insensitive) variables are:<table><tr><td>[year], [year2], [month], [day], [hour], [hour12], [ampm], [minute], [second]</td><td>Components of the current date when the number is generated</td></tr><tr><td>[randomDigit<i>n</i>], [randomHex<i>n</i>], [randomHex<i>n</i>], [randomLetter<i>n</i>], [randomULetter<i>n</i>], [randomLLetter<i>n</i>], [randomAlphanum<i>n</i>]</td><td>Random numbers an strings of length <i>n</i> (n=1 if left out)</td></tr><tr><td>[StoreId]</td><td>Id of the current store</td></tr><tr><td>[OrderNumber], [OrderID]</td><td>Order number and ID (not available for ordernumbers)</td></tr><tr><td>[firstname],[lastname], [company], [city], [zip], [postcode], [region], [regioncode], [regionid], [country], [countrycode2], [countrycode3], [countryid]</td><td>Components of the shipping address</td></tr><tr><td>[shippingfirstname], ...<br/>[billingfirstname], ...</td><td>Components of the shipping and billing addresses (fields as above)</td></tr><tr><td>[totalitems], [totalquantity]</td><td>Total number of items and total quantity of the order</td></tr><tr><td>[shippingmethod]</td><td>Internal name of the shipping method</td></tr></table>]]></tooltip>
+                            <comment><![CDATA[For help, see <a href="http://open-tools.net/documentation/ordernumber-plugin-for-virtuemart.html">our homepage</a>.]]></comment>
+                            <frontend_type>text</frontend_type>
+                            <sort_order>2</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </format>
+                        <digits translate="label">
+                            <label>Number of digits for the counter</label>
+                            <tooltip>A # will be replaced by the counter, left-padded with 0s to have at least this many digits</tooltip>
+                            <frontend_type>text</frontend_type>
+                            <validate>validate-not-negative-number</validate>
+                            <sort_order>3</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </digits>
+                        <global translate="label">
+                            <label>Use a global counter with no reset</label>
+                            <frontend_type>checkbox</frontend_type>
+                            <sort_order>4</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </global>
                     </fields>
                 </ordernumbers>
+
+                <invoicenumbers translate="label">
+                    <label>Invoice Numbers</label>
+                    <frontend_type>text</frontend_type>
+                    <sort_order>20</sort_order>
+                    <expanded>1</expanded>
+                    <show_in_default>1</show_in_default>
+                    <show_in_website>1</show_in_website>
+                    <show_in_store>1</show_in_store>
+                    <fields>
                         <active translate="label">
-                            <label>Enabled</label>
+                            <label>Customize invoice numbers</label>
                             <frontend_type>select</frontend_type>
+                            <source_model>adminhtml/system_config_source_yesno</source_model>
                             <sort_order>1</sort_order>
                             <show_in_default>1</show_in_default>
                             <show_in_website>1</show_in_website>
                             <show_in_store>1</show_in_store>
                         </active>
-                <invoicenumbers translate="label">
-                    <label>Invoice Numbers</label>
+                        <format translate="label">
+                            <label>Format of the invoice numbers</label>
+                            <frontend_type>text</frontend_type>
+                            <sort_order>2</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </format>
+                        <digits translate="label">
+                            <label>Number of digits for the counter</label>
+                            <frontend_type>text</frontend_type>
+                            <validate>validate-not-negative-number</validate>
+                            <sort_order>3</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </digits>
+                        <global translate="label">
+                            <label>Use a global counter with no reset</label>
+                            <frontend_type>checkbox</frontend_type>
+                            <sort_order>4</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </global>
+                    </fields>
+                </invoicenumbers>
+
+                <shipmentnumbers translate="label">
+                    <label>Shipment Numbers</label>
                     <frontend_type>text</frontend_type>
-                    <sort_order>20</sort_order>
+                    <sort_order>30</sort_order>
+                    <expanded>1</expanded>
                     <show_in_default>1</show_in_default>
                     <show_in_website>1</show_in_website>
                     <show_in_store>1</show_in_store>
                     <fields>
                         <active translate="label">
-                            <label>Customize Invoice Numbers</label>
+                            <label>Customize shipment numbers</label>
                             <frontend_type>select</frontend_type>
                             <source_model>adminhtml/system_config_source_yesno</source_model>
                             <sort_order>1</sort_order>
@@ -54,8 +129,86 @@
                             <show_in_website>1</show_in_website>
                             <show_in_store>1</show_in_store>
                         </active>
+                        <format translate="label">
+                            <label>Format of the shipment numbers</label>
+                            <frontend_type>text</frontend_type>
+                            <sort_order>2</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </format>
+                        <digits translate="label">
+                            <label>Number of digits for the counter</label>
+                            <frontend_type>text</frontend_type>
+                            <validate>validate-not-negative-number</validate>
+                            <sort_order>3</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </digits>
+                        <global translate="label">
+                            <label>Use a global counter with no reset</label>
+                            <frontend_type>checkbox</frontend_type>
+                            <sort_order>4</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </global>
                     </fields>
-                </invoicenumbers>
+                </shipmentnumbers>
+
+                <creditmemonumbers translate="label">
+                    <label>Credit Memo Numbers</label>
+                    <frontend_type>text</frontend_type>
+                    <sort_order>40</sort_order>
+                    <expanded>1</expanded>
+                    <show_in_default>1</show_in_default>
+                    <show_in_website>1</show_in_website>
+                    <show_in_store>1</show_in_store>
+                    <fields>
+                        <active translate="label">
+                            <label>Customize credit memo numbers</label>
+                            <frontend_type>select</frontend_type>
+                            <source_model>adminhtml/system_config_source_yesno</source_model>
+                            <sort_order>1</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                        </active>
+                        <format translate="label">
+                            <label>Format of the credit memo numbers</label>
+                            <frontend_type>text</frontend_type>
+                            <sort_order>2</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </format>
+                        <digits translate="label">
+                            <label>Number of digits for the counter</label>
+                            <frontend_type>text</frontend_type>
+                            <validate>validate-not-negative-number</validate>
+                            <sort_order>3</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </digits>
+                        <global translate="label">
+                            <label>Use a global counter with no reset</label>
+                            <frontend_type>checkbox</frontend_type>
+                            <sort_order>4</sort_order>
+                            <show_in_default>1</show_in_default>
+                            <show_in_website>1</show_in_website>
+                            <show_in_store>1</show_in_store>
+                            <depends><active>1</active></depends>
+                        </global>
+                    </fields>
+                </creditmemonumbers>
+
             </groups>
         </ordernumber>
     </sections>
diff --git a/Ordernumber/sql/opentools_ordernumber_setup/install-0.1.0.php b/Ordernumber/sql/opentools_ordernumber_setup/install-0.1.0.php
new file mode 100644
index 0000000..6f374f8
--- /dev/null
+++ b/Ordernumber/sql/opentools_ordernumber_setup/install-0.1.0.php
@@ -0,0 +1,34 @@
+<?php
+/** Ordernmber installation script
+ *  @author OpenTools
+ */
+/**
+ * @var $installer Mage_Core_Model_Resource_Setup
+ */
+$installer = $this;
+
+/**
+ * Create table opentools_ordernumber
+ */
+$table = $installer->getConnection()
+    ->newTable($installer->getTable('opentools_ordernumber/ordernumber'))
+    ->addColumn('ordernumber_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
+        'unsigned' => true,
+        'identity' => true,
+        'nullable' => false,
+        'primary'  => true,
+    ), 'Ordernumber id')
+    ->addColumn('number_type',  Varien_Db_Ddl_Table::TYPE_TEXT,      63, array('nullable'=> false),                 'Number Type')
+    ->addColumn('number_format',Varien_Db_Ddl_Table::TYPE_TEXT,     255, array('nullable'=> true),                  'Number Format')
+    ->addColumn('count',        Varien_Db_Ddl_Table::TYPE_INTEGER, null, array('unsigned'=>true,'nullable'=>false), 'Counter')
+    ->addIndex($installer->getIdxName(
+            $installer->getTable('opentools_ordernumber/ordernumber'),
+            array('number_type', 'number_format'),
+            Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE
+        ),
+        array('number_type', 'number_format'),
+        array('type' => Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE)
+    )
+    ->setComment('Ordernumber Counter Table');
+// TODO: drop table if exists!
+$installer->getConnection()->createTable($table);
-- 
GitLab