Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telephone number validation #29599

Open
atm-florianm opened this issue May 3, 2024 · 5 comments
Open

Telephone number validation #29599

atm-florianm opened this issue May 3, 2024 · 5 comments
Labels
Feature request This is a feature request

Comments

@atm-florianm
Copy link
Contributor

atm-florianm commented May 3, 2024

Feature Request

It would be interesting to have an optional conf for validating telephone numbers (in contacts and third parties).

It would include a phone number parser, a formatter for the database (Dolibarr already has a formatter for grouping digits when displaying a phone number for some countries).

I am aware that international telephone numbering standards are complicated; there are many edge cases (national specificities, countries with more than one international calling code, private telephone networks with different numbering rules, ever-varying national conventions for writing / dialing numbers…) but since it would be conf based, we could start by covering the most common cases and then extend it gradually.

Use case

Some companies use Dolibarr a lot and have many users with privileges to add contacts or third parties.

Sometimes, modules provide services that require a valid telephone number from a contact (for instance an external API that wants to do two-factor authentication, or a VoIP module that allows users to dial from Dolibarr, etc.).

For such companies, it can be very useful to control the validity of phone numbers entered by users, and to store those numbers in a uniform international format rather than free-form format that may have implicit elements like national or regional prefixes whose meaning could be ambiguous.

Suggested implementation

First step

The first step would be a dictionary of international subscriber dialing codes (like 33 for France, 49 for Germany, 1 for USA etc.). I think an entity column is not required because those prefixes are supposed to change only when the international bodies (the ITU) make decisions, not depending on entities.

