partkeepr

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

PartEditor.js (18975B)


      1 /**
      2  * @class PartKeepr.PartEditor
      3 
      4  * <p>The PartEditor provides an editing form for a part. It contains multiple tabs, one for each nested record.</p>
      5  */
      6 Ext.define('PartKeepr.PartEditor', {
      7     extend: 'PartKeepr.Editor',
      8 
      9     // Assigned model
     10     model: 'PartKeepr.PartBundle.Entity.Part',
     11     bodyPadding: '0 0 0 0',
     12     // Layout stuff
     13     border: false,
     14     layout: 'fit',
     15     editAfterSave: false,
     16 
     17     /**
     18      * Initializes the editor fields
     19      */
     20     initComponent: function ()
     21     {
     22         // Defines the overall height of all fields, used to calculate the anchoring for the description field
     23         var overallHeight = (this.partMode == "create") ? 320: 265;
     24 
     25         this.nameField = Ext.create("Ext.form.field.Text", {
     26             name: 'name',
     27             fieldLabel: i18n("Name"),
     28             allowBlank: false,
     29             labelWidth: 150
     30         });
     31 
     32         this.storageLocationComboBox = Ext.create("PartKeepr.StorageLocationPicker",
     33             {
     34                 fieldLabel: i18n("Storage Location"),
     35                 name: 'storageLocation',
     36                 allowBlank: false,
     37                 labelWidth: 150
     38             });
     39 
     40         this.footprintNone = Ext.create("Ext.form.field.Radio", {
     41             boxLabel: i18n("None"),
     42             name: 'footprint_mode',
     43             value: "unset",
     44             width: 70,
     45             listeners: {
     46                 scope: this,
     47                 change: function (field, newValue)
     48                 {
     49                     if (newValue === true) {
     50                         this.footprintComboBox.clearValue();
     51                     }
     52                 }
     53             }
     54         });
     55 
     56         this.footprintSet = Ext.create("Ext.form.field.Radio", {
     57             name: 'footprint_mode',
     58             width: 20,
     59             value: "set"
     60         });
     61 
     62         /*
     63          * Creates the footprint combo box. We listen for the "change" event, because we need to set the footprint
     64          * comboboxes to the right state, depending on the selection. Another way would be to patch the combobox
     65          * to support "null" values, however, this is a major change within ExtJS and probably not supported for
     66          * updates of ExtJS.
     67          */
     68         this.footprintComboBox = Ext.create("PartKeepr.FootprintComboBox", {
     69             name: 'footprint',
     70             returnObject: true,
     71             flex: 1,
     72             listeners: {
     73                 scope: this,
     74                 change: function (field, newValue)
     75                 {
     76                     if (typeof(newValue) === 'object') {
     77                         this.footprintSet.setValue(true);
     78                     }
     79                 }
     80             }
     81         });
     82 
     83         // Defines the basic editor fields
     84         var basicEditorFields = [
     85             this.nameField,
     86             {
     87                 xtype: 'textfield',
     88                 fieldLabel: i18n("Description"),
     89                 allowBlank: this.isOptional("description"),
     90                 name: 'description'
     91             }, {
     92                 layout: 'column',
     93                 xtype: 'fieldcontainer',
     94                 margin: {
     95                     bottom: "0 5px 5px 0"
     96                 },
     97                 border: false,
     98                 items: [
     99                     {
    100                         xtype: 'numberfield',
    101                         fieldLabel: i18n('Minimum Stock'),
    102                         allowDecimals: false,
    103                         allowBlank: false,
    104                         labelWidth: 150,
    105                         name: 'minStockLevel',
    106                         value: 0,
    107                         columnWidth: 0.5,
    108                         minValue: 0
    109                     }, {
    110                         padding: "0 0 0 5px",
    111                         xtype: 'PartUnitComboBox',
    112                         fieldLabel: i18n("Measurement Unit"),
    113                         labelWidth: 120,
    114                         columnWidth: 0.5,
    115                         returnObject: true,
    116                         name: 'partUnit'
    117                     }
    118                 ]
    119             }, {
    120                 xtype: 'CategoryComboBox',
    121                 fieldLabel: i18n("Category"),
    122                 name: 'category',
    123                 displayField: "name",
    124                 returnObject: true
    125             },
    126             this.storageLocationComboBox,
    127             {
    128                 xtype: 'fieldcontainer',
    129                 layout: 'hbox',
    130                 fieldLabel: i18n("Footprint"),
    131                 defaults: {
    132                     hideLabel: true
    133                 },
    134                 items: [
    135                     this.footprintNone,
    136                     this.footprintSet,
    137                     this.footprintComboBox
    138                 ]
    139             }, {
    140                 xtype: 'textarea',
    141                 fieldLabel: i18n("Comment"),
    142                 name: 'comment',
    143                 allowBlank: this.isOptional("comment"),
    144                 anchor: '100% ' + (-overallHeight).toString()
    145             },
    146             {
    147                 xtype: 'textfield',
    148                 fieldLabel: i18n("Production Remarks"),
    149                 name: 'productionRemarks',
    150                 allowBlank: this.isOptional("productionRemarks"),
    151             },{
    152                 xtype: 'fieldcontainer',
    153                 layout: 'hbox',
    154                 fieldLabel: i18n("Status"),
    155                 defaults: {
    156                     hideLabel: true
    157                 },
    158                 items: [
    159                     {
    160                         xtype: 'textfield',
    161                         fieldLabel: i18n("Status"),
    162                         flex: 1,
    163                         allowBlank: this.isOptional("status"),
    164                         name: 'status'
    165                     }, {
    166                         xtype: 'checkbox',
    167                         hideEmptyLabel: false,
    168                         fieldLabel: '',
    169                         boxLabel: i18n("Needs Review"),
    170                         name: 'needsReview'
    171                     }
    172                 ]
    173             }, {
    174                 xtype: 'textfield',
    175                 fieldLabel: i18n("Condition"),
    176                 name: 'partCondition',
    177                 allowBlank: this.isOptional("partCondition"),
    178             }, {
    179                 xtype: 'fieldcontainer',
    180                 layout: 'hbox',
    181                 items: [
    182                     {
    183                         xtype: 'textfield',
    184                         labelWidth: 150,
    185                         fieldLabel: i18n("Internal Part Number"),
    186                         name: 'internalPartNumber',
    187                         allowBlank: this.isOptional("internalPartNumber"),
    188                         flex: 1
    189                     }, {
    190                         xtype: 'displayfield',
    191                         qtip: i18n("The first number is the ID in decimal, the second number is the ID in base36"),
    192                         fieldLabel: i18n("Internal ID"),
    193                         listeners: {
    194                             render: function (c)
    195                             {
    196                                 Ext.QuickTips.register({
    197                                     target: c.getEl(),
    198                                     text: c.qtip
    199                                 });
    200                             }
    201                         },
    202                         itemId: 'idField',
    203                         name: '@id',
    204                         fieldStyle: {
    205                             color: "blue",
    206                             "text-decoration": "underline",
    207                         },
    208                         renderer: function (value)
    209                         {
    210                             var values = value.split("/");
    211                             var idstr = values[values.length - 1];
    212                             var idint = parseInt(idstr);
    213 
    214                             return idstr + " (#" + idint.toString(36) + ")";
    215                         }
    216                     }
    217                 ]
    218 
    219             }
    220         ];
    221 
    222         // Creates the distributor grid
    223         this.partDistributorGrid = Ext.create("PartKeepr.PartDistributorGrid", {
    224             title: i18n("Distributors"),
    225             iconCls: 'web-icon lorry',
    226             layout: 'fit'
    227         });
    228 
    229         // Creates the manufacturer grid
    230         this.partManufacturerGrid = Ext.create("PartKeepr.PartManufacturerGrid", {
    231             title: i18n("Manufacturers"),
    232             iconCls: 'fugue-icon building',
    233             layout: 'fit'
    234         });
    235 
    236         // Creates the attachment grid
    237         this.partParameterGrid = Ext.create("PartKeepr.PartParameterGrid", {
    238             title: i18n("Part Parameters"),
    239             iconCls: 'fugue-icon table',
    240             layout: 'fit'
    241         });
    242 
    243         // Creates the attachment grid
    244         this.partAttachmentGrid = Ext.create("PartKeepr.PartAttachmentGrid", {
    245             title: i18n("Attachments"),
    246             iconCls: 'web-icon attach',
    247             layout: 'fit'
    248         });
    249 
    250         // Adds stock level fields for new items
    251         if (this.partMode && this.partMode == "create") {
    252             this.initialStockLevel = Ext.create("Ext.form.field.Number", {
    253                 fieldLabel: i18n("Initial Stock Level"),
    254                 name: "initialStockLevel",
    255                 labelWidth: 150,
    256                 columnWidth: 0.5
    257             });
    258 
    259             this.initialStockLevelUser = Ext.create("PartKeepr.UserComboBox", {
    260                 fieldLabel: i18n("Stock User"),
    261                 name: 'initialStockLevelUser',
    262                 columnWidth: 0.5,
    263                 returnObject: true
    264             });
    265 
    266             basicEditorFields.push({
    267                 xtype: 'container',
    268                 layout: 'column',
    269                 border: false,
    270                 items: [
    271                     this.initialStockLevel,
    272                     this.initialStockLevelUser
    273                 ]
    274             });
    275 
    276             this.initialStockLevelPrice = Ext.create("PartKeepr.CurrencyField", {
    277                 fieldLabel: i18n('Price'),
    278                 labelWidth: 150,
    279                 columnWidth: 0.5,
    280                 name: 'initialStockLevelPrice'
    281             });
    282 
    283             this.initialStockLevelPricePerItem = Ext.create("Ext.form.field.Checkbox", {
    284                 boxLabel: i18n("Per Item"),
    285                 columnWidth: 0.5,
    286                 name: 'initialStockLevelPricePerItem'
    287             });
    288 
    289             basicEditorFields.push({
    290                 xtype: 'container',
    291                 layout: 'column',
    292                 border: false,
    293                 items: [
    294                     this.initialStockLevelPrice,
    295                     this.initialStockLevelPricePerItem
    296                 ]
    297             });
    298 
    299 
    300         }
    301 
    302         // Create a tab panel of all fields
    303         this.items = {
    304             xtype: 'tabpanel',
    305             border: false,
    306             items: [
    307                 {
    308                     iconCls: 'web-icon brick',
    309                     ui: 'default-framed',
    310                     xtype: 'panel',
    311                     autoScroll: false,
    312                     layout: 'anchor',
    313                     defaults: {
    314                         anchor: '100%',
    315                         labelWidth: 150
    316                     },
    317                     title: i18n("Basic Data"),
    318                     items: basicEditorFields
    319                 },
    320                 this.partDistributorGrid,
    321                 this.partManufacturerGrid,
    322                 this.partParameterGrid,
    323                 this.partAttachmentGrid
    324             ]
    325         };
    326 
    327         this.on("startEdit", this.onEditStart, this, {delay: 200});
    328         this.on("itemSaved", this._onItemSaved, this);
    329 
    330         this.callParent();
    331 
    332         this.on("itemSave", this.onItemSave, this);
    333         this.down("#idField").on("beforedestroy", this.onBeforeDestroy, this.down("#idField"));
    334 
    335     },
    336     /**
    337      * Unregisters the quick tip immediately prior destroying
    338      */
    339     onBeforeDestroy: function (field) {
    340         Ext.QuickTips.unregister(field.getEl());
    341     },
    342     /**
    343      * Cleans up the record prior saving.
    344      */
    345     onItemSave: function ()
    346     {
    347         var removeRecords = [], j, errors = [],
    348             minDistributorCount = PartKeepr.getApplication().getSystemPreference(
    349                 "partkeepr.part.constraints.distributorCount", 0),
    350             minManufacturerCount = PartKeepr.getApplication().getSystemPreference(
    351                 "partkeepr.part.constraints.manufacturerCount", 0),
    352             minAttachmentCount = PartKeepr.getApplication().getSystemPreference(
    353                 "partkeepr.part.constraints.attachmentCount", 0);
    354 
    355         /**
    356          * Iterate through all records and check if a valid distributor
    357          * ID is assigned. If not, the record is removed as it is assumed
    358          * that the record is invalid and being removed.
    359          */
    360         for (j = 0; j < this.record.distributors().getCount(); j++) {
    361             if (this.record.distributors().getAt(j).getDistributor() === null) {
    362                 removeRecords.push(this.record.distributors().getAt(j));
    363             }
    364         }
    365 
    366         if (removeRecords.length > 0) {
    367             this.record.distributors().remove(removeRecords);
    368         }
    369 
    370         if (this.record.distributors().getCount() < minDistributorCount) {
    371             errors.push(
    372                 Ext.String.format(i18n("The number of distributors must be greater than {0}"), minDistributorCount));
    373         }
    374 
    375         removeRecords = [];
    376 
    377         /**
    378          * Iterate through all records and check if a valid manufacturer
    379          * ID is assigned. If not, the record is removed as it is assumed
    380          * that the record is invalid and being removed.
    381          */
    382 
    383         /*for (j = 0; j < this.record.manufacturers().getCount(); j++) {
    384             if (this.record.manufacturers().getAt(j).getManufacturer() === null) {
    385                 removeRecords.push(this.record.manufacturers().getAt(j));
    386             }
    387         }*/
    388 
    389         if (removeRecords.length > 0) {
    390             this.record.manufacturers().remove(removeRecords);
    391         }
    392 
    393         if (this.record.manufacturers().getCount() < minManufacturerCount) {
    394             errors.push(
    395                 Ext.String.format(i18n("The number of manufacturers must be greater than {0}"), minManufacturerCount));
    396         }
    397 
    398         if (this.record.attachments().getCount() < minAttachmentCount) {
    399             errors.push(
    400                 Ext.String.format(i18n("The number of attachments must be greater than {0}"), minAttachmentCount));
    401         }
    402 
    403         // Force footprint to be "null" when the checkbox is checked.
    404         if (this.footprintNone.getValue() === true) {
    405             this.record.setFootprint(null);
    406         }
    407 
    408         if (this.initialStockLevel) {
    409             var initialStockLevel = this.initialStockLevel.getValue();
    410 
    411             if (this.record.phantom && initialStockLevel > 0) {
    412                 var stockLevel = Ext.create("PartKeepr.StockBundle.Entity.StockEntry");
    413                 stockLevel.set("stockLevel", initialStockLevel);
    414                 stockLevel.setUser(this.initialStockLevelUser.getValue());
    415 
    416                 if (this.initialStockLevelPricePerItem.getValue() === false) {
    417                     stockLevel.set("price", this.initialStockLevelPrice.getValue() / initialStockLevel);
    418                 } else {
    419                     stockLevel.set("price", this.initialStockLevelPrice.getValue());
    420                 }
    421 
    422                 this.record.stockLevels().add(stockLevel);
    423             }
    424         }
    425 
    426         if (errors.length > 0) {
    427             Ext.Msg.alert(i18n("Error"), errors.join("<br/>"));
    428             return false;
    429         }
    430     },
    431     onEditStart: function ()
    432     {
    433         this.bindChildStores();
    434         this.nameField.focus();
    435 
    436         // Re-trigger validation because of asynchronous loading of the storage location field,
    437         // which would be marked invalid because validation happens immediately, but after loading
    438         // the storage locations, the field is valid, but not re-validated.
    439 
    440         // This workaround is done twice; once after the store is loaded and once when we start editing,
    441         // because we don't know which event will come first
    442         this.getForm().isValid();
    443 
    444         if (this.record.getFootprint() !== null) {
    445             this.footprintSet.setValue(true);
    446         } else {
    447             this.footprintNone.setValue(true);
    448         }
    449     },
    450     _onItemSaved: function ()
    451     {
    452         this.fireEvent("partSaved", this.record);
    453 
    454         if (this.keepOpenCheckbox.getValue() !== true && this.createCopyCheckbox.getValue() !== true) {
    455             this.fireEvent("editorClose", this);
    456         } else {
    457             var newItem, data;
    458 
    459             if (this.partMode == "create") {
    460                 if (this.copyPartDataCheckbox.getValue() === true) {
    461                     data = this.record.getData();
    462                     delete data["@id"];
    463 
    464                     newItem = Ext.create("PartKeepr.PartBundle.Entity.Part");
    465                     newItem.set(data);
    466                     newItem.setAssociationData(this.record.getAssociationData());
    467                     newItem.stockLevels().removeAll();
    468                     newItem.set("stockLevel", 0);
    469                     this.editItem(newItem);
    470 
    471                 } else {
    472                     newItem = Ext.create("PartKeepr.PartBundle.Entity.Part");
    473                     newItem.setPartUnit(PartKeepr.getApplication().getDefaultPartUnit());
    474 
    475                     newItem.setCategory(this.record.getCategory());
    476 
    477                     this.editItem(newItem);
    478                 }
    479             } else {
    480                 data = this.record.getData();
    481                 delete data["@id"];
    482 
    483                 newItem = Ext.create("PartKeepr.PartBundle.Entity.Part");
    484                 newItem.set(data);
    485                 newItem.setAssociationData(this.record.getAssociationData());
    486                 this.editItem(newItem);
    487             }
    488         }
    489     },
    490     bindChildStores: function ()
    491     {
    492         this.partDistributorGrid.bindStore(this.record.distributors());
    493         this.partManufacturerGrid.bindStore(this.record.manufacturers());
    494         this.partAttachmentGrid.bindStore(this.record.attachments());
    495         this.partParameterGrid.bindStore(this.record.parameters());
    496     },
    497     onCancelEdit: function () {
    498         this.record.distributors().rejectChanges();
    499         this.record.manufacturers().rejectChanges();
    500         this.record.attachments().rejectChanges();
    501         this.record.parameters().rejectChanges();
    502         this.callParent(arguments);
    503     },
    504     setTitle: function (title)
    505     {
    506         var tmpTitle;
    507 
    508         if (this.record.phantom) {
    509             tmpTitle = i18n("Add Part");
    510         } else {
    511             tmpTitle = i18n("Edit Part");
    512         }
    513 
    514         if (title !== "") {
    515             tmpTitle = tmpTitle + ": " + title;
    516         }
    517 
    518         this.fireEvent("_titleChange", tmpTitle);
    519     },
    520     isOptional: function (field)
    521     {
    522         var fields = PartKeepr.getApplication().getSystemPreference("partkeepr.part.requiredFields", []);
    523 
    524         if (Ext.Array.contains(fields, field)) {
    525             return false;
    526         } else {
    527             return true;
    528         }
    529     }
    530 });