ajout test api tva

This commit is contained in:
Jean-Philippe Staelen 2026-01-14 14:05:23 +01:00
parent 2f01441922
commit cf40b88b97
6 changed files with 482 additions and 4 deletions

View File

@ -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',

View File

@ -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.

View File

@ -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;

View File

@ -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,
];
}
}

View File

@ -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 = '<div class="notice notice-warning"><p>' +
'Aucun champ TVA trouvé dans les commandes récentes.' +
'</p></div>';
} else if (fields.length === 1) {
// Un seul champ trouvé : afficher et proposer de sauvegarder
var field = fields[0];
html = '<div class="notice notice-info">';
html += '<p><strong>Champ TVA détecté :</strong> <code>' +
field.key + '</code></p>';
html += '<p>Exemple de valeur : <code>' +
(field.sample_value || 'N/A') + '</code></p>';
html += '<p>Nombre d\'occurrences : ' + field.count + '</p>';
html += '<button type="button" class="button button-primary esi-peppol-save-vat-field" ' +
'data-vat-field-key="' + field.key + '" style="margin-top: 10px;">' +
'Enregistrer ce champ</button>';
html += '</div>';
} else {
// Plusieurs champs trouvés : afficher des boutons radio
html = '<div class="notice notice-info">';
html += '<p><strong>Plusieurs champs TVA détectés. Veuillez sélectionner celui à utiliser :</strong></p>';
html += '<table class="widefat" style="margin-top: 10px;">';
html += '<thead><tr><th style="width: 50px;">Sélection</th><th>Nom du champ</th><th>Exemple de valeur</th><th>Occurrences</th></tr></thead>';
html += '<tbody>';
fields.forEach(function(field, index) {
var checked = index === 0 ? ' checked' : '';
html += '<tr>';
html += '<td><input type="radio" name="esi_peppol_vat_field_radio" ' +
'value="' + field.key + '" id="vat_field_' + index + '"' + checked + '></td>';
html += '<td><label for="vat_field_' + index + '"><code>' + field.key + '</code></label></td>';
html += '<td><code>' + (field.sample_value || 'N/A') + '</code></td>';
html += '<td>' + field.count + '</td>';
html += '</tr>';
});
html += '</tbody></table>';
html += '<button type="button" class="button button-primary esi-peppol-save-vat-field" ' +
'data-vat-field-key="" style="margin-top: 10px;">' +
'Enregistrer le champ sélectionné</button>';
html += '</div>';
}
$vatFieldsResult.html(html);
} else {
var errorMsg = (response && response.data && response.data.message)
? response.data.message
: 'Erreur lors de la détection des champs TVA.';
$vatFieldsResult.html(
'<div class="notice notice-error"><p>' + errorMsg + '</p></div>'
);
}
})
.fail(function () {
$vatFieldsResult.html(
'<div class="notice notice-error"><p>Erreur de communication avec le serveur WordPress.</p></div>'
);
})
.always(function () {
$btn.prop('disabled', false).removeClass('updating-message');
});
});
// Gestion du bouton "Enregistrer" pour le champ TVA
$(document).on('click', '.esi-peppol-save-vat-field', function (e) {
e.preventDefault();
var $btn = $(this);
var vatFieldKey = $btn.data('vat-field-key');
// Si pas de clé dans data-attribute, récupérer depuis le radio sélectionné
if (!vatFieldKey || vatFieldKey === '') {
var $selectedRadio = $('input[name="esi_peppol_vat_field_radio"]:checked');
if ($selectedRadio.length) {
vatFieldKey = $selectedRadio.val();
} else {
alert('Veuillez sélectionner un champ TVA.');
return;
}
}
if (!vatFieldKey) {
alert('Erreur : aucun champ TVA sélectionné.');
return;
}
$btn.prop('disabled', true).addClass('updating-message');
$.post(window.esiPeppolAdmin.ajax_url, {
action: 'esi_peppol_save_vat_field',
nonce: window.esiPeppolAdmin.vat_save_nonce,
vat_field_key: vatFieldKey
})
.done(function (response) {
if (response && response.success) {
var message = (response.data && response.data.message)
? response.data.message
: 'Champ TVA enregistré avec succès.';
$vatFieldsResult.html(
'<div class="notice notice-success is-dismissible"><p>' + message + '</p></div>'
);
// Recharger la page après 1 seconde pour afficher le champ sauvegardé
setTimeout(function () {
window.location.reload();
}, 1000);
} else {
var errorMsg = (response && response.data && response.data.message)
? response.data.message
: 'Erreur lors de l\'enregistrement du champ TVA.';
$vatFieldsResult.html(
'<div class="notice notice-error"><p>' + errorMsg + '</p></div>'
);
}
})
.fail(function () {
$vatFieldsResult.html(
'<div class="notice notice-error"><p>Erreur de communication avec le serveur WordPress.</p></div>'
);
})
.always(function () {
$btn.prop('disabled', false).removeClass('updating-message');
});
});
}
});
})(jQuery);

View File

@ -151,6 +151,39 @@ if ($logo_email_id) {
</td>
</tr>
<tr>
<th scope="row">
<label><?php esc_html_e('Champ TVA', 'esi_peppol'); ?></label>
</th>
<td>
<?php
$saved_vat_field = get_option('esi_peppol_vat_field_key', '');
?>
<div class="esi-peppol-vat-field-wrapper">
<button type="button"
id="esi-peppol-detect-vat-fields"
class="button button-secondary">
<?php esc_html_e('Détecter les champs TVA', 'esi_peppol'); ?>
</button>
<div id="esi-peppol-vat-fields-result" style="margin-top: 15px;"></div>
<?php if ($saved_vat_field) : ?>
<p class="description" style="margin-top: 10px;">
<?php
printf(
/* translators: %s: nom du champ TVA sauvegardé */
esc_html__('Champ TVA actuellement utilisé : %s', 'esi_peppol'),
'<strong>' . esc_html($saved_vat_field) . '</strong>'
);
?>
</p>
<?php endif; ?>
</div>
<p class="description">
<?php esc_html_e('Utilisez cet utilitaire pour détecter automatiquement le champ TVA utilisé dans vos commandes WooCommerce. Si un seul champ est trouvé, il sera automatiquement sélectionné. Si plusieurs champs sont détectés, vous pourrez choisir celui à utiliser.', 'esi_peppol'); ?>
</p>
</td>
</tr>
<!-- <tr>
<th scope="row">
<label for="esi_peppol_logo_email"><?php esc_html_e('Logo Email', 'esi_peppol'); ?></label>