partkeepr

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

commit 6d441028d221756e0aeef94fad1a17020730c241
parent 01ccda027bb9e2a0192e1567c4585700aa4ad4a8
Author: Timo A. Hummel <timo@netraver.de>
Date:   Sun, 12 Jun 2011 07:58:00 +0200

Finalized parameter editing

Diffstat:
Mfrontend/index.php | 2++
Mfrontend/js/Components/Part/PartEditor.js | 5+++++
Mfrontend/js/Components/Part/PartEditorWindow.js | 4++--
Mfrontend/js/Components/Part/PartParameterGrid.js | 102++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mfrontend/js/Components/Widgets/PartParameterComboBox.js | 11++++++++++-
Afrontend/js/Components/Widgets/SiUnitField.js | 392+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afrontend/js/Components/Widgets/SiUnitList.js | 8++++++++
Mfrontend/js/Components/Widgets/UnitComboBox.js | 2+-
Mfrontend/js/Models/PartParameter.js | 4+++-
Mfrontend/js/PartDB2.js | 17+++++++++++++++++
Msetup/data/siprefixes.yaml | 3+++
Msetup/data/units.yaml | 43++++++++++++++++++++++++++++++++++++++++---
Msrc/de/RaumZeitLabor/PartDB2/Part/PartManager.php | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/de/RaumZeitLabor/PartDB2/Part/PartService.php | 4++++
Msrc/de/RaumZeitLabor/PartDB2/PartParameter/PartParameter.php | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/de/RaumZeitLabor/PartDB2/Unit/Unit.php | 8++++++++
Mtesting/SetupDatabase.php | 5++++-
17 files changed, 666 insertions(+), 63 deletions(-)

