diff --git a/ESI_peppol.php b/ESI_peppol.php index 3961fa5..f62b927 100644 --- a/ESI_peppol.php +++ b/ESI_peppol.php @@ -152,6 +152,18 @@ add_action( [PEPPOL_Plugin::class, 'ajax_get_log_details'] ); +// AJAX admin pour détecter les champs TVA +add_action( + 'wp_ajax_esi_peppol_detect_vat_fields', + [PEPPOL_Plugin::class, 'ajax_detect_vat_fields'] +); + +// AJAX admin pour sauvegarder le champ TVA sélectionné +add_action( + 'wp_ajax_esi_peppol_save_vat_field', + [PEPPOL_Plugin::class, 'ajax_save_vat_field'] +); + // Champ TVA de la boutique dans les réglages WooCommerce > Général. add_filter( 'woocommerce_general_settings', diff --git a/app/controllers/Plugin.php b/app/controllers/Plugin.php index 69d32e4..bf4db59 100644 --- a/app/controllers/Plugin.php +++ b/app/controllers/Plugin.php @@ -421,6 +421,8 @@ class PEPPOL_Plugin { 'logs_nonce_resend' => wp_create_nonce('esi_peppol_resend_invoice'), 'logs_nonce_status' => wp_create_nonce('esi_peppol_check_invoice_status'), 'logs_nonce_details' => wp_create_nonce('esi_peppol_get_log_details'), + 'vat_detect_nonce' => wp_create_nonce('esi_peppol_detect_vat_fields'), + 'vat_save_nonce' => wp_create_nonce('esi_peppol_save_vat_field'), 'i18n_missing' => __('Veuillez renseigner l\'API Key et le Password avant de tester la connexion.', 'esi_peppol'), 'i18n_success' => __('Connexion réussie à l\'API ESIPeppol.', 'esi_peppol'), 'i18n_error' => __('La connexion a échoué. Veuillez vérifier vos identifiants.', 'esi_peppol'), @@ -681,6 +683,184 @@ class PEPPOL_Plugin { wp_send_json_error($payload_response, 200); } + /** + * Handler AJAX pour détecter les champs TVA dans les commandes WooCommerce. + * + * @return void + */ + public static function ajax_detect_vat_fields(): void { + if (!check_ajax_referer('esi_peppol_detect_vat_fields', 'nonce', false)) { + wp_send_json_error( + [ + 'message' => __('Sécurité : nonce invalide.', 'esi_peppol'), + ], + 400 + ); + } + + if (!current_user_can('manage_options')) { + wp_send_json_error( + [ + 'message' => __('Vous n\'avez pas les permissions nécessaires pour effectuer cette action.', 'esi_peppol'), + ], + 403 + ); + } + + // Récupérer les dernières commandes pour scanner les champs TVA + $orders = wc_get_orders([ + 'limit' => 50, + 'orderby' => 'date', + 'order' => 'DESC', + 'status' => ['completed', 'processing', 'on-hold'], + ]); + + $vat_fields_found = []; + + foreach ($orders as $order) { + if (!$order instanceof \WC_Order) { + continue; + } + + // Utiliser la fonction helper pour scanner tous les champs TVA possibles + $vat_candidates = []; + + // Scanner les meta keys contenant vat/tva + foreach ($order->get_meta_data() as $meta) { + $k = (string) $meta->key; + if (preg_match('/\b(vat|tva)\b/i', $k) || preg_match('/(vat|tva)/i', $k)) { + $v = $meta->value; + if (is_string($v)) { + $v = trim($v); + } + if (!empty($v)) { + if (!isset($vat_candidates[$k])) { + $vat_candidates[$k] = [ + 'key' => $k, + 'value' => $v, + 'count' => 0, + ]; + } + $vat_candidates[$k]['count']++; + } + } + } + + // Scanner les champs de checkout + if (function_exists('WC') && WC()->checkout()) { + $fields = WC()->checkout()->get_checkout_fields(); + $patterns = ['vat', 'tva', 'btw', 'ust', 'mwst', 'piva', 'moms', 'mva']; + + foreach ($fields as $group_fields) { + foreach ($group_fields as $key => $def) { + $k = strtolower($key); + $found = false; + + foreach ($patterns as $p) { + if (strpos($k, $p) !== false) { + $found = true; + break; + } + } + + if (!$found) { + $label = strtolower((string) ($def['label'] ?? '')); + foreach ($patterns as $p) { + if (preg_match('/\b' . $p . '\b/i', $label)) { + $found = true; + break; + } + } + } + + if ($found) { + foreach ([$key, '_' . $key] as $meta_key) { + $val = $order->get_meta($meta_key, true); + if (!empty($val)) { + if (!isset($vat_candidates[$meta_key])) { + $vat_candidates[$meta_key] = [ + 'key' => $meta_key, + 'value' => trim((string) $val), + 'count' => 0, + ]; + } + $vat_candidates[$meta_key]['count']++; + } + } + } + } + } + } + + // Ajouter les candidats trouvés + foreach ($vat_candidates as $candidate) { + $key = $candidate['key']; + if (!isset($vat_fields_found[$key])) { + $vat_fields_found[$key] = [ + 'key' => $key, + 'count' => 0, + 'sample_value' => $candidate['value'], + ]; + } + $vat_fields_found[$key]['count'] += $candidate['count']; + } + } + + // Trier par nombre d'occurrences (décroissant) + usort($vat_fields_found, function ($a, $b) { + return $b['count'] - $a['count']; + }); + + wp_send_json_success([ + 'fields' => array_values($vat_fields_found), + 'count' => count($vat_fields_found), + ]); + } + + /** + * Handler AJAX pour sauvegarder le champ TVA sélectionné. + * + * @return void + */ + public static function ajax_save_vat_field(): void { + if (!check_ajax_referer('esi_peppol_save_vat_field', 'nonce', false)) { + wp_send_json_error( + [ + 'message' => __('Sécurité : nonce invalide.', 'esi_peppol'), + ], + 400 + ); + } + + if (!current_user_can('manage_options')) { + wp_send_json_error( + [ + 'message' => __('Vous n\'avez pas les permissions nécessaires pour effectuer cette action.', 'esi_peppol'), + ], + 403 + ); + } + + $vat_field_key = isset($_POST['vat_field_key']) ? sanitize_text_field(wp_unslash($_POST['vat_field_key'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing + + if ($vat_field_key === '') { + wp_send_json_error( + [ + 'message' => __('Clé de champ TVA invalide.', 'esi_peppol'), + ], + 400 + ); + } + + // Sauvegarder le champ sélectionné + update_option('esi_peppol_vat_field_key', $vat_field_key); + + wp_send_json_success([ + 'message' => __('Champ TVA enregistré avec succès.', 'esi_peppol'), + 'vat_field_key' => $vat_field_key, + ]); + } + /** * Handler AJAX pour récupérer les détails complets d'un log Peppol * à partir de l'ID du log. diff --git a/app/controllers/Woocommerce_controller.php b/app/controllers/Woocommerce_controller.php index 87fb5a1..dc4c53d 100644 --- a/app/controllers/Woocommerce_controller.php +++ b/app/controllers/Woocommerce_controller.php @@ -59,9 +59,21 @@ class PEPPOL_Woocommerce_controller { // N'envoyer vers Peppol que les commandes avec un numéro de TVA saisi ET valide. // Le plugin "WooCommerce EU VAT Number" stocke le numéro validé dans la meta "_billing_vat_number" // et le statut de validation dans "_vat_number_is_valid" (valeur 'true' ou 'false'). - // Utilisation de la fonction helper pour gérer différentes variantes de clés (billing_vat_number, billing_tva, etc.) - $billing_vat_number = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_order_vat_number($order); - $vat_is_valid = (string) $order->get_meta('_vat_number_is_valid'); + // Utilisation du champ TVA configuré dans les settings si disponible, sinon utilisation de la fonction helper + $saved_vat_field = \get_option('esi_peppol_vat_field_key', ''); + + if (!empty($saved_vat_field)) { + // Utiliser le champ TVA sauvegardé dans les settings + $billing_vat_number = $order->get_meta($saved_vat_field, true); + if (is_string($billing_vat_number)) { + $billing_vat_number = trim($billing_vat_number); + } + } else { + // Fallback : utiliser la fonction helper pour gérer différentes variantes de clés + $billing_vat_number = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_order_vat_number($order); + } + + $vat_is_valid = (string) $order->get_meta('_vat_number_is_valid'); if ($billing_vat_number === '' || $vat_is_valid !== 'true') { return; diff --git a/app/helpers/Woo_Helper.php b/app/helpers/Woo_Helper.php index 1ce25bf..adaada8 100644 --- a/app/helpers/Woo_Helper.php +++ b/app/helpers/Woo_Helper.php @@ -34,7 +34,7 @@ class PEPPOL_Woo_Helper { if ( is_numeric( $order ) ) { $order = wc_get_order( $order ); } - + if ( ! $order instanceof \WC_Order ) { return ''; } @@ -93,5 +93,98 @@ class PEPPOL_Woo_Helper { // sinon le 1er trouvé return ! empty($vat_candidates) ? reset($vat_candidates) : ''; } + + public static function esi_get_order_vat( $order ) { + if ( is_numeric($order) ) { + $order = wc_get_order($order); + } + if ( ! $order instanceof WC_Order ) { + return [ + 'found' => false, + 'key' => null, + 'value' => null, + 'source' => null, + ]; + } + + $patterns = [ 'vat','tva','btw','ust','mwst','piva','moms','mva' ]; + + /* ================================================== + * 1) CHECKOUT FIELDS (si présents) + * ================================================== */ + if ( function_exists('WC') && WC()->checkout() ) { + $fields = WC()->checkout()->get_checkout_fields(); + + foreach ( $fields as $group_fields ) { + foreach ( $group_fields as $key => $def ) { + $k = strtolower($key); + + foreach ( $patterns as $p ) { + if ( strpos($k, $p) !== false ) { + + // tentatives de lecture meta + foreach ( [ $key, '_' . $key ] as $meta_key ) { + $val = $order->get_meta($meta_key, true); + if ( ! empty($val) ) { + return [ + 'found' => true, + 'key' => $meta_key, + 'value' => trim((string)$val), + 'source' => 'checkout_field', + ]; + } + } + } + } + + // fallback label + $label = strtolower((string)($def['label'] ?? '')); + if ( $label && preg_match('/\b(vat|tva|btw|mwst|ust|piva|moms|mva)\b/i', $label) ) { + foreach ( [ $key, '_' . $key ] as $meta_key ) { + $val = $order->get_meta($meta_key, true); + if ( ! empty($val) ) { + return [ + 'found' => true, + 'key' => $meta_key, + 'value' => trim((string)$val), + 'source' => 'checkout_label', + ]; + } + } + } + } + } + } + + /* ================================================== + * 2) SCAN META (fallback ultime) + * ================================================== */ + foreach ( $order->get_meta_data() as $meta ) { + $k = strtolower((string)$meta->key); + + foreach ( $patterns as $p ) { + if ( strpos($k, $p) !== false ) { + $v = $meta->value; + if ( is_string($v) ) $v = trim($v); + + if ( ! empty($v) ) { + return [ + 'found' => true, + 'key' => $meta->key, + 'value' => $v, + 'source' => 'meta_scan', + ]; + } + } + } + } + + return [ + 'found' => false, + 'key' => null, + 'value' => null, + 'source' => null, + ]; + } } \ No newline at end of file diff --git a/assets/js/admin.js b/assets/js/admin.js index a762f47..6c98ea5 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -507,6 +507,154 @@ }); } + // Gestion de la détection des champs TVA + var $detectVatBtn = $('#esi-peppol-detect-vat-fields'); + var $vatFieldsResult = $('#esi-peppol-vat-fields-result'); + + if ($detectVatBtn.length && typeof window.esiPeppolAdmin !== 'undefined') { + $detectVatBtn.on('click', function (e) { + e.preventDefault(); + + var $btn = $(this); + $btn.prop('disabled', true).addClass('updating-message'); + $vatFieldsResult.empty(); + + $.post(window.esiPeppolAdmin.ajax_url, { + action: 'esi_peppol_detect_vat_fields', + nonce: window.esiPeppolAdmin.vat_detect_nonce + }) + .done(function (response) { + if (response && response.success && response.data) { + var fields = response.data.fields || []; + var html = ''; + + if (fields.length === 0) { + html = '
' + + 'Aucun champ TVA trouvé dans les commandes récentes.' + + '
Champ TVA détecté : ' +
+ field.key + '
Exemple de valeur : ' +
+ (field.sample_value || 'N/A') + '
Nombre d\'occurrences : ' + field.count + '
'; + html += ''; + html += 'Plusieurs champs TVA détectés. Veuillez sélectionner celui à utiliser :
'; + html += '| Sélection | Nom du champ | Exemple de valeur | Occurrences |
|---|---|---|---|
| '; + html += ' | '; + html += ' | ' + (field.sample_value || 'N/A') + ' | ';
+ html += '' + field.count + ' | '; + html += '
' + errorMsg + '
Erreur de communication avec le serveur WordPress.
' + message + '
' + errorMsg + '
Erreur de communication avec le serveur WordPress.
+ ' . esc_html($saved_vat_field) . '' + ); + ?> +
+ ++ +
+