Today I will introduce a simple solution to do international phone number parsing, validation and formatting in Infor Smart Office. The goal is to validate phone numbers entered by users in Infor M3 Programs such as M3 Customer – CRS610/E, or in Infor Customer Lifecycle Management (CLM) Account Details, against international phone number specifications, and to get the resulting phone number in any of the desired output formats: E.164, international, national, and RFC3966. For that, I will use libphonenumber, “Google’s phone number handling library, powering Android and more”, and more specifically I will use libphonenumber-csharp, the known port for C#.
Examples
Here are some examples of parsing, validation and formatting of a US phone number:
- Valid phone number: 415 535 5452
- Invalid phone number: 415 535 545222
- Country code: 1
- Phone Number region: US
- Number type: FIXED_LINE_OR_MOBILE
- E.164 format: +14155355452
- International format: +1 415-535-5452
- National format: (415) 535-5452
- RFC3966 format: tel:+1-415-535-5452
- Format for out-of-country calling from France: 00 1 415-535-5452
Why does it matter?
Phone number parsing, validation and formatting may be important in some scenarios.
For instance, for one of my customers, I’m integrating Cisco IP Communicator and Cisco Agent Desktop with CLM in Smart Office such that when customer service representatives receive incoming phone calls from their customers, Smart Office automatically searches for that incoming phone number in CLM and displays a list of possible matches. Then, the user can select the desired match and open the corresponding CLM Account Details. It saves precious time during the call. I wrote a previous post about it with some preliminary findings.
Conversely, users can click a phone number in CLM to make that outgoing phone call.
To implement that programmatically, how do we match the phone number of the incoming call with the phone numbers entered by users in CLM? Cisco Agent Desktop returns the ANI of incoming phone numbers as format 4155355452. What if the user entered the phone number in CLM as format (415) 535-5452? What if another user entered a duplicate record in CLM as format 415-535-5452? What if a user entered the phone number in CRS610 as format +14155355452? Also, for outgoing calls Cisco Agent Desktop will accept phone numbers as format 14155355452. That’s five different formats for the same phone number, and it requires record linkage.
That’s why it’s crucial to normalize the phone numbers so we can compare them.
Also, M3 and CLM synchronize their records with each other via Event Hub. So we have to validate entries on both sides or they would risk polluting each other.
The solution is to do phone number validation and formatting at user input so the user has a chance to enter the correct phone number. But doing it at the user interface level alone is not sufficient. We would also need to cover the other entry points such as M3 API, M3 Web Services of type M3 Display Program (MDP), and REST/SOAP Web Services. Also, as a reminder, we never do direct data entry in the database with SQL CREATE/UPDATE/DELETE as that could potentially break the integrity of the system, so we don’t need to cover that side.
Insufficient solutions
A naive solution to search a record by phone number is to select all records that match the input without normalization.
For example, searching CLM with SQL could be: SELECT AccountID FROM LCLM.Account WHERE Phone=’4155355452′. But that will fail to find the alternate valid numbers (415) 535-5452 and 415-535-5452. And we would have to protect it against SQL injection attacks.
Another naive solution is to strip all non-digit characters and count the resulting number of characters. For example, phone numbers in the United States have 10 digits, so the valid phone number (415) 535-5452 would be correctly validated, but the alternate valid phone number +1 (415) 535-5452 would incorrectly be rejected whereas it’s valid. We could improve the solution and say we now accept 11 digits, but then the valid French phone number +33 6 15 62 07 51 would incorrectly be validated as a US phone number, which is not true.
We could go further and restrict the space of phone numbers to only a specific country, say United States and use the North American Numbering Plan (NANP). But that will just temporarily buy time as there will likely be a need to support international phone numbers at a later point in the future.
Going further, we could use regular expressions. For example, according to this Microsoft pattern & practices document, the regular expression ^[01]?[- .]?(\([2-9]\d{2}\)|[2-9]\d{2})[- .]?\d{3}[- .]?\d{4}$ “Validates a U.S. phone number. It must consist of 3 numeric characters, optionally enclosed in parentheses, followed by a set of 3 numeric characters and then a set of 4 numeric characters.” Unfortunately, that will not validate a valid phone number such as +14155355452; we could improve the regular expression to validate the international prefix +1. Also, it will only validate NANP phone numbers, not international numbers; we could add more regular expressions for other countries.
Also, what about legitimate phone numbers with extensions like (415) 535-5452#1738 ?
We could iteratively improve the solutions, adding more tests and fixes, but it will unfortunately prove to be insufficient unless we spend a tremendous amount of effort. It’s like trying to implement ones own library of time and time zones, or trying to implement ones own cryptographic library.
Advantages of using a known library
libphonenumber is “Google’s common Java, C++ and Javascript library for parsing, formatting, storing and validating international phone numbers. The Java version is optimized for running on smartphones, and is used by the Android framework since 4.0 (Ice Cream Sandwich).”
Using proven robust open source libraries is always a good choice. This library is used by the billion Android phones on the market, so that tells something about its robustness and correctness. And it supports Java and JavaScript so we could use it in M3 Business Engine (Java) and H5 Client (JavaScript). And there is a port for C# so we can also use it in Smart Office, which I will.
Examples for Smart Office
I will use libphonenumber-csharp in a Smart Office script.
For that, I need to create a new feature in Smart Office SDK to add a reference to libphonenumber.dll and to deploy it with ClickOnce. I had originally tried simply using System.Reflection.Assembly.LoadFrom(String), but I couldn’t get it to work.
Once I have a reference to the assembly, I validate the phone number with:
import libphonenumber; ... var number: PhoneNumber = PhoneNumberUtil.Instance.Parse(phoneNumberStr, "US"); if (number.IsValidNumber) { // valid } else { // invalid }
Then, I format the phone number with:
number.Format(PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL) number.Format(PhoneNumberUtil.PhoneNumberFormat.NATIONAL) number.Format(PhoneNumberUtil.PhoneNumberFormat.E164) number.Format(PhoneNumberUtil.PhoneNumberFormat.RFC3966) number.FormatOutOfCountryCallingNumber("US") number.FormatOutOfCountryCallingNumber("FR")
Example for M3 Programs
The sample source code to validate and format phones numbers in M3 Programs is the following:
import System; import libphonenumber; package MForms.JScript { class Test { public function Init(element : Object, args : Object, controller : Object, debug : Object) { var number: PhoneNumber = PhoneNumberUtil.Instance.Parse(element.Text, "US"); if (number.IsValidNumber) { debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.E164)); debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.RFC3966)); debug.WriteLine(number.FormatOutOfCountryCallingNumber("US")); debug.WriteLine(number.FormatOutOfCountryCallingNumber("FR")); } } } }
Here is a sample screenshot of the validation and formatting of the field Telephone no 1 (WRPHNO) in M3 Customer. Open – CRS610/E:
Example for CLM
My sample source code to validate and format phones numbers in CLM Accounts is the following:
import System; import System.Windows; import lclmControls.Classes.UI; import lclmControls.Common; import lclmControls.Custom; import Mango.Services; import Mango.UI.Core; import Mango.UI.Services; import libphonenumber; package MForms.JScript { class Test { public function Init(element : Object, args : Object, controller : Object, debug : Object) { try { var runners: RunnerCollection = DashboardTaskService.Manager.ExecutingTasks(); var runner: IRunner = runners[4]; // I'm Feeling Lucky var task: ITask = runner.Task; var view: TabularDetailsView = task.Parameter; var detailsView: DetailsView = view.DetailsView; var baseDialog: BaseDialog = detailsView.BaseDialog; var groups: DataGroup[] = baseDialog.DataGroups; var group: DataGroup = groups[0]; // I'm Feeling Lucky var sections: DataSection[] = group.GetSections(); var section: DataSection = sections[0]; // I'm Feeling Lucky var dataField: DataField = section.GetField("Phone"); var dataEditor: FrameworkElement = dataField.DataEditor; var txtbox: SingleLineTextBox = dataEditor; var number: PhoneNumber = PhoneNumberUtil.Instance.Parse(txtbox.Text, "US"); if (number.IsValidNumber) { debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.INTERNATIONAL)); debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.NATIONAL)); debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.E164)); debug.WriteLine(number.Format(PhoneNumberUtil.PhoneNumberFormat.RFC3966)); debug.WriteLine(number.FormatOutOfCountryCallingNumber("US")); debug.WriteLine(number.FormatOutOfCountryCallingNumber("FR")); } } catch (ex : Exception) { debug.WriteLine(ex); } } } }
Here is a sample screenshot of the validation and formatting of the field Phone in a CLM Account Details View:
This is a proof-of-concept source code for demonstration purposes with “I’m feeling lucky” about the array indexes and not checking if object references are null. I will let the reader write the proper, and more lengthy code.
Completeness
For completeness, a robust solution would need to cover all entry points:
For M3 Programs:
- MForms in Smart Office
- MForms in H5 Client
- M3 API (MvxAPI protocol, REST, and SOAP)
- M3 Web Services (MWS) of type M3 Display Program (MDP) (REST and SOAP)
For the M3 UI, the solution would involve a combination of Smart Office Scripts in .NET, and H5 Client Web Parts in JavaScript. And for the backend, it would be M3 Java modifications with MAK; using Event Hub would be too late.
For CLM:
- CLM in Smart Office
- CLM-Web
- CLM REST Web Services
- CLM SOAP Web Services
For the CLM UI, the solution would involve the same combination of Smart Office Scripts in .NET, and H5 Client Web Parts in JavaScript. And for the backend, I’m not a CLM expert but I heard database triggers would do it.
Also, we would need to do retro- validation and formatting of phone numbers that were already entered in the M3 Programs and CLM.
That’s a lot of work.
My wish
My wish is that Infor Product Development implements phone number validation standard into Smart Office. Same for address validation in M3.
Conclusion
In this article I introduced my simple solution to do phone number parsing, validation and formatting for M3 Programs and CLM Accounts in Smart Office using the proven open source library libphonenumber and its known port for C#. I also explained why parsing, validation and formatting of phone numbers matters in some cases. I implemented a demo for M3 Programs and one for CLM Account Details. I also presented my thoughts on insufficient solutions. Then, I discussed what a complete solution would look like.
In my next article, I will present a proof-of-concept of AsYouTypeFormatter to format a phone number as the user is typing the digits.
That’s it!
If you liked this, please Follow the blog by clicking the button below, and let us know your comments in the section below. And share this with your colleagues, customers and partners to grow the community. Or become an author and share your ideas here. Or be a ninja and create your own blog!
/Thibaud
3 thoughts on “International phone number parsing, validation and formatting for Smart Office”