Si comme l’artiste JR, au travers de sa boutique Social Animals,  vous êtes amené à envoyer des colis dans le monde entier, et notamment en Chine et au Brésil, vous allez être confronté à la problèmatique des numéros CPF / CNPJ / CR. Documentation difficilement trouvable et donc nécessairement en anglais.

Dear English reader, if you need a translation, ask us in comment.

Pour les impatients, vous trouverez le code plus bas.

Pour les autres, un peu d’explication s’impose.

Comment connaitre si le pays requiert un numéro d’identification fiscale, dans Prestashop, dans l’interface d’administration au menu Localisation, Pays, puis sur la modification d’un pays, c’est le champs nommé « Requiert un numéro d’identification fiscale ? » ou directement en base de données le champs « need_identification_number » de la table _DB_PREFIX.’country’.

Certains pays (Brésil, Chine, Mexique) obligent les transporteurs à indiquer un numéro personnel d’identification lors de l’import de produit. De ce fait, lorsqu’un e-marchant, sous Prestashop par exemple, décide de livrer dans ces pays, il doit demander un numéro à ses clients afin de le fournir au livreur pour une présentation aux douanes locales.

Pour les personnes habitant en Chine, il s’agit du numéro CR (Custom Registration – Numéro d’enregistrement des douanes), pour un particulier au Brésil , il s’agit du numéro CPF (Cadastro de Pessoas Físicas – Cadastre des personnes physiques) et pour les professionnels au Brésil, il s’agit du numéro CNPJ (Cadastro Nacional da Pessoa Jurídica – Cadastre des sociétés).

Si le numéro d’identification fiscale est obligatoire pour le pays de destination, Prestashop dans ces versions 1.6.* va vérifier si le champs Numéro d’identification, stocké dans le champs « dni » de la table _DB_PREFIX.’address’, est renseigné ou pas.

Or la méthode isDniLite() de la classe Validate, vérifie seulement si cette valeur contient au moins un caractère.

Ce qui est limité en terme de vérification surtout que la vérification des numéros CPF et CNPJ est plus complexe et que le vérification du format d’un numéro CR peut être plus poussé.

 

Voici les règles mise en place pour la validation d’un numéro

Ces vérifications ne permettent pas de valider qu’un numéro existe et/ou est bien associé au titulaire déclaré. Ces verifications permettent juste de s’assurer que la structure du numéro fournie est valide.

 

Voici le fichier à ajouter.

!! Attention de toujours faire une sauvegarde de vos fichiers avant de les modifier. !!

Si vous ne comprenez pas ce que vous faites, ne pas modifier votre fichier et demander à votre agence ou une agence de développement spécialisée dans Prestashop d’apporter ces modifications sur votre site Internet.

 

override/controllers/front/AddressController.php

<?php
/**
* 2007-2017 Pliciweb Solutions
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
*  @author    Pliciweb Solutions &amp;lt;contact@pliciweb.com&amp;gt;
*  @copyright  2007-2017 Pliciweb Solutions
*  @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
*  International Registered Trademark && Property of Pliciweb Solutions
*/

