diff --git a/app/controllers/Plugin.php b/app/controllers/Plugin.php index 111ca26..23bb105 100644 --- a/app/controllers/Plugin.php +++ b/app/controllers/Plugin.php @@ -708,8 +708,7 @@ class PEPPOL_Plugin { /** * Détecte automatiquement les champs TVA lors de l'installation. - * Limite la recherche aux champs billing et shipping dans cet ordre. - * Utilise la fonction helper PEPPOL_Woo_Helper::esi_detect_vat_fields_billing_shipping(). + * Utilise la fonction helper PEPPOL_Woo_Helper::esi_get_vat_meta_keys(). * * @return string|null La clé du champ TVA détecté, ou null si aucun trouvé */ @@ -721,66 +720,22 @@ class PEPPOL_Plugin { } // Vérifier si WooCommerce est disponible - if (!function_exists('wc_get_orders')) { + if (!function_exists('wc_get_order')) { // Marquer que la détection a été tentée (même si WooCommerce n'est pas disponible) update_option('esi_peppol_vat_field_auto_detected', true); return null; } - // 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 = []; - - // Utiliser la fonction helper pour détecter les champs TVA dans chaque commande - foreach ($orders as $order) { - if (!$order instanceof \WC_Order) { - continue; - } - - $fields = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_detect_vat_fields_billing_shipping($order); - - // Compter les occurrences de chaque champ - foreach ($fields as $field) { - $key = $field['key']; - if (!isset($vat_fields_found[$key])) { - $vat_fields_found[$key] = [ - 'key' => $key, - 'count' => 0, - 'group' => $field['group'], - ]; - } - $vat_fields_found[$key]['count']++; - } - } + $vat_keys = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_vat_meta_keys(50); // Si aucun champ trouvé, marquer que la détection a été effectuée et retourner null - if (empty($vat_fields_found)) { + if (empty($vat_keys)) { update_option('esi_peppol_vat_field_auto_detected', true); return null; } - // Trier : d'abord par groupe (billing avant shipping), puis par nombre d'occurrences - usort($vat_fields_found, function ($a, $b) { - // Priorité au groupe billing - if ($a['group'] === 'billing' && $b['group'] !== 'billing') { - return -1; - } - if ($a['group'] !== 'billing' && $b['group'] === 'billing') { - return 1; - } - // Si même groupe, trier par nombre d'occurrences - return $b['count'] - $a['count']; - }); - - // Prendre le premier champ trouvé (priorité billing) - $selected_field = reset($vat_fields_found); - $vat_field_key = $selected_field['key']; + // Prendre la première clé trouvée + $vat_field_key = reset($vat_keys); // Sauvegarder le champ détecté update_option('esi_peppol_vat_field_key', $vat_field_key); @@ -815,109 +770,14 @@ class PEPPOL_Plugin { ); } - // 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']; - }); + $vat_keys = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_vat_meta_keys(); + $vat_fields_found = array_map(function ($key) { + return [ + 'key' => $key, + 'count' => 0, + 'sample_value' => null, + ]; + }, $vat_keys); wp_send_json_success([ 'fields' => array_values($vat_fields_found), diff --git a/app/helpers/Woo_Helper.php b/app/helpers/Woo_Helper.php index d36ad47..934eee2 100644 --- a/app/helpers/Woo_Helper.php +++ b/app/helpers/Woo_Helper.php @@ -187,6 +187,114 @@ class PEPPOL_Woo_Helper { ]; } + /** + * Retourne les IDs des commandes ayant un meta_key TVA/VAT. + * Compatible HPOS et legacy. + * + * @param int $limit 0 pour aucune limite. + * @param string $order ASC|DESC + * @param array $statuses Statuts Woo (sans préfixe wc-). + * @return int[] + */ + public static function esi_get_order_ids_with_vat_meta( + int $limit = 0, + string $order = 'DESC', + array $statuses = ['completed', 'processing', 'on-hold'] + ): array { + global $wpdb; + + $order = strtoupper($order) === 'ASC' ? 'ASC' : 'DESC'; + $like_tva = '%tva%'; + $like_vat = '%vat%'; + $exclude_key = 'is_vat_exempt'; + + $statuses = array_map(function ($status) { + $status = (string) $status; + return strpos($status, 'wc-') === 0 ? $status : 'wc-' . $status; + }, $statuses); + + $status_placeholders = implode(',', array_fill(0, count($statuses), '%s')); + + $is_hpos = class_exists('\Automattic\WooCommerce\Utilities\OrderUtil') + && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled(); + + if ($is_hpos) { + $orders_table = $wpdb->prefix . 'wc_orders'; + $orders_meta_table = $wpdb->prefix . 'wc_orders_meta'; + + $sql = " + SELECT DISTINCT o.id + FROM {$orders_table} o + INNER JOIN {$orders_meta_table} om ON om.order_id = o.id + WHERE o.type = 'shop_order' + AND (om.meta_key LIKE %s OR om.meta_key LIKE %s) + AND om.meta_key != %s + AND o.status IN ({$status_placeholders}) + ORDER BY o.date_created_gmt {$order} + "; + } else { + $sql = " + SELECT DISTINCT p.ID + FROM {$wpdb->posts} p + INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID + WHERE p.post_type = 'shop_order' + AND (pm.meta_key LIKE %s OR pm.meta_key LIKE %s) + AND pm.meta_key != %s + AND p.post_status IN ({$status_placeholders}) + ORDER BY p.post_date {$order} + "; + } + + $params = array_merge([$like_tva, $like_vat, $exclude_key], $statuses); + if ($limit > 0) { + $sql .= ' LIMIT %d'; + $params[] = $limit; + } + + $prepared_sql = $wpdb->prepare($sql, $params); + $ids = $wpdb->get_col($prepared_sql); + + return array_map('intval', $ids); + } + + /** + * Retourne les meta_key TVA/VAT distinctes. + * Compatible HPOS et legacy. + * + * @param int $limit + * @return string[] + */ + public static function esi_get_vat_meta_keys( int $limit = 50 ): array { + global $wpdb; + + $like_tva = '%tva%'; + $like_vat = '%vat%'; + $exclude_key = 'is_vat_exempt'; + + $is_hpos = class_exists('\Automattic\WooCommerce\Utilities\OrderUtil') + && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled(); + + $meta_table = $is_hpos ? $wpdb->prefix . 'wc_orders_meta' : $wpdb->postmeta; + + $sql = " + SELECT DISTINCT meta_key + FROM {$meta_table} + WHERE (meta_key LIKE %s OR meta_key LIKE %s) + AND meta_key <> %s + LIMIT %d + "; + + $prepared_sql = $wpdb->prepare($sql, $like_tva, $like_vat, $exclude_key, $limit); + $keys = $wpdb->get_col($prepared_sql); + + $keys = array_values(array_unique(array_map('strval', $keys))); + $keys = array_values(array_filter($keys, function ($key) { + return $key !== 'is_vat_exempt'; + })); + + return $keys; + } + /** * Détecte tous les champs TVA dans une commande, limités aux groupes billing et shipping. * Retourne un tableau de tous les champs trouvés avec leur groupe et leur valeur.