0001362: Currency selector
authorPhilipp Schüle <p.schuele@metaways.de>
Tue, 27 Jun 2017 10:05:28 +0000 (12:05 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Tue, 27 Jun 2017 11:27:06 +0000 (13:27 +0200)
Allow to configure currency symbol

 - introduce Ext.ux.MoneyField
 - retrieve currency symbol from config
 - replace all numberfields with MoneyField

https://forge.tine20.org/view.php?id=1362

Change-Id: I84cd5602cddddde80a0a83c644c9b3a208737530
Reviewed-on: http://gerrit.tine20.com/customers/4943
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
20 files changed:
tine20/Crm/js/LeadEditDialog.js
tine20/Crm/js/LeadGridPanel.js
tine20/Crm/js/Product.js
tine20/Inventory/Model/InventoryItem.php
tine20/Inventory/js/InventoryItemEditDialog.js
tine20/Sales/Model/Invoice.php
tine20/Sales/Model/Product.php
tine20/Sales/Model/PurchaseInvoice.php
tine20/Sales/Setup/DemoData.php
tine20/Sales/js/InvoiceDetailsPanel.js
tine20/Sales/js/ProductEditDialog.js
tine20/Sales/js/PurchaseInvoiceDetailsPanel.js
tine20/Tinebase/Config.php
tine20/Tinebase/Frontend/Json.php
tine20/Tinebase/ModelConfiguration.php
tine20/Tinebase/Record/DoctrineMappingDriver.php
tine20/Tinebase/Tinebase.jsb2
tine20/Tinebase/js/ApplicationStarter.js
tine20/Tinebase/js/extInit.js
tine20/Tinebase/js/ux/form/MoneyField.js [new file with mode: 0644]

index 7cb224f..d386ac8 100644 (file)
@@ -418,14 +418,12 @@ Tine.Crm.LeadEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
                                     border:false,
                                     items: [
                                     {
-                                        xtype:'extuxnumberfield',
-                                        suffix: ' €',
-                                        fieldLabel: this.app.i18n._('Expected turnover'), 
+                                        xtype:'extuxmoneyfield',
+                                        fieldLabel: this.app.i18n._('Expected turnover'),
                                         name: 'turnover',
                                         selectOnFocus: true,
                                         anchor: '95%',
-                                        minValue: 0,
-                                        decimalSeparator: Tine.Tinebase.registry.get('decimalSeparator')
+                                        minValue: 0
                                     },  
                                         this.combo_probability,
                                         new Ext.ux.form.ClearableDateField({
index 47b64d5..ed55881 100644 (file)
@@ -115,10 +115,10 @@ Tine.Crm.LeadGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
                 {header: this.app.i18n._('Leadstate'), id: 'leadstate_id', dataIndex: 'leadstate_id', width: 100, renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Crm', 'leadstates')},
                 {header: this.app.i18n._('Leadsource'), id: 'leadsource_id', dataIndex: 'leadsource_id', width: 100, renderer: Tine.Tinebase.widgets.keyfield.Renderer.get('Crm', 'leadsources')},
                 {header: this.app.i18n._('Probability'), id: 'probability', dataIndex: 'probability', width: 50, renderer: Ext.ux.PercentRenderer },
-                {header: this.app.i18n._('Turnover'), id: 'turnover', dataIndex: 'turnover', width: 100, renderer: Ext.util.Format.euMoney },
+                {header: this.app.i18n._('Turnover'), id: 'turnover', dataIndex: 'turnover', width: 100, renderer: Ext.util.Format.money },
 
                 {header: this.app.i18n._('Estimated end'), id: 'end_scheduled', dataIndex: 'end_scheduled', width: 100, renderer: Tine.Tinebase.common.dateRenderer, sortable: true },
-                {header: this.app.i18n._('Probable Turnover'), id: 'probableTurnover', dataIndex: 'probableTurnover', width: 100, renderer: Ext.util.Format.euMoney, sortable: false },
+                {header: this.app.i18n._('Probable Turnover'), id: 'probableTurnover', dataIndex: 'probableTurnover', width: 100, renderer: Ext.util.Format.money, sortable: false },
                 {header: this.app.i18n._('Resubmission Date'), id: 'resubmission_date', dataIndex: 'resubmission_date', width: 100, renderer: Tine.Tinebase.common.dateRenderer, sortable: true }
                 
             ].concat(this.getModlogColumns().concat(this.getCustomfieldColumns()))
index 10424ad..c1313bc 100644 (file)
@@ -153,7 +153,7 @@ Tine.Crm.Product.GridPanel = Ext.extend(Ext.grid.EditorGridPanel, {
                     // TODO hardcode separator or get it from locale?
                     decimalSeparator: ','
                 }),
-                renderer: Ext.util.Format.euMoney
+                renderer: Ext.util.Format.money
             }, {
                 header: this.app.i18n._("Quantity"),
                 id: 'remark_quantity',
index 1d6649c..dab3c75 100644 (file)
@@ -144,8 +144,7 @@ class Inventory_Model_InventoryItem extends Tinebase_Record_Abstract
                 'hidden'     => TRUE
             ),
             'price' => array(
-                'type'         => 'float',
-                'specialType'  => 'euMoney',
+                'type'         => 'money',
                 'nullable'     => true,
                 'validators'   => array(Zend_Filter_Input::ALLOW_EMPTY => TRUE),
                 'label'        => 'Price', // _('Price')
index 65fc707..7bb411d 100644 (file)
@@ -246,10 +246,8 @@ Tine.Inventory.InventoryItemEditDialog = Ext.extend(Tine.widgets.dialog.EditDial
                     },
                     items: [
                         [{
-                            xtype: 'extuxnumberfield',
+                            xtype: 'extuxmoneyfield',
                             name: 'price',
-                            suffix: ' €',
-                            decimalSeparator: Tine.Tinebase.registry.get('decimalSeparator'),
                             fieldLabel: this.app.i18n._('Price'),
                             columnWidth: 0.5
                         }, 
index 95af8e6..cf21978 100644 (file)
@@ -146,24 +146,21 @@ class Sales_Model_Invoice extends Tinebase_Record_Abstract
             ),
             'price_net' => array(
                 'label' => 'Price Net', // _('Price Net')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
             ),
             'price_tax' => array(
                 'label' => 'Price Tax', // _('Price Tax')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
             ),
             'price_gross' => array(
                 'label' => 'Price Gross', // _('Price Gross')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
@@ -178,8 +175,7 @@ class Sales_Model_Invoice extends Tinebase_Record_Abstract
             ),
             'inventory_change' => array(
                     'label' => 'Inventory Change', // _('Inventory Change')
-                    'type'  => 'float',
-                    'specialType' => 'euMoney',
+                    'type'  => 'money',
                     'default' => 0,
                     'inputFilters' => array('Zend_Filter_Empty' => 0),
                     'shy' => TRUE,
index 8bfe99e..057773f 100644 (file)
@@ -63,16 +63,14 @@ class Sales_Model_Product extends Tinebase_Record_Abstract
             'purchaseprice' => array(
                 'label'        => 'Purchaseprice', // _('Purchaseprice')
                 'validators'   => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 0),
-                'type'         => 'float',
-                'specialType'  => 'euMoney',
+                'type'         => 'money',
                 'default'      => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
             ),
             'salesprice'    => array(
                 'label'        => 'Salesprice',   // _('Salesprice')
                 'validators'   => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 0),
-                'type'         => 'float',
-                'specialType'  => 'euMoney',
+                'type'         => 'money',
                 'default'      => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
             ),
index 5b0f648..9a11f67 100644 (file)
@@ -136,32 +136,28 @@ class Sales_Model_PurchaseInvoice extends Tinebase_Record_Abstract
             ),
             'price_net' => array(
                 'label' => 'Price Net', // _('Price Net')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
             ),
             'price_gross' => array(
                 'label' => 'Price Gross', // _('Price Gross')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
             ),
             'price_gross2' => array(
                 'label' => 'Additional Price Gross', // _('Additional Price Gross')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
             ),
             'price_tax' => array(
                 'label' => 'Price Tax', // _('Price Tax')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
                 'shy' => TRUE,
@@ -176,8 +172,7 @@ class Sales_Model_PurchaseInvoice extends Tinebase_Record_Abstract
             ),
             'price_total' => array(
                 'label' => 'Total Price', // _('Total Price')
-                'type'  => 'float',
-                'specialType' => 'euMoney',
+                'type'  => 'money',
                 'default' => 0,
                 'inputFilters' => array('Zend_Filter_Empty' => 0),
             ),
index 40b8118..964b82d 100644 (file)
@@ -119,22 +119,22 @@ class Sales_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
             array(
                 'name' => '10 Port 100 MBit Ethernet Switch',
                 'description' => '10 Port Fast Ethernet Switch, RJ45',
-                'price' => '28.13',
+                'price' => 28.13,
             ),
             array(
                 'name' => '28 Port 100 MBit Ethernet Switch PoE',
                 'description' => '28 Port Fast Ethernet Switch, PoE, RJ45',
-                'price' => '1029.99',
+                'price' => 1029.99,
             ),
             array(
                 'name' => '10 Port Gigabit Ethernet Switch',
                 'description' => '10 Port 1 Gigabit Switch, RJ45',
-                'price' => '78.87',
+                'price' => 78.87,
             ),
             array(
                 'name' => '28 Port Gigabit Ethernet Switch PoE',
                 'description' => '28 Port 1 Gigabit Ethernet Switch PoE',
-                'price' => '3496.45',
+                'price' => 3496.45,
             )
         );
         
@@ -151,22 +151,22 @@ class Sales_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
             array(
                 'name' => self::$_en ? '10m Cat. 5a red' : '10m Kat. 5a rot',
                 'description' => self::$_en ? '10m Cat. 5a red cable up to 100MBit.' : '10m Kat. 5a rotes Kabel. Erlaubt Übertragungsraten von bis zu 100MBit.',
-                'price' => '5.99',
+                'price' => 5.99,
             ),
             array(
                 'name' => self::$_en ? '10m Cat. 5a blue' : '10m Kat. 5a blau',
                 'description' => self::$_en ? '10m Cat. 5a blue cable up to 100MBit.' : '10m Kat. 5a blaues Kabel. Erlaubt Übertragungsraten von bis zu 100MBit.',
-                'price' => '5.99',
+                'price' => 5.99,
             ),
             array(
                 'name' => self::$_en ? '10m Cat. 6 red' : '10m Kat. 6 rot',
                 'description' => self::$_en ? '10m Cat. 6 red cable up to 1000MBit.' : '10m Kat. 5a rotes Kabel. Erlaubt Übertragungsraten von bis zu 1000MBit.',
-                'price' => '9.99',
+                'price' => 9.99,
             ),
             array(
                 'name' => self::$_en ? '10m Cat. 6 blue' : '10m Kat. 6 blau',
                 'description' => self::$_en ? '10m Cat. 6 blue cable up to 1000MBit.' : '10m Kat. 5a blaues Kabel. Erlaubt Übertragungsraten von bis zu 1000MBit.',
-                'price' => '9.99',
+                'price' => 9.99,
             ),
         );
         
index f8307ab..8360869 100644 (file)
@@ -155,11 +155,11 @@ Tine.Sales.InvoiceDetailsPanel = Ext.extend(Tine.widgets.grid.DetailsPanel, {
                             var renderer = Tine.widgets.grid.RendererManager.get('Sales', 'Invoice', key);
                             return renderer(that.record.get('relations'));
                         case 'price_gross':
-                            return Ext.util.Format.euMoney(value.price_gross);
+                            return Ext.util.Format.money(value.price_gross);
                         case 'price_net':
-                            return Ext.util.Format.euMoney(value.price_net);
+                            return Ext.util.Format.money(value.price_net);
                         case 'price_tax':
-                            return Ext.util.Format.euMoney(value.price_tax);
+                            return Ext.util.Format.money(value.price_tax);
                     }
 
                     var renderer = Tine.widgets.grid.RendererManager.get('Sales', 'Invoice', key);
index 4e8f0ae..d2416e6 100644 (file)
@@ -98,20 +98,14 @@ Tine.Sales.ProductEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
                         fieldLabel: this.app.i18n._('Manufacturer'),
                         name: 'manufacturer'
                     }], [{
-                        xtype: 'extuxnumberfield',
+                        xtype: 'extuxmoneyfield',
                         fieldLabel: this.app.i18n._('Purchaseprice'),
-                        decimalSeparator: Tine.Tinebase.registry.get('decimalSeparator'),
-                        decimalPrecision: 2,
-                        suffix: ' €',
                         name: 'purchaseprice',
                         allowNegative: false,
                         allowBlank: true
                     }, {
-                        xtype: 'extuxnumberfield',
+                        xtype: 'extuxmoneyfield',
                         fieldLabel: this.app.i18n._('Salesprice'),
-                        decimalSeparator: Tine.Tinebase.registry.get('decimalSeparator'),
-                        decimalPrecision: 2,
-                        suffix: ' €',
                         name: 'salesprice',
                         allowNegative: false,
                         allowBlank: true
index 72e9fea..c0e18d8 100644 (file)
@@ -155,11 +155,11 @@ Tine.Sales.PurchaseInvoiceDetailsPanel = Ext.extend(Tine.widgets.grid.DetailsPan
                             var renderer = Tine.widgets.grid.RendererManager.get('Sales', 'Invoice', key);
                             return renderer(that.record.get('relations'));
                         case 'price_total':
-                            return Ext.util.Format.euMoney(value.price_total);
+                            return Ext.util.Format.money(value.price_total);
                         case 'price_net':
-                            return Ext.util.Format.euMoney(value.price_net);
+                            return Ext.util.Format.money(value.price_net);
                         case 'price_tax':
-                            return Ext.util.Format.euMoney(value.price_tax);
+                            return Ext.util.Format.money(value.price_tax);
                     }
 
                     var renderer = Tine.widgets.grid.RendererManager.get('Sales', 'Invoice', key);
index 8a8a066..b5eb8c7 100644 (file)
@@ -458,6 +458,8 @@ class Tinebase_Config extends Tinebase_Config_Abstract
     const BRANDING_WEBURL = 'branding_weburl';
     const BRANDING_DESCRIPTION = 'branding_description';
 
+    const CURRENCY_SYMBOL = 'currencySymbol';
+
     /**
      * @var string
      */
@@ -1461,6 +1463,17 @@ class Tinebase_Config extends Tinebase_Config_Abstract
             'setBySetupModule'      => FALSE,
             'default'               => NULL,
         ),
+        self::CURRENCY_SYMBOL => array(
+            //_('currency symbol')
+            'label' => 'urrency symbol',
+            //_('Path to custom favicon.')
+            'description' => 'Define currency symbol to be used.',
+            'type' => 'string',
+            'default' => '€',
+            'clientRegistryInclude' => true,
+            'setByAdminModule' => false,
+            'setBySetupModule' => false,
+        ),
         self::FILESYSTEM => array(
             //_('Filesystem settings')
             'label'                 => 'Filesystem settings',
index 19d2881..3b9afcf 100644 (file)
@@ -763,6 +763,7 @@ class Tinebase_Frontend_Json extends Tinebase_Frontend_Json_Abstract
             'maxPostSize'       => Tinebase_Helper::convertToBytes(ini_get('post_max_size')),
             'thousandSeparator' => $symbols['group'],
             'decimalSeparator'  => $symbols['decimal'],
+            'currencySymbol'    => Tinebase_Config::getInstance()->get(Tinebase_Config::CURRENCY_SYMBOL),
             'filesystemAvailable' => Tinebase_Core::isFilesystemAvailable(),
             'brandingWeburl'    => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_WEBURL),
             'brandingLogo'      => Tinebase_Config::getInstance()->get(Tinebase_Config::BRANDING_LOGO),
index 2ce6ed8..f106e94 100644 (file)
@@ -400,8 +400,7 @@ class Tinebase_ModelConfiguration {
      * integer     seconds       Seconds             integer  integer                       int               Tinebase_Model_Filter_Int
      * integer     minutes       Minutes             integer  integer                       int               Tinebase_Model_Filter_Int
      * float                     Float               float    float                         float             Tinebase_Model_Filter_Int
-     * float       usMoney       Dollar in Cent      float    float                         int               Tinebase_Model_Filter_Int
-     * float       euMoney       Euro in Cent        float    float                         int               Tinebase_Model_Filter_Int
+     * float       money         value and currency  float    float                         int               Tinebase_Model_Filter_Int
      * json                      Json String         text     string                        array             Tinebase_Model_Filter_Text
      * container                 Container           string   Tine.Tinebase.Model.Container Tinebase_Model_Container                                    tine.widget.container.filtermodel
      * tag tinebase.tag
index a82858b..9628d7c 100644 (file)
@@ -42,6 +42,7 @@ class Tinebase_Record_DoctrineMappingDriver implements Doctrine\Common\Persisten
         // NOTE 1: smallint is not working somehow ...
         // NOTE 2: we need int here because otherwise we need to typecast values for pgsql
         'boolean'       => 'integer',
+        'money'         => 'float'
     );
 
     /**
index ec40cab..61fba0c 100644 (file)
           "path": "js/ux/form/"
         },
         {
+          "text": "MoneyField.js",
+          "path": "js/ux/form/"
+        },
+        {
           "text": "LayerCombo.js",
           "path": "js/ux/form/"
         },
index ba03459..caa8b45 100644 (file)
@@ -153,16 +153,10 @@ Tine.Tinebase.ApplicationStarter = {
                             case 'seconds':
                                 gridRenderer = Tine.Tinebase.common.secondsRenderer;
                                 break;
-                            case 'usMoney':
-                                gridRenderer = Ext.util.Format.usMoney;
-                                break;
-                            case 'euMoney':
-                                gridRenderer = Ext.util.Format.euMoney;
-                                break;
                             case 'percent':
                                 gridRenderer = function(value, cell, record) {
                                     return Tine.Tinebase.common.percentRenderer(value, config.type);
-                                }
+                                };
                                 break;
                             default:
                                 gridRenderer = Ext.util.Format.htmlEncode;
@@ -193,6 +187,9 @@ Tine.Tinebase.ApplicationStarter = {
                 case 'boolean':
                     gridRenderer = Tine.Tinebase.common.booleanRenderer;
                     break;
+                case 'money':
+                    gridRenderer = Ext.util.Format.money;
+                    break;
                 case 'relation':
                     var cc = config.config;
                     gridRenderer = new Tine.widgets.relation.GridRenderer({
@@ -223,7 +220,7 @@ Tine.Tinebase.ApplicationStarter = {
                 filter.valueType = 'user';
                 break;
             case 'boolean': 
-                filter.valueType = 'bool'
+                filter.valueType = 'bool';
                 filter.defaultValue = false;
                 break;
             case 'record':
index ea41c86..94b7d7b 100644 (file)
@@ -144,30 +144,31 @@ Date.prototype.toJSON = function(key) {
  * additional formats
  */
 Ext.util.Format = Ext.apply(Ext.util.Format, {
-    euMoney: function(v){
+    money: function (v) {
         if (Ext.isEmpty(v) || v == null) {
             v = 0;
         }
         v.toString().replace(/,/, '.');
-        
+
         var decimalSeparator = Tine.Tinebase.registry.get('decimalSeparator');
-        
-        v = (Math.round(parseFloat(v)*100))/100;
-        
-        v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
+        var currencySymbol = Tine.Tinebase.registry.get('currencySymbol');
+
+        v = (Math.round(parseFloat(v) * 100)) / 100;
+
+        v = (v == Math.floor(v)) ? v + ".00" : ((v * 10 == Math.floor(v * 10)) ? v + "0" : v);
         v = String(v);
         var ps = v.split('.');
         var whole = ps[0];
-        var sub = ps[1] ? decimalSeparator+ ps[1] : decimalSeparator + '00';
+        var sub = ps[1] ? decimalSeparator + ps[1] : decimalSeparator + '00';
         var r = /(\d+)(\d{3})/;
         while (r.test(whole)) {
             whole = whole.replace(r, '$1' + '.' + '$2');
         }
         v = whole + sub;
-        if(v.charAt(0) == '-'){
-            return '- ' + v.substr(1) + ' €';
+        if (v.charAt(0) == '-') {
+            return '- ' + v.substr(1) + ' ' + currencySymbol;
         }
-        return v + " €";
+        return v + " " + currencySymbol;
     },
     percentage: function(v){
         if(v === null) {
diff --git a/tine20/Tinebase/js/ux/form/MoneyField.js b/tine20/Tinebase/js/ux/form/MoneyField.js
new file mode 100644 (file)
index 0000000..4e72755
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Tine 2.0
+ * 
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
+ */
+Ext.ns('Ext.ux', 'Ext.ux.form');
+
+/**
+ * @namespace   Ext.ux.form
+ * @class       Ext.ux.form.MoneyField
+ * @extends     Ext.form.MoneyField
+ */
+Ext.ux.form.MoneyField = Ext.extend(Ext.ux.form.NumberField, {
+    initComponent: function() {
+        this.suffix = Tine.Tinebase.registry.get('currencySymbol');
+        this.decimalPrecision = 2;
+        this.decimalSeparator = Tine.Tinebase.registry.get('decimalSeparator');
+
+        this.supr().initComponent.apply(this, arguments);
+    }
+});
+
+Ext.reg('extuxmoneyfield', Ext.ux.form.MoneyField);
\ No newline at end of file