For one of our modules, we have already started by declaring a dictionary like this (due to time constraints I didn't name the column and the dictionary consistently).

The dictionary we implemented only has 1 data column (the others being foreign key and technical ID), but I think it could be interesting to have more column for additional data, like validation regexps, minimum and maximum number of digits, etc.

Some (unfortunately not all) useful and official country-specific information can be found here.

We could also take data from already existing libraries to avoid reinventing the wheel (like Google's libphonenumber). Unfortunately, the latter is written in Java and the data is (apparently) in a custom binary format, not easily extractible (I'd have preferred JSON).

llx_c_calling_code.sql

-- ========================================================================
-- Copyright (C) 2024 Dolibarr
-- SPDX-License-Identifier: GPL-3.0-or-later
-- This file is part of the Dolibarr project
-- ========================================================================
CREATE TABLE llx_c_calling_code (
    rowid INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    fk_c_country  INTEGER NOT NULL,
    isd_code      VARCHAR(5) NOT NULL,
    active        TINYINT(1) NOT NULL
) ENGINE = innodb;

key:

ALTER TABLE llx_c_calling_code ADD UNIQUE KEY uk_c_calling_code_fk_c_country_isd_code(fk_c_country, isd_code);
ALTER TABLE llx_c_calling_code ADD CONSTRAINT fk_c_calling_code_fk_c_country FOREIGN KEY (fk_c_country) REFERENCES llx_c_country(rowid);

data:

-- Note: All hyphens have been removed from the ISD codes. Hyphenation proceeds as follows:
--       - All ISD codes starting with 1 should be displayed with a hyphen (e.g. '1-684').
--       - The ISD code for Vatican (Holy Seat) has a hyphen in the middle: '39-06'.
--       - All other codes are in one piece.

-- Source des données: https://r2.datahub.io/clt97y5hy0000jz087j66iiuk/master/raw/data/country-codes.csv (license ODC PDDL v1.0, compatible GNU GPL)
INSERT INTO llx_c_calling_code (fk_c_country, isd_code, active) VALUES  (213, '886', 1), (30, '93', 1), (32, '355', 1), (13, '213', 1), (33, '1684', 1), (34, '376', 1), (35, '244', 1), (36, '1264', 1), (37, '672', 1), (38, '1268', 1), (23, '54', 1), (39, '374', 1), (40, '297', 1), (28, '61', 1), (41, '43', 1), (42, '994', 1), (43, '1242', 1), (44, '973', 1), (45, '880', 1), (46, '1246', 1), (47, '375', 1), (2, '32', 1), (48, '501', 1), (49, '229', 1), (50, '1441', 1), (51, '975', 1), (52, '591', 1), (53, '387', 1), (54, '267', 1), (55, '47', 1), (56, '55', 1), (57, '246', 1), (234, '1284', 1), (58, '673', 1), (59, '359', 1), (60, '226', 1), (61, '257', 1), (63, '238', 1), (62, '855', 1), (24, '237', 1), (14, '1', 1), (64, '1345', 1), (65, '236', 1), (66, '235', 1), (67, '56', 1), (9, '86', 1), (115, '852', 1), (141, '853', 1), (68, '61', 1), (69, '61', 1), (70, '57', 1), (71, '269', 1), (72, '242', 1), (74, '682', 1), (75, '506', 1), (76, '385', 1), (77, '53', 1), (300, '599', 1), (78, '357', 1), (79, '420', 1), (21, '225', 1), (128, '850', 1), (73, '243', 1), (80, '45', 1), (81, '253', 1), (82, '1767', 1), (83, '1809', 1), (83, '1829', 1), (83, '1849', 1), (84, '593', 1), (85, '20', 1), (86, '503', 1), (87, '240', 1), (88, '291', 1), (89, '372', 1), (211, '268', 1), (90, '251', 1), (91, '500', 1), (92, '298', 1), (93, '679', 1), (94, '358', 1), (1, '33', 1), (95, '594', 1), (96, '689', 1), (97, '262', 1), (16, '241', 1), (98, '220', 1), (99, '995', 1), (5, '49', 1), (100, '233', 1), (101, '350', 1), (102, '30', 1), (103, '299', 1), (104, '1473', 1), (106, '1671', 1), (107, '502', 1), (241, '44', 1), (108, '224', 1), (109, '245', 1), (111, '509', 1), (112, '672', 1), (113, '3906', 1), (114, '504', 1), (18, '36', 1), (116, '354', 1), (117, '91', 1), (118, '62', 1), (119, '98', 1), (120, '964', 1), (8, '353', 1), (242, '44', 1), (121, '972', 1), (3, '39', 1), (122, '1876', 1), (123, '81', 1), (243, '44', 1), (124, '962', 1), (125, '7', 1), (126, '254', 1), (127, '686', 1), (130, '965', 1), (131, '996', 1), (132, '856', 1), (133, '371', 1), (134, '961', 1), (135, '266', 1), (136, '231', 1), (137, '218', 1), (138, '423', 1), (139, '370', 1), (140, '352', 1), (143, '261', 1), (144, '265', 1), (145, '60', 1), (146, '960', 1), (147, '223', 1), (148, '356', 1), (149, '692', 1), (151, '222', 1), (152, '230', 1), (153, '262', 1), (154, '52', 1), (155, '691', 1), (27, '377', 1), (157, '976', 1), (244, '382', 1), (158, '1664', 1), (12, '212', 1), (159, '258', 1), (160, '95', 1), (161, '264', 1), (162, '674', 1), (163, '977', 1), (17, '31', 1), (165, '687', 1), (166, '64', 1), (167, '505', 1), (168, '227', 1), (169, '234', 1), (170, '683', 1), (171, '672', 1), (172, '1670', 1), (173, '47', 1), (174, '968', 1), (175, '92', 1), (176, '680', 1), (178, '507', 1), (179, '675', 1), (180, '595', 1), (181, '51', 1), (182, '63', 1), (183, '870', 1), (184, '48', 1), (25, '351', 1), (185, '1', 1), (186, '974', 1), (129, '82', 1), (156, '373', 1), (188, '40', 1), (19, '7', 1), (189, '250', 1), (245, '590', 1), (190, '290', 1), (191, '1869', 1), (192, '1758', 1), (246, '590', 1), (193, '508', 1), (194, '1784', 1), (195, '685', 1), (196, '378', 1), (197, '239', 1), (26, '966', 1), (22, '221', 1), (198, '381', 1), (199, '248', 1), (200, '232', 1), (29, '65', 1), (301, '1721', 1), (201, '421', 1), (202, '386', 1), (203, '677', 1), (204, '252', 1), (205, '27', 1), (206, '500', 1), (4, '34', 1), (207, '94', 1), (177, '970', 1), (208, '249', 1), (209, '597', 1), (210, '47', 1), (20, '46', 1), (6, '41', 1), (212, '963', 1), (214, '992', 1), (216, '66', 1), (142, '389', 1), (217, '670', 1), (15, '228', 1), (218, '690', 1), (219, '676', 1), (220, '1868', 1), (10, '216', 1), (221, '90', 1), (222, '993', 1), (223, '1649', 1), (224, '688', 1), (225, '256', 1), (226, '380', 1), (227, '971', 1), (7, '44', 1), (215, '255', 1), (235, '1340', 1), (11, '1', 1), (229, '598', 1), (230, '998', 1), (231, '678', 1), (232, '58', 1), (233, '84', 1), (236, '681', 1), (237, '212', 1), (238, '967', 1), (239, '260', 1), (240, '263', 1), (31, '358', 1);

-- Source des données additionnelles: UIT-T (https://www.itu.int/oth/T0202.aspx?parent=T0202) absentes de datahub.io
-- Jamaica: code supplémentaire
INSERT INTO llx_c_calling_code (fk_c_country, isd_code, active) VALUES (122, '1658', 1);

-- Puerto Rico
INSERT INTO llx_c_calling_code (fk_c_country, isd_code, active) VALUES (185, '1787', 1);
INSERT INTO llx_c_calling_code (fk_c_country, isd_code, active) VALUES (185, '1939', 1);

-- Saint Helena
INSERT INTO llx_c_calling_code (fk_c_country, isd_code, active) VALUES (190, '247', 1);

dictionary declaration

[
    'tabname' => 'c_calling_code',
    'tablib' => 'CallingCode',
    'tabsql' =>
        'SELECT cc.rowid, c.label AS country, cc.isd_code FROM '.$db->prefix().'c_calling_code AS cc'
        .' INNER JOIN '.$db->prefix().'c_country AS c ON cc.fk_c_country = c.rowid',
    'tabsqlsort' => 'c.label ASC',
    'tabfield' => 'country,isd_code',
    'tabfieldvalue' => 'country,isd_code',
    'tabfieldinsert' => 'fk_c_country,isd_code',
    'tabrowid' => 'rowid',
    'tabcond' => '($conf->financement->enabled) ?? 0 && ($conf->clihlf->enabled ?? 0)'
],

Suggested steps

  1. Use <input type="tel"> for telephone numbers on cards
  2. Create a dictionary with the country-specific phone number data
  3. Create a class (phonenumber.class.php) to parse, validate, uniformise and pretty-print phone numbers (using the above dictionary as much as possible to avoid having hard-coded country-specific rules)
  4. Create a conf that, if enabled, will cause contact.class.php and societe.class.php (among possible others) to use phonenumber.class.php to validate and uniformise phone numbers.
@atm-florianm atm-florianm added the Feature request This is a feature request label May 3, 2024
@JonBendtsen
Copy link
Contributor

good idea. Have in mind that you can not assume that the country code address and phone number will match.

@atm-maxime
Copy link
Member

@eldy any thoughts about this standardization of all phone numbers across Dolibarr ?

@eldy
Copy link
Member

eldy commented May 3, 2024

A phone format is not something a user need to change everyday. So do we really need to use a database for this. A text file (xml, json, csv) looks a more oragmatic choice for such a need ?
Or we can add a column in the existing table of countries. Why not a json with possible format descriptions as a text field in country tables.
Adding a table for this, seems a bazooka to kill a fly.

@atm-florianm
Copy link
Contributor Author

Or we can add a column in the existing table of countries

A single column with just the country code wouldn't work for the few countries that have more than one code (Dominican Republic, Puerto Rico and Saint Helena).

I like your other suggestion better : with a json-encoded column, we could leverage DB engines' json capability (although from what I've read the json queries of mariadb and postgres seem to have very very different syntaxes).

A single json file stored outside the database could also work, but I can't find any benefit over a database table. If a table seems too much, maybe it could be created only when the user activates the feature?

@hansemschnokeloch
Copy link
Contributor

Why not use https://github.com/giggsey/libphonenumber-for-php ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature request This is a feature request
Projects
None yet
Development

No branches or pull requests

5 participants