class AddressController extends AddressControllerCore
{
	/**
	 * @author contact@pliciweb.com
	 * 
	 * Check CPF / CNPF / CR for Brazil and China
	 * 
	 * @throws PrestaShopException
	 */
	protected function processSubmitAddress() {
		$address = new Address();
		$this->errors = $address->validateController();
		$address->id_customer = (int)$this->context->customer->id;

		if ($address->id_country) {
			if (!($country = new Country($address->id_country)) || !Validate::isLoadedObject($country))
				throw new PrestaShopException('Country cannot be loaded with address->id_country');

			if ($country->isNeedDni()) {
				if (!Tools::getValue('dni') || !ValidateCore::isDniLite(Tools::getValue('dni'))) {
					$error = true;
				} else {
					$dni = Tools::getValue('dni');
					$error = false;
					switch ($country->iso_code) {
						case 'BR':

							// Try CPF format
							// http://www.geradorcpf.com/algoritmo_do_cpf.htm

							// Remove un-usefull car
							$only_digit = preg_replace("/[^0-9]/i", '', $dni);
							// Check length
							if ($only_digit <=0 || 11 != strlen($only_digit)) {
								$error = true;
							} else {

								// Checking for dump data
								if ($only_digit === "" ||
								  $only_digit === "00000000000" ||
								  $only_digit === "11111111111" ||
								  $only_digit === "22222222222" ||
								  $only_digit === "33333333333" ||
								  $only_digit === "44444444444" ||
								  $only_digit === "55555555555" ||
								  $only_digit === "66666666666" ||
								  $only_digit === "77777777777" ||
								  $only_digit === "88888888888" ||
								  $only_digit === "99999999999"
								) {
								  $error = true;
								}

								$sumFirstCn = 0;
								$sumSecondCn = 0;
								for ($i = 0 ; $i < 9; $i++) {
									$sumFirstCn += (10-$i) * (int) $only_digit[$i];
									$sumSecondCn += (11-$i) * (int) $only_digit[$i];
								}


								$moduloFirstCn = $sumFirstCn % 11;
								$firstCN = 11 - $moduloFirstCn;
								if ($moduloFirstCn < 2) {
									$firstCN = 0;
								}

								$sumSecondCn += (2 * $firstCN);

								$moduloSecondCn = $sumSecondCn % 11;
								$secondCN = 11 - $moduloSecondCn;
								if ($moduloSecondCn < 2) {
									$secondCN = 0;
								}

								if (substr($only_digit, -2) != ($firstCN.$secondCN)) {
									$error = true;
								}
							}

							if ($error) {
								$error = false;
								// Try CNPJ format
								// http://www.geradorcnpj.com/algoritmo_do_cnpj.htm
								
								if ($only_digit <=0 || 14 != strlen($only_digit)) {
									$error = true;
								} else {

									// Checking for dump data
									if ($only_digit === "" ||
									  $only_digit === "00000000000000" ||
									  $only_digit === "11111111111111" ||
									  $only_digit === "22222222222222" ||
									  $only_digit === "33333333333333" ||
									  $only_digit === "44444444444444" ||
									  $only_digit === "55555555555555" ||
									  $only_digit === "66666666666666" ||
									  $only_digit === "77777777777777" ||
									  $only_digit === "88888888888888" ||
									  $only_digit === "99999999999999"
									) {
									  $error = true;
									}

									$firstCnData = [5,4,3,2,9,8,7,6,5,4,3,2];
									$secondCnData = [6,5,4,3,2,9,8,7,6,5,4,3,2];
									$sumFirstCn = 0;
									$sumSecondCn = 0;
									for ($i = 0 ; $i < count($firstCnData); $i++) {
										$sumFirstCn += $firstCnData[$i] * (int) $only_digit[$i];
										$sumSecondCn += $secondCnData[$i] * (int) $only_digit[$i];
									}

									$moduloFirstCn = $sumFirstCn % 11;
									$firstCN = 11 - $moduloFirstCn;
									if ($moduloFirstCn < 2) {
										$firstCN = 0;
									}

									$sumSecondCn += (2 * $firstCN);

									$moduloSecondCn = $sumSecondCn % 11;
									$secondCN = 11 - $moduloSecondCn;
									if ($moduloSecondCn < 2) {
										$secondCN = 0;
									}

									if (substr($only_digit, -2) != ($firstCN.$secondCN)) {
										$error = true;
									}
								}
							}
							
							break;

						case 'CN':
							// http://ecolorworld.com/wiki/cr-number/
							$only_digit = preg_replace("/[^0-9]/i", '', $dni);
							if ($only_digit <=0 || 10 != strlen($only_digit)) { $error = true; } break; } } if ($error) { $this->errors[] = Tools::displayError('The identification number is incorrect or has already been used.');
				}
			}
		}

		if (0 == count($this->errors)) {
			parent::processSubmitAddress();
		}
	}

Cette vérification n’est pas effectuée au meilleur endroit à notre avis. Elle aurait plus de sens dans la classe Validate, mais cela obligerait à modifier beaucoup de code au niveau du cœur de Prestashop. Ce qui n’est pas forcément une bonne chose.

Nous n’avons pas étudié les modifications a effectuer pour des versions de Prestashop 1.7.*. Mais si vous avez besoin, n’hésitez pas à nous contacter afin que nous vous aidions dans vos démarches.

 

Crédit Photo – Wikimedia