<style>
/* override the font-size of the default widgets for readaiblity in the grid */
.dx-widget {
font-size: 16px !important;
}
</style>
<div id="divLoadPanel"></div>
<h1>Billing History</h1>
<div id="divBillingHistoryWrapper" style="min-height:800px;">
<div id="divSummary"
style="display:none;background-color:#dedede; border:1px solid #ababab;padding:25px;vertical-align:middle;width:100%;">
<h3 id="hCustomerName" style="margin-top:0px;"></h3>
<div style="float:right;">
<div id="btnMakePayment" class="btn btn-primary-theme"></div>
</div>
<div style="float:left;">
<b>Account Balance:</b><br />
<span id="spnBalance" style="font-size:36px;"></span><br />
<span id="spnDunningMessage" style="font-size:small;"></span>
</div>
<div style="text-align:left;margin-left:150px;display:inline-block;font-size:smaller;">
<div style="display:table;">
<div style="display:table-row">
<div style="display:table-cell;padding-right:20px;">
<b>Current:</b>
</div>
<div style="display:table-cell;text-align:right;">
<span id="spnCurrent"></span><br />
</div>
</div>
<div style="display:table-row">
<div style="display:table-cell;">
<b>30 Days:</b>
</div>
<div style="display:table-cell;text-align:right;">
<span id="spn30Days"></span><br />
</div>
</div>
<div style="display:table-row">
<div style="display:table-cell;">
<b>60 Days:</b>
</div>
<div style="display:table-cell;text-align:right;">
<span id="spn60Days"></span><br />
</div>
</div>
<div style="display:table-row">
<div style="display:table-cell;">
<b>90+ Days:</b>
</div>
<div style="display:table-cell;text-align:right;">
<span id="spn90Days" class="format-currency"></span><br />
</div>
</div>
</div>
</div>
</div>
<br />
<div id="grdInvoices"></div>
</div>
<div id="popupContainer"></div>
<script type="text/javascript">
/*'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Widget: BillingHistory_Grid.html
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' (c) 2021 by Dovetail Internet Technologies, LLC
' www.dovetailinternet.com
' info@dovetailinternet.com
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' All rights reserved. Not to be used without permission.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Development Date: May - September 2021
' Release Version: 2.20.0
' Revision: 2.0
' Last Modified: 09/7/2021
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: display customer aging and open invoices with
' links for viewing and making payments
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Usage: Requires B2B login shopper context
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Example SitePages usage:
<Control src="LoadWidgetControl.ascx"
FileLocation="BillingHistory_Grid.html"
RequireLogin="true"
/>
'
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */
// >>>>>>>>>>>>>>>>>>>>> Declare widget variables in GLOBAL space <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
var Invoices = [];
var InvoicesToPay = [];
var BillingLoadPanel = null
var InvoiceGrid;
var PayButton;
var Aging = {};
var PreferredPaymentType = '';
var PaymentTypes = [];
var SelectedPaymentType = '';
///-------------------------------------------------------------------------------------------------
/// <summary>
/// The main widget function. Executes on page load.
/// <returns>
/// .
/// </returns>
///-------------------------------------------------------------------------------------------------
$(function () {
MakeAJAXCall("Billing.GetPaymentMethods", {}, function (DATA) {
if (ValidAJAXResponse(DATA)) {
var res = JSON.parse(DATA);
PaymentTypes = res.Data.Methods
for (var p = 0; p < PaymentTypes.length; p++) {
if (PaymentTypes[p].MethodType === 'BA' || PaymentTypes[p].MethodType === 'CC') {
PreferredPaymentType = PaymentTypes[p].MethodType;
break;
}
}
SelectedPaymentType = PreferredPaymentType;
}
BillingLoadPanel = $('#divLoadPanel').dxLoadPanel({
shadingColor: 'rgba(0,0,0,0.7)',
visible: true,
showIndicator: true,
showPane: true,
closeOnOutsideClick: false,
message: 'Loading account history ...',
}).dxLoadPanel('instance');
PayButton = $('#btnMakePayment').dxButton({
text: "Pay Account in Full",
type: 'danger',
}).dxButton('instance');
InvoiceGrid = $('#grdInvoices').dxDataGrid({
visible: false,
}).dxDataGrid('instance');
function createInvoiceGrid() {
InvoiceGrid.option({
columnHidingEnabled: true,
columns: [{
dataField: 'InvoiceDate',
caption: 'Date',
dataType: 'date',
sortIndex: 0,
sortOrder: 'desc',
hidingPriority: 2,
},
{
dataField: 'InvoiceNumber',
caption: 'Invoice Number',
dataType: 'string',
sortIndex: 1,
alignment: 'center',
cellTemplate: function (e, item) {
var num = item.data.InvoiceNumber;
if (num != '') {
return $("<div>").append(
$("<a>")
//.attr('onclick', 'GetInvoicePopup(\'' + num + '\');')
.attr('onclick', 'showDocumentViewerPopUp("Invoice","' + num + '");')
.text(num)
)
} else {
return num;
}
}
},
{
dataField: 'PONumber',
caption: 'P.O. Number',
dataType: 'string',
hidingPriority: 0,
},
{
dataField: 'DueDate',
dataType: 'date'
},
{
dataField: 'InvoiceTotal',
dataType: 'string',
hidingPriority: 1,
},
{
dataField: 'CurrentBalance',
caption: 'Balance Due',
dataType: 'string'
},
{
dataField: 'InvoiceTotalAmount',
dataType: 'decimal',
visible: false
},
{
dataField: 'CurrentBalanceAmount',
dataType: 'decimal',
visible: false
},
{
caption: 'View',
type: 'buttons',
alignment: 'center',
hidingPriority: 3,
buttons: [{
text: 'Open Invoice',
icon: 'fa fa-file-text-o',
onClick: function (e) {
// showDocumentViewerPopUp(doctype, doc id)
showDocumentViewerPopUp('Invoice', e.row.data.InvoiceNumber);
},
visible: function (e) {
return e.row.data.InvoiceNumber != '';
}
}]
},
{
caption: 'Pay',
type: 'buttons',
alignment: 'center',
visible: (PreferredPaymentType === 'BA' || PreferredPaymentType === 'CC'),
buttons: [{
text: 'Pay Invoice',
icon: 'fa fa-credit-card',
onClick: function (e) {
InvoicesToPay = [{
Invoice: e.row.data.InvoiceNumber,
AmountToPay: e.row.data.CurrentBalanceAmount
}];
ShowPaymentPopup(e.row.data.CurrentBalanceAmount, true);
},
visible: function (e) {
return e.row.data.CurrentBalanceAmount > 0 && e.row.data.InvoiceNumber != '';
}
}]
}
],
hoverStateEnabled: true,
allowColumnReordering: true,
noDataText: 'No rows returned!',
showBorders: false,
showColumnHeaders: true,
showColumnLines: false,
rowAlternationEnabled: true,
paging: {
enabled: (widgetOptions.enablePaging != null) ?
widgetOptions.enablePaging :
true,
pageIndex: 0,
pageSize: widgetOptions.pageSize || 20,
},
pager: {
allowedPageSizes: "auto",
infoText: "Page {0} of {1} ({2} Total Invoices)",
showInfo: true,
showNavigationButtons: false,
showPageSizeSelector: true,
visible: "auto"
},
selection: {
allowSelectAll: true,
deferred: false,
mode: (PreferredPaymentType === 'BA' || PreferredPaymentType === 'CC')
? 'multiple'
: 'none',
selectAllMode: 'allPages',
showCheckBoxesMode: 'always'
},
searchPanel: {
highlightCaseSensitive: false,
highlightSearchText: true,
placeholder: 'Filter by...',
searchVisibleColumnsOnly: true,
text: '',
visible: true,
width: 160
},
onToolbarPreparing: function (e) {
var dataGrid = e.component;
e.toolbarOptions.items.unshift({
location: 'before',
widget: 'dxButton',
options: {
text: 'Pay Selected Invoices',
visible: (PreferredPaymentType === 'BA' || PreferredPaymentType === 'CC'),
type: 'default',
onClick: function (e) {
var selectedRows = dataGrid.getSelectedRowsData();
// clear out the global InvoicesToPay value since we will reset it
InvoicesToPay = [];
if (selectedRows.length > 0) {
var totalToPay = selectedRows.reduce((accum, item) => accum + item.CurrentBalanceAmount, 0);
for (var r = 0; r < selectedRows.length; r++) {
InvoicesToPay.push({
Invoice: selectedRows[r].InvoiceNumber,
AmountToPay: selectedRows[r].CurrentBalanceAmount
});
}
ShowPaymentPopup(totalToPay, false);
} else {
ToastAlert('You must select at least one Invoice to pay.', false);
}
}
}
});
},
// Customize what happens on these calls
onEditorPreparing: onEditorPreparing,
onSelectionChanged: onSelectionChanged
});
}
MakeAJAXCall('Visitor.GetAccountProfile', {}, function (DATA) {
var response = JSON.parse(DATA);
if (response.Result.Success) {
var profile = response.Data;
$('#hCustomerName').text(profile.Name);
//
// setup the payment object valuess tied to the profile
if (typeof Payment !== 'undefined') {
Payment.Address1 = profile.BillingAddress.Address1;
Payment.Address2 = profile.BillingAddress.Address2;
Payment.City = profile.BillingAddress.City;
Payment.State = profile.BillingAddress.State;
Payment.ZipCode = profile.BillingAddress.Zip;
Payment.Country = profile.BillingAddress.Country;
Payment.FirstName = profile.FirstName;
Payment.LastName = profile.LastName;
Payment.BaNameOnAccount1 = profile.FirstName;
Payment.BaNameOnAccount2 = profile.LastName;
Payment.Email = profile.Email;
Payment.Telephone = profile.BillingAddress.Phone;
}
} else {
$('#hCustomerName').css('display', 'none');
}
});
var selectAllCheckBox;
var checkBoxUpdating = false;
// get aging and invoice details
LoadAccountDetails(true);
createInvoiceGrid();
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Is selectable.
/// </summary>
///
/// <param name="item">
/// The item.
/// </param>
///
/// <returns>
/// .
/// </returns>
///-------------------------------------------------------------------------------------------------
function isSelectable(item) {
// Return true or false is the editor is selectable
if (item.CurrentBalanceAmount < 0.01) {
return false;
}
if (item.InvoiceNumber == '') {
return false;
}
if (item.CurrentBalanceAmount > 0.00 && item.InvoiceNumber != '') {
return true;
}
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Is select all.
/// </summary>
///
/// <param name="dataGrid">
/// The data grid.
/// </param>
///
/// <returns>
/// .
/// </returns>
///-------------------------------------------------------------------------------------------------
function isSelectAll(dataGrid) {
var items = [];
items = Invoices;
var selectableItems = items.filter(isSelectable);
var selectedRowKeys = dataGrid.option("selectedRowKeys");
if (!selectedRowKeys.length) {
return false;
}
return selectedRowKeys.length >= selectableItems.length ? true : undefined;
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Executes the editor preparing action.
/// </summary>
///
/// <param name="e">
/// The editor.
/// </param>
///
/// <returns>
/// .
/// </returns>
///-------------------------------------------------------------------------------------------------
function onEditorPreparing(e) {
var dataGrid = e.component;
if (e.command === "select") {
if (e.parentType === "dataRow" && e.row) {
if (!isSelectable(e.row.data))
e.editorOptions.disabled = true;
} else if (e.parentType === "headerRow") {
e.editorOptions.onInitialized = function (e) {
selectAllCheckBox = e.component;
}
e.editorOptions.value = isSelectAll(dataGrid);
e.editorOptions.onValueChanged = function (e) {
if (!e.event) {
if (e.previousValue && !checkBoxUpdating) {
e.component.option("value", e.previousValue);
}
return;
}
if (isSelectAll(dataGrid) === e.value) {
return;
}
e.value ? dataGrid.selectAll() : dataGrid.deselectAll();
e.event.preventDefault();
}
}
}
}
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Executes the selection changed action.
/// </summary>
///
/// <param name="e">
/// The editor.
/// </param>
///
/// <returns>
/// .
/// </returns>
///-------------------------------------------------------------------------------------------------
function onSelectionChanged(e) {
var deselectRowKeys = [];
e.selectedRowsData.forEach((item) => {
if (!isSelectable(item))
deselectRowKeys.push(e.component.keyOf(item));
});
if (deselectRowKeys.length) {
e.component.deselectRows(deselectRowKeys);
}
checkBoxUpdating = true;
selectAllCheckBox.option("value", isSelectAll(e.component));
checkBoxUpdating = false;
//NOTE if InvoicesToPay ==[] then fill
if (InvoicesToPay.length <= 0) {
e.selectedRowsData.forEach(j => {
InvoicesToPay.push({ Invoice: j.InvoiceNumber, AmountToPay: j.CurrentBalanceAmount });
});
}
}
});
});
// >>>>>>>>>>>>>>>>>>>>> Widget Functions in GLOBAL space <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
///-------------------------------------------------------------------------------------------------
/// <summary>
/// Loads account details.
/// </summary>
///
/// <returns>
/// The account details.
/// </returns>
///-------------------------------------------------------------------------------------------------
function LoadAccountDetails(firstLoad) {
// Get Aging
MakeAJAXCall('Billing.GetCustomerAging', {}, function (DATA) {
if (ValidAJAXResponse(DATA)) {
var response = JSON.parse(DATA);
if (response.Result.Success) {
var data = response.Data;
$('#spnBalance').text(data.CurrentBalance);
$('#spnCurrent').text(data.Current);
$('#spn30Days').text(data.Days30);
$('#spn60Days').text(data.Days60);
// we are only going to show up to 90 days of aging, so
// sum up the totals for 90, 120, 150 and 180 to come up
// with the 90 Days+ display number
var days90 = 0;
if (!isNaN(data.Days90Amount)) {
days90 = data.Days90Amount;
}
if (!isNaN(data.Days120Amount)) {
days90 += data.Days120Amount;
}
if (!isNaN(data.Days150Amount)) {
days90 += data.Days150Amount;
}
if (!isNaN(data.Days180Amount)) {
days90 += data.Days180Amount;
}
// load the sum into the text, it'll be formatted as currency because #spn90Days has class of .format-currency on it
$('#spn90Days').text(days90);
if (data.CustomerOnHold) {
$('#spnDunningMessage')
.text('Account On Hold')
.css('color', 'red');
}
if (data.CurrentBalanceAmount > 0 && data.CurrentBalanceAmount - data.CurrentAmount > 0) {
$('#spnDunningMessage')
.text('Account Past Due')
.css('color', 'red');
} else {
var availableCredit = data.CreditLimitAmount - data.CurrentBalanceAmount;
$('#spnDunningMessage')
// .html('Available Credit: <span class=\'format-currency\'>' + availableCredit + '</span>');
if (availableCredit < 0) {
$('#spnDunningMessage').css('color', 'red');
}
}
PayButton.option('onClick', function (e) {
InvoicesToPay = [];
InvoiceGrid.selectAll();
setTimeout(() => { ShowPaymentPopup(data.CurrentBalanceAmount, false) }, 40);
});
if (data.CurrentBalanceAmount <= 0 || !(PreferredPaymentType === 'BA' || PreferredPaymentType === 'CC')) {
PayButton.option('visible', false);
}
$('#divSummary').css('display', 'block');
InvoiceGrid.option('visible', true);
Aging = data;
PretifyNumbers();
}
}
//
// Get Open Invoices
//
MakeAJAXCall('Billing.GetCustomerInvoices', {}, function (DATA) {
if (ValidAJAXResponse(DATA)) {
var response = JSON.parse(DATA);
if (response.Result.Success) {
Invoices = response.Data;
InvoiceGrid.option('dataSource', Invoices);
BillingLoadPanel.hide();
// get url and eveluate for direct link
if (firstLoad) {
var pagePath = window.location.pathname.toLowerCase();
if (pagePath.indexOf(String.Format('{0}/pay/', SITE_PATHS.application).toLowerCase()) > -1) {
var directPayInvoiceNumber = DOMPurify.sanitize(pagePath.substring(pagePath.lastIndexOf('/') + 1));
if (directPayInvoiceNumber !== null && directPayInvoiceNumber !== '') {
var balDue = GetPropertyValueFromArrayByKeyValue(Invoices, 'InvoiceNumber', directPayInvoiceNumber, 'CurrentBalanceAmount');
if (balDue !== '') {
if (balDue <= 0) {
var alertResult = DevExpress.ui.dialog.alert('Invoice number <b>' + directPayInvoiceNumber + '</b> does not have a balance due.<br><br>Please review the list of open invoices to make a payment.', 'Message');
alertResult.done(function () {
// nothing to do here
});
}
else {
InvoicesToPay = [{
Invoice: directPayInvoiceNumber,
AmountToPay: balDue
}];
ShowPaymentPopup(balDue, true);
}
}
else {
var alertResult = DevExpress.ui.dialog.alert('Sorry, invoice number <b>' + directPayInvoiceNumber + '</b> could not be found.<br><br>Please review the list of open invoices to make a payment.', 'Message');
alertResult.done(function () {
// nothing to do here
});
}
}
}
}
} else {
// handle if the call is not successful by alerting and redirecting to my account
var alertResult = DevExpress.ui.dialog.alert('Sorry, this feature is not currently available for your account.', 'Message');
alertResult.done(function () {
BillingLoadPanel.option('message', 'Redirecting...');
document.location.href = SITE_PATHS.application + '/CustomerMyAccount.aspx'
});
}
}
});
});
}
</script>
<style>
.dx-datagrid-headers {
color: #333;
font-weight: 700;
}
span#spnBalance {
line-height: 40px;
}
.dx-popup-title {
background: #333;
color: #fff;
}
.dx-popup-content {
justify-content: space-around;
}
.dx-button-mode-contained .dx-icon,
.dx-button-mode-text .dx-icon {
color: #fff;
}
.dx-button-mode-contained.dx-button-default {
background: #106e0b;
}
.dx-button-mode-contained.dx-button-default.dx-state-hover {
background: #0f640a;
}
/* .invoice-box {
max-width: 800px;
margin: auto;
padding: 30px;
border: 1px solid #eee;
box-shadow: 0 0 10px rgba(0, 0, 0, .15);
font-size: 16px;
line-height: 24px;
font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;
color: #555;
}*/
.invoice-box table {
width: 100;
line-height: inherit;
text-align: left;
}
.invoice-box table td {
padding: 5px;
vertical-align: top;
}
.invoice-box table tr td:nth-child(2) {
text-align: right;
}
.invoice-box table tr.top table td {
padding-bottom: 20px;
}
.invoice-box table tr.top table td.title {
font-size: 45px;
line-height: 45px;
color: #333;
}
.invoice-box table tr.information table td {
padding-bottom: 40px;
}
.invoice-box table tr.heading td {
background: #eee;
border-bottom: 1px solid #ddd;
font-weight: bold;
}
.invoice-box table tr.details td {
padding-bottom: 20px;
}
.invoice-box table tr.item td {
border-bottom: 1px solid #eee;
}
.invoice-box table tr.item.last td {
border-bottom: none;
}
.invoice-box table tr.total td:nth-child(2) {
border-top: 2px solid #eee;
font-weight: bold;
}
a.dx-link.dx-icon.fa.fa-file-text-o:focus {
outline: 0px auto -webkit-focus-ring-color;
}
@media only screen and (max-width: 600px) {
.invoice-box table tr.top table td {
width;
display: block;
text-align: center;
}
.invoice-box table tr.information table td {
width: 100;
display: block;
text-align: center;
}
}
/** RTL **/
.rtl {
direction: rtl;
font-family: 'Open Sans', Arial, sans-serif;
}
.rtl table {
text-align: right;
}
.rtl table tr td:nth-child(2) {
text-align: left;
}
.divTable {
display: table;
width;
}
.divTableRow {
display: flex;
/*table-row*/
justify-content: space-between;
}
.divTableRow.headerRow {
background-color: #106e0b;
color: #fff;
}
.divTableHeading {
background-color: #EEE;
display: table-header-group;
}
.divTableCell.logo {
background-image: url(/ecommerce/site/themes/images/logo.png);
background-repeat: no-repeat;
width: 250px;
height: 100px;
background-size: 100;
margin: 0 0px;
min-height: 10px;
max-height: 180px;
}
.divTableCell {
display: table-cell;
padding: 5px 10px;
}
.divTableHead {
display: table-cell;
font-weight: bold;
font-weight: bold;
}
.divTableRight {
text-align: right;
}
.divTableBody {
display: table-row-group;
}
.pseudocell {
display: inline-block;
width: 100px;
text-align: left;
}
.divTableCell.StockCode,
.divTableCell.divTableHead.divTableItem {
min-width;
}
.divTableCell.StockDes,
.divTableCell.divTableHead.divTableDesc {
min-width: 35;
}
.divTableCell.Qty,
.divTableCell.divTableHead.divTableQTY {
min-width;
text-align: center;
}
.divTableCell.Price,
.divTableCell.divTableHead.divTableEA {
min-width: 15;
text-align: right;
}
.divTableCell.Gross,
.divTableCell.divTableHead.divTablePrice {
min-width;
text-align: right;
}
.divTableCell.orderTotal {
font-weight: 700;
}
.dx-toolbar-label > div {
overflow: visible;
width: fit-content;
}
</style>
<link href="/ECommerce/site/Themes/css/widget.css" rel="stylesheet">