partkeepr

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

commit 8f33e1362e207620185066968a2ff2a1f0b8e0b6
parent 115f4745e96aa1e7ee93e8298d48c2c50cf7555f
Author: Felicitus <felicitus@felicitus.org>
Date:   Sun,  2 Aug 2015 14:32:56 +0200

Migrated part details panel

Diffstat:
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartDisplay.js | 443+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsManager.js | 627++++++++++++++++++++++++++++++++++++++++++-------------------------------------
2 files changed, 559 insertions(+), 511 deletions(-)

diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartDisplay.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartDisplay.js @@ -3,228 +3,241 @@ * <p>This component displays information about a specific part.</p> */ Ext.define('PartKeepr.PartDisplay', { - extend: 'Ext.panel.Panel', - bodyCls: 'partdisplay', - - autoScroll: true, - - /** - * Initializes the component and adds a template as well as the add/remove stock and edit part buttons. - */ - initComponent: function () { - /** - * Create the template - */ - this.tpl = new Ext.XTemplate( - '<h1>{name}</h1>', - '<h2>{description}</h2>', - '<table>', - '<tr>', - '<td class="o">'+i18n("Category")+':</td>', - '<td style="width: 100%;" class="o">{categoryName}</td>', - '</tr>', - '<tr>', - '<td class="e">'+i18n("Stock Level")+':</td>', - '<td class="e">{stockLevel}</td>', - '</tr>', - '<tr>', - '<td class="o">'+i18n("Minimum Stock Level")+':</td>', - '<td class="o">{minStockLevel}</td>', - '</tr>', - '<tr>', - '<td class="e">'+i18n("Footprint")+':</td>', - '<td class="e">{footprintName}</td>', - '</tr>', - '<tr>', - '<td style="white-space: nowrap;" class="o">'+i18n("Storage Location")+':</td>', - '<td class="o">{storageLocationName}</td>', - '</tr>', - '<tr>', - '<td class="e">'+i18n("Comment")+':</td>', - '<td class="e">{comment}</td>', - '</tr>', - '<tr>', - '<td class="o">'+i18n("Create Date")+':</td>', - '<td class="o">{createDate}</td>', - '</tr>', - '<tr>', - '<td class="e">'+i18n("Status")+':</td>', - '<td class="e">{status}</td>', - '</tr>', - '<tr>', - '<td class="o">'+i18n("Condition")+':</td>', - '<td class="o">{partCondition}</td>', - '</tr>', - '<tr>', - '<td class="e">'+i18n("Needs Review")+':</td>', - '<td class="e">{needsReview}</td>', - '</tr>', - '<tr>', - '<td class="o">'+i18n("Internal ID")+':</td>', - '<td class="o">{id}</td>', - '</tr>', - '<tr>', - '<td class="e">'+i18n("Used in projects")+':</td>', - '<td class="e">{[ (values.projects == "") ? "'+i18n("none")+'" : values.projects ]}</td>', - '</tr>', - '<tr>', - '<td class="o">'+i18n("Attachments")+':</td>', - '<td class="o">' + - '<tpl for="values.processedAttachments">' + - '<p><a href="{link}" target="_blank">{data.originalFilename}</a></p>' + - '</tpl>' + - '</td>' + - '</tr>', - '</table>'); - - /** - * Create the "add stock" button - */ - this.addButton = new Ext.Button({ - text: i18n("Add Stock"), - icon: 'resources/silkicons/brick_add.png', - handler: Ext.bind(this.addPartPrompt, this) - }); - - /** - * Create the "remove stock" button - */ - this.deleteButton = new Ext.Button({ - text: i18n("Remove Stock"), - icon: 'resources/silkicons/brick_delete.png', - handler: Ext.bind(this.deletePartPrompt, this) - }); - - /** - * Create the "edit part" button - */ - this.editButton = new Ext.Button({ - text: i18n("Edit Part"), - icon: 'resources/silkicons/brick_edit.png', - handler: Ext.bind(function () { this.fireEvent("editPart", this.record.get("id"));}, this) - }); - - /** - * Create the toolbar which holds our buttons - */ - this.tbar = Ext.create("Ext.toolbar.Toolbar", { - enableOverflow: true, - items: [ - this.addButton, - this.deleteButton, - this.editButton - ] - }); - - /** - * Add the event "editPart". This event is fired as soon as the "edit" button is clicked. - * - * @todo Add the events "addStock" and "removeStock" and manage these events from the PartManager. - */ - - this.imageDisplay = Ext.create("PartKeepr.PartImageDisplay"); - this.infoContainer = Ext.create("Ext.container.Container"); - - this.items = [ this.infoContainer, this.imageDisplay ]; - this.callParent(); - }, - /** - * Sets the values for the template. - * - * Note that the data of the record is applied with htmlentities(), i.e. <b>Test</b> will be - * displayed as such and not in bold. - */ - setValues: function (r) { - this.record = r; - - var values = {}; + extend: 'Ext.panel.Panel', + bodyCls: 'partdisplay', + + autoScroll: true, + + /** + * Initializes the component and adds a template as well as the add/remove stock and edit part buttons. + */ + initComponent: function () + { + /** + * Create the template + */ + this.tpl = new Ext.XTemplate( + '<h1>{name}</h1>', + '<h2>{description}</h2>', + '<table>', + '<tr>', + '<td class="o">' + i18n("Category") + ':</td>', + '<td style="width: 100%;" class="o">{categoryName}</td>', + '</tr>', + '<tr>', + '<td class="e">' + i18n("Stock Level") + ':</td>', + '<td class="e">{stockLevel}</td>', + '</tr>', + '<tr>', + '<td class="o">' + i18n("Minimum Stock Level") + ':</td>', + '<td class="o">{minStockLevel}</td>', + '</tr>', + '<tr>', + '<td class="e">' + i18n("Footprint") + ':</td>', + '<td class="e">{footprintName}</td>', + '</tr>', + '<tr>', + '<td style="white-space: nowrap;" class="o">' + i18n("Storage Location") + ':</td>', + '<td class="o">{storageLocationName}</td>', + '</tr>', + '<tr>', + '<td class="e">' + i18n("Comment") + ':</td>', + '<td class="e">{comment}</td>', + '</tr>', + '<tr>', + '<td class="o">' + i18n("Create Date") + ':</td>', + '<td class="o">{createDate}</td>', + '</tr>', + '<tr>', + '<td class="e">' + i18n("Status") + ':</td>', + '<td class="e">{status}</td>', + '</tr>', + '<tr>', + '<td class="o">' + i18n("Condition") + ':</td>', + '<td class="o">{partCondition}</td>', + '</tr>', + '<tr>', + '<td class="e">' + i18n("Needs Review") + ':</td>', + '<td class="e">{needsReview}</td>', + '</tr>', + '<tr>', + '<td class="o">' + i18n("Internal ID") + ':</td>', + '<td class="o">{id}</td>', + '</tr>', + '<tr>', + '<td class="e">' + i18n("Used in projects") + ':</td>', + '<td class="e">{[ (values.projects == "") ? "' + i18n("none") + '" : values.projects ]}</td>', + '</tr>', + '<tr>', + '<td class="o">' + i18n("Attachments") + ':</td>', + '<td class="o">' + + '<tpl for="values.processedAttachments">' + + '<p><a href="{link}" target="_blank">{data.originalFilename}</a></p>' + + '</tpl>' + + '</td>' + + '</tr>', + '</table>'); + + /** + * Create the "add stock" button + */ + this.addButton = new Ext.Button({ + text: i18n("Add Stock"), + icon: 'resources/silkicons/brick_add.png', + handler: Ext.bind(this.addPartPrompt, this) + }); + + /** + * Create the "remove stock" button + */ + this.deleteButton = new Ext.Button({ + text: i18n("Remove Stock"), + icon: 'resources/silkicons/brick_delete.png', + handler: Ext.bind(this.deletePartPrompt, this) + }); + + /** + * Create the "edit part" button + */ + this.editButton = new Ext.Button({ + text: i18n("Edit Part"), + icon: 'resources/silkicons/brick_edit.png', + handler: Ext.bind(function () + { + this.fireEvent("editPart", this.record.get("id")); + }, this) + }); + + /** + * Create the toolbar which holds our buttons + */ + this.tbar = Ext.create("Ext.toolbar.Toolbar", { + enableOverflow: true, + items: [ + this.addButton, + this.deleteButton, + this.editButton + ] + }); + + /** + * Add the event "editPart". This event is fired as soon as the "edit" button is clicked. + * + * @todo Add the events "addStock" and "removeStock" and manage these events from the PartManager. + */ + + this.imageDisplay = Ext.create("PartKeepr.PartImageDisplay"); + this.infoContainer = Ext.create("Ext.container.Container"); + + this.items = [this.infoContainer, this.imageDisplay]; + this.callParent(); + }, + /** + * Sets the values for the template. + * + * Note that the data of the record is applied with htmlentities(), i.e. <b>Test</b> will be + * displayed as such and not in bold. + */ + setValues: function (r) + { + this.record = r; + + var values = {}; var i; - for (i in r.data) { - if (r.data[i] !== null) { - values[i] = htmlentities(r.data[i]); - } else { - values[i] = r.data[i]; - } - } + var recordData = this.record.getData(); + + for (i in recordData) { + if (recordData[i] !== null) { + values[i] = htmlentities(recordData[i]); + } else { + values[i] = recordData[i]; + } + } values.processedAttachments = this.record.attachments().data; - for (i=0;i<values.processedAttachments.getCount();i++) { + for (i = 0; i < values.processedAttachments.getCount(); i++) { var data = values.processedAttachments.getAt(i); - data.link = "file.php?type=PartAttachment&id="+data.internalId; + data.link = "file.php?type=PartAttachment&id=" + data.internalId; } - this.tpl.overwrite(this.infoContainer.getEl(), values); - this.imageDisplay.setStore(this.record.attachments()); - - this.doLayout(); - // Scroll the container to top in case the user scrolled the part, then switched to another part - this.getTargetEl().scrollTo("top", 0); - - }, - /** - * Prompt the user for the stock level he wishes to add. - */ - addPartPrompt: function () { - var j = new PartKeepr.PartStockWindow({ partUnitName: this.record.get("partUnitName") }); - j.addStock(this.addPartHandler, this); - }, - /** - * Callback after the "add stock" dialog is complete. - */ - addPartHandler: function (quantity, price, comment) { - var call = new PartKeepr.ServiceCall( - "Part", - "addStock"); - call.setParameter("stock", quantity); - call.setParameter("price", price); - call.setParameter("comment", comment); - call.setParameter("part", this.record.get("id")); - call.setHandler(Ext.bind(this.reloadPart, this)); - call.doCall(); - }, - /** - * Prompts the user for the stock level to decrease for the item. - */ - deletePartPrompt: function () { - var j = new PartKeepr.PartStockWindow({ partUnitName: this.record.get("partUnitName") }); - j.removeStock(this.deletePartHandler, this); - }, - /** - * Callback after the "delete stock" dialog is complete. - */ - deletePartHandler: function (quantity) { - var call = new PartKeepr.ServiceCall( - "Part", - "deleteStock"); - call.setParameter("stock", quantity); - call.setParameter("part", this.record.get("id")); - call.setHandler(Ext.bind(this.reloadPart, this)); - call.doCall(); - }, - /** - * Reloads the current part - */ - reloadPart: function () { - this.loadPart(this.record.get("id")); - }, - /** - * Load the part from the database. - */ - loadPart: function (id) { - PartKeepr.Part.load(id, { - scope: this, - success: this.onPartLoaded - }); - }, - /** - * Callback after the part is loaded - */ - onPartLoaded: function (record) { - this.record = record; - this.setValues(this.record); - this.record.commit(); - } + this.tpl.overwrite(this.infoContainer.getEl(), values); + this.imageDisplay.setStore(this.record.attachments()); + + // Scroll the container to top in case the user scrolled the part, then switched to another part + this.getTargetEl().scrollTo("top", 0); + + }, + /** + * Prompt the user for the stock level he wishes to add. + */ + addPartPrompt: function () + { + var j = new PartKeepr.PartStockWindow({partUnitName: this.record.get("partUnitName")}); + j.addStock(this.addPartHandler, this); + }, + /** + * Callback after the "add stock" dialog is complete. + */ + addPartHandler: function (quantity, price, comment) + { + var call = new PartKeepr.ServiceCall( + "Part", + "addStock"); + call.setParameter("stock", quantity); + call.setParameter("price", price); + call.setParameter("comment", comment); + call.setParameter("part", this.record.get("id")); + call.setHandler(Ext.bind(this.reloadPart, this)); + call.doCall(); + }, + /** + * Prompts the user for the stock level to decrease for the item. + */ + deletePartPrompt: function () + { + var j = new PartKeepr.PartStockWindow({partUnitName: this.record.get("partUnitName")}); + j.removeStock(this.deletePartHandler, this); + }, + /** + * Callback after the "delete stock" dialog is complete. + */ + deletePartHandler: function (quantity) + { + var call = new PartKeepr.ServiceCall( + "Part", + "deleteStock"); + call.setParameter("stock", quantity); + call.setParameter("part", this.record.get("id")); + call.setHandler(Ext.bind(this.reloadPart, this)); + call.doCall(); + }, + /** + * Reloads the current part + */ + reloadPart: function () + { + this.loadPart(this.record.get("id")); + }, + /** + * Load the part from the database. + */ + loadPart: function (id) + { + PartKeepr.Part.load(id, { + scope: this, + success: this.onPartLoaded + }); + }, + /** + * Callback after the part is loaded + */ + onPartLoaded: function (record) + { + this.record = record; + this.setValues(this.record); + this.record.commit(); + } }); diff --git a/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsManager.js b/src/PartKeepr/FrontendBundle/Resources/public/js/Components/Part/PartsManager.js @@ -1,16 +1,16 @@ /** * @class PartKeepr.PartManager - * @todo Document the editor system a bit better - * + * @todo Document the editor system a bit better + * * The part manager encapsulates the category tree, the part display grid and the part detail view. */ Ext.define('PartKeepr.PartManager', { - extend: 'Ext.panel.Panel', - alias: 'widget.PartManager', - layout: 'border', - id: 'partkeepr-partmanager', - border: false, - padding: 5, + extend: 'Ext.panel.Panel', + alias: 'widget.PartManager', + layout: 'border', + id: 'partkeepr-partmanager', + border: false, + padding: 5, /** * Defines if the border layout should be compact or regular. @@ -22,19 +22,22 @@ Ext.define('PartKeepr.PartManager', { */ compactLayout: false, - initComponent: function () { - - /** - * Create the store with the default sorter "name ASC" - */ - this.createStore({ - model: 'PartKeepr.PartBundle.Entity.Part', - groupField: 'categoryPath', - sorters: [{ - property: 'name', - direction:'ASC' - }] - }); + initComponent: function () + { + + /** + * Create the store with the default sorter "name ASC" + */ + this.createStore({ + model: 'PartKeepr.PartBundle.Entity.Part', + groupField: 'categoryPath', + sorters: [ + { + property: 'name', + direction: 'ASC' + } + ] + }); var treeConfig = { region: 'west', @@ -53,34 +56,38 @@ Ext.define('PartKeepr.PartManager', { treeConfig.collapsible = true; // We want to collapse the tree panel on small screens } - // Create the tree - this.tree = Ext.create("PartKeepr.PartCategoryTree", treeConfig); - - // Trigger a grid reload on category change - this.tree.on("itemclick", this.onCategoryClick, this); - - // Create the detail panel - this.detail = Ext.create("PartKeepr.PartDisplay", { title: i18n("Part Details") }); - this.detail.on("editPart", this.onEditPart, this); - - // Create the grid - this.grid = Ext.create("PartKeepr.PartsGrid", { title: i18n("Parts List"), region: 'center', layout: 'fit', store: this.getStore()}); - this.grid.on("editPart", this.onEditPart, this); - - // Create the grid listeners - this.grid.on("itemSelect", this.onItemSelect, this); - this.grid.on("itemDeselect", this.onItemSelect, this); - this.grid.on("itemAdd", this.onItemAdd, this); - this.grid.on("itemDelete", this.onItemDelete, this); - this.grid.on("duplicateItemWithBasicData", this.onDuplicateItemWithBasicData, this); - this.grid.on("duplicateItemWithAllData", this.onDuplicateItemWithAllData, this); - this.tree.on("syncCategory", this.onSyncCategory, this); - - // Listen on the partChanged event, which is fired when the users edits the part - this.detail.on("partChanged", function () { this.grid.getStore().load(); }, this); - - // Create the stock level panel - this.stockLevel = Ext.create("PartKeepr.PartStockHistory", { title: "Stock History"}); + // Create the tree + this.tree = Ext.create("PartKeepr.PartCategoryTree", treeConfig); + + // Trigger a grid reload on category change + this.tree.on("itemclick", this.onCategoryClick, this); + + // Create the detail panel + this.detail = Ext.create("PartKeepr.PartDisplay", {title: i18n("Part Details")}); + this.detail.on("editPart", this.onEditPart, this); + + // Create the grid + this.grid = Ext.create("PartKeepr.PartsGrid", + {title: i18n("Parts List"), region: 'center', layout: 'fit', store: this.getStore()}); + this.grid.on("editPart", this.onEditPart, this); + + // Create the grid listeners + this.grid.on("itemSelect", this.onItemSelect, this); + this.grid.on("itemDeselect", this.onItemSelect, this); + this.grid.on("itemAdd", this.onItemAdd, this); + this.grid.on("itemDelete", this.onItemDelete, this); + this.grid.on("duplicateItemWithBasicData", this.onDuplicateItemWithBasicData, this); + this.grid.on("duplicateItemWithAllData", this.onDuplicateItemWithAllData, this); + this.tree.on("syncCategory", this.onSyncCategory, this); + + // Listen on the partChanged event, which is fired when the users edits the part + this.detail.on("partChanged", function () + { + this.grid.getStore().load(); + }, this); + + // Create the stock level panel + this.stockLevel = Ext.create("PartKeepr.PartStockHistory", {title: "Stock History"}); var detailPanelConfig = { title: i18n("Part Details"), @@ -91,7 +98,7 @@ Ext.define('PartKeepr.PartManager', { titleCollapse: true, split: true, animCollapse: false, - items: [ this.detail, this.stockLevel ] + items: [this.detail, this.stockLevel] }; if (this.compactLayout) { @@ -101,55 +108,59 @@ Ext.define('PartKeepr.PartManager', { detailPanelConfig.width = 300; } - this.detailPanel = Ext.create("Ext.tab.Panel", detailPanelConfig); - - this.filterPanel = Ext.create("PartKeepr.PartFilterPanel", { - title: i18n("Filter"), - region: 'south', - height: 225, - animCollapse: false, - floatable: false, - titleCollapse: true, - split: true, - collapsed: true, - collapsible: true, - store: this.store - }); + this.detailPanel = Ext.create("Ext.tab.Panel", detailPanelConfig); + + this.filterPanel = Ext.create("PartKeepr.PartFilterPanel", { + title: i18n("Filter"), + region: 'south', + height: 225, + animCollapse: false, + floatable: false, + titleCollapse: true, + split: true, + collapsed: true, + collapsible: true, + store: this.store + }); if (this.compactLayout) { // Create two border layouts: One for the center panel and one for the left panel. Each border layout // has two columns each, containing Categories+Part Details and Part List+Part Filter Panel. - this.items = [{ - layout: 'border', - border: false, - region: 'west', - animCollapse: false, - width: 300, - split: true, - title: i18n("Categories / Part Details"), - titleCollapse: true, - collapsed: false, - collapsible: true, - items: [ this.tree, this.detailPanel ] - }, { - layout: 'border', - border: false, - region: 'center', - items: [ this.grid, this.filterPanel ] - } ]; + this.items = [ + { + layout: 'border', + border: false, + region: 'west', + animCollapse: false, + width: 300, + split: true, + title: i18n("Categories / Part Details"), + titleCollapse: true, + collapsed: false, + collapsible: true, + items: [this.tree, this.detailPanel] + }, { + layout: 'border', + border: false, + region: 'center', + items: [this.grid, this.filterPanel] + } + ]; } else { // The regular 3-column layout. The tree, then the part list+part filter, then the part details. - this.items = [ this.tree, { - layout: 'border', - border: false, - region: 'center', - items: [ this.grid, this.filterPanel ] - }, this.detailPanel ]; + this.items = [ + this.tree, { + layout: 'border', + border: false, + region: 'center', + items: [this.grid, this.filterPanel] + }, this.detailPanel + ]; } - this.callParent(); - }, - /** + this.callParent(); + }, + /** * Applies the category filter to the store when a category is selected * * @param {Ext.tree.View} tree The tree view @@ -165,7 +176,7 @@ Ext.define('PartKeepr.PartManager', { this.store.addFilter(filter); }, - /** + /** * Returns the ID for this node and all child nodes * * @param {Ext.data.Model} The node @@ -183,233 +194,257 @@ Ext.define('PartKeepr.PartManager', { return childNodes; }, - /** - * Called when the sync button was clicked. Highlights the category - * of the selected part for a short time. We can't select the category - * as this would affect the parts grid. - */ - onSyncCategory: function () { - var r = this.grid.getSelectionModel().getLastSelected(); - - var rootNode = this.tree.getRootNode(); - var cat = r.get("category"); - - var node = rootNode.findChild("id", cat, true); - - this.tree.getView().ensureVisible(node); - this.tree.getView().scrollIntoView(node); - - var htmlNode = new Ext.Element(this.tree.getView().getNode(node)); - - htmlNode.first().highlight("2aaad3"); - }, - /** + /** + * Called when the sync button was clicked. Highlights the category + * of the selected part for a short time. We can't select the category + * as this would affect the parts grid. + */ + onSyncCategory: function () + { + var r = this.grid.getSelectionModel().getLastSelected(); + + var rootNode = this.tree.getRootNode(); + var cat = r.get("category"); + + var node = rootNode.findChild("id", cat, true); + + this.tree.getView().ensureVisible(node); + this.tree.getView().scrollIntoView(node); + + var htmlNode = new Ext.Element(this.tree.getView().getNode(node)); + + htmlNode.first().highlight("2aaad3"); + }, + /** * Called when the delete button was clicked. - * + * * Prompts the user if he really wishes to delete the part. If yes, it calls deletePart. */ - onItemDelete: function () { - var r = this.grid.getSelectionModel().getLastSelected(); - - Ext.Msg.confirm(i18n("Delete Part"), sprintf(i18n("Do you really wish to delete the part %s?"),r.get("name")), this.deletePart, this); - }, - /** - * Creates a duplicate with the basic data only from the selected item. Loads the selected part and calls - * createPartDuplicate after the part was loaded. - * - * @param none - * @return nothing - */ - onDuplicateItemWithBasicData: function () { - var r = this.grid.getSelectionModel().getLastSelected(); - - this.loadPart(r.get("id"), Ext.bind(this.createPartDuplicate, this)); - }, - /** - * Creates a full duplicate from the selected item. Loads the selected part and calls createPartDuplicate - * after the part was loaded. - * - * @param none - * @return nothing - */ - onDuplicateItemWithAllData: function () { - var r = this.grid.getSelectionModel().getLastSelected(); - - this.loadPart(r.get("id"), Ext.bind(this.createFullPartDuplicate, this)); - }, - /** - * Creates a part duplicate from the given record and opens the editor window. - * @param rec The record to duplicate - */ - createPartDuplicate: function (rec) { - var copy = rec.copy(); - Ext.data.Model.id(copy); - copy.set("id", null); - - var j = Ext.create("PartKeepr.PartEditorWindow", { - partMode: 'create' - }); - - j.editor.on("partSaved", this.onPartSaved, this); - j.editor.editItem(copy); - j.show(); - }, - /** - * Creates a part duplicate from the given record and opens the editor window. - * @param rec The record to duplicate - */ - createFullPartDuplicate: function (rec) { - var data = rec.getData(true); - data.id = null; - newItem = Ext.create("PartKeepr.PartBundle.Entity.Part"); - newItem.setDataWithAssociations(data); - - var j = Ext.create("PartKeepr.PartEditorWindow", { - partMode: 'create' - }); - - j.editor.on("partSaved", this.onPartSaved, this); - j.editor.editItem(newItem); - j.show(); - }, - /** + onItemDelete: function () + { + var r = this.grid.getSelectionModel().getLastSelected(); + + Ext.Msg.confirm(i18n("Delete Part"), sprintf(i18n("Do you really wish to delete the part %s?"), r.get("name")), + this.deletePart, this); + }, + /** + * Creates a duplicate with the basic data only from the selected item. Loads the selected part and calls + * createPartDuplicate after the part was loaded. + * + * @param none + * @return nothing + */ + onDuplicateItemWithBasicData: function () + { + var r = this.grid.getSelectionModel().getLastSelected(); + + this.loadPart(r.get("id"), Ext.bind(this.createPartDuplicate, this)); + }, + /** + * Creates a full duplicate from the selected item. Loads the selected part and calls createPartDuplicate + * after the part was loaded. + * + * @param none + * @return nothing + */ + onDuplicateItemWithAllData: function () + { + var r = this.grid.getSelectionModel().getLastSelected(); + + this.loadPart(r.get("id"), Ext.bind(this.createFullPartDuplicate, this)); + }, + /** + * Creates a part duplicate from the given record and opens the editor window. + * @param rec The record to duplicate + */ + createPartDuplicate: function (rec) + { + var copy = rec.copy(); + Ext.data.Model.id(copy); + copy.set("id", null); + + var j = Ext.create("PartKeepr.PartEditorWindow", { + partMode: 'create' + }); + + j.editor.on("partSaved", this.onPartSaved, this); + j.editor.editItem(copy); + j.show(); + }, + /** + * Creates a part duplicate from the given record and opens the editor window. + * @param rec The record to duplicate + */ + createFullPartDuplicate: function (rec) + { + var data = rec.getData(true); + data.id = null; + newItem = Ext.create("PartKeepr.PartBundle.Entity.Part"); + newItem.setDataWithAssociations(data); + + var j = Ext.create("PartKeepr.PartEditorWindow", { + partMode: 'create' + }); + + j.editor.on("partSaved", this.onPartSaved, this); + j.editor.editItem(newItem); + j.show(); + }, + /** * Deletes the selected part. - * + * * @param {String} btn The clicked button in the message box window. * @todo We use the current selection of the grid. If for some reason the selection changes during the user is prompted, * we delete the wrong part. Fix that to pass the selected item to the onItemDelete then to this function. */ - deletePart: function (btn) { - var r = this.grid.getSelectionModel().getLastSelected(); - - if (btn == "yes") { - var call = new PartKeepr.ServiceCall( - "Part", - "deletePart"); - - call.setLoadMessage(sprintf(i18n("Deleting part %s"), r.get("name"))); - call.setParameter("part", r.get("id")); - call.setHandler(Ext.bind(function () { - this.store.load(); - }, this)); - call.doCall(); - } - }, - /** + deletePart: function (btn) + { + var r = this.grid.getSelectionModel().getLastSelected(); + + if (btn == "yes") { + var call = new PartKeepr.ServiceCall( + "Part", + "deletePart"); + + call.setLoadMessage(sprintf(i18n("Deleting part %s"), r.get("name"))); + call.setParameter("part", r.get("id")); + call.setHandler(Ext.bind(function () + { + this.store.load(); + }, this)); + call.doCall(); + } + }, + /** * Creates a new, empty part editor window */ - onItemAdd: function () { - var j = Ext.create("PartKeepr.PartEditorWindow", { - partMode: 'create' - }); - - var defaults = {}; - - var defaultPartUnit = PartKeepr.getApplication().getPartUnitStore().findRecord("default", true); - - defaults.partUnit = defaultPartUnit.get("id"); - defaults.category = this.grid.currentCategory; - - record = Ext.create("PartKeepr.Part", defaults); - - // Inject the defaults to the editor, so the editor can create a new item on its own - j.editor.partDefaults = defaults; - - j.editor.editItem(record); - j.show(); - - return j; - }, - /** + onItemAdd: function () + { + var j = Ext.create("PartKeepr.PartEditorWindow", { + partMode: 'create' + }); + + var defaults = {}; + + var defaultPartUnit = PartKeepr.getApplication().getPartUnitStore().findRecord("default", true); + + defaults.partUnit = defaultPartUnit.get("id"); + defaults.category = this.grid.currentCategory; + + record = Ext.create("PartKeepr.Part", defaults); + + // Inject the defaults to the editor, so the editor can create a new item on its own + j.editor.partDefaults = defaults; + + j.editor.editItem(record); + j.show(); + + return j; + }, + /** * Called when a part was edited. Refreshes the grid. */ - onEditPart: function (id) { - this.loadPart(id, Ext.bind(this.onPartLoaded, this)); - }, - /** + onEditPart: function (id) + { + this.loadPart(id, Ext.bind(this.onPartLoaded, this)); + }, + /** * Called when a part was loaded. Displays the part in the editor window. */ - onPartLoaded: function (f,g) { - var j = Ext.create("PartKeepr.PartEditorWindow"); - j.editor.on("partSaved", this.onPartSaved, this); - j.editor.editItem(f); - j.show(); - }, - onPartSaved: function (record) { - - var idx = this.grid.store.find("id", record.get("id")); - - // Only reload the grid if the edited record is contained - if (idx !== -1) { - this.grid.store.load(); - } - - this.detail.setValues(record); - }, - /** + onPartLoaded: function (f, g) + { + var j = Ext.create("PartKeepr.PartEditorWindow"); + j.editor.on("partSaved", this.onPartSaved, this); + j.editor.editItem(f); + j.show(); + }, + onPartSaved: function (record) + { + + var idx = this.grid.store.find("id", record.get("id")); + + // Only reload the grid if the edited record is contained + if (idx !== -1) { + this.grid.store.load(); + } + + this.detail.setValues(record); + }, + /** * Called when a part was selected in the grid. Displays the details for this part. */ - onItemSelect: function () { - if (this.grid.getSelectionModel().getCount() > 1) { + onItemSelect: function () + { + if (this.grid.getSelection().length > 1) { this.detailPanel.collapse(); this.tree.syncButton.disable(); - } else if (this.grid.getSelectionModel().getCount() == 1) { - var r = this.grid.getSelectionModel().getLastSelected(); - - this.detailPanel.setActiveTab(this.detail); - this.detailPanel.expand(); - this.detail.setValues(r); - this.stockLevel.part = r.get("id"); - - this.tree.syncButton.enable(); } else { - this.tree.syncButton.disable(); + if (this.grid.getSelection().length == 1) { + var selection = this.grid.getSelection(); + + var r = selection[0]; + + this.detailPanel.setActiveTab(this.detail); + this.detailPanel.expand(); + this.detail.setValues(r); + this.stockLevel.part = r.get("id"); + + this.tree.syncButton.enable(); + } else { + this.tree.syncButton.disable(); + } } - - }, - /** - * Triggers loading of a part + + }, + /** + * Triggers loading of a part * @param {Integer} id The ID of the part to load * @param {Function} handler The callback to call when the part was loaded */ - loadPart: function (id, handler) { - // @todo we have this method duplicated in PartEditor - var model = Ext.ModelManager.getModel("PartKeepr.PartBundle.Entity.Part"); - - model.load(id, { - scope: this, - success: handler - }); - }, - /** - * Creates the store + loadPart: function (id, handler) + { + // @todo we have this method duplicated in PartEditor + var model = Ext.ModelManager.getModel("PartKeepr.PartBundle.Entity.Part"); + + model.load(id, { + scope: this, + success: handler + }); + }, + /** + * Creates the store */ - createStore: function (config) { - Ext.Object.merge(config, { - autoLoad: true, - autoSync: false, // Do not change. If true, new (empty) records would be immediately commited to the database. - remoteFilter: true, - remoteSort: true, - pageSize: 50}); - - this.store = Ext.create('Ext.data.Store', config); - - // Workaround for bug http://www.sencha.com/forum/showthread.php?133767-Store.sync()-does-not-update-dirty-flag&p=607093#post607093 - this.store.on('write', function(store, operation) { - var success=operation.wasSuccessful(); - if (success) { - Ext.each(operation.records, function(record){ - if (record.dirty) { - record.commit(); - } - }); - } - }); - }, - /** - * Returns the store + createStore: function (config) + { + Ext.Object.merge(config, { + autoLoad: true, + autoSync: false, // Do not change. If true, new (empty) records would be immediately commited to the database. + remoteFilter: true, + remoteSort: true, + pageSize: 50 + }); + + this.store = Ext.create('Ext.data.Store', config); + + // Workaround for bug http://www.sencha.com/forum/showthread.php?133767-Store.sync()-does-not-update-dirty-flag&p=607093#post607093 + this.store.on('write', function (store, operation) + { + var success = operation.wasSuccessful(); + if (success) { + Ext.each(operation.records, function (record) + { + if (record.dirty) { + record.commit(); + } + }); + } + }); + }, + /** + * Returns the store */ - getStore: function () { - return this.store; - } + getStore: function () + { + return this.store; + } }); \ No newline at end of file