Hi,
One of the developers created an HTML button and an HTML web resource that gets data from quote and calls an shipping API web service. I see the data render, but not my users. I don't see where we are using a custom entity, what security addition do I need to give the users? They can see data in an another HTML web resource without any additional security, what am I missing?
// JavaScript source code
<html><!-- NOTE: Dynamics CRM deletes the <!Doctype HTML> tag when saving the file using the Text Editor. This tag is needed for the page to render in the proper HTML version. To preserve this tag, the file must be uploaded rather than changed in the Text Editor. If the tag is missing, add it above the <html> tag. --><head>
<title></title>
<script src="ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="dm_/Common/jquery_1.10.2.js" type="text/javascript"></script>
<script src="dm_/Common/XrmServiceToolkit.js" type="text/javascript"></script>
<script src="">xrmtoolscode.azureedge.net/CRMWebAPI.js" type="text/javascript"></script>
<script src="">www.promisejs.org/.../script>
<script type="text/javascript">
function SetRate(rate, name, freightQuoteId) {
//debugger;
var page = window.opener.parent.Xrm.Page;
page.getAttribute('freightcost').setValue(rate);
page.getAttribute('new_freightservice').setValue(name);
page.getAttribute('frtbsrtqte').setValue(freightQuoteId);
page.data.save();
window.close();
}
(function () {
var _qParams;
var _quoteId;
var _productsUpdateReceived = false;
var _data = {
Quantity: 0,
Weight: 0.0,
false: {
Updated: false, Weight: 0.0, Pallets: 0, Rates: {}, ButtonId: "#btnFedex"
},
true: {
Updated: false, Weight: 0.0, Pallets: 0, Rates: {}, ButtonId: "#btnStandard"
},
};
$(window).load(function () {
//debugger;
_qParams = new URLSearchParams(window.location.search);
_quoteId = GetParam("id");
document.getElementById("btnStandard").addEventListener('click', function () { GetData(true); });
document.getElementById("btnFedex").addEventListener('click', function () { GetData(false); });
GetData(null);
});
function GetData(standard) {
log("Getting quote data for " + _quoteId + "...");
XrmServiceToolkit.Soap.Retrieve("quote", _quoteId, null,
function (quote) {
if (quote) {
//debugger;
var dateText = getAV(quote, "new_shippingdate");
if (Date.parse(dateText) > 0) {
_data.Date = new Date(dateText);
}
else {
_data.Date = new Date();
}
setFieldText("contact", quote, "customerid");
setFieldText("zip", quote, "billto_postalcode");
log("Getting proposal products for quote " + _quoteId + "...");
XrmServiceToolkit.Soap.Fetch(GetQuoteDetailsQuery(quote.id),
function (quoteDetails) {
if (quoteDetails && quoteDetails.length > 0) {
if (standard != null) {
HandleProposalProducts(quote, quoteDetails, standard);
}
} // some products
else {
log("No proposal products found for proposal " + quote.id);
}
}
); // XrmServiceToolkit.Soap.Fetch(GetQuoteDetailsQuery
} // quote ok
else {
log("No such quote: " + _quoteId);
}
}
); // XrmServiceToolkit.Soap.Retrieve(quote)
}
function HandleProposalProducts(quote, quoteDetails, standard) {
//debugger;
log("Found proposal products: " + quoteDetails.length);
var products = getProducts(quoteDetails);
RequestShippingInfo(quote, products, standard);
}
function getProducts(quoteDetails) {
//debugger;
var list = [];
for (var i = 0; i < quoteDetails.length; i++) {
var product = quoteDetails[i];
var p = {};
p.Desc = getAV(product, "description"); // PCD DELL 9010 SFF...
p.Key = getAV(product, "productdescription"); // PCD_DELL_9010_SFF_ICi3-3.3 3220
p.Quantity = getAV(product, "quantity", true);
p.Price = getAV(product, "priceperunit", true); // true? it's money, may be tricky
p.Id = product.id;
list.push(p);
}
return list;
}
function RequestShippingInfo(quote, products, standard) {
/*
{"Header":{"City":"Thousand Oaks","Country":"United States","Gaylord":true,
"OrderCrmGuid":"00000000-0000-0000-0000-000000000000","ShipDate":"\/Date(1549926266456)\/",
"State":"CA","StreetAddress":null,"Zip":"91362"},
"Details":[
{"Dimensions":null,"ItemNumber":"DSP_DELL_22\"_WIDE",
"OrderLineGuid":"00000000-0000-0000-0000-000000000000","Price":0,"Qty":11},
{"Dimensions":null,"ItemNumber":"PCD_DELL_9010_SFF_ICi3-3.3 3220",
"OrderLineGuid":"00000000-0000-0000-0000-000000000000","Price":0,"Qty":666}
]}
*/
//debugger;
LaunchRequest(standard);
//LaunchRequest(true);
//LaunchRequest(false);
/////////////////////////
function LaunchRequest(standard) {
var request = getShippingApiRequest(standard);
var json = JSON.stringify(request);
//log("Shipping API request:");
//log(json);
var url = "">xxxxxx.com/.../Shipping";
log("Sending Shipping Api Request to " + url);
$.ajax({
type: "POST",
url: url,
data: json,
contentType: "application/json",
dataType: "text",
success: onSuccess,
failure: onFailure,
error: onFailure
});
}
function onSuccess(dataText, textStatus, jqXHR) {
// this call is used for both Standard and Fedex responses, possibly in parallel
//debugger;
log("Shipping API Success: " + textStatus);
log("Response:");
log(dataText);
// convert data dataText to usable shipping info
var data = JSON.parse(dataText);
if (!data.Success) {
alert(data.Message);
}
// update products weight with shipping RateResponseItemDetails if found
UpdateProducts(data);
// populate _data
UpdateData(data);
/////////////////////////////////////////
function UpdateProducts(data) {
var footer = $("#footer");
if (!_productsUpdateReceived) {
_productsUpdateReceived = true; // so second call does not update it, supposed to be the same
var found = data.RateResponseItemDetails.filter(function (itm) { return itm.ItemWeightFound && itm.ItemPalletCountFound; }
);
for (var i = 0; i < products.length; i++) {
var p = products[i];
var matches = found.filter(function (f) { return f.ItemNumber == p.Key; });
if (matches.length > 0) {
var m = matches[0];
products[i].Weight = m.UnitWeight;
products[i].UnitsPerPalette = m.ItemPalletCount;
if (matches.length > 1) {
log("More than one line returned for product " + p.Key + ": " + matches.length);
}
}
else {
log("No complete shipping info found for product " + p.Key);
}
}
for (var i = 0; i < products.length; i++) {
AddProduct(products[i]);
}
} // no product update yet
///////////////////////////
function AddProduct(p) {
var wht = p.Quantity * p.Weight;
_data.Quantity += p.Quantity;
_data.Weight += wht;
var html = '<div class="white-box-1"><p class="align-left">' + p.Desc + '</p></div>';
html += '<div class="steel-blue-box"><p>' + p.Quantity + '</p></div>';
html += '<div class="steel-blue-box"><p>' + wht + '</p></div>';
footer.before($(html));
}
}
function UpdateData(data) {
if (data.Header) {
var standard = data.Header.Gaylord;
var insideDelivery = data.Header.InsideDelivery;
var liftGate = data.Header.LiftGateDelivery;
var indBoxed = data.Header.IndividuallyBoxed;
if (insideDelivery) { document.getElementById("InsideDelivery").checked = insideDelivery; }
if (liftGate) { document.getElementById("LiftGate").checked = liftGate; }
if (indBoxed) { document.getElementById("IndividuallyBoxed").checked = indBoxed; }
if (!_data[standard].Updated) {
//_data[standard].Updated = true; // so if second call comes with the same key, we ignore it
// Weight: 0.0, Pallets: 0, Rates: {}
_data[standard].Weight = data.TotalWeight; // with pallets
_data[standard].Pallets = data.Pallets ? data.Pallets.length : 0; // it looks like we don't need details
_data[standard].Rates = data.Rates;
// enable button and assign onclick
showShippingRates(standard);
} // this _data section was not updated yet
} // data seems ok
}
}
function onFailure(jqXHR, textStatus, errorThrown) {
//debugger;
alert("Shipping API Error.\r\n" + textStatus);
}
function getShippingApiRequest(standard) {
log("Building shipping API request...");
// populate header
request = {};
if (quote) {
request.Header = getShippingRequestHeader(standard);
request.Details = [];
for (var i = 0; i < products.length; i++) {
var p = products[i];
var d = getShippingRequestDetails(p);
request.Details.push(d);
}
}
return request;
/////////////////////////
function getShippingRequestHeader(standard) {
var hdr = {};
hdr.City = getAV(quote, "billto_city");
hdr.Country = getAV(quote, "billto_country");
hdr.Gaylord = standard
hdr.OrderCrmGuid = quote.id;
hdr.ShipDate = _data.Date;
hdr.State = getAV(quote, "billto_stateorprovince");
hdr.StreetAddress = getAV(quote, "billto_line1");
hdr.Zip = getAV(quote, "billto_postalcode");
hdr.InsideDelivery = $("#InsideDelivery").get(0).checked;
hdr.LiftGateDelivery = $("#LiftGate").get(0).checked;
hdr.IndividuallyBoxed = $("#IndividuallyBoxed").get(0).checked;
return hdr;
}
function getShippingRequestDetails(product) {
var details = {};
details.Dimensions = '';
details.ItemNumber = product.Key;
details.OrderLineGuid = product.Id;
details.Price = product.Price;
details.Qty = product.Quantity;
return details;
}
}
}
function showShippingRates(standard) {
log("Showing shipping rates");
ShowTotals(standard);
ShowRates(standard);
/////////////////////////////////////////
function ShowTotals(standard) {
$("#totalQ").text(_data.Quantity); // so, we overwrite this each time, no biggie
$("#totalW").text(_data.Weight); // so, we overwrite this each time, no biggie
$("#totalP").text(_data[standard].Pallets);
$("#totalWP").text(_data[standard].Weight);
}
function ShowRates(standard) {
var title = standard ? "Standard" : "FedEx";
var html = "";
var shipping = _data[standard];
var allRates = shipping.Rates;
var totalServicesCount = allRates && allRates.length > 0 ? allRates.length : 0;
log("services count: ");
log(totalServicesCount);
if (totalServicesCount > 0) {
var goodRates = allRates.filter(function (r) { return r.Success; });
console.log("Good rates: ");
console.log(goodRates);
if (goodRates.length > 0) {
AddServices(goodRates, true, standard);
} // available services ok
else {
AddServices(allRates, false, standard);
} // no available services
} // total services ok
else {
html += "<span>No shipping services found for: " + title + ".</span>";
} // no services at all
var div = $("#rates");
div.html(html);
return;
///////////////////////////
function AddServices(rates, good) {
console.log("Adding services");
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
var dateText = _data.Date.toLocaleDateString('en-US', options);
html += '<div class="long-box med-grey bottom-margin">';
html += '<p class="align-left">' + title + ' Shipping Options for ' + dateText + '</p>';
html += '</div>';
html += '<div class="long-box med-grey bottom-margin">';
html += '<div class="long-box header-box-2 dark-grey"><p class="white-text">Select</p></div>';
html += '<div class="long-box header-box-3 dark-grey"><p class="white-text">Provider</p></div>';
html += '<div class="long-box header-box-4 dark-grey"><p class="white-text">Service</p></div>';
var text = good ? "Rate" : "Error";
html += '<div class="long-box header-box-5 dark-grey"><p class="white-text">' + text + '</p></div>';
html += '</div>';
html += '<div class="container-2"></div>';
var type = standard ? "UPS" : "FedEx";
for (var i = 0; i < rates.length; i++) {
if (type == rates[i].ProviderName) {
AddService(rates[i], good);
}
}
html += '<div class="clear-float"><p></p></div>';
}
function AddService(rate, good) {
console.log("Adding a service");
// {"ProviderSymbol":"ADSI.PROJECT44.ODFL.ODFL",
// "ProviderName": "Old Dominion",
// "ServiceDescription": "Old Dominion",
// "Rate": 440.98,
// "Message": null,
// "Success": true
var text = good ? ('<button data-symbol="' + rate.ProviderSymbol + '" onclick="SetRate(' + rate.Rate + ',\'' + rate.ServiceDescription + '\',\'' + rate.FreightQuoteId + '\')">Select</button>') : '<span>N/A</span>';
html += '<div class="white-box-2 wb-fix">' + text + '</div>';
html += '<div class="white-box-3"><p>' + rate.ProviderName + '</p></div>';
html += '<div class="white-box-4"><p>' + rate.ServiceDescription + '</p></div>';
if (good) {
html += '<div class="white-box-5"><p>' + rate.Rate + '</p></div>';
}
else {
html += '<div class="white-box-5"><p>' + rate.Message + '</p></div>';
}
}
}
}
function getAV(e, attr, getValue) {
var text = "Error: Empty entity data";
if (e) {
var a = e.attributes[attr];
if (a) {
var text = a.formattedValue && !getValue ? a.formattedValue : a.value;
}
else {
text = 'No such attribute: ' + attr;
}
}
return text;
}
function setFieldText(id, e, attr) {
var field = $("#" + id);
var text = getAV(e, attr);
field.text(text);
}
function GetQuoteDetailsQuery(id) {
var xml =
"<fetch mapping='logical'>" +
"<entity name='quotedetail'>" +
"<all-attributes />" +
"<filter type='and'>" +
"<condition attribute='quoteid' operator='eq' value='" + id + "' />" +
"</filter>" +
"</entity>" +
"</fetch>";
return xml;
}
function GetParam(name) {
log("Getting parameter '" + name + "'");
var value = _qParams.get(name);
log(name + "=" + value);
return value;
}
function log(text) {
window.console && console.log(text);
}
})();
</script>
<style>
p {
margin: 8px;
}
body {
width: 100%;
min-width: 900px;
}
.align-left {
text-align: left;
padding-left: 12px;
}
.blank-box {
float: left;
width: 70%;
height: 10%;
}
.button-lg {
padding: 6px;
margin: 4px;
font-size: 12pt;
}
.bottom-margin {
margin: 0px 0px 4px 0px;
}
.clear-float {
clear: both !important;
}
.contact-box {
width: auto !important;
min-width: 9%;
}
.container-1 {
margin-top: 20px;
}
.container-2 {
margin-left: 0.5%;
}
.header-box-0 {
width: 80.7% !important;
float: left;
margin: 4px 0px 4px 4px;
}
.header-box-1 {
float: left;
margin: 4px 0px;
}
.header-box-2 {
width: 10% !important;
float: left;
margin: 4px 0px 4px 4px;
}
.header-box-3 {
width: 10% !important;
float: left;
margin: 4px 0px;
}
.header-box-4 {
width: 58.9% !important;
float: left;
margin: 4px 0px;
}
.header-box-5 {
width: 19.7% !important;
float: left;
margin: 4px 0px;
}
.long-box {
width: 100%;
height: 6%;
border: 1px solid #d8d8d8;
display: inline-block;
}
.outer-box {
text-align: center;
border: 1px solid #d8d8d8;
padding: 8px;
width: 96%;
}
.padded {
padding: 4px;
}
.short-box {
width: 9%;
height: 4%;
float: left;
border: 1px solid #d8d8d8;
}
.total-container {
float: right;
width: 25%;
min-width: 242px;
margin-right: 0.9%;
}
.total-box-labels {
width: 60%;
min-width: 160px;
height: 4%;
float: right;
border: 1px solid #d8d8d8;
}
.total-box-results {
width: 30%;
min-width: 78px;
height: 4%;
float: right;
border: 1px solid #d8d8d8;
}
.wb-fix {
min-height: 34.4px;
}
.white-box-1 {
border: 1px solid #d8d8d8;
float: left;
width: 80%;
margin-left: 0.7%;
height: 10%;
background-color: white;
}
.white-box-2 {
float: left;
width: 10%;
height: 10%;
background-color: white;
border: 1px solid black;
}
.white-box-2 > button {
margin: 6.2px;
}
.white-box-3 {
float: left;
width: 10%;
height: 10%;
background-color: white;
border: 1px solid black;
}
.white-box-4 {
float: left;
width: 59.2%;
height: 10%;
background-color: white;
border: 1px solid black;
}
.white-box-5 {
float: left;
width: 19.5%;
height: 10%;
background-color: white;
border: 1px solid black;
}
.steel-blue-box {
width: 8%;
height: 4%;
float: left;
border: 1px solid #d8d8d8;
margin-left: 1%;
background-color: #dae2f3;
}
.dark-grey {
background-color: #bfbfbf;
}
.med-grey {
background-color: #d8d8d8;
}
.light-grey {
background-color: #f2f2f2;
}
.steel-blue {
background-color: #dae2f3;
}
.link-text {
color: #5176c4;
}
.white-text {
color: white;
}
</style>
<meta charset="utf-8">
<meta charset="utf-8"></head>
<body style="overflow-wrap: break-word;" dir="LTR" onfocusout="parent.setEmailRange();" lang="en-US">
<div class="outer-box light-grey" style="font-family: undefined;">
<div class="long-box med-grey bottom-margin"><p class="align-left">Client Info</p></div>
<div class="short-box dark-grey bottom-margin"><p class="white-text">Name</p></div>
<div class="short-box contact-box bottom-margin"><p class="link-text" id="contact">...</p></div>
<div class="short-box dark-grey bottom-margin"><p class="white-text">ZIP</p></div>
<div class="short-box contact-box bottom-margin"><p class="link-text" id="zip">...</p></div>
<div class="container">
<h3>
<label class="checkbox-inline">
<input id="InsideDelivery" type="checkbox" value="">Inside Delivery
</label>
<label class="checkbox-inline">
<input id="LiftGate" type="checkbox" value="">Lift Gate
</label>
<label class="checkbox-inline">
<input id="IndividuallyBoxed" type="checkbox" value="">Individually Boxed (LTL)
</label>
</h3>
<div class="long-box med-grey bottom-margin" id="hdr">
<div class="long-box header-box-0 dark-grey"><p class="white-text">Product Description</p></div>
<div class="short-box header-box-1 dark-grey"><p class="white-text">QTY</p></div>
<div class="short-box header-box-1 dark-grey"><p class="white-text">Total Weight (LBS)</p></div>
</div>
<div class="blank-box" id="footer" style="width: 100%"><p style="margin-top: 50px;"> </p></div>
<!-- These float right, which is why the div order is reversed -->
<div class="total-container">
<div class="total-box-results"><p class="link-text" id="totalQ">...</p></div>
<div class="total-box-labels dark-grey"><p class="white-text">Total Quantity</p></div>
<div class="total-box-results"><p class="link-text" id="totalW">...</p></div>
<div class="total-box-labels dark-grey"><p class="white-text">Total Weight</p></div>
<div class="total-box-results"><p class="link-text" id="totalP">...</p></div>
<div class="total-box-labels dark-grey"><p class="white-text">Pallets</p></div>
<div class="total-box-results"><p class="link-text" id="totalWP">...</p></div>
<div class="total-box-labels dark-grey"><p class="white-text">Weight With Pallets</p></div>
</div>
<div class="clear-float">
<button class="button-lg" id="btnStandard">Standard Shipping</button>
<button class="button-lg" id="btnFedex">FedEx Unit</button>
</div>
</div>
<div class="outer-box container-1 light-grey" id="rates"></div>
</div>
</body></html>
*This post is locked for comments