ProjectReportResultGrid.js (16366B)
1 Ext.define("PartKeepr.Components.Project.ProjectReportResultGrid", { 2 extend: "PartKeepr.BaseGrid", 3 4 features: [ 5 { 6 ftype: 'summary' 7 } 8 ], 9 10 viewConfig: { 11 markDirty: false 12 }, 13 14 initComponent: function () 15 { 16 17 this.subGrid = { 18 xtype: 'gridfoo', 19 bind: { 20 store: '{record.subParts}', 21 parentRecord: '{record}' 22 } 23 }; 24 25 this.columns = [ 26 { 27 header: i18n("Qty"), dataIndex: 'quantity', 28 width: 100, 29 renderers: [{ 30 rtype: "projectReportQuantity", 31 rendererConfig: { 32 quantityField: "quantity" 33 } 34 }] 35 }, { 36 header: i18n("Part Name"), 37 renderers: [{ 38 rtype: "projectReportMetaPart" 39 }], 40 flex: 2 41 }, { 42 header: i18n("Part Description"), 43 dataIndex: "part.description", 44 flex: 2 45 }, { 46 header: i18n("Remarks"), 47 dataIndex: 'remarks', 48 renderers: [{ 49 rtype: "projectReportRemarks" 50 }], 51 flex: 1 52 }, { 53 header: i18n("Production Remarks"), 54 dataIndex: 'productionRemarks', 55 flex: 1 56 }, { 57 header: i18n("Projects"), 58 dataIndex: 'projectNames', 59 flex: 1 60 }, { 61 header: i18n("Storage Location"), dataIndex: 'part.storageLocation.name', 62 width: 100 63 }, { 64 header: i18n("Available"), 65 dataIndex: "part.stockLevel", 66 renderers: [{ 67 rtype: "projectReportMetaPartAvailability" 68 }], 69 width: 75 70 }, { 71 header: i18n("Distributor"), 72 dataIndex: 'distributor', 73 renderers: [{ 74 rtype: 'objectField', 75 rendererConfig: { 76 displayField: 'distributor.name' 77 } 78 }], 79 flex: 1, 80 editor: { 81 xtype: 'DistributorComboBox', 82 returnObject: true, 83 triggerAction: 'query', 84 ignoreQuery: true, 85 forceSelection: true, 86 editable: false 87 } 88 }, { 89 header: i18n("Distributor Order Number"), dataIndex: 'distributorOrderNumber', 90 flex: 1, 91 editor: { 92 xtype: 'textfield' 93 } 94 }, { 95 header: i18n("Item Price"), dataIndex: 'part.averagePrice', 96 renderers: [{ 97 rtype: 'currency' 98 }], 99 width: 100 100 }, { 101 header: i18n("Sum"), 102 dataIndex: 'itemSum', 103 renderers: [{ 104 rtype: 'currency' 105 }], 106 summaryType: 'sum', 107 summaryRenderer: PartKeepr.getApplication().formatCurrency, 108 width: 100 109 }, { 110 header: i18n("Order Amount"), dataIndex: 'missing', 111 renderers: [{ 112 rtype: "projectReportQuantity", 113 rendererConfig: { 114 quantityField: "missing" 115 } 116 }], 117 width: 100 118 }, { 119 header: i18n("Sum (Order)"), 120 dataIndex: 'orderSum', 121 renderers: [{ 122 rtype: 'currency' 123 }], 124 summaryType: 'sum', 125 summaryRenderer: PartKeepr.getApplication().formatCurrency, 126 width: 100 127 } 128 ]; 129 130 this.nextMetaPart = Ext.create("Ext.button.Button", { 131 text: i18n("Next Meta-Part"), 132 iconCls: 'partkeepr-icon bricks_down', 133 listeners: { 134 click: this.jumpToNextMetaPart, 135 scope: this 136 } 137 }); 138 139 this.previousMetaPart = Ext.create("Ext.button.Button", { 140 text: i18n("Previous Meta-Part"), 141 iconCls: 'partkeepr-icon bricks_up', 142 listeners: { 143 click: this.jumpToPreviousMetaPart, 144 scope: this 145 } 146 }); 147 148 this.removeStockButton = Ext.create('Ext.button.Button', { 149 text: i18n("Remove parts from stock"), 150 iconCls: 'fugue-icon notification-counter-03', 151 listeners: { 152 click: this.onStockRemovalClick, 153 scope: this 154 } 155 }); 156 157 this.saveReportButton = Ext.create('Ext.button.Button', { 158 text: i18n("Save Project Report"), 159 iconCls: 'fugue-icon notification-counter-04', 160 listeners: { 161 click: this.onSaveReportClick, 162 scope: this 163 } 164 }); 165 166 this.autoFillButton = Ext.create('Ext.button.Button', { 167 text: i18n("Auto-Fill Distributors"), 168 iconCls: 'fugue-icon notification-counter-02', 169 listeners: { 170 click: this.onAutoFillClick, 171 scope: this 172 } 173 }); 174 175 this.rowExpander = new PartKeepr.Components.ProjectReport.MetaPartRowExpander({ 176 widget: this.subGrid 177 }); 178 179 this.editing = Ext.create('Ext.grid.plugin.CellEditing', { 180 clicksToEdit: 1, 181 listeners: { 182 beforeedit: this.onBeforeEdit, 183 edit: this.onEdit, 184 scope: this 185 } 186 }); 187 188 this.plugins = [ 189 this.rowExpander, this.editing 190 ]; 191 192 this.bbar = [ 193 this.autoFillButton, 194 this.removeStockButton, 195 this.saveReportButton, 196 {xtype: 'tbseparator'}, 197 this.nextMetaPart, 198 this.previousMetaPart, 199 {xtype: 'tbseparator'}, 200 Ext.create("PartKeepr.Exporter.GridExporterButton", { 201 itemId: 'export', 202 genericExporter: false, 203 tooltip: i18n("Export"), 204 iconCls: "fugue-icon application-export" 205 }), 206 Ext.create("PartKeepr.Components.Grid.GridPresetButton", { 207 grid: this 208 }) 209 210 ]; 211 212 this.callParent(arguments); 213 214 }, 215 /** 216 * Called when the distributor field is about to be edited. 217 * 218 * Filters the distributor list and show only distributors which are assigned to the particular item. 219 * @param e 220 * @param context 221 */ 222 onBeforeEdit: function (e, context) 223 { 224 if (context.field !== "distributor") 225 { 226 return; 227 } 228 229 var distributors = context.record.getPart().distributors(); 230 231 var filterIds = []; 232 for (var i = 0; i < distributors.count(); i++) 233 { 234 if (distributors.getAt(i).getDistributor().get("enabledForReports") === false) 235 { 236 continue; 237 } 238 239 if (distributors.getAt(i).get("ignoreForReports")) 240 { 241 continue; 242 } 243 244 filterIds.push(distributors.getAt(i).getDistributor().getId()); 245 } 246 247 var filter = Ext.create("PartKeepr.util.Filter", { 248 property: "@id", 249 operator: 'in', 250 value: filterIds 251 }); 252 253 context.column.getEditor().store.clearFilter(); 254 context.column.getEditor().store.addFilter(filter); 255 }, 256 onSaveReportClick: function () 257 { 258 Ext.Msg.prompt( 259 i18n("Project Report Name"), 260 i18n("Please enter the project report name:"), 261 this.doSaveReport, 262 this, 263 false, 264 this.projectReport.get("name") 265 ); 266 }, 267 doSaveReport: function (button, value) 268 { 269 if (button === "ok") 270 { 271 this.projectReport.set("name", value); 272 this.projectReportManager.doSaveProjectReport(); 273 } 274 }, 275 /** 276 * Removes all parts in the project view. 277 */ 278 onStockRemovalClick: function () 279 { 280 if (this.hasMetaParts()) 281 { 282 Ext.Msg.alert(i18n("Unassigned Meta-Parts"), 283 i18n("You have unassigned meta-parts. In order to remove parts from stock, you need to assign the meta-parts first.")); 284 return; 285 } 286 287 Ext.Msg.confirm(i18n("Remove parts from stock"), 288 i18n("Do you really want to remove the parts in the project report from the stock?"), 289 this.removeStocks, this); 290 }, 291 jumpToPreviousMetaPart: function () 292 { 293 var i; 294 295 for (i = this.getSelectedMetaPartStartIndex() - 1; i >= 0; i--) 296 { 297 if (this.selectMetaPart(i)) 298 { 299 break; 300 } 301 } 302 }, 303 jumpToNextMetaPart: function () 304 { 305 var i; 306 307 for (i = this.getSelectedMetaPartStartIndex() + 1; i < this.getStore().getCount(); i++) 308 { 309 if (this.selectMetaPart(i)) 310 { 311 break; 312 } 313 } 314 }, 315 selectMetaPart: function (index) 316 { 317 var record = this.getStore().getAt(index); 318 319 if (record.get("metaPart")) 320 { 321 this.ensureVisible(record); 322 this.getSelectionModel().select(record); 323 return true; 324 } 325 326 return false; 327 }, 328 getSelectedMetaPartStartIndex: function () 329 { 330 var selection = this.getSelection(); 331 332 if (selection.length === 1) 333 { 334 return this.getStore().indexOf(selection[0]); 335 } else 336 { 337 return 0; 338 } 339 }, 340 341 removeStocks: function (btn) 342 { 343 if (btn === "yes") 344 { 345 346 var store = this.getStore(); 347 var removals = []; 348 349 for (var i = 0; i < store.count(); i++) 350 { 351 var item = store.getAt(i); 352 353 removals.push({ 354 part: item.getPart().getId(), 355 amount: item.get("quantity"), 356 comment: item.getReport().reportProjects().getFieldValues("project.name").join(", "), 357 lotNumber: item.projectParts().getFieldValues("lotNumber").join(", "), 358 projects: [] // item.getReport().reportProjects() 359 }); 360 } 361 362 PartKeepr.PartBundle.Entity.Part.callPostCollectionAction("massRemoveStock", 363 { 364 "removals": Ext.encode(removals), 365 "projects": Ext.encode(this.getProjectsToReport()) 366 }, 367 function (options, success) 368 { 369 if (success) 370 { 371 Ext.Msg.alert( 372 i18n("Stock Removal Complete"), 373 i18n("Removed stock and created a new project run.") 374 ); 375 } 376 } 377 ); 378 } 379 }, 380 onEdit: function (editor, context) 381 { 382 if (context.field === "distributor" && context.record.getDistributor() !== null) 383 { 384 var partDistributors = context.record.getPart().distributors(); 385 386 for (var i = 0; i < partDistributors.count(); i++) 387 { 388 if (partDistributors.getAt(i).getDistributor().getId() === context.record.getDistributor().getId()) 389 { 390 context.record.set("itemPrice", partDistributors.getAt(i).get("price")); 391 context.record.set("distributorOrderNumber", partDistributors.getAt(i).get("orderNumber")); 392 context.record.set("orderSum", context.record.get("missing") * context.record.get("itemPrice")); 393 context.record.set("itemSum", context.record.get("quantity") * context.record.get("itemPrice")); 394 } 395 } 396 } 397 398 }, 399 onAutoFillClick: function () 400 { 401 var partCount = this.getStore().getCount(); 402 var activeRecord; 403 404 this.projectPartStack = []; 405 406 407 for (var i = 0; i < partCount; i++) 408 { 409 410 activeRecord = this.getStore().getAt(i); 411 412 this.projectPartStack.push(activeRecord); 413 } 414 415 this.processCheapestDistributorStack(this.projectPartStack.length); 416 417 if (this.waitMessage instanceof Ext.window.MessageBox) 418 { 419 this.waitMessage.hide(); 420 } 421 }, 422 processCheapestDistributorStack: function (totalCount) 423 { 424 if (this.projectPartStack.length === 0) 425 { 426 if (this.waitMessage instanceof Ext.window.MessageBox) 427 { 428 this.waitMessage.hide(); 429 } 430 return; 431 } 432 this.displayWaitWindow( 433 i18n("Processing distributors…"), 434 (totalCount - this.projectPartStack.length) + " / " + totalCount, 435 1 / totalCount * (totalCount - this.projectPartStack.length)); 436 this.processCheapestDistributorForProjectPart(this.projectPartStack.shift()); 437 438 Ext.defer(this.processCheapestDistributorStack, 1, this, [totalCount]); 439 }, 440 processCheapestDistributorForProjectPart: function (projectPart) 441 { 442 var cheapestDistributor = this.getCheapestDistributor(projectPart.getPart()); 443 444 if (cheapestDistributor !== null) 445 { 446 projectPart.setDistributor(cheapestDistributor.getDistributor()); 447 projectPart.set("distributorOrderNumber", cheapestDistributor.get("orderNumber")); 448 projectPart.set("itemPrice", cheapestDistributor.get("price")); 449 projectPart.set("orderSum", projectPart.get("missing") * projectPart.get("itemPrice")); 450 projectPart.set("itemSum", projectPart.get("quantity") * projectPart.get("itemPrice")); 451 } 452 }, 453 getCheapestDistributor: function (part) 454 { 455 var cheapestDistributor = null; 456 var currentPrice; 457 var activeDistributor; 458 var lowestPrice; 459 var firstPositive; 460 461 firstPositive = true; 462 lowestPrice = 0; 463 464 for (var j = 0; j < part.distributors().count(); j++) 465 { 466 activeDistributor = part.distributors().getAt(j); 467 468 if (activeDistributor.getDistributor().get("enabledForReports") === false) 469 { 470 continue; 471 } 472 473 if (activeDistributor.get("ignoreForReports") === true) 474 { 475 continue; 476 } 477 478 currentPrice = parseFloat(activeDistributor.get("price")); 479 480 if (currentPrice !== 0) 481 { 482 if (firstPositive) 483 { 484 lowestPrice = currentPrice; 485 cheapestDistributor = activeDistributor; 486 firstPositive = false; 487 } 488 else 489 { 490 if (currentPrice < lowestPrice) 491 { 492 lowestPrice = currentPrice; 493 cheapestDistributor = activeDistributor; 494 } 495 } 496 } 497 } 498 499 return cheapestDistributor; 500 }, 501 hasMetaParts: function () 502 { 503 var i, record; 504 for (i = 0; i < this.getStore().getCount(); i++) 505 { 506 record = this.getStore().getAt(i); 507 if (record.get("metaPart")) 508 { 509 return true; 510 } 511 } 512 513 return false; 514 }, 515 displayWaitWindow: function (text, description, value) 516 { 517 this.waitMessage = Ext.MessageBox.show({ 518 msg: text, 519 title: i18n("Applying distributors…"), 520 progressText: description, 521 progress: true, 522 width: 300 523 }); 524 525 this.waitMessage.updateProgress(value); 526 }, 527 setProjectsToReport: function (projects) 528 { 529 this.reportedProjects = projects; 530 }, 531 getProjectsToReport: function () 532 { 533 return this.reportedProjects; 534 } 535 });