partkeepr

fork of partkeepr
git clone https://git.e1e0.net/partkeepr.git
Log | Files | Refs | Submodules | README | LICENSE

commit ff06a515375fe7f9c75009603d0d7b5431f9920e
parent 9feef19e75123fe576cfecfafa42158a6776d653
Author: felicitus <felicitus@felicitus.org>
Date:   Thu, 24 May 2012 00:25:29 +0200

Merge branch 'master' of github.com:partkeepr/PartKeepr

Diffstat:
Mbuild.xml | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Ddocumentation/developer-installation/DEBIAN-6.0 | 2--
Ddocumentation/developer-installation/UBUNTU | 122-------------------------------------------------------------------------------
Adocumentation/developer/INSTALL-DEBIAN | 1+
Adocumentation/developer/INSTALL-UBUNTU | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocumentation/developer/SUBMITTING-CODE | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/backend/de/RaumZeitLabor/PartKeepr/Part/PartDistributor.php | 2+-
Msrc/backend/de/RaumZeitLabor/PartKeepr/Part/PartManager.php | 4+++-
Msrc/frontend/js/Components/Auth/LoginDialog.js | 3++-
Msrc/frontend/js/Components/Editor/EditorGrid.js | 43++++++++++++++++++++++++++++++++++++++++++-
Msrc/frontend/js/Components/Part/Editor/PartDistributorGrid.js | 189++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/frontend/js/Components/Part/Editor/PartEditor.js | 5++---
Msrc/frontend/js/Components/Part/PartStockWindow.js | 200+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/frontend/js/Components/Part/PartsGrid.js | 37+++++++++++++++++++++++++------------
Msrc/frontend/js/Components/Project/ProjectReport.js | 5+++++
Msrc/frontend/js/Components/Statistics/CurrentStatisticsPanel.js | 4++--
Msrc/frontend/js/Components/Statusbar.js | 3+--
Msrc/frontend/js/Components/StockReport/AbstractStockHistoryGrid.js | 5++---
Msrc/frontend/js/Components/StorageLocation/StorageLocationGrid.js | 3+++
Msrc/frontend/js/Components/User/Preferences/FormattingPreferences.js | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Asrc/frontend/js/Components/Widgets/CurrencyNumberField.js | 25+++++++++++++++++++++++++
Asrc/frontend/js/Ext.ux/NumericField.js | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/frontend/js/PartKeepr.js | 45+++++++++++++++++++++++++--------------------
23 files changed, 921 insertions(+), 431 deletions(-)

