I have a form that accepts currency from several fields and depending on what I enter into one, it calculates others. For this most part the code below works (example being the invoice amount field)
<xp:inputText value="#{FInvoiceDoc.InvoiceAmount}" id="InvoiceAmount">
<xp:this.converter>
<xp:convertNumber type="currency"></xp:convertNumber></xp:this.converter>
<xp:eventHandler event="onchange" submit="false" id="eventHandler5">
<xp:this.script><![CDATA[var rate = XSP.getElementById("#{id:ExchangeRate}").value;
var stAmount = XSP.getElementById("#{id:InvoiceAmount}").value;
var stTvat = XSP.getElementById("#{id:TVAT}").value;
var stTst = XSP.getElementById("#{id:TST}").value;
var stToop = XSP.getElementById("#{id:Toop}").value;
var tmp;
// get the dojo currency code
dojo.require("dojo.currency");
// get the numeric values using parse
amount = dojo.currency.parse(stAmount,{currency:"USD"});
total = rate * amount;
XSP.getElementById("#{id:EstUSAmount}").innerHTML = dojo.currency.format(total,{currency:"USD"});
XSP.getElementById("#{id:InvoiceAmount}").value = dojo.currency.format(amount,{currency:"USD"});
if (amount != 0) {
tvat = dojo.currency.parse(stTvat,{currency:"USD"});
tst = dojo.currency.parse(stTst,{currency:"USD"});
toop = dojo.currency.parse(stToop,{currency:"USD"});
tmp = (tvat / (amount-tvat-tst-toop)) * 100;
XSP.getElementById("#{id:VP}").innerHTML = tmp.toFixed(2);
tmp = (tst / (amount-tvat-tst-toop)) * 100;
XSP.getElementById("#{id:STP}").innerHTML = tmp.toFixed(2);
tmp = (toop / (amount-tvat-tst-toop)) * 100;
XSP.getElementById("#{id:OoPP}").innerHTML = tmp.toFixed(2);
}
]]></xp:this.script>
</xp:eventHandler>
</xp:inputText>
Like I said, for the most part this works, but if I enter only a partial number like
200.2
instead of
200.20
then it fails. Most data entry folks will want to not have to key in the last "0" just so it's legal.
btw, if I enter it in as just 200 with no cents, it's OK.
It's as if the statement;
amount = dojo.currency.parse(stAmount,{currency:"USD"});
requires a 2 digit penny amount or no pennys, but doesn't like just the leading cents digit.
Any way around this?
The currency format is very restrictive and needs the certain number of decimal places for a given currency. As you can see here there can be different number of decimal places depending on currency. For most currencies it needs two decimal places though. But, you can trick the parser. Just try it with standard number of decimal places and if it fails try it with one decimal place again. Your code would look like this then:
var amount = dojo.currency.parse(stAmount,{currency:"USD"});
if (!amount && amount!==0) {
amount = dojo.currency.parse(stAmount,{currency:"USD",places:"1"});
}
This accepts inputs like
12
12.3
12.34
$12
$12.3
$12.34
But it is still very picky. It doesn't accept spaces or more decimal places then currency allows.
If you want more flexibility for your users and need USD currency only I'd go for dojo.number or other number parsing instead and show the "$" outside the input field. Then, you'd be able to accept much more formats and could add functions like rounding.
Yeah, I've played with the converters. Thing is I want the converter to stay "Currency". The data I receive from the notes database is always correct and formats properly. Its the manipulation that I''m having a problem with. Since this is all on the client side, I've created a function that I can reuse throughout the page.
<script>
function isNumeric(n) {
n = parseFloat(n);
return !isNaN(n) || n != n;
}
// We call this if its a currency amount. If they only entered one number after
// the decimal, then we just append a 0. We're expecting a string and we send
// the string back.
function cMask(Amount)
{
if (Amount == "") { return "0.00" }
a = Amount.split(".");
if (a.length == 2)
{
if (a[1] != null)
{
if (a[1].length == 1)
{
Amount = Amount + "0";
}
}
}
// get the dojo currency code
dojo.require("dojo.currency");
b = dojo.currency.parse(Amount,{currency:"USD"});
if (isNumeric(b)) {return Amount} else {return "0.00"}
}
</script>
Then its a matter of just changing the initial variable load line.
var stAmount = cMask(XSP.getElementById("#{id:InvoiceAmount}").value);
Seems to work and I've just taught myself how to create reusable client-side javascript for my page.
Related
I found this Gist for money formatting on Shopify using JavaScript https://gist.github.com/stewartknapman/8d8733ea58d2314c373e94114472d44c
I placed it in my cart page and when I try:
Shopify.formatMoney(2000, '$')
I get this:
cart:2166 Uncaught TypeError: Cannot read property '1' of null
at Object.Shopify.formatMoney (cart:2166)
at <anonymous>:1:9
this is at switch(formatString.match(placeholderRegex)[1]) {
I expect to get $20.00
Do you know where the problem is?
Similar question: Shopify Buy Button Error: "cannot read property '1' of null"
The Gist content
var Shopify = Shopify || {};
// ---------------------------------------------------------------------------
// Money format handler
// ---------------------------------------------------------------------------
Shopify.money_format = "${{amount}}";
Shopify.formatMoney = function(cents, format) {
if (typeof cents == 'string') { cents = cents.replace('.',''); }
var value = '';
var placeholderRegex = /\{\{\s*(\w+)\s*\}\}/;
var formatString = (format || this.money_format);
function defaultOption(opt, def) {
return (typeof opt == 'undefined' ? def : opt);
}
function formatWithDelimiters(number, precision, thousands, decimal) {
precision = defaultOption(precision, 2);
thousands = defaultOption(thousands, ',');
decimal = defaultOption(decimal, '.');
if (isNaN(number) || number == null) { return 0; }
number = (number/100.0).toFixed(precision);
var parts = number.split('.'),
dollars = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + thousands),
cents = parts[1] ? (decimal + parts[1]) : '';
return dollars + cents;
}
switch(formatString.match(placeholderRegex)[1]) {
case 'amount':
value = formatWithDelimiters(cents, 2);
break;
case 'amount_no_decimals':
value = formatWithDelimiters(cents, 0);
break;
case 'amount_with_comma_separator':
value = formatWithDelimiters(cents, 2, '.', ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = formatWithDelimiters(cents, 0, '.', ',');
break;
}
return formatString.replace(placeholderRegex, value);
};
I found formatMoney function in my theme and I was able to call it and it worked.
pipelineVendor.themeCurrency.formatMoney(2000);
result: $20.00
It's probably too late for the original asker, but for anyone else looking for an answer, I believe the correct call for this function is:
Shopify.formatMoney(2000, '${{amount}}')
instead of
Shopify.formatMoney(2000, '$')
based on the comment of the gist author on Github:
It doesn’t actually care what the currency is. The first argument is
the unformatted amount. The second argument is the format, which could
be whatever you need for the currently displayed currency i.e. “£{{
amount }}”. Then pass it a new amount and new format when you change
the currency.
In my app i use ios-charts library (swift alternative of MPAndroidChart).
All i need is to display line chart with dates and values.
Right now i use this function to display chart
func setChart(dataPoints: [String], values: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = ChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let lineChartDataSet = LineChartDataSet(yVals: dataEntries, label: "Items count")
let lineChartData = LineChartData(xVals: dataPoints, dataSet: lineChartDataSet)
dateChartView.data = lineChartData
}
And this is my data:
xItems = ["27.05", "03.06", "17.07", "19.09", "20.09"] //String
let unitsSold = [25.0, 30.0, 45.0, 60.0, 20.0] //Double
But as you can see - xItems are dates in "dd.mm" format. As they are strings they have same paddings between each other. I want them to be more accurate with real dates. For example 19.09 and 20.09 should be very close. I know that i should match each day with some number in order to accomplish it. But i don't know what to do next - how i can adjust x labels margins?
UPDATE
After small research where i found out that many developers had asked about this feature but nothing happened - for my case i found very interesting alternative to this library in Swift - PNChart. It is easy to use, it solves my problem.
The easiest solution will be to loop through your data and add a ChartDataEntry with a value of 0 and a corresponding label for each missing date.
In response to the question in the comments here is a screenshot from one of my applications where I am filling in date gaps with 0 values:
In my case I wanted the 0 values rather than an averaged line from data point to data point as it clearly indicates there is no data on the days skipped (8/11 for instance).
From #Philipp Jahoda's comments it sounds like you could skip the 0 value entries and just index the data you have to the correct labels.
I modified the MPAndroidChart example program to skip a few data points and this is the result:
As #Philipp Jahoda mentioned in the comments the chart handles missing Entry by just connecting to the next data point. From the code below you can see that I am generating x values (labels) for the entire data set but skipping y values (data points) for index 11 - 29 which is what you want. The only thing remaining would be to handle the x labels as it sounds like you don't want 15, 20, and 25 in my example to show up.
ArrayList<String> xVals = new ArrayList<String>();
for (int i = 0; i < count; i++) {
xVals.add((i) + "");
}
ArrayList<Entry> yVals = new ArrayList<Entry>();
for (int i = 0; i < count; i++) {
if (i > 10 && i < 30) {
continue;
}
float mult = (range + 1);
float val = (float) (Math.random() * mult) + 3;// + (float)
// ((mult *
// 0.1) / 10);
yVals.add(new Entry(val, i));
}
What I did is fully feed the dates for x data even no y data for it, and just not add the data entry for the specific xIndex, then it will not draw the y value for the xIndex to achieve what you want, this is the easiest way since you just write a for loop and continue if you detect no y value there.
I don't suggest use 0 or nan, since if it is a line chart, it will connect the 0 data or bad things will happen for nan. You might want to break the lines, but again ios-charts does not support it yet (I also asked a feature for this), you need to write your own code to break the line, or you can live with connecting the 0 data or just connect to the next valid data.
The down side is it may has performance drop since many xIndex there, but I tried ~1000 and it is acceptable. I already asked for such feature a long time ago, but it took lot of time to think about it.
Here's a function I wrote based on Wingzero's answer (I pass NaNs for the entries in the values array that are empty) :
func populateLineChartView(lineChartView: LineChartView, labels: [String], values: [Float]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<labels.count {
if !values[i].isNaN {
let dataEntry = ChartDataEntry(value: Double(values[i]), xIndex: i)
dataEntries.append(dataEntry)
}
}
let lineChartDataSet = LineChartDataSet(yVals: dataEntries, label: "Label")
let lineChartData = LineChartData(xVals: labels, dataSet: lineChartDataSet)
lineChartView.data = lineChartData
}
The solution which worked for me is splitting Linedataset into 2 Linedatasets. First would hold yvals till empty space and second after emptyspace.
//create 2 LineDataSets. set1- till empty space set2 after empty space
set1 = new LineDataSet(yVals1, "DataSet 1");
set2= new LineDataSet(yVals2,"DataSet 1");
//load datasets into datasets array
ArrayList<ILineDataSet> dataSets = new ArrayList<ILineDataSet>();
dataSets.add(set1);
dataSets.add(set2);
//create a data object with the datasets
LineData data = new LineData(xVals, dataSets);
// set data
mChart.setData(data);
Has anyone tried to create a plugin that updates the record's values in the Quote Product form? I created one, because I need a custom formula that calculates the Extended Amount field, but there are automatic calculations in the CRM that fill these fields. This doesn't allow me to update the formula of calculation at all.
What my plugin do, is:
Gets the values from the fields Price per unit, Quantity and Discount % (which is a custom field);
Calculates the value that I need;
Sets it at the extended amount field.
But, none of this works because I get a "Business Process Error";
Basically this error tells me that I can't use the record's guid to access it.
Here is my code:
protected void ExecutePostQuoteProductUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
Guid quoteProductID = (Guid)((Entity)context.InputParameters["Target"]).Id;
ColumnSet set = new ColumnSet();
set.AllColumns = true;
var quote = service.Retrieve("quotedetail", quoteProductID, set);
var priceperunit = quote.Attributes["priceperunit"];
var teamleader = quote.Attributes["new_disc"];
var manualdiscountamount = quote.Attributes["manualdiscountamount"];
var volumediscountamount = quote.Attributes["volumediscountamount"];
var VAT = (int)quote.Attributes["new_vat"];
var discountamount = (double)priceperunit * (double)teamleader / 100;
var baseamount = (double)priceperunit - discountamount;
var tax = baseamount * VAT / 100;
var extendedamount = baseamount + tax;
quote.Attributes["new_discountamount"] = discountamount;
quote.Attributes["baseamount"] = baseamount;
quote.Attributes["tax"] = tax;
quote["description"] = priceperunit;
quote.Attributes["extendedamount"] = extendedamount;
service.Update(quote);
}
Please, tell me if there is a way to access those fields and use/set them.
Thanks in Advance!
:/
You cannot update extendedamount directly - instead if you use the built-in fields which automatically calculate it then CRM will take care of this for you.
Those fields are: baseamount, quantity, discountamount, and tax. These are Money fields so you will need to store them as such:
quote.Attributes["baseamount"] = new Money(baseamount); // Money and decimal numbers use the "decimal" datatype and not double.
As for your custom calculation, you just need to convert the percentage stored in your custom field into the actual amount and assign it to the discountamount attribute. Something like:
var discountamount = (decimal)priceperunit * (decimal)teamleader / 100;
// Assigning the actual discount amount will automatically recalculate the extended amount on the line.
// No need to recalculate all fields.
quote.Attributes["discountamount"] = new Money(discountamount);
Note that tax may need to be recalculated to reflect the discount, but the process should work exactly the same.
http://jsfiddle.net/2e7gq/633/
I am trying to create a slider that will display 3 html input boxes.
Quantity
Price per unit
Total (quantity*price per unit)
the goal is to change var price per unit when the slider slides to increments of 50.
thanks!
I am new to programming here is my code:
`$(function() {
var inputValue = $(".mpd_value").val();
$( ".mpq_slider" ).slider({
min:50,
max:50000,
step:50,
value:50,
slide:function(event,ui){
$(".mpd_value").val(ui.value);
$(function(){
calculate();
});
}
});
$(".mpq_value").val($(".mpq_slider").slider("value"));
$(".mpq_value").change(function(event){
$(".mpq_slider").slider("option","value",inputValue);
})
function calculate(){
var quantity = $(".mpd_value").val();
var ppu = .84;
var total = quantity*ppu
$("#total").val(total);
$("#ppu").val(ppu);
};
});
It seems that you're missing a <div class="mpq_slider"></div>. Put it as the first line in the HTML spot. This is the part that makes room for the actual slider. Also, if I understand what you are trying to do correctly, you want the Quantity to increase with the slider and the ppu to change also (like things gets cheaper the more you buy)? If so, you are using the switch statement incorrectly. You could, instead do something with ifs and else ifs:
var ppu = defaultValue;
if (quantity <= 50) {
ppu = 1.117;
}
else if (quantity <= 75) {
ppu = .941;
}
...
Also note how I changed the >= into <=, since that'll make the program check in the right order. Here's what I changed to your code http://jsfiddle.net/2e7gq/637/ .
Now that I think about it, there's another way too. Y&ou can keep the switch if it says switch(true){ and then change all the >=s into <=s.
Glad to hear you're learning Javascript!
I have a SQL query that is pulling back results ordered correctly when I try the query in MSSQL Studio.
I am using Datatables from Datatables.net and everything is working great apart from a sorting issue. I have some properties in the first column and I would like to order these like this:
1
1a
1b
2
3
4a
5
etc
However what comes back is something like this:
1
10
100
11
11a
I have looked though various posts but nothing seems to work and I believe that this must be something I should trigger from the datatables plugin but cannot find anything.
Could someone advise?
Your data contain numbers and characters, so they will be sorted as string by default. You should write your own plugin for sorting your data type. Have a look at here and here
to see how to write a plugin and how to use it with your table.
Edit: got some time today to work with the datatable stuff. If you still need a solution, here you go:
//Sorting plug-in
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
//pre-processing
"numchar-pre": function(str){
var patt = /^([0-9]+)([a-zA-Z]+)$/; //match data like 1a, 2b, 1ab, 100k etc.
var matches = patt.exec($.trim(str));
var number = parseInt(matches[1]); //extract the number part
var str = matches[2].toLowerCase(); //extract the "character" part and make it case-insensitive
var dec = 0;
for (i=0; i<str.length; i++)
{
dec += (str.charCodeAt(i)-96)*Math.pow(26, -(i+1)); //deal with the character as a base-26 number
}
return number + dec; //combine the two parts
},
//sort ascending
"numchar-asc": function(a, b){
return a-b;
},
//sort descending
"numchar-desc": function(a, b){
return b-a;
}
});
//Automatic type detection plug-in
jQuery.fn.dataTableExt.aTypes.unshift(
function(sData)
{
var patt = /^([0-9]+)([a-zA-Z]+)$/;
var trimmed = $.trim(sData);
if (patt.test(trimmed))
{
return 'numchar';
}
return null;
}
);
You can use the automatic type detection function to let the data type automatically detected or you can set the data type for the column
"aoColumns": [{"sType": "numchar"}]