1 2angular.module('arPortal').controller('InvoiceGridCtrl', 3 [ 4 '$scope', 5 '$location', 6 '$filter', 7 '$http', 8 'InvoiceSvc', 9 '$routeParams', 10 '$modal', 11 'CustomerAccountSvc', 12 'GridSvc', 13 'gridData', 14 'principal', 15 'StatementSvc', 16 'cenposConstants', 17 'PaymentSvc', 18 '$route', 19 'csvService', 20 'dealerDetailsData', 21 'portalConfigService', 22 'accessService', 23 function( 24 $scope, 25 $location, 26 $filter, 27 $http, 28 InvoiceSvc, 29 $routeParams, 30 $modal, 31 CustomerAccountSvc, 32 GridSvc, 33 gridData, 34 principal, 35 StatementSvc, 36 cenposConstants, 37 PaymentSvc, 38 $route, 39 csvService, 40 dealerDetailsData, 41 portalConfigService, 42 accessService 43 ) { 44 $scope.csvService = csvService; 45 $scope.data = {}; 46 $scope.data.statementAmountTotal = 0; 47 $scope.data.showOnlinePayInfo = dealerDetailsData.allowingOnlinePayments; 48 $scope.data.showOnlineDispute = dealerDetailsData.allowCustomersToDispute; 49 $scope.detailsViewData = {}; 50 $scope.statementDetails = {}; 51 $scope.statementDetails.invoices = gridData; // In this controller, statementDetailsData equivalent is gridData, passed during route resolution 52 $scope.portalConfig = portalConfigService; 53 54 $scope.interactiveObj = {}; 55 $scope.interactiveHeader = {}; 56 $scope.detailsViewData.surchargePercent=0; 57 $scope.detailsViewData.allowSurcharge=false; 58 $scope.detailsViewData.payOptions=''; 59 $scope.detailsViewData.payOne = ''; 60 61 // helper formatting/type-coercion function 62 function twoDigitNum(x) { 63 return (typeof x !== 'number') ? parseFloat(x) : x; 64 } 65 66 function initStatementDetailsScope(statementDetails) { 67 var dealerAccessRole = accessService.getMostPrivilegedRole(portalConfigService.getDealerId(), principal.identity().getDealerAccessRoles()); 68 var portalCustomerId = (dealerAccessRole.getPortalCustomer() && dealerAccessRole.getPortalCustomer().getId()) ? dealerAccessRole.getPortalCustomer().getId() : null; 69 $scope.paymentTransaction = { 70 totalAmount: 0.00, 71 // FIXME UUID 72 statement: {invoices: gridData, dealer: {id: portalConfigService.getDealerId()}, dmsId: $routeParams.statementDmsId, statementDate: decodeURIComponent($routeParams.statementDate), dmsSourceRecord: false}, 73 dealer: {id: portalConfigService.getDealerId()}, 74 // portalCustomer: {id: $scope.identity().getPortalCustomerId()}, 75 portalCustomer: {id: portalCustomerId}, 76 customerAccount: {id: $routeParams.customerAccountId} 77 }; 78 79 $scope.detailsViewData.payOptions = sessionStorage.getItem('arpayoption'); 80 81 CustomerAccountSvc.loadCustomerAccount($routeParams.customerAccountId).then(function(customer) { 82 $scope.customerAccountName = customer.name; 83 $scope.paymentTransaction.statement.customerAccount = customer; 84 85 $scope.detailsViewData.allowSurcharge=customer.dmsLogon.allowSurcharge; 86 87 $scope.interactiveHeader = { 88 'stmtDate': decodeURIComponent($routeParams.statementDate), 89 'custNo': customer.controlNumber, 90 'userId': principal.identity().getId(), 91 'dmsId': $scope.paymentTransaction.statement.dmsId 92 }; 93 94 $scope.interactiveObj = InvoiceSvc.processInteractiveState($scope.interactiveHeader, $scope.statementDetails.invoices, 'get'); 95 96 $scope.statementDetails.invoices = angular.copy($scope.interactiveObj); // update the saved preferences to statementDetails.invoices 97 98 $scope.gridData = $scope.statementDetails.invoices; 99 100 updateTotalAmount(); 101 102 if (InvoiceSvc.hasInteractive) { 103 $scope.clearSelections(true); 104 } 105 }); 106 } 107 108 if (typeof gridData != 'undefined' && gridData.length > 0) { 109 angular.forEach(gridData, function(item) { 110 $scope.data.statementAmountTotal += item.statementAmount; 111 }); 112 } 113 114 $scope.modalData = {}; 115 116 $scope.fetchCsv = function() {
117 InvoiceSvc.fetchCsv($routeParams.customerAccountId); 118 }; 119 120 $scope.setPDFURL = function(invoice) { 121 InvoiceSvc.fetchPDFWithAvailabilityCheck(invoice, $routeParams.customerAccountId); 122 }; 123 124 var invoiceNumberCellTemplate; 125 var canViewInvoicePdf = principal.identity().hasRightToCustAcct('VIEW_INVOICE_PDF', $routeParams.customerAccountId); 126 127 if (canViewInvoicePdf) { 128 invoiceNumberCellTemplate = '<div class="ngCellText"><a href="javascript:void(0);" data-ng-click="showCMPopUp(row.entity)">{{row.getProperty(col.field)}}</a></div>'; 129 } else { 130 invoiceNumberCellTemplate = '<div class="ngCellText">{{row.getProperty(col.field)}}</div>'; 131 } 132 133 $scope.setCMPDFURL = function(invoice, dsdaRefer, invoiceAccount) { 134 var InvoiceCopy = angular.copy(invoice); 135 InvoiceCopy.invoiceNumber = dsdaRefer; 136 InvoiceCopy.invoiceAccount = invoiceAccount; 137 InvoiceSvc.fetchPDFWithAvailabilityCheck(InvoiceCopy, $routeParams.customerAccountId); 138 }; 139 140 $scope.showCMPopUp = function(entity) { 141 if (entity.dsdaRefers.length == 0) { 142 $scope.setCMPDFURL(entity, entity.invoiceNumber, entity.invoiceAccount); 143 } else if (entity.dsdaRefers.length > 1) { 144 $scope.askForCreditMemo(entity); 145 } else { 146 $scope.setCMPDFURL(entity, entity.dsdaRefers[0].dsdaRefer, entity.dsdaRefers[0].logon); 147 } 148 }; 149 150 // pops up creditMemo modal,sets entity values accordingly 151 $scope.askForCreditMemo = function(entity) { 152 $scope.modalData.headingText = 'Display Invoice or Credit Memo'; 153 $scope.modalData.entity = angular.copy(entity); 154 var modalInstance = $modal.open({ 155 templateUrl: 'creditMemoModal.html', 156 windowClass: 'cm-modal-window', 157 scope: $scope 158 }); 159 }; 160 161 $scope.clearSelections = function(current) { 162 $scope.modalData.headingText = (current) ? 'Previous Statement Selections Found' : 'Clear All Previous Statement Selections'; 163 $scope.modalData.confirmValue = ''; 164 var modalInstance = $modal.open({ 165 templateUrl: 'clearSelectionsModal.html', 166 windowClass: 'cm-modal-window', 167 scope: $scope 168 }); 169 modalInstance.result.then(function(data) { 170 if (data.confirmValue == 'YES') { 171 if (current) { 172 InvoiceSvc.completedInteractionState(); 173 } else { 174 InvoiceSvc.clearLocalStorage(); 175 } 176 InvoiceSvc.hasInteractive = false; 177 $route.reload(); 178 } 179 }, function() {// dismissed modal 180 // do nothing 181 }); 182 }; 183 184 initStatementDetailsScope($scope.statementDetails);// , $scope.paymentTransaction.statement); 185 186 // Moved into initStatementDetailsScope 187 // updateTotalAmount(); 188 189// if(InvoiceSvc.hasInteractive) { 190// $scope.clearSelections(true); 191// } 192 193 function canDispute() { 194 if (!$scope.detailsViewData.cantPay) return false; 195 for (var i = 0; i < gridData.length; i++) { 196 if (!gridData[i].previouslyDisputed && gridData[i].disputed) return true; 197 } 198 return false; 199 } 200 201 /** 202 * @description: Determines if credit or payment amount is valid for 203 * this invoice. If invalid, it will reset the value to the invoice 204 * balance. If valid, it will continue on to 'askForCommentAndUpdate'. 205 * #This is a comment 206 * # Another comment 207 * 208 * @function: $scope.validatePaymentBounds 209 * @param: {object} entity - object representing row of ng-grid data. 210 */ 211 $scope.validatePaymentBounds = function(entity) { 212 var pAmt = twoDigitNum(entity.currentPaidAmount), 213 sAmt = twoDigitNum(entity.statementAmount), 214 iBal = twoDigitNum(entity.invoiceBalance), 215 paid = entity.paid, 216 isCredit = false, 217 isPayment = false; 218 219 /* 220 console.log('entity:'); 221 console.log(entity); 222 console.log('pAmt:'); 223 console.log(pAmt); 224 console.log('sAmt:'); 225 console.log(sAmt); 226 console.log('iBal:'); 227 console.log(iBal); 228 console.log('isPaid:'); 229 console.log(entity.isPaid); 230 console.log('paid:'); 231 console.log(entity.paid); 232 */ 233 234 isCredit = ((iBal <= pAmt) && (pAmt < 0)); 235 // isPayment = ((0 <= pAmt) && (pAmt <= iBal)); 236 isPayment = ((0 <= pAmt) && (0 <= iBal)); 237 238 if (paid && (isCredit || isPayment)) { 239 // console.log('valid'); 240 $scope.askForCommentAndUpdate(entity); 241 } else { 242 // console.log('invalid'); 243 entity.currentPaidAmount = (paid) ? iBal : 0.00; 244 } 245 }; 246 247 // pops up paymentComment modal,sets entity values accordingly 248 $scope.askForCommentAndUpdate = function(entity) { 249 var currentPaidAmount = twoDigitNum(entity.currentPaidAmount), 250 statementAmount = twoDigitNum(entity.statementAmount), 251 invoiceBalance = twoDigitNum(entity.invoiceBalance), 252 modalInstance = null; 253 254 if (entity.paid && currentPaidAmount !== invoiceBalance) { 255 $scope.modalData.headingText = 'Reason for amount change'; 256 $scope.modalData.placeholdVal = 'Please provide reason for change in amount being paid'; 257 $scope.modalData.invoiceComment = entity.paymentComment ? entity.paymentComment : ''; 258 $scope.notes = {}; 259 $scope.notes.maxLength = 132; 260 261 modalInstance = $modal.open({
262 templateUrl: 'invoiceCommentModal.html', 263 scope: $scope 264 }); 265 266 // result promise 267 modalInstance.result.then( 268 function(invoiceComment) { 269 entity.paymentComment = invoiceComment; 270 InteractiveState('currentPaidAmount', entity.currentPaidAmount, entity); 271 updateTotalAmount(); 272 }, 273 function() { 274 // dismissed paymentComment modal, reset to statementAmount unless comment is already present; 275 if (!entity.paymentComment) { 276 entity.currentPaidAmount = entity.statementAmount; 277 InteractiveState('currentPaidAmount', entity.currentPaidAmount, entity); 278 updateTotalAmount(); 279 } 280 }); 281 } else { 282 entity.paymentComment = null; 283 updateTotalAmount(); 284 } 285 }; 286 287 $scope.disputeIt = function(entity) { 288 if (entity.disputed) { 289 $scope.modalData.headingText = 'Reason for dispute'; 290 $scope.modalData.placeholdVal = 'Please provide reason for disputing this invoice'; 291 $scope.modalData.invoiceComment = ''; 292 $scope.notes = {}; 293 $scope.notes.maxLength = 132; 294 295 var modalInstance = $modal.open({
296 templateUrl: 'invoiceCommentModal.html', 297 scope: $scope 298 }); 299 300 // result promise 301 modalInstance.result.then(function(invoiceComment) { // entered comment 302 entity.disputedComment = invoiceComment; 303 if (entity.paid) { 304 entity.currentPaidAmount = 0.00; 305 entity.paid = false; 306 entity.paymentComment = null; 307 updateTotalAmount(); 308 } 309 InteractiveState('disputed', true, entity); 310 }, function() {// dismissed disputeComment modal, reset to false; 311 entity.disputed = false; 312 InteractiveState('disputed', false, entity); 313 }); 314 } else { // unchecked disputed, 315 entity.disputedComment = null; 316 InteractiveState('disputed', false, entity); 317 } 318 }; 319 320 var InteractiveState = function(field, value, invoice) { 321// console.log(">>>>>>Interactive State"); 322// console.log(">>>>>>>>>>>>length: " + $scope.interactiveObj.length); 323 for (var i = 0; i < $scope.interactiveObj.length; i++) { 324 if ($scope.interactiveObj[i].invoiceNumber == invoice.invoiceNumber) { 325 $scope.interactiveObj[i].disputedComment = invoice.disputedComment; 326 $scope.interactiveObj[i].paymentComment = invoice.paymentComment; 327 328 if (field == 'paid') { 329 $scope.interactiveObj[i].paid = value; 330 if (value) { 331 $scope.interactiveObj[i].disputed = false; 332 $scope.interactiveObj[i].currentPaidAmount = twoDigitNum(invoice.statementAmount); 333 } else { 334 $scope.interactiveObj[i].currentPaidAmount = 0.00; 335 } 336 } else if (field == 'disputed') { 337 $scope.interactiveObj[i].disputed = value; 338 if (value) { 339 $scope.interactiveObj[i].paid = false; 340 $scope.interactiveObj[i].currentPaidAmount = 0.00; 341 } 342 } else if (field == 'currentPaidAmount') { 343 $scope.interactiveObj[i].currentPaidAmount = value; 344 } 345 } 346 } 347 InvoiceSvc.processInteractiveState($scope.interactiveHeader, $scope.interactiveObj, 'set'); 348 }; 349 350 $scope.payIt = function(entity) { 351 InteractiveState('paid', entity.paid, entity); 352 if (entity.paid) { 353 entity.disputed = false; 354 entity.disputedComment = null; 355 entity.currentPaidAmount = twoDigitNum(entity.statementAmount); 356 } else { 357 entity.paymentComment = null; 358 entity.currentPaidAmount = 0.00; 359 $scope.detailsViewData.allSelected = false; 360 } 361 if (!$scope.detailsViewData.payOne && $scope.detailsViewData.payOptions == "both" && dealerDetailsData.surcharge > 0) { 362 $scope.detailsViewData.payOne = "cards"; 363 } 364 updateTotalAmount(); 365 }; 366 367 $scope.updateSurchargeIfBothAllowed = function(){ 368 updateTotalAmount(); 369 }; 370 371 function updateTotalAmount() { 372 var total = twoDigitNum(0.00), 373 i = 0; 374 375 for (i = 0; i < $scope.statementDetails.invoices.length; i++) { 376 total += twoDigitNum($scope.statementDetails.invoices[i].currentPaidAmount); 377 } 378 $scope.paymentTransaction.surcharge = 0; //default amt for non card payment types or no surcharge 379 $scope.detailsViewData.surchargePercent = 0; //if 0 do not display surcharge (because not credit card or not setup for surcharge) 380 if (dealerDetailsData.allowingOnlinePayments){ 381 382 if ($scope.detailsViewData.allowSurcharge){ 383 if (dealerDetailsData.surcharge > 0 && ($scope.detailsViewData.payOne == "cards" || $scope.detailsViewData.payOptions == "cards")){ 384 $scope.detailsViewData.surchargePercent = twoDigitNum(dealerDetailsData.surcharge); 385 $scope.paymentTransaction.surcharge = Math.abs((total * $scope.detailsViewData.surchargePercent / 100)); 386 387 $scope.paymentTransaction.surcharge = $scope.paymentTransaction.surcharge.toFixed(2); 388 total += twoDigitNum($scope.paymentTransaction.surcharge); 389 } 390 }else{ 391 $scope.paymentTransaction.surcharge = "notsetup"; 392 } 393 394 } 395 $scope.paymentTransaction.totalAmount = twoDigitNum(total); 396 $scope.myCsvRender($scope.data.statementAmountTotal, $scope.paymentTransaction.totalAmount); 397 } 398 399 $scope.disputeStatementItems = function() { 400 InvoiceSvc.disputeStatementItems($scope.paymentTransaction.statement) 401 .then(success, failure); 402 function success(statement) {
403 InvoiceSvc.completedInteractionState(); 404 $location.path($scope.identity().getDashBoardPath()); 405 } 406 function failure() { 407 bootbox.arAlert('Problem encountered disputing statement items'); 408 } 409 }; 410 411 $scope.payStatement = function() { 412 if ($scope.detailsViewData.payOptions == "both" && dealerDetailsData.surcharge > 0){ 413 //do not allow both, so they cannot switch check to card or back to avoid surcharges 414 sessionStorage.setItem('arpayoption', $scope.detailsViewData.payOne); 415 } 416 function success(rslt) {if (rslt)$location.path(cenposConstants.paymentPath);} // transaction started successfully,load iframe 417 function failure(reason) { // error starting transaction 418 bootbox.arAlert(reason); 419 $route.reload(); 420 } 421 422 if ($scope.paymentTransaction.totalAmount >= 1.00) { 423 var rslt = PaymentSvc.startPortalTransactionSynch($scope.paymentTransaction); 424 if (rslt.success) { 425 $location.path(cenposConstants.paymentPath + '/invoices'); 426 } else { 427 bootbox.arAlert(rslt.msg); 428 } 429 } else { 430 bootbox.arAlert('Payment total must be greater than or equal to $1.00'); 431 } 432 }; 433 434 // fired off by checking pay all box or when filterOptions change(in this case, uncheck all from full list) 435 $scope.togglePayAll = function(allSelected) { 436 var applyOnFilteredData = (allSelected && $scope.filterOptions.filterText); 437 // var statementDetails = (applyOnFilteredData)?$scope.gridOptions.ngGrid.filteredRows:$scope.statementDetails; 438 var statementDetails = (applyOnFilteredData) ? $scope.myData : $scope.statementDetails; 439 for (var i = 0; i < statementDetails.invoices.length; i++) { 440 if (statementDetails.invoices[i].statementAmount === 0) { 441 // Only select invoices with a statement amount to pay 442 continue; 443 } 444 445 for (var j = 0; j < $scope.interactiveObj.length; j++) { 446 if ($scope.interactiveObj[j].invoiceNumber == statementDetails.invoices[i].invoiceNumber) { 447 $scope.interactiveObj[j].paid = allSelected; 448 InteractiveState('paid', allSelected, $scope.interactiveObj[j]); 449 } 450 } 451 452 var invoice = statementDetails.invoices[i]; 453 setPaidCheckBox(invoice); 454 } 455 function setPaidCheckBox(invoice) { 456 invoice.paid = allSelected; 457 $scope.payIt(invoice); 458 } 459 }; 460 461 // WATCHES 462 $scope.detailsViewData.cantPay = true; 463 $scope.$watch( 464 'paymentTransaction.totalAmount', 465 function(newVal) { 466 $scope.detailsViewData.cantPay = (newVal <= 0.00); 467 }, 468 true 469 ); 470 471 $scope.data.cantDisputeStatementItems = true; 472 $scope.$watch( 473 'paymentTransaction.statement.invoices', 474// 'gridData', 475 function() { 476 $scope.detailsViewData.cantDisputeStatementItems = !canDispute(); 477 }, 478 true 479 ); 480 481 // uncheck payAll options if already checked. disable payAll if filter text != "" 482 $scope.$watch('filterOptions.filterText', function(newVal) { 483 if ($scope.detailsViewData.allSelected) { 484 $scope.togglePayAll(false); 485 } 486 $scope.payAllDisabled = (newVal); 487 }, true); 488 489 var disputeCheckBoxTemplate = 490 '<div class="ngCellText" ng-switch on="row.entity.disputeDisplay">' + 491 '<div ng-switch-when="blank"><small style="margin-bottom:10px"> </small></div>' + 492 '<div ng-switch-when="label"><small style="margin-bottom:10px" class="label important">Disputed</small></div>' + 493 '<div ng-switch-when="checkbox">' + 494 '<input style="margin-top:4px;" type="checkbox" ng-model="row.entity.disputed" ng-change="disputeIt(row.entity)" />' + 495 '<small style="margin-bottom:10px"> Dispute</small></div>' + 496 '</div>'; 497 498 var payCheckBoxTemplate = 499 '<div class="ngCellText"> ' + 500 '<input style="margin-top:4px;" type="checkbox" ' + 501 'data-ng-hide="row.entity.isPaid" ng-model="row.entity.paid" ng-change="payIt(row.entity)" /> ' + 502 '<small ng-hide="row.entity.isPaid" >Pay</small>' + 503 '<small ng-hide="!row.entity.isPaid" class="label success">Paid</small>' + 504 '</div> '; 505
506 var editableAmountTemplate = 507 '<div class="input-prepend"> ' + 508 '<span class="add-on">$</span>' + 509 '<input ' + 510 'data-ui-blur="validatePaymentBounds(row.entity)" ' + 511 'data-ng-readonly="(row.entity.currentPaidAmount == 0 && row.entity.isPaid) || (!row.entity.isPaid && !row.entity.paid)" ' + 512 'ng-class="\'colt\' + col.index" ' + 513 'data-ng-input="COL_FIELD" ' + 514 'ng-pattern="/^\\-?(\\d+\\.?\\d{0,2}|\\.\\d{1,2})$/" ' + 515 'ng-model="COL_FIELD" ' + 516 '/>' + 517 '</div>'; 518 519 var checkboxHeaderTemplate = 520 '<div class="ngCellText">' + 521 '<input style="margin-top:4px;" ' + 522 'type="checkbox" ' + 523 'data-ng-disabled="payAllDisabled" ' + 524 'ng-model="detailsViewData.allSelected" ' + 525 'ng-change="togglePayAll(detailsViewData.allSelected)"/>' + 526 '<span> PayAll</span>' + 527 '</div>'; 528 529 var paidAmountCellTemplate = '<div class="ngCellText" style="text-align:right;">{{row.getProperty(col.field) | currency}}</div>'; // Amount to Pay 530 var amountCellTemplate = '<div class="ngCellText">{{row.getProperty(col.field) | currency}}</div>'; 531 532 var columnDefs = [ 533 { 534 field: 'invoiceDate', 535 cellFilter: 'date:"MM/dd/yyyy"', 536 enableCellEdit: false, 537 displayName: 'Date', 538 headerClass: 'grid-header-center' 539 }, 540 { 541 field: 'invoiceNumber', 542 enableCellEdit: false, 543 displayName: 'Invoice Number', 544 headerClass: 'grid-header-center', 545 cellTemplate: invoiceNumberCellTemplate 546 }, 547 { 548 field: 'dealershipName', 549 width: '30%', 550 enableCellEdit: false, 551 displayName: 'Dealership Name', 552 headerClass: 'grid-header-center' 553 }, 554 { 555 field: 'amount', 556 enableCellEdit: false, 557 displayName: 'Purchases', 558 headerClass: 'grid-header-center', 559 cellFilter: 'currency' 560 }, 561 { 562 field: 'paidAmount', 563 enableCellEdit: false, 564 displayName: 'Payments /<br/>Credits', 565 headerClass: 'grid-header-center', 566 cellFilter: 'currency' 567 }, 568 { 569 field: 'statementAmount', 570 enableCellEdit: false, 571 displayName: 'Statement<br/>Amount', 572 headerClass: 'grid-header-center', 573 cellFilter: 'currency' 574 }]; 575 576 // Add in fields for Online Payments info if allowed 577 if ($scope.data.showOnlinePayInfo) { 578 columnDefs.push({ 579 field: 'portalPaidAmount', 580 enableCellEdit: false, 581 displayName: 'Recent Portal<br>Payment', 582 headerClass: 'grid-header-center', 583 cellTemplate: amountCellTemplate, 584 cellFilter: 'currency' 585 }); 586 587 columnDefs.push({ 588 field: 'invoiceBalance', 589 enableCellEdit: false, 590 displayName: 'Invoice<br>Balance', 591 headerClass: 'grid-header-center', 592 cellTemplate: amountCellTemplate, 593 cellFilter: 'currency' 594 }); 595 } 596 if ($scope.data.showOnlineDispute) { 597 columnDefs.push({ 598 field: 'disputed', 599 width: '70px', 600 enableCellEdit: false, 601 displayName: 'Dispute', 602 headerClass: 'grid-header-center', 603 cellTemplate: disputeCheckBoxTemplate 604 }); 605 } 606 607 if ($scope.data.showOnlinePayInfo) { 608 columnDefs.push({ 609 field: 'paid', 610 width: '70px', 611 enableCellEdit: false, 612 displayName: 'Pay All', 613 cellTemplate: payCheckBoxTemplate, 614 headerCellTemplate: checkboxHeaderTemplate 615 }); 616 617 columnDefs.push({ 618 field: 'currentPaidAmount', 619 width: '100px', 620 enableCellEdit: true, 621 displayName: 'Amount<br>To Pay', 622 headerClass: 'grid-header-center', 623 cellTemplate: paidAmountCellTemplate,
624 editableCellTemplate: editableAmountTemplate, 625 cellFilter: 'currency' 626 }); 627 } 628 629 $scope.gridOptions = GridSvc.config($scope, gridData, columnDefs) 630 .withOpts({headerRowHeight: 50}) 631 .withPaging(false) 632 .build(); 633 GridSvc.initWatches($scope); 634 635 // =============================================== 636 // CIARP-130 637 // Begin code to support client-side csv 638 639 var renderCsv = csvService.getRender(columnDefs, ['disputed', 'paid', 'currentPaidAmount']); 640 $scope.myCsvRender = function myCsvRender(statementAmountTotal, totalAmount) { 641 var totalsLine = { 642 cells: [ 643 ['string', 'Total Balance:'], 644 // ['currency', $scope.data.statementAmountTotal] 645 ['currency', statementAmountTotal] 646 ], 647 align: 'right' 648 }; 649 // var payAmountLine = { 650 // cells: [ 651 // ['string', 'Pay This Amount:'], 652 // // ['currency', $scope.paymentTransaction.totalAmount] 653 // ['currency', totalAmount] 654 // ], 655 // align: 'right' 656 // }; 657 // var customLines = [totalsLine, payAmountLine]; 658 var customLines = [totalsLine]; 659 renderCsv(gridData, customLines); 660 }; 661 662 // End code to support client-side csv 663 // =============================================== 664 }]); 665 666angular.module('arPortal').controller('InvoiceSearchCtrl', 667 ['$scope', '$routeParams', 'principal', 'GridSvc', 'InvoiceSvc','CustomerSvc', 'CustomerAccountSvc', '$timeout', 'csvService', 'accessService', 'portalConfigService', 'Utils', 668 function($scope, $routeParams, principal, GridSvc, InvoiceSvc, CustomerSvc, CustomerAccountSvc, $timeout, csvService, accessService, portalConfigService, Utils) { 669 // var thePortalConfig = portalConfig.getPortalConfig(); 670 var invoiceNumberCellTemplate = ''; 671 var columnDefs = []; 672 673 $scope.csvService = csvService; 674 $scope.grid = {}; 675 $scope.grid.visible = false; 676 677 // var sourceCellTemplate = '<div class="ngCellText"><span ng-show="{{row.entity.openArInvoice}}">Open AR</span>' + 678 // '<span ng-show="{{!row.entity.openArInvoice}}">Statement</span></div>'; 679 680 invoiceNumberCellTemplate = 681 '<div class="ngCellText">' + 682 '<a href="javascript:void(0);" ' + 683 'data-ng-click="showCMPopUp(row.entity)" ' + 684 'data-ng-show="{{canViewInvoicePdf}}">' + 685 '{{row.getProperty(col.field)}}' + 686 '</a>' + 687 '<span data-ng-show="{{!canViewInvoicePdf}}">' + 688 '{{row.getProperty(col.field)}}' + 689 '</span>' + 690 '</div>'; 691 692 columnDefs = [ 693 { 694 field: 'invoiceDate', 695 displayName: 'Date', 696 cellFilter: 'date:"MM/dd/yyyy"', 697 enableCellEdit: false, 698 headerClass: 'grid-header-center' 699 }, 700 { 701 field: 'dsdaType', 702 displayName: 'Type', 703 enableCellEdit: false, 704 headerClass: 'grid-header-center' 705 }, 706 { 707 field: 'translatedOpenArInvoice', 708 displayName: 'Source', 709 enableCellEdit: false, 710 headerClass: 'grid-header-center'//, 711 // cellTemplate: sourceCellTemplate, 712 }, 713 { 714 field: 'dsdaRefer', 715 displayName: 'Invoice #', 716 enableCellEdit: false, 717 headerClass: 'grid-header-center', 718 cellTemplate: invoiceNumberCellTemplate 719 }, 720 { 721 field: 'amount', 722 displayName: 'Amount', 723 enableCellEdit: false, 724 headerClass: 'grid-header-center', 725 cellFilter: 'currency' 726 }, 727 { 728 field: 'dealershipName', 729 displayName: 'Dealership', 730 enableCellEdit: false, 731 headerClass: 'grid-header-center' 732 } 733 ]; 734 735 function getEmptySearchParameters() { 736 return { 737 portalCustomerId: null, 738 customerAccountId: null, 739 customer: null, 740 fromDate: null, 741 toDate: null, 742 invoiceTypes: [], 743 invoiceAmt: null, 744 poNo: null, 745 vehicleNo: null, 746 invoiceNos: '' 747 }; 748 } 749 750 $scope.gridData = []; 751 $scope.invoiceTypeList = [ 752 // {'lookupCode': 'S*P*L*M*F*R*C', 'description': 'All Invoice Types'}, 753 {'lookupCode': 'F', 'description': 'Fuel Invoice', 'isChecked': false}, 754 {'lookupCode': 'L', 'description': 'Lease Invoice', 'isChecked': false}, 755 {'lookupCode': 'M', 'description': 'Misc Invoice', 'isChecked': false}, 756 {'lookupCode': 'P', 'description': 'Parts Invoice', 'isChecked': false}, 757 {'lookupCode': 'R', 'description': 'Rental Invoice', 'isChecked': false}, 758 {'lookupCode': 'S', 'description': 'Service Invoice', 'isChecked': false}, 759 {'lookupCode': 'C', 'description': 'Truck Invoice', 'isChecked': false} 760 ]; 761 762 $scope.$watch('data.checkAllInvoiceTypes', function(value) { 763 for (var i = 0; i < $scope.invoiceTypeList.length; i++) { 764 $scope.invoiceTypeList[i].isChecked = value; 765 } 766 }); 767 768 $scope.displayInvoiceTypes = []; 769 770 for (var i = 0; i < $scope.invoiceTypeList.length; i++) { 771 if ((i % 3) === 0) $scope.displayInvoiceTypes.push([]); 772 $scope.displayInvoiceTypes[$scope.displayInvoiceTypes.length - 1].push($scope.invoiceTypeList[i]); 773 } 774 775 $scope.refreshSelectAll = function() { 776 var totalSelected = 0, 777 totalUnselected = 0; 778 779 angular.forEach($scope.invoiceTypeList, function(item) { 780 if (item.isChecked) { 781 totalSelected++; 782 } else { 783 totalUnselected++; 784 } 785 if ($scope.invoiceTypeList.length == totalSelected) { 786 $scope.data.checkAllInvoiceTypes = true; 787 } 788 if ($scope.invoiceTypeList.length == totalUnselected) { 789 $scope.data.checkAllInvoiceTypes = false;
790 } 791 }); 792 }; 793 794 $scope.portalCustomerList = []; 795 $scope.customerAccountList = []; 796 $scope.formAccountList = []; 797 798 $scope.data = { 799 searchParameters: getEmptySearchParameters() 800 }; 801 802 // FIXME 803 var dealerAccessRole = accessService.getMostPrivilegedRole(portalConfigService.getDealerId(), principal.identity().getDealerAccessRoles()); 804 805 CustomerAccountSvc.loadCustomerAccountsForCU(Utils.helpGetDealerId()).then( 806 function success (customerAccountsData) { 807 angular.forEach(customerAccountsData, function(account) { 808 $scope.customerAccountList.push({ 809 'id': account.id, 810 'name': account.name, 811 'controlNumber': account.controlNumber, 812 'nameAndControlNumber': account.name + ' (' + account.controlNumber + ')', 813 'portalCustomerId': account.portalCustomerId 814 }); 815 }); 816 817 CustomerSvc.loadCustomers(Utils.helpGetDealerId()).then( 818 function success (portalCustomerData) { 819 var hasAccounts; 820 angular.forEach(portalCustomerData, function(customer) { 821 hasAccounts = false; 822 angular.forEach($scope.customerAccountList, function(account) { 823 if(account.portalCustomerId == customer.id){ 824 hasAccounts = true; 825 } 826 }); 827 if(hasAccounts){ 828 $scope.portalCustomerList.push({ 829 'id': customer.id, 830 'name': customer.name 831 }); 832 } 833 }); 834 835 $scope.portalCustomerList.sort(function(a, b) { 836 var n1 = a.name.trim(), 837 n2 = b.name.trim(); 838 if (n1 === n2) { 839 return 0; 840 } 841 return (n1 < n2) ? -1 : 1; 842 }); 843 844 $scope.loadFormAccounts($scope.portalCustomerList[0].id); 845 846 }, 847 function failure (result) { 848 console.error(result); 849 bootbox.arAlert('Failed to load customers.') 850 } 851 ); 852 853 }, 854 function failure (result) { 855 console.error(result); 856 bootbox.arAlert('Failed to load customer accounts.') 857 } 858 ) 859 .then( 860 function success () { 861 $scope.canViewInvoicePdf = principal 862 .identity() 863 .hasRightToCustAcct( 864 'VIEW_INVOICE_PDF', 865 $scope.data.searchParameters.customerAccountId 866 ); 867 }, 868 function failure () { 869 // pass 870 } 871 ); 872 873 $scope.loadFormAccounts = function(portalCustomerId){ 874 // console.log("loadFormAccounts - portalCustomerId = " + portalCustomerId); 875 $scope.formAccountList = []; 876 angular.forEach($scope.customerAccountList, function(customerAccount) { 877 if(customerAccount.portalCustomerId == portalCustomerId){ 878 $scope.formAccountList.push(customerAccount); 879 } 880 }); 881 $scope.formAccountList.sort(function(a, b) { 882 var n1 = a.nameAndControlNumber.trim(), 883 n2 = b.nameAndControlNumber.trim(); 884 if (n1 === n2) { 885 return 0; 886 } 887 return (n1 < n2) ? -1 : 1; 888 }); 889 // console.dir($scope.formAccountList); 890 891 $scope.data.searchParameters.portalCustomerId = portalCustomerId; 892 $scope.data.searchParameters.customerAccountId = $scope.formAccountList[0].id; 893 }; 894 895 896 $scope.setCMPDFURL = function(invoice, dsdaRefer, invoiceAccount) { 897 var InvoiceCopy = angular.copy(invoice); 898 InvoiceCopy.invoiceNumber = dsdaRefer; 899 InvoiceCopy.invoiceAccount = invoiceAccount; 900 InvoiceSvc.fetchPDFWithAvailabilityCheck(InvoiceCopy, $scope.data.searchParameters.customerAccountId); 901 }; 902 903 // pops up creditMemo modal, sets entity values accordingly 904 $scope.askForCreditMemo = function(entity) { 905 $scope.modalData.headingText = 'Display Invoice or Credit Memo'; 906 $scope.modalData.entity = angular.copy(entity); 907 var modalInstance = $modal.open({ 908 templateUrl: 'creditMemoModal.html', 909 windowClass: 'cm-modal-window', 910 scope: $scope 911 }); 912 }; 913 914 $scope.showCMPopUp = function(entity) { 915// console.log('entity:'); 916// console.dir(entity); 917 918 if (entity.dsdaRefers.length == 0) { 919 $scope.setCMPDFURL(entity, entity.invoiceNumber, entity.invoiceAccount); 920 } else if (entity.dsdaRefers.length > 1) { 921 $scope.askForCreditMemo(entity); 922 } else { 923 $scope.setCMPDFURL(entity, entity.dsdaRefers[0].dsdaRefer, entity.dsdaRefers[0].logon); 924 } 925 }; 926 927 $scope.gridOptions = GridSvc.config($scope, $scope.gridData, columnDefs) 928 // .withDefaults() 929 .withPaging(true) 930 .withPagingOptions({pageSizes: [25, 50, 100, 200], pageSize: 50, currentPage: 1}) 931 .build(); 932 GridSvc.initWatches($scope); 933 934// console.log('gridOptions:'); 935// console.dir($scope.gridOptions); 936 937 $scope.searchInvoices = function() { 938 if ($scope.data.searchParameters.fromDate && !$scope.data.searchParameters.toDate) { 939 bootbox.arAlert('Please enter To Date.'); 940 return; 941 } 942 943 if (!$scope.data.searchParameters.fromDate && $scope.data.searchParameters.toDate) { 944 bootbox.arAlert('Please enter From Date.'); 945 return; 946 } 947 948 $scope.data.searchParameters.invoiceTypes = []; 949 angular.forEach($scope.invoiceTypeList, function(item) { 950 if (item.isChecked) $scope.data.searchParameters.invoiceTypes.push(item.lookupCode); 951 }); 952 953 $scope.loading = true; 954 InvoiceSvc.invoiceSearch($scope.data.searchParameters).then(function(invoices) { 955 // console.log('invoices:'); 956 // console.dir(invoices); 957 958 if (invoices === '') { 959 console.log('Error: InvoiceSvc.invoiceSearch returned nothing.'); 960 } else { 961 $scope.canViewInvoicePdf = principal.identity().hasRightToCustAcct('VIEW_INVOICE_PDF', $scope.data.searchParameters.customerAccountId); 962 if (!$scope.canViewInvoicePdf) { 963 $scope.canViewInvoicePdf = false;
964 } 965 966 $timeout(function() { 967 $scope.gridData = []; 968 GridSvc.initWatches($scope); 969 $scope.redrawNgGrid(); 970 971 $timeout(function() { 972 $scope.gridData = angular.copy(invoices.invoices); 973 $scope.gridData.forEach(function translateOpenArInvoice(v) { 974 v.translatedOpenArInvoice = (v.openArInvoice) ? 'Open AR' : 'Statement'; 975 }); 976 $scope.grid.visible = true; 977 GridSvc.initWatches($scope); 978 $scope.redrawNgGrid(); 979 980 // CIARP-130 981 // FIXME 982 $timeout(function() { 983 $scope.renderCsv($scope.gridData); 984 }, 500); 985 }, 100); 986 }, 100); 987 } 988 $scope.loading = false; 989 }); 990 }; 991 992 $scope.resetSearchInvoices = function() { 993 // console.log('reset'); 994 // $scope.data.searchParameters.toDate = new Date(); 995 // $scope.data.searchParameters.fromDate = new Date(); 996 997 $scope.data.searchParameters = { 998 customerAccountId: $scope.customerAccountList[0].id, 999 customer: null, 1000 fromDate: new Date(), 1001 toDate: new Date(), 1002 invoiceTypes: [], 1003 invoiceAmt: null, 1004 poNo: null, 1005 vehicleNo: null, 1006 invoiceNos: '' 1007 }; 1008 $scope.data.checkAllInvoiceTypes = false; 1009 for (var i = 0; i < $scope.invoiceTypeList.length; i++) { 1010 $scope.invoiceTypeList[i].isChecked = false; 1011 } 1012 1013 $scope.gridData = []; 1014 $scope.grid.visible = false; 1015 GridSvc.initWatches($scope); 1016 $scope.redrawNgGrid(); 1017 1018 $timeout(function() { 1019 $scope.data.searchParameters.fromDate = null; 1020 $scope.data.searchParameters.toDate = null; 1021 }, 100); 1022 }; 1023 1024 $scope.redrawNgGrid = function() { 1025 // HACK -- Call resize window events to force redraw of ng-grid after reloading new search data 1026 // https://github.com/angular-ui/ng-grid/issues/50 1027 window.setTimeout(function() { 1028 $(window).resize(); 1029 $(window).resize(); 1030 // console.log('bonus round: ' + csvService.csv.isReady); 1031 }, 1); 1032 }; 1033 1034 $scope.fetchInvoiceSearchCsv = function() { 1035 InvoiceSvc.fetchInvoiceSearchCsv($scope.data.searchParameters); 1036 }; 1037 1038 // CIARP-130 1039 // Begin code to support client-side csv 1040 $scope.renderCsv = csvService.getRender(columnDefs); 1041 $scope.renderCsv($scope.gridData); 1042 // End code to support client-side csv 1043 }]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.