diff --git a/build.xml b/build.xml @@ -46,84 +46,170 @@ <exec command="git submodule update" checkreturn="true" /> </target> + <!-- Build the end-user 'setup' directory --> <target name="build-setup"> <mkdir dir="setup/" /> - <mkdir dir="setup/js/wizard" /> - - <copy overwrite="false" todir="setup/extjs/"> - <fileset dir="${extjs.path}"> - <include name="bootstrap.js" /> - <include name="compatibility/ext3-core-compat.js" /> - <include name="compatibility/ext3-compat.js" /> - <include name="ext-all.js" /> - <include name="ext-all-debug.js" /> - <include name="ext-all-debug-w-comments.js" /> - <include name="adapter/ext/ext-base.js" /> - <include name="adapter/ext/ext-base-debug-w-comments.js" /> - <include name="source/adapter/ext-base.js" /> - </fileset> - </copy> - - <copy overwrite="true" todir="setup/js/wizard"> - <fileset dir="3rdparty/ext-wizard/Ext.ux.Wizard"> - <include name="**" /> - </fileset> - </copy> - <copy overwrite="true" todir="setup/"> - <fileset dir="src/setup"> - <include name="**" /> - </fileset> - </copy> + <echo>Copying extjs</echo> + <mkdir dir="setup/extjs/" /> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -ptgo + --include=/bootstrap.js + --include=/ext-all.js + --include=/ext-all-debug.js + --include=/ext-all-debug-w-comments.js + --exclude=* + ${extjs.path}/* setup/extjs/" checkreturn="true" /> + </then> + <else> + <copy overwrite="false" todir="setup/extjs/"> + <fileset dir="${extjs.path}"> + <include name="bootstrap.js" /> + <include name="compatibility/ext3-core-compat.js" /> + <include name="compatibility/ext3-compat.js" /> + <include name="ext-all.js" /> + <include name="ext-all-debug.js" /> + <include name="ext-all-debug-w-comments.js" /> + <include name="adapter/ext/ext-base.js" /> + <include name="adapter/ext/ext-base-debug-w-comments.js" /> + <include name="source/adapter/ext-base.js" /> + </fileset> + </copy> + </else> + </if> + + <echo>Copying Ext.ux.Wizard</echo> + <mkdir dir="setup/js/wizard" /> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -a --delete 3rdparty/ext-wizard/Ext.ux.Wizard/ setup/js/wizard/" checkreturn="true" /> + </then> + <else> + <copy overwrite="true" todir="setup/js/wizard"> + <fileset dir="3rdparty/ext-wizard/Ext.ux.Wizard"> + <include name="**"/> + </fileset> + </copy> + </else> + </if> + + <echo>Copying src/setup</echo> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -a src/setup/ setup/" checkreturn="true" /> + </then> + <else> + <copy overwrite="true" todir="setup/"> + <fileset dir="src/setup"> + <include name="**" /> + </fileset> + </copy> + </else> + </if> </target> + <!-- Build the end-user 'frontend' directory --> <target name="build-frontend"> - <echo>Copying 3rdparty/extjs - this may take up to a few minutes</echo> <mkdir dir="frontend/" /> - <copy overwrite="false" todir="frontend/extjs/"> - <fileset dir="${extjs.path}"> - <include name="bootstrap.js" /> - <include name="compatibility/ext3-core-compat.js" /> - <include name="compatibility/ext3-compat.js" /> - <include name="ext-all.js" /> - <include name="ext-all-debug.js" /> - <include name="ext-all-debug-w-comments.js" /> - <include name="adapter/ext/ext-base.js" /> - <include name="adapter/ext/ext-base-debug-w-comments.js" /> - <include name="source/adapter/ext-base.js" /> - <include name="resources/**" /> - </fileset> - </copy> + + <echo>Copying 3rdparty/extjs - this may take up to a few minutes</echo> + <mkdir dir="frontend/extjs" /> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -ptgo -r + --include=/bootstrap.js + --include=/ext-all.js + --include=/ext-all-debug.js + --include=/ext-all-debug-w-comments.js + --include=/resources/*** + --exclude=/* + ${extjs.path}/* frontend/extjs/" checkreturn="true" /> + </then> + <else> + <copy overwrite="false" todir="frontend/extjs/"> + <fileset dir="${extjs.path}"> + <include name="bootstrap.js" /> + <include name="compatibility/ext3-core-compat.js" /> + <include name="compatibility/ext3-compat.js" /> + <include name="ext-all.js" /> + <include name="ext-all-debug.js" /> + <include name="ext-all-debug-w-comments.js" /> + <include name="adapter/ext/ext-base.js" /> + <include name="adapter/ext/ext-base-debug-w-comments.js" /> + <include name="source/adapter/ext-base.js" /> + <include name="resources/**" /> + </fileset> + </copy> + </else> + </if> <echo>Copying Ext Statusbar</echo> - <copy overwrite="false" todir="frontend/js/Ext.ux/"> - <fileset dir="${extjs.path}/examples/ux"> - <include name="statusbar/**" /> - </fileset> - </copy> + <mkdir dir="frontend/js/Ext.ux/statusbar"/> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -a --delete ${extjs.path}/examples/ux/statusbar/ frontend/js/Ext.ux/statusbar/" checkreturn="true" /> + </then> + <else> + <copy overwrite="false" todir="frontend/js/Ext.ux/"> + <fileset dir="${extjs.path}/examples/ux"> + <include name="statusbar/**" /> + </fileset> + </copy> + </else> + </if> <echo>Copying Ext.tab.TabCloseMenu</echo> - <copy overwrite="false" todir="frontend/js/Ext.ux/"> - <fileset dir="${extjs.path}/examples/ux"> - <include name="TabCloseMenu.js" /> - </fileset> - </copy> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -a ${extjs.path}/examples/ux/TabCloseMenu.js frontend/js/Ext.ux/" checkreturn="true" /> + </then> + <else> + <copy overwrite="false" todir="frontend/js/Ext.ux/"> + <fileset dir="${extjs.path}/examples/ux"> + <include name="TabCloseMenu.js" /> + </fileset> + </copy> + </else> + </if> <echo>Copying phpjs</echo> <mkdir dir="frontend/js/org.phpjs.lib/" /> - <copy overwrite="false" todir="frontend/js/org.phpjs.lib"> - <fileset dir="3rdparty/phpjs/"> - <include name="php.default.min.js" /> - </fileset> - </copy> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -a 3rdparty/phpjs/php.default.min.js frontend/js/org.phpjs.lib/" checkreturn="true" /> + </then> + <else> + <copy overwrite="false" todir="frontend/js/org.phpjs.lib"> + <fileset dir="3rdparty/phpjs/"> + <include name="php.default.min.js" /> + </fileset> + </copy> + </else> + </if> <echo>Copying JS frontend</echo> - <copy overwrite="true" todir="frontend/"> - <fileset dir="src/frontend"> - <include name="**" /> - </fileset> - </copy> + <if> + <isset property="build.fast" /> + <then> + <exec dir="." command="rsync -a --exclude=.git src/frontend/ frontend/" checkreturn="true" /> + </then> + <else> + <copy overwrite="true" todir="frontend/"> + <fileset dir="src/frontend"> + <include name="**" /> + </fileset> + </copy> + </else> + </if> <phingcall target="create-orm-proxies" /> @@ -138,6 +224,19 @@ <phingcall target="build-setup" /> </target> + <!-- Fast build uses rsync instead of standard file copy --> + <target name="build-fast"> + <condition property="build.fast"> + <or> + <os family="mac" /> + <os family="unix" /> + </or> + </condition> + + <fail unless="build.fast" message="Fast build only supported on Mac and Unix-like systems (rsync required)" /> + + <phingcall target="build" /> + </target> <!-- Creates a new PartKeepr release. diff --git a/documentation/developer-installation/DEBIAN-6.0 b/documentation/developer-installation/DEBIAN-6.0 @@ -1 +0,0 @@ -Please see UBUNTU-11.04, as the differences are minimal.- \ No newline at end of file diff --git a/documentation/developer-installation/UBUNTU b/documentation/developer-installation/UBUNTU @@ -1,122 +0,0 @@ -Step-by-Step Guide for Ubuntu -============================= - -This guide explains how to setup your workstation so that you can develop using PartKeepr. - -Tested with: -- Ubuntu 11.04 -- Ubuntu 12.04 - -In this guide, I've used "/var/www" as web server root. - - -------------- -SYSTEM CONFIG -------------- - -# NOTE: These steps require root access. -# Prefix all commands with 'sudo' or run as the root user. - -# Install apache, mysql, php, imagemagick and git -apt-get install apache2 mysql-server mysql-client libapache2-mod-php5 php5-mysql php5-curl php5-cli php-pear php5-imagick php5-xsl php-apc php5-dev imagemagick git - -# On Ubuntu Server, you need to install ia32-libs (installed automatically on Ubuntu Desktop) -# Note: This is only required with 64-bit Ubuntu (not 32-bit) -apt-get install ia32-libs - -# Install Java SDK -apt-get install default-jdk - -# Update PEAR -pear update-channels -pear upgrade - -# Install phpunit, phing, doctrine components and twig -pear config-set auto_discover 1 -pear channel-discover pear.phpunit.de -pear install phpunit/PHPUnit -pear channel-discover pear.phing.info -pear install phing/phing - -pear install pear.doctrine-project.org/DoctrineORM -pear install phpunit/phpcpd -# (the required domain 'components.ez.no' seems to be unavailable quite often) - -pear install --alldeps phpmd/PHP_PMD-alpha - -pear install doctrine/DoctrineSymfonyYaml -pear install doctrine/DoctrineSymfonyConsole - -pear channel-discover pear.twig-project.org -pear install twig/Twig - - ------------------- -# PARTKEEPR CONFIG ------------------- - -# NOTE: These steps can be performed as a regular user. -$ Steps requiring root permission - -# Create a new database and user 'partkeepr' -mysql -uroot -p -CREATE DATABASE partkeepr CHARACTER SET utf8; -grant usage on *.* to partkeepr@localhost identified by 'partkeepr'; -grant all privileges on partkeepr.* to partkeepr@localhost; -quit - -# Test if you can connect -mysql -upartkeepr -ppartkeepr -quit - -# Setting up the PartKeepr repository -# NOTE: You should secure the PartKeepr directory with appropriate permissions once setup is complete -cd /var/www -sudo mkdir PartKeepr -sudo chmod 777 PartKeepr - -# Cloning will take a while, approx 50MB repository will be downloaded -git clone git://github.com/partkeepr/PartKeepr.git - -cd /var/www/PartKeepr - -# This will update the submodules. Another 400MB will be downloaded -git submodule sync -git submodule init -git submodule update - -# Copy the default configuration file -cp config.php.template config.php - -# You have now two choices: Proceed with or without sample data. - -# ==== WITH SAMPLE DATA ==== -mysql -uroot -p -CREATE DATABASE partdb; -grant usage on *.* to partdb@localhost identified by 'partdb'; -grant all privileges on partdb.* to partdb@localhost; -FLUSH PRIVILEGES; -quit - -mysql -upartdb -ppartdb partdb < testing/setup.sql -cd testing -php SetupDatabase.php --yes --migrate - -# ==== WITHOUT SAMPLE DATA ==== -cd testing -php SetupDatabase.php --yes - -# ==== Issuing migrations ==== -# Ignore all messages about "was executed but did not result in any SQL statements" -cd /var/www/PartKeepr/ -php doctrine.php migrations:migrate - -# Build the frontend -phing build - -# Restart apache2 to make sure php5 is enabled (requires root permission) -sudo /etc/init.d/apache2 restart - -# Open up your browser and navigate to http://localhost/PartKeepr/frontend -# The frontend should now appear - diff --git a/documentation/developer/INSTALL-DEBIAN b/documentation/developer/INSTALL-DEBIAN @@ -0,0 +1 @@ +Please see INSTALL-UBUNTU, as the differences are minimal. diff --git a/documentation/developer/INSTALL-UBUNTU b/documentation/developer/INSTALL-UBUNTU @@ -0,0 +1,137 @@ +Step-by-Step Guide for Ubuntu +============================= + +This guide explains how to setup your workstation so that you can develop using PartKeepr. + +Tested with: +- Ubuntu 11.04 +- Ubuntu 12.04 + +In this guide, I've used "/var/www" as web server root. + + +-------- +CONTENTS +-------- +- System config +- Partkeepr config +- Submitting code + + +------------- +SYSTEM CONFIG +------------- + +# NOTE: These steps require root access. +# Prefix all commands with 'sudo' or run as the root user. + +# Install apache, mysql, php, imagemagick and git +apt-get install apache2 mysql-server mysql-client libapache2-mod-php5 php5-mysql php5-curl php5-cli php-pear php5-imagick php5-xsl php-apc php5-dev imagemagick git + +# On Ubuntu Server, you need to install ia32-libs (installed automatically on Ubuntu Desktop) +# Note: This is only required with 64-bit Ubuntu (not 32-bit) +apt-get install ia32-libs + +# Install Java SDK +apt-get install default-jdk + +# Update PEAR +pear update-channels +pear upgrade + +# Install phpunit, phing, doctrine components and twig +pear config-set auto_discover 1 +pear channel-discover pear.phpunit.de +pear install phpunit/PHPUnit +pear channel-discover pear.phing.info +pear install phing/phing + +pear install pear.doctrine-project.org/DoctrineORM +pear install phpunit/phpcpd +# (the required domain 'components.ez.no' seems to be unavailable quite often) + +pear install --alldeps phpmd/PHP_PMD-alpha + +pear install doctrine/DoctrineSymfonyYaml +pear install doctrine/DoctrineSymfonyConsole + +pear channel-discover pear.twig-project.org +pear install twig/Twig + + +------------------ +# PARTKEEPR CONFIG +------------------ + +# NOTE: These steps can be performed as a regular user. +$ Steps requiring root permission + +# Create a new database and user 'partkeepr' +mysql -uroot -p +CREATE DATABASE partkeepr CHARACTER SET utf8; +grant usage on *.* to partkeepr@localhost identified by 'partkeepr'; +grant all privileges on partkeepr.* to partkeepr@localhost; +quit + +# Test if you can connect +mysql -upartkeepr -ppartkeepr +quit + +# Setting up the PartKeepr repository +# NOTE: You should secure the PartKeepr directory with appropriate permissions once setup is complete +cd /var/www +sudo mkdir PartKeepr +sudo chmod 777 PartKeepr + +# Cloning will take a while, approx 50MB repository will be downloaded +git clone git://github.com/partkeepr/PartKeepr.git + +cd /var/www/PartKeepr + +# This will update the submodules. Another 400MB will be downloaded +git submodule sync +git submodule init +git submodule update + +# Copy the default configuration file +cp config.php.template config.php + +# You have now two choices: Proceed with or without sample data. + +# ==== WITH SAMPLE DATA ==== +mysql -uroot -p +CREATE DATABASE partdb; +grant usage on *.* to partdb@localhost identified by 'partdb'; +grant all privileges on partdb.* to partdb@localhost; +FLUSH PRIVILEGES; +quit + +mysql -upartdb -ppartdb partdb < testing/setup.sql +cd testing +php SetupDatabase.php --yes --migrate + +# ==== WITHOUT SAMPLE DATA ==== +cd testing +php SetupDatabase.php --yes + +# ==== Issuing migrations ==== +# Ignore all messages about "was executed but did not result in any SQL statements" +cd /var/www/PartKeepr/ +php doctrine.php migrations:migrate + +# Build the frontend +phing build + +# Restart apache2 to make sure php5 is enabled (requires root permission) +sudo /etc/init.d/apache2 restart + +# Open up your browser and navigate to http://localhost/PartKeepr/frontend +# The frontend should now appear + + +----------------- +# SUBMITTING CODE +----------------- + +Please see the document SUBMITTING-CODE + diff --git a/documentation/developer/SUBMITTING-CODE b/documentation/developer/SUBMITTING-CODE @@ -0,0 +1,84 @@ +Guide for submitting code +========================= + +-------- +CONTENTS +-------- +- git branches +- Code checking with jslint + + +------------ +GIT BRANCHES +------------ + +All code changes you want included in the main project need to be made in a 'feature branch' and pushed to GitHub. +The following is a sample workflow for developers without write access to the project's main repo. + +First, fork the project in GitHub: + (see GitHub help - http://help.github.com/fork-a-repo) + +Then clone it locally: + (see GitHub help for forking - http://help.github.com/fork-a-repo/) + git clone git@github.com:USERNAME/partkeepr.git + cd partkeepr + +Add a remote named 'upstream': + git remote add upstream git://github.com/partkeepr/PartKeepr.git + +Each new feature you add or bug fix you make should be in a separate branch, based on the 'master' branch: + git branch my-feature upstream/master + +Hack and commit. + +Before submitting, ensure you are working off the latest upstream code: + git checkout my-feature + git rebase upstream/master + (fix any conflicts or use 'git rebase -i master' for interactive rebasing) +NOTE: After pushing your new branch, you should not rebase again. Use fetch and merge instead. + +When complete, the branch should be pushed to GitHub and a pull request submitted. + git checkout my-feature + git push origin my-feature + (see GitHub for help with pull requests - http://help.github.com/send-pull-requests) + + +------------------------- +CODE CHECKING WITH JSLINT +------------------------- + +JavaScript code should be checked with jslint prior to committing in git. + +Get and install 'JavaScript Lint' (jsl) + - Linux + - Download jsl source tarball + http://http://www.javascriptlint.com/download.htm + - Extract the tarball + tar -xzf jsl-0.3.0-src.tar.gz + - Switch to the src directory + cd jsl-0.3.0/src + - Build it + make -f Makefile.ref BUILD_OPT=1 + - Copy the binary to a directory on your path, e.g. + sudo cp Linux_All_OPT.OBJ/jsl /usr/local/bin/ + sudo chown root:root /usr/local/bin/jsl + sudo chmod 755 /usr/local/bin/jsl + - Mac + - As per Linux + - Windows + - Download jsl + http://http://www.javascriptlint.com/download.htm + - Extract 'jsl.exe' from the zip file + - Move 'jsl.exe' to a location on the system path + +Enter 'jsl' to test it's working. + +Add a git hook to check code on commit + - Copy the supplied pre-commit hook file 'pre-commit.hook' to '.git/hooks' + - Rename the file to 'pre-commit' + +Now, all JavaScript files will be checked when you 'git commit'. + +You can check the files at any time using: + phing jslint + diff --git a/src/backend/de/RaumZeitLabor/PartKeepr/Part/PartDistributor.php b/src/backend/de/RaumZeitLabor/PartKeepr/Part/PartDistributor.php @@ -41,7 +41,7 @@ class PartDistributor extends BaseEntity implements Serializable, Deserializable * Specifies the price of the part. Note that the price * needs to be per item, not per packaging unit. * - * @Column(type="decimal",precision=5, scale=2,nullable=true) + * @Column(type="decimal",precision=13,scale=4,nullable=true) * @var float */ private $price; diff --git a/src/backend/de/RaumZeitLabor/PartKeepr/Part/PartManager.php b/src/backend/de/RaumZeitLabor/PartKeepr/Part/PartManager.php @@ -101,13 +101,15 @@ class PartManager extends AbstractManager { protected function getResult (Query $query) { $result = parent::getResult($query); - /* Add attachment counts to the result set */ + /* Add attachment counts to the result set and re-format the date */ foreach ($result as $key => $item) { $dql = "SELECT COUNT(pa) FROM de\RaumZeitLabor\PartKeepr\Part\PartAttachment pa WHERE pa.part = :part"; $query = PartKeepr::getEM()->createQuery($dql); $query->setParameter("part", $item["id"]); $result[$key]["attachmentCount"] = $query->getSingleScalarResult(); + + $result[$key]["createDate"] = $result[$key]["createDate"]->format("Y-m-d H:i:s"); } foreach ($result as $key => $item) { diff --git a/src/frontend/js/Components/Auth/LoginDialog.js b/src/frontend/js/Components/Auth/LoginDialog.js @@ -70,7 +70,8 @@ Ext.define('PartKeepr.LoginDialog', { this.callParent(arguments); // Focus the login field on show - this.on("show", function () { this.loginField.focus(); }, this); + // @workaround Set the focus 100ms after the dialog has been shown. + this.on("show", function () { this.loginField.focus(); }, this, { delay: 100 }); }, /** * Fires the "login" event diff --git a/src/frontend/js/Components/Editor/EditorGrid.js b/src/frontend/js/Components/Editor/EditorGrid.js @@ -41,6 +41,16 @@ Ext.define('PartKeepr.EditorGrid', { */ buttonTextMode: 'hide', + /** + * @cfg {Boolean} boolean Defines if the grid should automatically calculate it's page size + */ + automaticPageSize: false, + + /** + * @cfg {Integer} integer Defines the row height with which the calculator should assume + */ + automaticPageSizeRowHeight: 21, + initComponent: function () { this.addEvents( @@ -103,7 +113,6 @@ Ext.define('PartKeepr.EditorGrid', { }); this.searchField = Ext.create("Ext.ux.form.SearchField",{ - id: 'thesearchfield', store: this.store }); @@ -135,6 +144,38 @@ Ext.define('PartKeepr.EditorGrid', { this.plugins = [ 'gridmenu' ]; this.callParent(); + + if (this.automaticPageSize) { + this.on("afterlayout", this.reassignPageSize, this); + } + }, + /** + * Re-calculates and re-assigns the page size for the assigned store. + * + * Automatically reloads the store. + * + * @param none + * @return nothing + */ + reassignPageSize: function () { + if (this.store.isLoading()) { return; } + if (this.getView().getHeight() === 0) { return; } + + var numRecords = Math.floor(this.getView().getHeight() / this.automaticPageSizeRowHeight); + + if (numRecords < 1) { numRecords = 1; } + + var oldStartIndex = this.store.pageSize * this.store.currentPage; + + this.store.pageSize = numRecords; + + var newStartPage = Math.floor(oldStartIndex / numRecords); + + if (newStartPage < 1) { + newStartPage = 1; + } + + this.store.loadPage(newStartPage); }, syncChanges: function (record) { // Simply reload the store for now diff --git a/src/frontend/js/Components/Part/Editor/PartDistributorGrid.js b/src/frontend/js/Components/Part/Editor/PartDistributorGrid.js @@ -1,118 +1,119 @@ Ext.define('PartKeepr.PartDistributorGrid', { - extend: 'PartKeepr.BaseGrid', - alias: 'widget.PartDistributorGrid', - border: false, - initComponent: function () { + extend : 'PartKeepr.BaseGrid', + alias : 'widget.PartDistributorGrid', + border : false, + initComponent : function() { this.store = Ext.create("Ext.data.Store", { - model: 'PartKeepr.PartDistributor', - proxy: { - type: 'memory', - reader: { - type: 'json' + model : 'PartKeepr.PartDistributor', + proxy : { + type : 'memory', + reader : { + type : 'json' } } - + }); - + this.editing = Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit: 1 - }); - - this.plugins = [ this.editing ]; - + clicksToEdit : 1 + }); + + this.plugins = [ this.editing ]; + this.deleteButton = Ext.create("Ext.button.Button", { - text: 'Delete', - disabled: true, - itemId: 'delete', - scope: this, - icon: 'resources/silkicons/lorry_delete.png', - handler: this.onDeleteClick - }); - - this.dockedItems = [{ - xtype: 'toolbar', - items: [{ - text: 'Add', - scope: this, - icon: 'resources/silkicons/lorry_add.png', - handler: this.onAddClick - }, this.deleteButton] - }]; - - this.columns = [ - { - header: i18n("Distributor"), - dataIndex: 'distributor_id', - xtype: 'templatecolumn', - tpl: '{distributor_name}', - flex: 0.3, - editor: { - xtype:'DistributorComboBox', - allowBlank:true - } - }, - { - header: i18n("Order Number"), - dataIndex: 'orderNumber', - flex: 0.3, - editor: { - xtype:'textfield', - allowBlank:true - } - },{ - header: i18n("Packaging Unit"), - dataIndex: 'packagingUnit', - flex: 0.2, - editor: { - xtype:'numberfield', - allowDecimals: false, - allowBlank:false, - minValue: 1 - } - },{ - header: i18n("Price per Item"), - dataIndex: 'price', - flex: 0.2, - editor: { - xtype:'numberfield', - allowDecimals: true, - allowBlank:true - } - } - ]; - + text : 'Delete', + disabled : true, + itemId : 'delete', + scope : this, + icon : 'resources/silkicons/lorry_delete.png', + handler : this.onDeleteClick + }); + + this.dockedItems = [ { + xtype : 'toolbar', + items : [ { + text : 'Add', + scope : this, + icon : 'resources/silkicons/lorry_add.png', + handler : this.onAddClick + }, this.deleteButton ] + } ]; + + this.columns = [ { + header : i18n("Distributor"), + dataIndex : 'distributor_id', + xtype : 'templatecolumn', + tpl : '{distributor_name}', + flex : 0.3, + editor : { + xtype : 'DistributorComboBox', + allowBlank : true + } + }, { + header : i18n("Order Number"), + dataIndex : 'orderNumber', + flex : 0.3, + editor : { + xtype : 'textfield', + allowBlank : true + } + }, { + header : i18n("Packaging Unit"), + dataIndex : 'packagingUnit', + flex : 0.2, + editor : { + xtype : 'numberfield', + allowDecimals : false, + allowBlank : false, + minValue : 1 + } + }, { + header : i18n("Price per Item"), + dataIndex : 'price', + flex : 0.2, + renderer : function(val, p, rec) { + return PartKeepr.getApplication().formatCurrency(val); + }, + editor : { + xtype : 'CurrencyField', + allowBlank : false + } + } ]; + this.callParent(); - - this.getSelectionModel().on('selectionchange', this.onSelectChange, this); + + this.getSelectionModel().on('selectionchange', + this.onSelectChange, + this); this.on("edit", this.onEdit, this); }, - onEdit: function (data) { + onEdit : function(data) { var id = data.record.get("distributor_id"); - + var rec = PartKeepr.getApplication().getDistributorStore().findRecord("id", id); - + if (rec) { data.record.set("distributor_name", rec.get("name")); } }, - onAddClick: function () { + onAddClick : function() { this.editing.cancelEdit(); - + var rec = new PartKeepr.PartDistributor({ - packagingUnit: 1 + packagingUnit : 1 }); - + this.store.insert(0, rec); - - this.editing.startEdit(0,0); + + this.editing.startEdit(0, 0); }, - onDeleteClick: function () { + onDeleteClick : function() { var selection = this.getView().getSelectionModel().getSelection()[0]; - if (selection) { - this.store.remove(selection); - } + if (selection) { + this.store.remove(selection); + } }, - onSelectChange: function(selModel, selections){ - this.deleteButton.setDisabled(selections.length === 0); - } + onSelectChange : function(selModel, selections) { + this.deleteButton.setDisabled(selections.length === 0); + } }); \ No newline at end of file diff --git a/src/frontend/js/Components/Part/Editor/PartEditor.js b/src/frontend/js/Components/Part/Editor/PartEditor.js @@ -225,12 +225,11 @@ Ext.define('PartKeepr.PartEditor', { ] }); - this.initialStockLevelPrice = Ext.create("Ext.form.field.Number", { + this.initialStockLevelPrice = Ext.create("PartKeepr.CurrencyField", { fieldLabel: i18n('Price'), labelWidth: 150, columnWidth: 0.5, - name: 'initialStockLevelPrice', - decimalPrecision: 4 + name: 'initialStockLevelPrice' }); this.initialStockLevelPricePerItem = Ext.create("Ext.form.field.Checkbox", { diff --git a/src/frontend/js/Components/Part/PartStockWindow.js b/src/frontend/js/Components/Part/PartStockWindow.js @@ -1,119 +1,121 @@ /** - * This class defines a window which is used to in- or decrease the stock level - * for a specific part. Logic and service calls are not contained in this window, - * and need to be implemented from the caller. + * This class defines a window which is used to in- or decrease the stock level for a specific part. Logic and service + * calls are not contained in this window, and need to be implemented from the caller. */ Ext.define('PartKeepr.PartStockWindow', { - extend: 'Ext.window.Window', - + extend : 'Ext.window.Window', + // Configurations - constrainHeader: true, - width: 305, - height: 155, - - resizable: false, - + constrainHeader : true, + width : 305, + height : 155, + + resizable : false, + // We set the title later - title: "", - + title : "", + // Window title texts - removePartText: i18n("Remove Part(s)"), - addPartText: i18n("Add Part(s)"), - - layout: 'anchor', - bodyStyle: { - padding: "5px" + removePartText : i18n("Remove Part(s)"), + addPartText : i18n("Add Part(s)"), + + layout : 'anchor', + bodyStyle : { + padding : "5px" }, - + /* - * Initializes the window with the quantity and price fields. - * The price field is hidden when a stock decrease happens. + * Initializes the window with the quantity and price fields. The price field is hidden when a stock decrease + * happens. */ - initComponent: function () { - + initComponent : function() { + this.quantityField = Ext.create("Ext.form.field.Number", { - value: 0, // The initial value is 0, to indicate that this is a number field - minValue: 1, // The minimum value is 1. That way we force the user to enter a value - width: 100, - listeners: { - specialkey: { - fn: function(field, e){ - if (e.getKey() == e.ENTER) { - this.onOKClick(); - } - }, - scope: this - } + value : 0, // The initial value is 0, to indicate that this is a number field + minValue : 1, // The minimum value is 1. That way we force the user to enter a value + width : 100, + listeners : { + specialkey : { + fn : function(field, e) { + if (e.getKey() == e.ENTER) { + this.onOKClick(); + } + }, + scope : this + } } }); - - this.priceField = Ext.create("Ext.form.field.Number", { - hideTrigger: true, - keyNavEnabled: false, - mouseWheelEnabled: false, - anchor: '100%', - value: 0, - decimalPrecision: 4, - fieldLabel: i18n("Price"), - listeners: { - specialkey: { - fn: function(field, e){ - if (e.getKey() == e.ENTER) { - this.onOKClick(); - } - }, - scope: this - } + + this.priceField = Ext.create("PartKeepr.CurrencyField", { + anchor : '100%', + value : 0, + fieldLabel : i18n("Price"), + listeners : { + specialkey : { + fn : function(field, e) { + if (e.getKey() == e.ENTER) { + this.onOKClick(); + } + }, + scope : this + } } }); - + this.priceCheckbox = Ext.create("Ext.form.field.Checkbox", { - boxLabel: i18n("Price per item"), - hideEmptyLabel: false, - checked: true + boxLabel : i18n("Price per item"), + hideEmptyLabel : false, + checked : true }); - + this.form = Ext.create("Ext.form.Panel", { - bodyStyle: 'background:#DBDBDB;', - border: false, - items: [{ - xtype: 'fieldcontainer', - fieldLabel: i18n("Quantity"), - layout: 'hbox', - items: [ this.quantityField, { width: 75, xtype: 'displayfield', margin: "0 0 0 5", value: this.partUnitName }] + bodyStyle : 'background:#DBDBDB;', + border : false, + items : [ { + xtype : 'fieldcontainer', + fieldLabel : i18n("Quantity"), + layout : 'hbox', + items : [ this.quantityField, { + width : 75, + xtype : 'displayfield', + margin : "0 0 0 5", + value : this.partUnitName + } ] }, this.priceField, this.priceCheckbox ] }); - + this.items = this.form; - + this.okButton = Ext.create("Ext.button.Button", { - text: i18n("OK"), - handler: this.onOKClick, - scope: this + text : i18n("OK"), + handler : this.onOKClick, + scope : this + }); + + this.buttons = [ { + text : i18n("Close"), + handler : this.onCloseClick, + icon : "resources/silkicons/cancel.png", + scope : this + }, this.okButton ]; + this.on("show", function() { + this.quantityField.focus(); + this.quantityField.selectText(0); + }, this, { + delay : 100 }); - - this.buttons = [ - { - text: i18n("Close"), - handler: this.onCloseClick, - icon: "resources/silkicons/cancel.png", - scope: this - }, - this.okButton - ]; - this.on("show", function () { this.quantityField.focus(); this.quantityField.selectText(0); }, this, { delay: 100 }); this.callParent(); }, /** * Closes the window */ - onCloseClick: function () { + onCloseClick : function() { this.close(); }, /** * Checks if the form is valid. If yes, execute the callback. */ - onOKClick: function () { + onOKClick : function() { if (this.form.getForm().isValid()) { var price; if (this.priceCheckbox.getValue()) { @@ -121,19 +123,23 @@ Ext.define('PartKeepr.PartStockWindow', { } else { price = this.priceField.getValue() / this.quantityField.getValue(); } - - Ext.callback(this.callbackFn, this.callbackScope, [ this.quantityField.getValue(), price ]); + + Ext.callback( this.callbackFn, + this.callbackScope, + [ this.quantityField.getValue(), price ]); this.close(); } }, /** - * Opens the window in "add stock" mode. The target callback receives two parameters: - * the value of the quantity field and the value of the price field. + * Opens the window in "add stock" mode. The target callback receives two parameters: the value of the quantity + * field and the value of the price field. * - * @param fn The callback - * @param scope The scope in which to execute the callback + * @param fn + * The callback + * @param scope + * The scope in which to execute the callback */ - addStock: function (fn, scope) { + addStock : function(fn, scope) { this.callbackFn = fn; this.callbackScope = scope; this.setTitle(this.addPartText); @@ -141,13 +147,15 @@ Ext.define('PartKeepr.PartStockWindow', { this.show(); }, /** - * Opens the window in "remove stock" mode. The target callback receives one parameters: - * the value of the quantity field + * Opens the window in "remove stock" mode. The target callback receives one parameters: the value of the quantity + * field * - * @param fn The callback - * @param scope The scope in which to execute the callback + * @param fn + * The callback + * @param scope + * The scope in which to execute the callback */ - removeStock: function (fn, scope) { + removeStock : function(fn, scope) { this.callbackFn = fn; this.callbackScope = scope; this.setTitle(this.removePartText); diff --git a/src/frontend/js/Components/Part/PartsGrid.js b/src/frontend/js/Components/Part/PartsGrid.js @@ -98,6 +98,31 @@ Ext.define('PartKeepr.PartsGrid', { this.topToolbar.insert(2, this.addFromTemplateButton); + this.mapSearchHotkey(); + }, + /** + * Maps a search hotkey to the search box. + * + * Right now, this is hardcoded to alt+x. + * + * @param none + * @return nothing + */ + mapSearchHotkey: function () { + this.searchKey = new Ext.util.KeyMap(Ext.get(document), { + key: 'x', + ctrl: false, + alt: true, + fn: function(e) { + var searchBox = this.searchField; + if (Ext.get(document).activeElement != searchBox) { + searchBox.focus('',10); + } + searchBox.setValue(''); + }, + scope: this, + stopEvent: true + }); }, /** * Called when an item was selected. Enables/disables the delete button. @@ -207,18 +232,6 @@ Ext.define('PartKeepr.PartsGrid', { } }, /** - * Used as renderer for the average price column. - */ - averagePriceRenderer: function (val,q,rec) - { - var numDecimals = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.price.numdecimals"); - if (numDecimals === null) { - numDecimals = 2; - } - - return val.toFixed(numDecimals); - }, - /** * Used as renderer for the icon column. */ iconRenderer: function (val,q,rec) diff --git a/src/frontend/js/Components/Project/ProjectReport.js b/src/frontend/js/Components/Project/ProjectReport.js @@ -98,11 +98,14 @@ Ext.define('PartKeepr.ProjectReportView', { } },{ header: i18n("Price per Item"), dataIndex: 'price', + renderer: PartKeepr.getApplication().formatCurrency, width: 100 },{ header: i18n("Sum"), dataIndex: 'sum', + renderer: PartKeepr.getApplication().formatCurrency, summaryType: 'sum', + summaryRenderer: PartKeepr.getApplication().formatCurrency, width: 100 },{ header: i18n("Amount to Order"), dataIndex: 'missing', @@ -110,7 +113,9 @@ Ext.define('PartKeepr.ProjectReportView', { },{ header: i18n("Sum (Order)"), dataIndex: 'sum_order', + renderer: PartKeepr.getApplication().formatCurrency, summaryType: 'sum', + summaryRenderer: PartKeepr.getApplication().formatCurrency, width: 100 }], store: this.projectReportStore, diff --git a/src/frontend/js/Components/Statistics/CurrentStatisticsPanel.js b/src/frontend/js/Components/Statistics/CurrentStatisticsPanel.js @@ -23,11 +23,11 @@ Ext.define('PartKeepr.CurrentStatisticsPanel', { '</tr>', '<tr>', '<td style="width: 200px;" class="e">'+i18n("Total Part Value")+':</td>', - '<td style="width: 200px;" class="e">{totalPrice}</td>', + '<td style="width: 200px;" class="e">{[PartKeepr.getApplication().formatCurrency(values.totalPrice)]}</td>', '</tr>', '<tr>', '<td style="width: 200px;" class="o">'+i18n("Average Part Value")+':</td>', - '<td style="width: 200px;" class="o">{averagePrice}</td>', + '<td style="width: 200px;" class="o">{[PartKeepr.getApplication().formatCurrency(values.averagePrice)]}</td>', '</tr>', '<tr>', '<td style="width: 200px;" class="e">'+i18n("Parts with price")+':</td>', diff --git a/src/frontend/js/Components/Statusbar.js b/src/frontend/js/Components/Statusbar.js @@ -63,8 +63,7 @@ Ext.define('PartKeepr.Statusbar', { if (PartKeepr.getApplication().getSession()) { PartKeepr.getApplication().logout(); } else { - var o = new PartKeepr.LoginDialog(); - o.show(); + PartKeepr.getApplication().getSessionManager().login(); } } }); diff --git a/src/frontend/js/Components/StockReport/AbstractStockHistoryGrid.js b/src/frontend/js/Components/StockReport/AbstractStockHistoryGrid.js @@ -43,8 +43,7 @@ Ext.define('PartKeepr.AbstractStockHistoryGrid', { { header: i18n("Price"), editor: { - xtype:'numberfield', - decimalPrecision: 4, + xtype:'CurrencyField', allowBlank:false }, dataIndex: 'price', @@ -53,7 +52,7 @@ Ext.define('PartKeepr.AbstractStockHistoryGrid', { if (rec.get("dir") == "out") { return "-"; } else { - return val; + return PartKeepr.getApplication().formatCurrency(val); } } },{ diff --git a/src/frontend/js/Components/StorageLocation/StorageLocationGrid.js b/src/frontend/js/Components/StorageLocation/StorageLocationGrid.js @@ -1,6 +1,9 @@ Ext.define('PartKeepr.StorageLocationGrid', { extend: 'PartKeepr.EditorGrid', alias: 'widget.StorageLocationGrid', + + automaticPageSize: true, + columns: [ {header: i18n("Storage Location"), dataIndex: 'name', flex: 1} ], diff --git a/src/frontend/js/Components/User/Preferences/FormattingPreferences.js b/src/frontend/js/Components/User/Preferences/FormattingPreferences.js @@ -1,8 +1,54 @@ +/** + * Contains the formatting preferences for various places throughout the system + */ Ext.define('PartKeepr.FormattingPreferencesPanel', { extend: 'Ext.form.FormPanel', title: i18n("Formatting"), bodyStyle: 'background:#DBDBDB;padding: 10px;', + initComponent: function () { + + this.createWidgets(); + this.loadDefaults(); + + + this.items = [ + this.priceNumDecimalsField, + this.useThousandSeparatorCheckbox, + this.currencySymbolField, + this.currencyAtEndCheckbox + ]; + + this.callParent(); + }, + /** + * Loads the defaults for the user preferences + * + * @param none + * @return nothing + */ + loadDefaults: function () { + var numDecimals = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.numdecimals", 2); + this.priceNumDecimalsField.setValue(numDecimals); + + var useThousandsSeparator = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.thousandsSeparator", true); + this.useThousandSeparatorCheckbox.setValue(useThousandsSeparator); + + + var currencyAtEnd = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.currencySymbolAtEnd", true); + this.currencyAtEndCheckbox.setValue(currencyAtEnd); + + var currencySymbol = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.symbol", true); + this.currencySymbolField.setValue(currencySymbol); + }, + /** + * Creates the widgets used in this form. + * + * @param none + * @return nothing + * + */ + createWidgets: function () { this.priceNumDecimalsField = Ext.create("Ext.form.field.Number", { name: 'priceNumDecimalsField', fieldLabel: i18n('Decimal precision'), @@ -13,17 +59,37 @@ Ext.define('PartKeepr.FormattingPreferencesPanel', { allowDecimals: false, listeners: { change: function(field, newValue) { - PartKeepr.getApplication().setUserPreference("partkeepr.formatting.price.numdecimals", newValue); + PartKeepr.getApplication().setUserPreference("partkeepr.formatting.currency.numdecimals", newValue); + } + } + }); + + this.useThousandSeparatorCheckbox = Ext.create("Ext.form.field.Checkbox", { + boxLabel: i18n("Separate thousands"), + listeners: { + change: function(field, newValue) { + PartKeepr.getApplication().setUserPreference("partkeepr.formatting.currency.thousandsSeparator", newValue); + } + } + }); + + this.currencySymbolField = Ext.create("Ext.form.field.Text", { + fieldLabel: i18n("Currency Symbol"), + maxLength: 5, + listeners: { + change: function(field, newValue) { + PartKeepr.getApplication().setUserPreference("partkeepr.formatting.currency.symbol", newValue); + } + } + }); + + this.currencyAtEndCheckbox = Ext.create("Ext.form.field.Checkbox", { + boxLabel: i18n("Currency at end"), + listeners: { + change: function(field, newValue) { + PartKeepr.getApplication().setUserPreference("partkeepr.formatting.currency.currencySymbolAtEnd", newValue); } } }); - - var numDecimals = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.price.numdecimals", 2); - this.priceNumDecimalsField.setValue(numDecimals); - - this.items = [ this.priceNumDecimalsField ]; - - this.callParent(); } -}); - +});+ \ No newline at end of file diff --git a/src/frontend/js/Components/Widgets/CurrencyNumberField.js b/src/frontend/js/Components/Widgets/CurrencyNumberField.js @@ -0,0 +1,24 @@ +/** + * Extends the Ext.ux.NumericField and applies defaults stored within the user preferences. + */ +Ext.define("PartKeepr.CurrencyField", { + extend: "Ext.ux.NumericField", + alias: 'widget.CurrencyField', + + initComponent: function () { + this.decimalPrecision = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.numdecimals", 2); + this.currencySign = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.symbol", "€"); + this.currencyAtEnd = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.currencySymbolAtEnd", true); + + if (PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.thousandsSeparator", true) === true) { + // @todo This is hard-coded for now + this.thousandSeparator = ","; + } else { + this.thousandSeparator = ""; + } + + + + this.callParent(); + } +});+ \ No newline at end of file diff --git a/src/frontend/js/Ext.ux/NumericField.js b/src/frontend/js/Ext.ux/NumericField.js @@ -0,0 +1,124 @@ +/** + * @author: Frédéric Thomas + * Date: 22/03/12 + * Time: 16:37 + */ +Ext.define('Ext.ux.NumericField', { + extend: 'Ext.form.field.Number', + alias: ['widget.currencyField'], + config: { + thousandSeparator: ' ', + currencyAtEnd: true, + currencySign: '€' + }, + + listeners: { + /** + * When this component get the focus, change the Currency + * representation to a Float one for edition. + * + * @param me + * @param eOpts + */ + focus: function (me, eOpts) { + me.inputEl.dom.value = this.getValue(); + } + }, + + /** + * Converts a Float value into a currency formated value ready to display . + * + * @param {Object} value + * @return {Object} The converted value. + */ + valueToCurrency: function (value) { + var format = Ext.util.Format; + format.currencyPrecision = this.decimalPrecision; + format.thousandSeparator = this.thousandSeparator; + format.currencySign = this.currencySign; + format.currencyAtEnd = this.currencyAtEnd; + return format.currency(value); + }, + /** + * Converts a mixed-type value to a raw representation suitable for displaying in the field. This allows controlling + * how value objects passed to {@link #setValue} are shown to the user, including localization. + * + * See {@link #rawToValue} for the opposite conversion. + * + * This implementation converts the raw value to a value formated as currency. + * + * @param {Object} value The mixed-type value to convert to the raw representation. + * @return {Object} The converted raw value. + */ + valueToRaw: function (value) { + return this.valueToCurrency(value); + }, + + /** + * Performs any necessary manipulation of a raw String value to prepare it for conversion and/or + * {@link #validate validation}. Overrided to apply the {@link #parseValue} + * to the raw value. + * + * @param {String} value The unprocessed string value + * @return {String} The processed string value + */ + processRawValue: function (value) { + value = this.callParent(arguments); + + if (isNaN(value) || value === null || value === "") { + return value; + } + + return this.parseValue(value); + }, + /** + * Runs all of Number's validations and returns an array of any errors. Note that this first runs Text's + * validations, so the returned array is an amalgamation of all field errors. The additional validations run test + * that the value is a number, and that it is within the configured min and max values. + * @param {Object} [value] The value to get errors for (defaults to the current field value) + * @return {String[]} All validation errors for this field + */ + getErrors: function(value) { + var me = this, + errors = [], // This is a hack because of the strange class layout... + format = Ext.String.format, + num; + + value = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue()); + + if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid + return errors; + } + + value = this.parseValue(value); + + if(isNaN(value)){ + errors.push(format(me.nanText, value)); + } + + if (me.minValue === 0 && value < 0) { + errors.push(this.negativeText); + } + else if (value < me.minValue) { + errors.push(format(me.minText, me.minValue)); + } + + if (value > me.maxValue) { + errors.push(format(me.maxText, me.maxValue)); + } + + + return errors; + }, + /** + * Overrided to remove thousand separator. + * + * @param value + */ + parseValue: function (value) { + value = String(value).replace(this.thousandSeparator, ""); + value = String(value).replace(this.currencySign, ""); + value = parseFloat(String(value).replace(this.decimalSeparator, '.')); + return isNaN(value) ? null : value; + } +});+ \ No newline at end of file diff --git a/src/frontend/js/PartKeepr.js b/src/frontend/js/PartKeepr.js @@ -38,20 +38,6 @@ Ext.application({ } } - new Ext.util.KeyMap(Ext.get(document), { - key: 'x', - ctrl: false, - alt: true, - fn: function(e) { - var searchBox = Ext.getCmp('thesearchfield'); - if (Ext.get(document).activeElement != searchBox) { - searchBox.focus('',10); - } - searchBox.setValue(''); - }, - stopEvent: true - }); - Ext.fly(document.body).on('contextmenu', this.onContextMenu, this); }, onContextMenu: function (e, target) { @@ -158,16 +144,20 @@ Ext.application({ return this.sessionManager; }, /* - * Checks for unacknowledged system notices + * Checks for unacknowledged system notices. Triggers a service call against the server. + * + * Checks if a session is active; otherwise, nothing will happen. * * @param none * @return nothing */ doUnacknowledgedNoticesCheck: function () { - var call = new PartKeepr.ServiceCall("SystemNotice", "hasUnacknowledgedNotices"); - - call.setHandler(Ext.bind(this.onUnacknowledgedNoticesCheck, this)); - call.doCall(); + if (this.getSessionManager().getSession() !== null) { + var call = new PartKeepr.ServiceCall("SystemNotice", "hasUnacknowledgedNotices"); + + call.setHandler(Ext.bind(this.onUnacknowledgedNoticesCheck, this)); + call.doCall(); + } }, /** * Handler for the unacknowledged system notices check @@ -185,7 +175,7 @@ Ext.application({ logout: function () { this.menuBar.disable(); this.centerPanel.removeAll(true); - this.getSessionManager.logout(); + this.getSessionManager().logout(); }, createGlobalStores: function () { this.footprintStore = Ext.create("Ext.data.Store", @@ -496,6 +486,21 @@ Ext.application({ */ getUsername: function () { return this.username; + }, + formatCurrency: function (value) { + var format = Ext.util.Format; + format.currencyPrecision = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.numdecimals", 2); + format.currencySign = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.symbol", "€"); + format.currencyAtEnd = PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.currencySymbolAtEnd", true); + + if (PartKeepr.getApplication().getUserPreference("partkeepr.formatting.currency.thousandsSeparator", true) === true) { + // @todo This is hard-coded for now + format.thousandSeparator = ","; + } else { + format.thousandSeparator = ""; + } + + return format.currency(value); } });