diff --git a/frontend/index.php b/frontend/index.php @@ -40,6 +40,8 @@ <script type="text/javascript" src="js/Components/Widgets/PartUnitComboBox.js"></script> <script type="text/javascript" src="js/Components/Widgets/UnitComboBox.js"></script> <script type="text/javascript" src="js/Components/Widgets/PartParameterComboBox.js"></script> + <script type="text/javascript" src="js/Components/Widgets/SiUnitField.js"></script> + <script type="text/javascript" src="js/Components/Widgets/SiUnitList.js"></script> <script type="text/javascript" src="js/Util/ServiceCall.js"></script> diff --git a/frontend/js/Components/Part/PartEditor.js b/frontend/js/Components/Part/PartEditor.js @@ -113,6 +113,11 @@ Ext.define('PartDB2.PartEditor', { "removals": PartDB2.serializeRecords(this.partManufacturerGrid.getStore().getRemovedRecords()) }; + values.parameterChanges = { + "inserts": PartDB2.serializeRecords(this.partParameterGrid.getStore().getNewRecords()), + "updates": PartDB2.serializeRecords(this.partParameterGrid.getStore().getUpdatedRecords()), + "removals": PartDB2.serializeRecords(this.partParameterGrid.getStore().getRemovedRecords()) + }; call.setParameters(values); call.setHandler(Ext.bind(this.onPartSave, this)); call.doCall(); diff --git a/frontend/js/Components/Part/PartEditorWindow.js b/frontend/js/Components/Part/PartEditorWindow.js @@ -1,8 +1,8 @@ Ext.define('PartDB2.PartEditorWindow', { extend: 'Ext.window.Window', constrainHeader: true, - width: 500, - minWidth: 500, + width: 600, + minWidth: 600, minHeight: 300, height: 350, layout: 'fit', diff --git a/frontend/js/Components/Part/PartParameterGrid.js b/frontend/js/Components/Part/PartParameterGrid.js @@ -10,12 +10,16 @@ Ext.define('PartDB2.PartParameterGrid', { reader: { type: 'json' } - } - + } }); - this.editing = Ext.create('Ext.grid.plugin.RowEditing', { - clicksToEdit: 1 + this.editing = Ext.create('Ext.grid.plugin.CellEditing', { + clicksToEdit: 1, + listeners: { + scope: this, + beforeedit: this.onBeforeEdit, + edit: this.onAfterEdit + } }); this.plugins = [ this.editing ]; @@ -46,7 +50,10 @@ Ext.define('PartDB2.PartParameterGrid', { flex: 0.2, editor: { xtype:'PartParameterComboBox', - allowBlank:true + allowBlank:false, + lazyRender: true, + listClass: 'x-combo-list-small', + selectOnTab: true } }, { @@ -59,81 +66,60 @@ Ext.define('PartDB2.PartParameterGrid', { } }, { - header: i18n("Unit"), + header: i18n("Value"), flex: 0.2, - dataIndex: 'unit_id', + dataIndex: "prefixedValue", renderer: function (val,p,rec) { - var foundRec = PartDB2.getApplication().getUnitStore().findRecord("id", val); + if (!Ext.isObject(val)) { return ""; } + + var foundRec = PartDB2.getApplication().getUnitStore().findRecord("id", rec.get("unit_id")); if (foundRec) { - return foundRec.get("name"); + return val.value + " "+val.symbol + foundRec.get("symbol"); } else { - return ""; + return val.value + " "+val.symbol; } + }, editor: { - xtype:'UnitComboBox', - allowBlank:true - } + xtype: 'SiUnitField', + decimalPrecision: 20 + } }, { - header: i18n("Value"), + header: i18n("Unit"), flex: 0.2, - dataIndex: "value", + dataIndex: 'unit_id', renderer: function (val,p,rec) { - /* Convert the value into a "nice" si-prefixed unit */ - - var foundRec = PartDB2.getApplication().getUnitStore().findRecord("id", rec.get("unit_id")); - var allowedSiPrefixes = foundRec.prefixes().getRange(); - - console.log(allowedSiPrefixes.length); - finalValue = null; - - for (var i=0;i<allowedSiPrefixes.length;i++) { - var test = val / Math.pow(10, allowedSiPrefixes[i].get("power")); - - if (test >= 1 && test < 1000) { - finalValue = test.toPrecision(6) + " "+allowedSiPrefixes[i].get("symbol")+foundRec.get("symbol"); - } - } + var foundRec = PartDB2.getApplication().getUnitStore().findRecord("id", val); - if (finalValue === null) { - finalValue = val.toPrecision(6) + foundRec.get("symbol"); + if (foundRec) { + return foundRec.get("name"); + } else { + return ""; } - - return finalValue; }, editor: { - xtype: 'numberfield', - decimalPrecision: 20 - } + xtype:'UnitComboBox', + allowBlank:true + } } ]; this.callParent(); this.getSelectionModel().on('selectionchange', this.onSelectChange, this); - this.on("edit", this.onEdit, this); - }, - onEdit: function (data) { - /*var id = data.record.get("unit_id"); - - var rec = PartDB2.getApplication().getManufacturerStore().findRecord("id", id); - - if (rec) { - data.record.set("manufacturer_name", rec.get("name")); - }*/ }, onAddClick: function () { this.editing.cancelEdit(); - /*var rec = new PartDB2.PartManufacturer({ - packagingUnit: 1 + var rec = new PartDB2.PartParameter({ + }); this.store.insert(0, rec); - this.editing.startEdit(0,0);*/ + this.editing.startEditByPosition({ row: 0, column: 0}); }, onDeleteClick: function () { var selection = this.getView().getSelectionModel().getSelection()[0]; @@ -143,5 +129,21 @@ Ext.define('PartDB2.PartParameterGrid', { }, onSelectChange: function(selModel, selections){ this.deleteButton.setDisabled(selections.length === 0); + }, + onBeforeEdit: function (editor, e, o) { + var header = this.headerCt.getHeaderAtIndex(editor.colIdx); + var edit = this.editing.getEditor(editor.record, header); + + if (editor.field == "prefixedValue") { + var unit = PartDB2.getApplication().getUnitStore().getById(editor.record.get("unit_id")); + if (unit) { + edit.field.setStore(unit.prefixes()); + } + } + }, + onAfterEdit: function (editor, e) { + console.log(e); + var f = e.record.get("prefixedValue"); + e.record.set("siprefix_id", f.siprefix_id); } }); \ No newline at end of file diff --git a/frontend/js/Components/Widgets/PartParameterComboBox.js b/frontend/js/Components/Widgets/PartParameterComboBox.js @@ -4,7 +4,8 @@ Ext.define("PartDB2.PartParameterComboBox",{ displayField: 'name', valueField: 'name', autoSelect: false, - queryMode: 'remote', + allowBlank: false, + queryMode: 'local', triggerAction: 'all', forceSelection: false, editable: true, @@ -22,6 +23,8 @@ Ext.define("PartDB2.PartParameterComboBox",{ } } }); + + this.store.load(); /* Workaround to remember the value when loading */ this.store.on("beforeload", function () { @@ -34,6 +37,12 @@ Ext.define("PartDB2.PartParameterComboBox",{ }, this); this.callParent(); + }, + getValue: function () { + var j = this.callParent(); + + console.log(j); + return j; } }); diff --git a/frontend/js/Components/Widgets/SiUnitField.js b/frontend/js/Components/Widgets/SiUnitField.js @@ -0,0 +1,391 @@ +/** + * This class represents a field which can handle a number (value) bound to a specific SI prefix. + * + * Internally, we use an object as value. Example: + * + * { + * value: 10 // The base value, in our case 10 + * symbol: "n" // The symbol for display + * power: -9 // The power + * siprefix_id: 5 // The ID of the siprefix record + * } + * + */ +Ext.define("PartDB2.SiUnitField",{ + extend:"Ext.form.field.Picker", + alias: 'widget.SiUnitField', + + siPrefix: null, + + /** + * @cfg {RegExp} stripCharsRe @hide + */ + /** + * @cfg {RegExp} maskRe @hide + */ + + /** + * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true) + */ + allowDecimals : true, + + /** + * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.') + */ + decimalSeparator : '.', + + /** + * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2) + */ + decimalPrecision : 2, + + /** + * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY). Will be used by + * the field's validation logic. + */ + minValue: Number.NEGATIVE_INFINITY, + + /** + * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE). Will be used by + * the field's validation logic. + */ + maxValue: Number.MAX_VALUE, + + /** + * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to 'The minimum + * value for this field is {minValue}') + */ + minText : 'The minimum value for this field is {0}', + + /** + * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to 'The maximum + * value for this field is {maxValue}') + */ + maxText : 'The maximum value for this field is {0}', + + /** + * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen + * if a valid character like '.' or '-' is left in the field with no number (defaults to '{value} is not a valid number') + */ + nanText : '{0} is not a valid number', + + /** + * @cfg {String} negativeText Error text to display if the value is negative and {@link #minValue} is set to + * <tt>0</tt>. This is used instead of the {@link #minText} in that circumstance only. + */ + negativeText : 'The value cannot be negative', + + /** + * @cfg {String} baseChars The base set of characters to evaluate as valid numbers (defaults to '0123456789'). + */ + baseChars : '0123456789', + + /** + * @cfg {Boolean} autoStripChars True to automatically strip not allowed characters from the field. Defaults to <tt>false</tt> + */ + autoStripChars: false, + + initComponent: function() { + var me = this, + allowed; + + me.callParent(); + + me.setMinValue(me.minValue); + me.setMaxValue(me.maxValue); + + // Build regexes for masking and stripping based on the configured options + if (me.disableKeyFilter !== true) { + allowed = me.baseChars + ''; + + var store = PartDB2.getApplication().getSiPrefixStore(); + + for (var i=0;i<store.count();i++) { + allowed += store.getAt(i).get("symbol"); + } + + /** + * Fix because the µ-symbol on your keyboard is not greek "Mu" as defined by the Si standard. We wish that + * the user still can enter "µ", which automatically gets converted to "Mu". + */ + allowed += "µ"; + + if (me.allowDecimals) { + allowed += me.decimalSeparator; + } + if (me.minValue < 0) { + allowed += '-'; + } + allowed = Ext.String.escapeRegex(allowed); + me.maskRe = new RegExp('[' + allowed + ']'); + if (me.autoStripChars) { + me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi'); + } + } + }, + onTriggerClick: function () { + this.expand(); + + var node = this.picker.getNode(this.siPrefix); + + if (node) { + this.picker.highlightItem(node); + this.picker.listEl.scrollChildIntoView(node, false); + } + }, + getStore: function () { + if (this.store) { + return this.store; + } + + return PartDB2.getApplication().getSiPrefixStore(); + }, + setStore: function (store) { + if (this.picker) { + this.picker.bindStore(store); + } else { + this.store = store; + } + }, + createPicker: function() { + var siprefixtpl = new Ext.XTemplate( + '<tpl for=".">', + '<div class="thumb-wrap">', + '{symbol} {prefix}', + '</div>', + '</tpl>' + ); + + var tmp = Ext.create('PartDB2.SiUnitList', { + store: this.getStore(), + singleSelect: true, + ownerCt: this.ownerCt, + renderTo: document.body, + //width: 200, + //height:200, + floating: true, + maxHeight: 300, + shadow: 'sides', + focusOnToFront: false, + hidden: true, + focusOnShow: true, + displayField: 'symbol', + isteners: { + scope: this, + itemclick: this.onSelect + } + }); + + this.mon(tmp, { + itemclick: this.onSelect, + scope: this + }); + return tmp; + }, + onSelect: function (t, rec) { + var val = this.getValue(); + + val.symbol = rec.get("symbol"); + val.power = rec.get("power"); + val.siprefix_id = rec.get("id"); + + //this.siUnit = rec; + this.setValue(val); + this.collapse(); + }, + /** + * 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 {Mixed} value The value to get errors for (defaults to the current field value) + * @return {Array} All validation errors for this field + */ + getErrors: function(value) { + var me = this, + errors = me.callParent(arguments), + format = Ext.String.format, + num, retVal; + + retVal = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue()); + + value = retVal.value; + + if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid + return errors; + } + + value = String(value).replace(me.decimalSeparator, '.'); + + if(isNaN(value)){ + errors.push(format(me.nanText, value)); + } + + num = me.parseValue(value); + + if (me.minValue === 0 && num < 0) { + errors.push(this.negativeText); + } + else if (num < me.minValue) { + errors.push(format(me.minText, me.minValue)); + } + + if (num > me.maxValue) { + errors.push(format(me.maxText, me.maxValue)); + } + + + return errors; + }, + rawToValue: function(rawValue) { + var processValue; + + if (Ext.isObject(rawValue)) { + processValue = rawValue.value; + } else { + processValue = rawValue; + } + + return this.fixPrecision(this.parseValue(processValue)) || processValue || null; + }, + + valueToRaw: function(value) { + var me = this, + decimalSeparator = me.decimalSeparator; + value = me.parseValue(value); + value = me.fixPrecision(value); + value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.')); + value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator); + + if (Ext.isObject(this.siPrefix) && this.siPrefix.get("symbol") !== "") { + return value + " "+this.siPrefix.get("symbol"); + } else { + return value; + } + + }, + + onChange: function() { + var me = this, + value = me.getValue(), + valueIsNull = value === null; + + me.callParent(arguments); + }, + getValue: function () { + var v = this.callParent(arguments); + + if (this.siPrefix) { + return { + value: v, + symbol: this.siPrefix.get("symbol"), + power: this.siPrefix.get("power"), + siprefix_id: this.siPrefix.get("id") + }; + } else { + return { + value: v, + symbol: "", + power: 1, + siprefix_id: null + }; + } + }, + /** + * Replaces any existing {@link #minValue} with the new value. + * @param {Number} value The minimum value + */ + setMinValue : function(value) { + this.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY); + }, + + /** + * Replaces any existing {@link #maxValue} with the new value. + * @param {Number} value The maximum value + */ + setMaxValue: function(value) { + this.maxValue = Ext.Number.from(value, Number.MAX_VALUE); + }, + + // private + parseValue : function(value) { + value = parseFloat(String(value).replace(this.decimalSeparator, '.')); + return isNaN(value) ? null : value; + }, + + /** + * @private + * + */ + fixPrecision : function(value) { + var me = this, + nan = isNaN(value), + precision = me.decimalPrecision; + + if (nan || !value) { + return nan ? '' : value; + } else if (!me.allowDecimals || precision <= 0) { + precision = 0; + } + + return parseFloat(Ext.Number.toFixed(parseFloat(value), precision)); + }, + + beforeBlur : function() { + var me = this, + v = me.parseValue(me.getRawValue()); + + if (!Ext.isEmpty(v)) { + me.setValue(v); + } + }, + findSiPrefix: function (value) { + var store = PartDB2.getApplication().getSiPrefixStore(); + var symbol; + + for (var i=0;i<store.count();i++) { + + symbol = store.getAt(i).get("symbol"); + + if (symbol !== "") { + if (strpos(value, symbol) !== false) { + return store.getAt(i); + } + } else { + emptyPrefix = store.getAt(i); + } + + } + + if (emptyPrefix) { + return emptyPrefix; + } else { + return null; + } + }, + setValue: function (v) { + if (Ext.isObject(v)) { + this.siPrefix = PartDB2.getApplication().getSiPrefixStore().getById(v.siprefix_id); + + return this.callParent([v.value]); + } else { + return v; + } + }, + processRawValue: function (value) { + var prefix; + + value = PartDB2.getApplication().convertMicroToMu(value); + + var siPrefix = this.findSiPrefix(value); + + this.siPrefix = siPrefix; + + if (siPrefix !== null) { + value = str_replace(siPrefix.get("symbol"), "", value); + return { value: value, symbol: siPrefix.get("symbol"), power: siPrefix.get("power"), siprefix_id: siPrefix.get("id") }; + } else { + return { value: value, symbol: "", power: 0, siprefix_id: null }; + } + } +});+ \ No newline at end of file diff --git a/frontend/js/Components/Widgets/SiUnitList.js b/frontend/js/Components/Widgets/SiUnitList.js @@ -0,0 +1,7 @@ +Ext.define('PartDB2.SiUnitList', { + extend: 'Ext.view.BoundList', + alias: 'widget.siunitlist', + getInnerTpl: function(displayField) { + return '<span style="display: inline-block; width: 15px;">{' + displayField + '}</span><span style="display: inline-block; width: 40px;">{prefix}</span>(10<sup>{power}</span>)'; + }, +});+ \ No newline at end of file diff --git a/frontend/js/Components/Widgets/UnitComboBox.js b/frontend/js/Components/Widgets/UnitComboBox.js @@ -7,7 +7,7 @@ Ext.define("PartDB2.UnitComboBox",{ queryMode: 'local', triggerAction: 'all', forceSelection: true, - editable: false, + editable: true, initComponent: function () { this.store = PartDB2.getApplication().getUnitStore(); diff --git a/frontend/js/Models/PartParameter.js b/frontend/js/Models/PartParameter.js @@ -6,7 +6,9 @@ PartDB2.PartParameter = Ext.define("PartParameter", { { name: 'name', type: 'string' }, { name: 'description', type: 'string' }, { name: 'unit_id', type: 'int' }, - { name: 'value', type: 'float' } + { name: 'siprefix_id', type: 'int' }, + { name: 'value', type: 'float' }, + { name: 'prefixedValue' } ], proxy: PartDB2.getRESTProxy("PartParameter") }); diff --git a/frontend/js/PartDB2.js b/frontend/js/PartDB2.js @@ -93,6 +93,23 @@ Ext.application({ return this.siPrefixStore; }, /** + * Converts the Character "micro" (µ, available on german keyboards via AltGr+m) to the Character "Mu" (μ). + * + * The standard for Si-Prefixes defines that the "Mu"-character should be used instead of the "micro" character. + * + * Wikipedia Entry for the "Micro" Si Prefix: http://en.wikipedia.org/wiki/Micro- + * + */ + convertMicroToMu: function (value) { + /** + * Since the Si-Prefix for "micro" is μ, but keyboard have "µ" on it + * (note: both chars might look identical, depending on your font), we need + * to convert "µ" (on the keyboard, Unicode U+00B5) to the Mu (U+03BC). + */ + + return str_replace("µ", "μ", value); + }, + /** * Reload all global stores each 100 seconds. * * @todo In the future, it would be nice to trigger a specific diff --git a/setup/data/siprefixes.yaml b/setup/data/siprefixes.yaml @@ -28,6 +28,9 @@ hecto: deca: symbol: da power: 1 +"-": + symbol: "" + power: 0 deci: symbol: d power: -1 diff --git a/setup/data/units.yaml b/setup/data/units.yaml @@ -7,19 +7,26 @@ Meter: - c - d - k -Gramm: + - "" +Gram: symbol: g prefixes: - k - m -Sekunde: + - "" +Second: symbol: s prefixes: - m + - "" Kelvin: symbol: K + prefixes: + - "" Mol: symbol: mol + prefixes: + - "" Candela: symbol: cd prefixes: @@ -29,6 +36,7 @@ Ampere: prefixes: - k - m + - "" - µ - n - p @@ -38,33 +46,39 @@ Ohm: - k - m - M + - "" - G - T Volt: symbol: V prefixes: - m + - "" - k Hertz: symbol: Hz prefixes: - k - M + - "" - G - T Newton: symbol: N prefixes: - k + - "" Pascal: symbol: Pa prefixes: - m + - "" - k Joule: symbol: J prefixes: - m + - "" - k - M Watt: @@ -72,6 +86,7 @@ Watt: prefixes: - k - M + - "" - G - m - µ @@ -79,45 +94,67 @@ Coulomb: symbol: C prefixes: - k + - "" Farad: symbol: F prefixes: - m - p + - "" - n - μ Siemens: symbol: S prefixes: - m + - "" Weber: symbol: Wb + prefixes: + - "" Tesla: symbol: T + prefixes: + - "" Henry: symbol: H prefixes: - m - µ + - "" Celsius: symbol: °C + prefixes: + - "" Lumen: symbol: lm + prefixes: + - "" Lux: symbol: lx + prefixes: + - "" Becquerel: symbol: Bq + prefixes: + - "" Gray: symbol: Gy + prefixes: + - "" Sievert: symbol: Sv prefixes: - m + - "" - µ Katal: symbol: kat -Amperestunde: + prefixes: + - "" +Ampere Hour: symbol: Ah prefixes: - m + - "" - k \ No newline at end of file diff --git a/src/de/RaumZeitLabor/PartDB2/Part/PartManager.php b/src/de/RaumZeitLabor/PartDB2/Part/PartManager.php @@ -1,5 +1,10 @@ <?php namespace de\raumzeitlabor\PartDB2\Part; +use de\RaumZeitLabor\PartDB2\PartParameter\PartParameter; + +use de\RaumZeitLabor\PartDB2\Unit\Unit; +use de\RaumZeitLabor\PartDB2\SiPrefix\SiPrefix; + use de\RaumZeitLabor\PartDB2\Part\PartDistributor; use de\RaumZeitLabor\PartDB2\Part\PartManufacturer; @@ -179,6 +184,12 @@ class PartManager extends Singleton { } } + if (array_key_exists("parameterChanges", $aParameters)) { + if (is_array($aParameters["parameterChanges"])) { + $this->processParameterChanges($part, $aParameters["parameterChanges"]); + } + } + if (array_key_exists("partUnit", $aParameters)) { if ($aParameters["partUnit"] === null || $aParameters["partUnit"] === 0) { $part->setPartUnit(null); @@ -193,6 +204,50 @@ class PartManager extends Singleton { } + private function processParameterChanges (Part $part, Array $data) { + if (array_key_exists("updates", $data)) { + foreach ($data["updates"] as $record) { + foreach ($part->getParameters() as $partParameter) { + if ($partParameter->getId() == $record["id"]) { + $partParameter->setName($record["name"]); + $partParameter->setDescription($record["description"]); + $partParameter->setValue($record["value"]); + $partParameter->setSiPrefix(SiPrefix::loadById($record["siprefix_id"])); + $partParameter->setUnit(Unit::loadById($record["unit_id"])); + break; + } + } + } + } + + if (array_key_exists("removals", $data)) { + foreach ($data["removals"] as $record) { + foreach ($part->getParameters() as $partParameter) { + if ($partParameter->getId() == $record["id"]) { + PartDB2::getEM()->remove($partParameter); + $part->getParameters()->removeElement($partParameter); + break; + } + } + } + } + + if (array_key_exists("inserts", $data)) { + foreach ($data["inserts"] as $record) { + $partParameter = new PartParameter(); + $partParameter->setPart($part); + + $partParameter->setName($record["name"]); + $partParameter->setDescription($record["description"]); + $partParameter->setValue($record["value"]); + $partParameter->setSiPrefix(SiPrefix::loadById($record["siprefix_id"])); + $partParameter->setUnit(Unit::loadById($record["unit_id"])); + + $part->getParameters()->add($distributor); + } + } + } + private function processDistributorChanges (Part $part, Array $data) { if (array_key_exists("updates", $data)) { foreach ($data["updates"] as $record) { @@ -201,6 +256,7 @@ class PartManager extends Singleton { $partDistributor->setOrderNumber($record["orderNumber"]); $partDistributor->setDistributor(Distributor::loadById($record["distributor_id"])); $partDistributor->setPackagingUnit($record["packagingUnit"]); + break; } } } @@ -236,6 +292,7 @@ class PartManager extends Singleton { if ($partManufacturer->getId() == $record["id"]) { $partManufacturer->setPartNumber($record["partNumber"]); $partManufacturer->setManufacturer(Manufacturer::loadById($record["manufacturer_id"])); + break; } } } diff --git a/src/de/RaumZeitLabor/PartDB2/Part/PartService.php b/src/de/RaumZeitLabor/PartDB2/Part/PartService.php @@ -134,6 +134,10 @@ class PartService extends Service implements RestfulService { $aParameters["manufacturerChanges"] = $this->getParameter("manufacturerChanges"); } + if ($this->hasParameter("parameterChanges")) { + $aParameters["parameterChanges"] = $this->getParameter("parameterChanges"); + } + if ($this->hasParameter("partUnit_id")) { $aParameters["partUnit"] = $this->getParameter("partUnit_id"); } diff --git a/src/de/RaumZeitLabor/PartDB2/PartParameter/PartParameter.php b/src/de/RaumZeitLabor/PartDB2/PartParameter/PartParameter.php @@ -13,7 +13,7 @@ use de\RaumZeitLabor\PartDB2\PartDB2, * This object represents a parameter. Each parameter can have an unit (defined by the class "Unit") associated with * a numeric value. * - * @Entity + * @Entity @HasLifecycleCallbacks **/ class PartParameter { /** @@ -53,13 +53,30 @@ class PartParameter { private $unit; /** - * The value of the unit. + * The value of the unit. Together with the prefix, it becomes the actual value. + * + * Example: If you have 10µ, the value field will contain "10", the prefix object is linked to the SiPrefix + * representing "µ" and the rawValue field will contain 0.000001 * @Column(type="float") * @var float */ private $value; /** + * The SiPrefix of the unit + * @ManyToOne(targetEntity="de\RaumZeitLabor\PartDB2\SiPrefix\SiPrefix") + * @var object + */ + private $siPrefix; + + /** + * The raw value of the unit. + * @Column(type="float") + * @var float + */ + private $rawValue; + + /** * Sets the name for this parameter * @param string $name The name */ @@ -128,7 +145,9 @@ class PartParameter { * @param float $value The value to set */ public function setValue ($value) { - $this->value = $value; + $this->value = $value; + + $this->recalculateRawValue(); } /** @@ -140,6 +159,24 @@ class PartParameter { } /** + * Sets the si prefix for this parameter + * @param SiPrefix $prefix The prefix to set, or null + */ + public function setSiPrefix (SiPrefix $prefix = null) { + $this->siPrefix = $prefix; + + $this->recalculateRawValue(); + } + + /** + * Returns the si prefix for this parameter + * @return SiPrefix the si prefix or null + */ + public function getSiPrefix () { + return $this->siPrefix; + } + + /** * Returns the ID for this object. * @param none * @return int The ID for this object @@ -148,6 +185,16 @@ class PartParameter { return $this->id; } + private function recalculateRawValue () { + if (is_object($this->getSiPrefix())) { + $power = $this->getSiPrefix()->getPower(); + } else { + $power = 0; + } + + $this->rawValue = $this->getValue() * pow(10, $power); + } + /** * Returns the data of this object in a serialized form. * @return array The result array @@ -159,8 +206,15 @@ class PartParameter { "description" => $this->getDescription(), "value" => $this->getValue(), "part_id" => $this->getPart()->getId(), + "siprefix_id" => is_object($this->getSiPrefix()) ? $this->getSiPrefix()->getId() : null, + "prefixedValue" => array( + /* We duplicate most data because of strange ExtJS stuff... */ + "value" => $this->getValue(), + "power" => is_object($this->getSiPrefix()) ? $this->getSiPrefix()->getPower() : 0, + "symbol" => is_object($this->getSiPrefix()) ? $this->getSiPrefix()->getSymbol() : "", + "siprefix_id" => is_object($this->getSiPrefix()) ? $this->getSiPrefix()->getId() : null + ), "unit_id" => is_object($this->getUnit()) ? $this->getUnit()->getId() : null - ); } } diff --git a/src/de/RaumZeitLabor/PartDB2/Unit/Unit.php b/src/de/RaumZeitLabor/PartDB2/Unit/Unit.php @@ -100,4 +100,12 @@ class Unit { public function getId () { return $this->id; } + + /** + * Loads a unit by ID + * @param int $id The ID to load + */ + public static function loadById ($id) { + return PartDB2::getEM()->find(get_called_class(), $id); + } } \ No newline at end of file diff --git a/testing/SetupDatabase.php b/testing/SetupDatabase.php @@ -286,13 +286,16 @@ while ($part = mysql_fetch_assoc($r)) { for ($i=0;$i<rand(1,15);$i++) { - $val = rand(0,999) * (pow(10,(rand(-20,20)))); + $val = rand(0,999); + $prefix = $aSiPrefixes[array_rand($aSiPrefixes)]; + $oPartParameter = new PartParameter(); $oPartParameter->setName($aRandomUnitNames[array_rand($aRandomUnitNames)]); $oPartParameter->setDescription("Testbeschreibung"); $oPartParameter->setPart($oPart); $oPartParameter->setUnit($aUnits[array_rand($aUnits)]); $oPartParameter->setValue($val); + $oPartParameter->setSiPrefix($prefix); PartDB2::getEM()->persist($oPartParameter); }