diff --git a/app/controllers/Plugin.php b/app/controllers/Plugin.php index f90d14a..69d32e4 100644 --- a/app/controllers/Plugin.php +++ b/app/controllers/Plugin.php @@ -40,6 +40,14 @@ class PEPPOL_Plugin { // Notice globale pour le numéro de TVA \add_action('admin_notices', [self::class, 'maybe_show_vat_notice']); + + // Ajouter une colonne "Statut Peppol" dans le listing des commandes WooCommerce + if (class_exists(\ESI_PEPPOL\controllers\PEPPOL_Woocommerce_controller::class)) { + \add_filter('manage_edit-shop_order_columns', [\ESI_PEPPOL\controllers\PEPPOL_Woocommerce_controller::class, 'add_peppol_status_column'], 20); + \add_filter('manage_woocommerce_page_wc-orders_columns', [\ESI_PEPPOL\controllers\PEPPOL_Woocommerce_controller::class, 'add_peppol_status_column'], 20); + \add_action('manage_shop_order_posts_custom_column', [\ESI_PEPPOL\controllers\PEPPOL_Woocommerce_controller::class, 'show_peppol_status_column'], 10, 2); + \add_action('manage_woocommerce_page_wc-orders_custom_column', [\ESI_PEPPOL\controllers\PEPPOL_Woocommerce_controller::class, 'show_peppol_status_column'], 10, 2); + } } /** @@ -183,24 +191,181 @@ class PEPPOL_Plugin { wp_die(esc_html__('Vous n\'avez pas les permissions nécessaires pour accéder à cette page.', 'esi_peppol')); } - // Récupération des dernières lignes de la table custom - $rows = PEPPOL_Main_model::get_recent(50); + // Vérifier si on affiche la page de détail + $detail_id = isset($_GET['detail']) ? (int) $_GET['detail'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $template = ESI_PEPPOL_DIR . 'templates/admin/logs.php'; + if ($detail_id > 0) { + // Afficher la page de détail + $row = PEPPOL_Main_model::get_by_id($detail_id); - if (file_exists($template)) { - /** @noinspection PhpIncludeInspection */ - include $template; + if (!$row) { + wp_die(esc_html__('Aucun enregistrement Peppol trouvé pour cet ID.', 'esi_peppol')); + } + + // Préparer les données pour l'affichage (même logique que ajax_get_log_details) + $data_sent = $row->data_sent ?? null; + $response_data = $row->response_data ?? null; + + // Formater les données JSON si possible + $data_sent_formatted = ''; + if ($data_sent) { + if (is_array($data_sent) || is_object($data_sent)) { + $data_sent_formatted = wp_json_encode($data_sent, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } else { + $data_sent_formatted = (string) $data_sent; + } + } + + $response_data_formatted = ''; + if ($response_data) { + if (is_array($response_data) || is_object($response_data)) { + $response_data_formatted = wp_json_encode($response_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } else { + $response_data_formatted = (string) $response_data; + } + } + + // Extraire les informations financières depuis data_sent + $invoice_totals = null; + $vat_totals = null; + $customer_data = null; + + if ($data_sent && (is_array($data_sent) || is_object($data_sent))) { + // Convertir en tableau si c'est un objet + $data_array = is_object($data_sent) ? (array) $data_sent : $data_sent; + + // Extraire invoice_totals + if (isset($data_array['invoice_totals']) && is_array($data_array['invoice_totals'])) { + $invoice_totals = $data_array['invoice_totals']; + } + + // Extraire vat_totals + if (isset($data_array['vat_totals']) && is_array($data_array['vat_totals'])) { + $vat_totals = $data_array['vat_totals']; + } + + // Extraire les données client (buyer) + if (isset($data_array['parties']['buyer']) && is_array($data_array['parties']['buyer'])) { + $customer_data = $data_array['parties']['buyer']; + } + } + + // Récupérer les informations de la commande si disponible + $order_info = null; + $order_totals = null; + if (!empty($row->id_order)) { + $order = wc_get_order($row->id_order); + if ($order) { + $order_info = [ + 'id' => $order->get_id(), + 'number' => $order->get_order_number(), + 'status' => $order->get_status(), + 'total' => $order->get_total(), + 'billing_name' => trim($order->get_billing_first_name() . ' ' . $order->get_billing_last_name()), + 'billing_email' => $order->get_billing_email(), + 'billing_phone' => $order->get_billing_phone(), + 'billing_company' => $order->get_billing_company(), + 'billing_address_1' => $order->get_billing_address_1(), + 'billing_address_2' => $order->get_billing_address_2(), + 'billing_city' => $order->get_billing_city(), + 'billing_postcode' => $order->get_billing_postcode(), + 'billing_country' => $order->get_billing_country(), + 'billing_vat_number' => $order->get_meta('_billing_vat_number') ?: '', + 'edit_link' => get_edit_post_link($row->id_order), + ]; + + // Calculer les totaux depuis la commande si invoice_totals n'est pas disponible + if (!$invoice_totals) { + $order_totals = [ + 'total_amount_excluding_vat' => $order->get_total() - $order->get_total_tax(), + 'total_vat_amount' => $order->get_total_tax(), + 'total_amount_including_vat' => $order->get_total(), + ]; + } + } + } + + // Extraire le message d'erreur depuis response_data + $error_message_to_display = $row->message ?? ''; + + if (!empty($response_data) && (is_array($response_data) || is_object($response_data))) { + // Convertir en tableau si c'est un objet + $error_data = is_object($response_data) ? (array) $response_data : $response_data; + + if (is_array($error_data)) { + // Structure avec error.message + if (isset($error_data['error']['message'])) { + $error_message_to_display = (string) $error_data['error']['message']; + } + // Structure avec details.validation_error + elseif (isset($error_data['details']['validation_error'])) { + $error_message_to_display = (string) $error_data['details']['validation_error']; + } + // Structure avec validation_error directement + elseif (isset($error_data['validation_error'])) { + $error_message_to_display = (string) $error_data['validation_error']; + } + // Structure avec message directement + elseif (isset($error_data['message'])) { + $error_message_to_display = (string) $error_data['message']; + } + } + } + + // Préparer les données pour le template + $log_data = [ + 'id' => $row->id, + 'id_order' => $row->id_order ?? 0, + 'order_info' => $order_info, + 'document_id' => $row->document_id ?? '', + 'peppol_document_id' => $row->peppol_document_id ?? '', + 'status' => $row->status ?? '', + 'success' => !empty($row->success), + 'message' => $error_message_to_display, + 'http_code' => $row->http_code ?? null, + 'date_add' => $row->date_add ?? '', + 'date_update' => $row->date_update ?? '', + 'invoice_totals' => $invoice_totals ?? $order_totals, + 'vat_totals' => $vat_totals, + 'customer_data' => $customer_data, + 'data_sent' => $data_sent_formatted, + 'response_data' => $response_data_formatted, + ]; + + $template = ESI_PEPPOL_DIR . 'templates/admin/logs-detail.php'; + + if (file_exists($template)) { + /** @noinspection PhpIncludeInspection */ + include $template; + } else { + wp_die(esc_html__('Template de détail introuvable.', 'esi_peppol')); + } + } else { + // Afficher la liste des logs + // Récupération des dernières lignes de la table custom + $rows = PEPPOL_Main_model::get_recent(50); + + $template = ESI_PEPPOL_DIR . 'templates/admin/logs.php'; + + if (file_exists($template)) { + /** @noinspection PhpIncludeInspection */ + include $template; + } } } public static function enqueue_admin_assets() { $screen = function_exists('get_current_screen') ? get_current_screen() : null; - // Ne charger les assets que sur les pages du plugin + // Ne charger les assets que sur les pages du plugin ou la page des commandes WooCommerce $is_peppol_page = isset($_GET['page']) && strpos((string) $_GET['page'], 'esi-peppol') === 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $is_orders_page = $screen && ( + strpos((string) $screen->id, 'shop_order') !== false || + strpos((string) $screen->id, 'woocommerce_page_wc-orders') !== false || + $screen->id === 'edit-shop_order' + ); - if (!$is_peppol_page && $screen && strpos((string) $screen->base, 'esi-peppol') === false) { + if (!$is_peppol_page && !$is_orders_page && $screen && strpos((string) $screen->base, 'esi-peppol') === false) { return; } diff --git a/app/controllers/Woocommerce_controller.php b/app/controllers/Woocommerce_controller.php index b1a2b0f..e13c948 100644 --- a/app/controllers/Woocommerce_controller.php +++ b/app/controllers/Woocommerce_controller.php @@ -191,4 +191,71 @@ class PEPPOL_Woocommerce_controller { '
'; } + /** + * Ajoute une colonne "Statut Peppol" dans le listing des commandes WooCommerce. + * + * @param array $columns Colonnes existantes. + * @return array Colonnes modifiées. + */ + public static function add_peppol_status_column(array $columns): array { + $new_columns = []; + + foreach ($columns as $key => $column) { + $new_columns[$key] = $column; + + // Ajouter la colonne "Statut Peppol" après la colonne "Commande" ou "order_title" + if ('order_title' === $key || 'order_number' === $key || 'order_status' === $key) { + $new_columns['peppol_status'] = __('Statut Peppol', 'esi_peppol'); + } + } + + // Si aucune colonne appropriée n'a été trouvée, ajouter à la fin + if (!isset($new_columns['peppol_status'])) { + $new_columns['peppol_status'] = __('Statut Peppol', 'esi_peppol'); + } + + return $new_columns; + } + + /** + * Affiche le contenu de la colonne "Statut Peppol" dans le listing des commandes. + * + * @param string $column Nom de la colonne. + * @param int|object $order ID de la commande ou objet commande. + * @return void + */ + public static function show_peppol_status_column(string $column, $order): void { + if ('peppol_status' !== $column) { + return; + } + + // Récupérer l'ID de la commande + $order_id = is_numeric($order) ? (int) $order : (is_object($order) ? $order->get_id() : 0); + + if (!$order_id) { + echo '–'; + return; + } + + // Récupérer le log Peppol associé à cette commande + $log = \ESI_PEPPOL\models\PEPPOL_Main_model::get_by_order_id($order_id); + + if (!$log || empty($log->status)) { + echo '–'; + return; + } + + // Afficher le macaron cliquable vers la page de détail + $detail_url = \admin_url('admin.php?page=esi-peppol-logs&detail=' . $log->id); + $status_class = 'statut statut-' . \strtolower(\sanitize_html_class($log->status)); + + \printf( + '%s', + \esc_url($detail_url), + \esc_attr($status_class), + \esc_attr__('Voir les détails du log Peppol', 'esi_peppol'), + \esc_html($log->status) + ); + } + } \ No newline at end of file diff --git a/assets/css/admin.css b/assets/css/admin.css index 7ac43d6..756fd39 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -106,6 +106,23 @@ white-space: nowrap; } +/* Styles pour les liens de statut (macarons cliquables) */ +a.statut { + text-decoration: none; + transition: opacity 0.2s ease, transform 0.1s ease; + cursor: pointer; +} + +a.statut:hover { + opacity: 0.85; + text-decoration: none; + transform: scale(1.05); +} + +a.statut:visited { + color: inherit; +} + /* Couleurs par défaut pour les statuts communs */ .statut-success, .statut-succès, diff --git a/assets/js/admin.js b/assets/js/admin.js index 89f270d..a762f47 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -75,7 +75,8 @@ } // Actions AJAX sur la page de logs : renvoyer / vérifier le statut - if ($logsTable.length && typeof window.esiPeppolAdmin !== 'undefined') { + // Utiliser la délégation d'événements sur le document pour fonctionner aussi sur la page de détail + if (typeof window.esiPeppolAdmin !== 'undefined') { var $logsResult = $('#esi-peppol-logs-result'); var showLogsNotice = function (type, title, message, detail) { @@ -107,7 +108,8 @@ .append(html); }; - $logsTable.on('click', '.esi-peppol-log-resend', function (e) { + // Gestionnaire pour le bouton "Réenvoyer" (fonctionne dans le tableau et sur la page de détail) + $(document).on('click', '.esi-peppol-log-resend', function (e) { e.preventDefault(); var $btn = $(this); diff --git a/templates/admin/logs-detail.php b/templates/admin/logs-detail.php new file mode 100644 index 0000000..dbcc1ed --- /dev/null +++ b/templates/admin/logs-detail.php @@ -0,0 +1,263 @@ + + +| + | + ' . esc_html__('Oui', 'esi_peppol') . '' + : '' . esc_html__('Non', 'esi_peppol') . ''; + ?> + | +
|---|---|
| + | + |
| + | + + # + + | +
| + | # | +
| + | + |
| + | + |
| + | + + + + | +
| + | + |
| + | + |
| + | + |
| + | + |
|---|---|
| + | + |
| + | + |
| + | + |
| + | + |
| + |
+
+
+ + + + + |
+
| + | + | + | + |
|---|---|---|---|
| + | + | + | + |
| + | + |
|---|---|
| + | + |
| + | + |
| + | + |
- +
@@ -319,17 +334,17 @@ if ($order instanceof WC_Order) {| - - → + + → |