Invoices in much of Latin America and Europe must state the total in words as well as figures — “TWO THOUSAND FOUR HUNDRED FIFTY 00/100 MXN”. Zoho Books has no built-in field for it. This is a pure Deluge implementation, so it works offline, costs nothing, and cannot break because someone else’s website went down.
The external dependency is gone. The original called http://beautifytools.com/num-to-word.php — a third-party website, over plain HTTP, in the middle of your invoicing. That is a hard dependency on a stranger for a calculation your own code can do in thirty lines, and it puts invoice totals across the public internet unencrypted. If that site changes its response format or disappears, your invoices silently lose their amount-in-words. It has been replaced with a self-contained function. The original also called zoho.books.updateRecord without the now-mandatory connection argument.
Create this as a standalone function. It handles 1–999, which is the only hard part; everything above is repetition.
string Utils.hundredsToWords(int n)
{
ones = {"","one","two","three","four","five","six","seven","eight","nine","ten",
"eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen",
"eighteen","nineteen"};
tens = {"","","twenty","thirty","forty","fifty","sixty","seventy","eighty","ninety"};
remainder = n;
out = "";
if(remainder >= 100)
{
// Subtract the remainder first so the division is exact
hundreds = ((remainder - (remainder % 100)) / 100).toLong();
out = ones.get(hundreds) + " hundred";
remainder = remainder % 100;
if(remainder > 0)
{
out = out + " ";
}
}
if(remainder >= 20)
{
tensDigit = ((remainder - (remainder % 10)) / 10).toLong();
out = out + tens.get(tensDigit);
if(remainder % 10 > 0)
{
out = out + "-" + ones.get(remainder % 10);
}
}
else if(remainder > 0)
{
out = out + ones.get(remainder);
}
return out;
}This walks the number in groups of three and tags each with its scale.
string Utils.numberToWords(int n)
{
if(n == 0)
{
return "zero";
}
scales = {""," thousand"," million"," billion"};
parts = List();
remainder = n;
idx = 0;
while(remainder > 0)
{
chunk = remainder % 1000;
if(chunk > 0)
{
parts.add(thisapp.Utils.hundredsToWords(chunk) + scales.get(idx));
}
remainder = ((remainder - chunk) / 1000).toLong();
idx = idx + 1;
}
// parts were built smallest-first, so walk it backwards
out = "";
i = parts.size() - 1;
while(i >= 0)
{
out = out + parts.get(i);
if(i > 0)
{
out = out + " ";
}
i = i - 1;
}
return out;
}// Books custom function, triggered on invoice creation
invoiceId = invoice.get("invoice_id");
invoiceTotal = invoice.get("total");
currencyCode = invoice.get("currency_code");
wholePart = invoiceTotal.toLong();
cents = ((invoiceTotal - wholePart) * 100).round(0).toLong();
centsText = cents.toString();
if(centsText.length() == 1)
{
centsText = "0" + centsText;
}
amountInWords = thisapp.Utils.numberToWords(wholePart).toUpperCase()
+ " " + centsText + "/100 " + currencyCode;
customField = Map();
customField.put("customfield_id","YOUR_CUSTOM_FIELD_ID");
customField.put("value",amountInWords);
customFields = List();
customFields.add(customField);
updateMap = Map();
updateMap.put("custom_fields",customFields);
resp = zoho.books.updateRecord("invoices","YOUR_BOOKS_ORG_ID",invoiceId,updateMap,"zoho_books_connection");
info resp;/ does not always return an integer, and a decimal index throws when you pass it to list.get(). Removing the remainder first makes the division exact, and .toLong() guarantees the type. This is the bug people hit when they port this from another language."5" for 10.50 and "" for a whole number. Multiplying the fractional part by 100 and rounding is correct in both cases.{"","uno","dos","tres"...} and {"","","veinte","treinta"...}. The structure holds; Spanish needs a special case for ciento versus cien and the 21–29 contractions.customfield_id, not index. The id is stable; the index shifts the moment you reorder your custom fields.This script is part of the free Creator Scripts Deluge Library.
All 39 Deluge scripts, the full Zoho Creator course, and every downloadable asset are now free. Get free access →