Compare commits
No commits in common. "main" and "master" have entirely different histories.
@ -35,7 +35,7 @@ return [
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
'is_test_environment' => false,
|
||||
'is_test_environment' => true,
|
||||
|
||||
/**
|
||||
* Timeout pour les requêtes API (en secondes)
|
||||
|
||||
@ -509,12 +509,6 @@ class PEPPOL_peppol_controller {
|
||||
public static function build_payload_from_order(\WC_Order $order): array {
|
||||
$order_id = $order->get_id();
|
||||
$order_number = $order->get_order_number();
|
||||
$invoice_number = $order_number;
|
||||
|
||||
$wpo_invoice_number = (string) $order->get_meta('_wcpdf_invoice_number', true);
|
||||
if ($wpo_invoice_number !== '') {
|
||||
$invoice_number = $wpo_invoice_number;
|
||||
}
|
||||
$currency_code = $order->get_currency();
|
||||
|
||||
$invoice_date = $order->get_date_created()
|
||||
@ -555,25 +549,6 @@ class PEPPOL_peppol_controller {
|
||||
$order
|
||||
);
|
||||
|
||||
$payment_data = PEPPOL_Woo_Helper::esi_get_order_payment_data($order);
|
||||
$payment_means_code = $payment_data['payment_means_code'] ?? '30';
|
||||
if (!empty($payment_data['payment_terms'])) {
|
||||
$payment_terms = $payment_data['payment_terms'];
|
||||
}
|
||||
|
||||
$payment_means_code = \apply_filters(
|
||||
'esi_peppol_payment_means_code',
|
||||
$payment_means_code,
|
||||
$payment_data,
|
||||
$order
|
||||
);
|
||||
$payment_terms = \apply_filters(
|
||||
'esi_peppol_payment_terms_from_payment',
|
||||
$payment_terms,
|
||||
$payment_data,
|
||||
$order
|
||||
);
|
||||
|
||||
// Données vendeur (store) - basées sur les options WooCommerce
|
||||
$store_name = \get_bloginfo('name');
|
||||
$store_address = \get_option('woocommerce_store_address', '');
|
||||
@ -1004,8 +979,9 @@ class PEPPOL_peppol_controller {
|
||||
'total_amount_excluding_vat' => round($total_excl_vat, 2),
|
||||
'total_vat_amount' => round($total_vat, 2),
|
||||
'total_amount_including_vat' => round($total_incl_vat, 2),
|
||||
'total_paid_amount' => 0,
|
||||
'total_payable_amount' => round($order->get_total(), 2),
|
||||
'total_paid_amount' => round($order->get_total(), 2),
|
||||
'total_payable_amount' => 0,
|
||||
'amount_due' => round($order->get_total(), 2),
|
||||
];
|
||||
|
||||
$invoice_totals = \apply_filters('esi_peppol_invoice_totals', $invoice_totals, $order);
|
||||
@ -1019,13 +995,12 @@ class PEPPOL_peppol_controller {
|
||||
'document_type' => 'invoice',
|
||||
'external_reference' => $external_reference,
|
||||
'invoice' => [
|
||||
'invoice_number' => $invoice_number,
|
||||
'invoice_number' => $order_number,
|
||||
'invoice_date' => $invoice_date,
|
||||
'due_date' => $due_date,
|
||||
'currency_code' => $currency_code,
|
||||
'invoice_notes' => $invoice_notes,
|
||||
'payment_terms' => $payment_terms,
|
||||
'payment_means_code' => $payment_means_code,
|
||||
],
|
||||
'parties' => [
|
||||
'seller' => $seller,
|
||||
@ -1039,142 +1014,4 @@ class PEPPOL_peppol_controller {
|
||||
|
||||
return \apply_filters('esi_peppol_payload_from_order', $payload, $order);
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère et sauvegarde un numéro WPO si format de secours configuré.
|
||||
*/
|
||||
public static function ensure_invoice_number_meta(\WC_Order $order): void {
|
||||
$wpo_invoice_number = (string) $order->get_meta('_wcpdf_invoice_number', true);
|
||||
if ($wpo_invoice_number !== '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$use_backup_format = !\ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_has_wpo_invoice_numbers();
|
||||
if (!$use_backup_format) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backup_format = (string) \get_option('esi_peppol_invoice_number_format', '');
|
||||
if ($backup_format === '') {
|
||||
$backup_format = (string) \get_option('esi_peppol_vat_number_format', '');
|
||||
}
|
||||
if ($backup_format !== '') {
|
||||
$generated = self::build_invoice_number_from_format($backup_format, $order);
|
||||
if ($generated !== '') {
|
||||
$order->update_meta_data('_wcpdf_invoice_number', $generated);
|
||||
$order->save();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$fallback = (string) $order->get_order_number();
|
||||
if ($fallback === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$order->update_meta_data('_wcpdf_invoice_number', $fallback);
|
||||
$order->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un numéro de facture depuis un format de secours.
|
||||
* Placeholders supportés : {YYYY}, {YY}, {MM}, {DD}, {ORDER_ID}, {ORDER_NUMBER}
|
||||
*/
|
||||
private static function build_invoice_number_from_format(string $format, \WC_Order $order): string {
|
||||
$date = $order->get_date_created();
|
||||
$year = $date ? $date->date('Y') : \gmdate('Y');
|
||||
$month = $date ? $date->date('m') : \gmdate('m');
|
||||
$day = $date ? $date->date('d') : \gmdate('d');
|
||||
|
||||
$sequence = self::get_next_invoice_sequence($format, $order, $year);
|
||||
|
||||
$replacements = [
|
||||
'{YYYY}' => $year,
|
||||
'{YY}' => substr($year, -2),
|
||||
'{MM}' => $month,
|
||||
'{DD}' => $day,
|
||||
'{ORDER_ID}' => (string) $order->get_id(),
|
||||
'{ORDER_NUMBER}' => (string) $order->get_order_number(),
|
||||
'{number}' => $sequence,
|
||||
];
|
||||
|
||||
$result = strtr($format, $replacements);
|
||||
|
||||
return trim($result);
|
||||
}
|
||||
|
||||
private static function get_next_invoice_sequence(string $format, \WC_Order $order, string $current_year): string {
|
||||
if (!function_exists('wc_get_orders')) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
$orders = \wc_get_orders([
|
||||
'limit' => 1,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'status' => 'any',
|
||||
'meta_key' => '_wcpdf_invoice_number',
|
||||
'meta_compare' => 'EXISTS',
|
||||
'exclude' => [$order->get_id()],
|
||||
'return' => 'objects',
|
||||
]);
|
||||
|
||||
if (empty($orders)) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
$last_order = $orders[0];
|
||||
$last_invoice = (string) $last_order->get_meta('_wcpdf_invoice_number', true);
|
||||
if ($last_invoice === '') {
|
||||
return '1';
|
||||
}
|
||||
|
||||
$matches = self::match_invoice_number_format($format, $last_invoice);
|
||||
if (empty($matches['number'])) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
$last_number = (int) $matches['number'];
|
||||
$number_width = strlen((string) $matches['number']);
|
||||
|
||||
$next_number = $last_number + 1;
|
||||
if (strpos($format, '{YYYY}') !== false) {
|
||||
$last_year = (string) ($matches['year'] ?? '');
|
||||
if ($last_year !== $current_year) {
|
||||
$next_number = 1;
|
||||
}
|
||||
} elseif (strpos($format, '{YY}') !== false) {
|
||||
$current_year2 = substr($current_year, -2);
|
||||
$last_year2 = (string) ($matches['year2'] ?? '');
|
||||
if ($last_year2 !== $current_year2) {
|
||||
$next_number = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$next_str = (string) $next_number;
|
||||
if ($number_width > 1) {
|
||||
$next_str = str_pad($next_str, $number_width, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return $next_str;
|
||||
}
|
||||
|
||||
private static function match_invoice_number_format(string $format, string $value): array {
|
||||
$pattern = preg_quote($format, '/');
|
||||
$pattern = str_replace('\{YYYY\}', '(?P<year>\d{4})', $pattern);
|
||||
$pattern = str_replace('\{YY\}', '(?P<year2>\d{2})', $pattern);
|
||||
$pattern = str_replace('\{MM\}', '(?P<month>\d{2})', $pattern);
|
||||
$pattern = str_replace('\{DD\}', '(?P<day>\d{2})', $pattern);
|
||||
$pattern = str_replace('\{ORDER_ID\}', '\d+', $pattern);
|
||||
$pattern = str_replace('\{ORDER_NUMBER\}', '.+?', $pattern);
|
||||
$pattern = str_replace('\{number\}', '(?P<number>\d+)', $pattern);
|
||||
$pattern = '/^' . $pattern . '$/';
|
||||
|
||||
$matches = [];
|
||||
if (preg_match($pattern, $value, $matches) !== 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
}
|
||||
@ -155,21 +155,12 @@ class PEPPOL_Plugin {
|
||||
$password = isset($_POST['esi_peppol_password']) ? sanitize_text_field(wp_unslash($_POST['esi_peppol_password'])) : '';
|
||||
$email = isset($_POST['esi_peppol_email']) ? sanitize_email(wp_unslash($_POST['esi_peppol_email'])) : '';
|
||||
$logo_email_id = isset($_POST['esi_peppol_logo_email_id']) ? absint(wp_unslash($_POST['esi_peppol_logo_email_id'])) : 0;
|
||||
$invoice_number_format = get_option('esi_peppol_invoice_number_format', '');
|
||||
if ($invoice_number_format === '') {
|
||||
$invoice_number_format = get_option('esi_peppol_vat_number_format', '');
|
||||
}
|
||||
if (isset($_POST['esi_peppol_invoice_number_format'])) {
|
||||
$invoice_number_format = sanitize_text_field(wp_unslash($_POST['esi_peppol_invoice_number_format']));
|
||||
}
|
||||
$invoice_number_format = self::normalize_invoice_number_format($invoice_number_format);
|
||||
|
||||
// Sauvegarde systématique des options
|
||||
update_option('esi_peppol_api_key', $api_key);
|
||||
update_option('esi_peppol_password', $password);
|
||||
update_option('esi_peppol_email', $email);
|
||||
update_option('esi_peppol_logo_email_id', $logo_email_id);
|
||||
update_option('esi_peppol_invoice_number_format', $invoice_number_format);
|
||||
|
||||
if ($action === 'save') {
|
||||
$notice = __('Paramètres enregistrés avec succès.', 'esi_peppol');
|
||||
@ -708,7 +699,8 @@ class PEPPOL_Plugin {
|
||||
|
||||
/**
|
||||
* Détecte automatiquement les champs TVA lors de l'installation.
|
||||
* Utilise la fonction helper PEPPOL_Woo_Helper::esi_get_vat_meta_keys().
|
||||
* Limite la recherche aux champs billing et shipping dans cet ordre.
|
||||
* Utilise la fonction helper PEPPOL_Woo_Helper::esi_detect_vat_fields_billing_shipping().
|
||||
*
|
||||
* @return string|null La clé du champ TVA détecté, ou null si aucun trouvé
|
||||
*/
|
||||
@ -720,22 +712,66 @@ class PEPPOL_Plugin {
|
||||
}
|
||||
|
||||
// Vérifier si WooCommerce est disponible
|
||||
if (!function_exists('wc_get_order')) {
|
||||
if (!function_exists('wc_get_orders')) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
$vat_keys = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_vat_meta_keys(50);
|
||||
// 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']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucun champ trouvé, marquer que la détection a été effectuée et retourner null
|
||||
if (empty($vat_keys)) {
|
||||
if (empty($vat_fields_found)) {
|
||||
update_option('esi_peppol_vat_field_auto_detected', true);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prendre la première clé trouvée
|
||||
$vat_field_key = reset($vat_keys);
|
||||
// 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'];
|
||||
|
||||
// Sauvegarder le champ détecté
|
||||
update_option('esi_peppol_vat_field_key', $vat_field_key);
|
||||
@ -770,14 +806,109 @@ class PEPPOL_Plugin {
|
||||
);
|
||||
}
|
||||
|
||||
$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);
|
||||
// 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),
|
||||
@ -1053,14 +1184,7 @@ class PEPPOL_Plugin {
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
/**
|
||||
* Récupère toutes les notices TVA à afficher
|
||||
*
|
||||
* @return array Tableau de notices, chaque notice contient 'message' et 'type' (info, warning, etc.)
|
||||
*/
|
||||
public static function get_vat_notice_message(): array {
|
||||
$notices = [];
|
||||
|
||||
public static function get_vat_notice_message(): ?string {
|
||||
// Vérifier d'abord si un champ TVA client a été détecté
|
||||
$vat_field_key = (string) get_option('esi_peppol_vat_field_key', '');
|
||||
$auto_detected = (bool) get_option('esi_peppol_vat_field_auto_detected', false);
|
||||
@ -1068,45 +1192,35 @@ class PEPPOL_Plugin {
|
||||
// Si aucun champ TVA détecté, proposer la détection dans settings
|
||||
if ($vat_field_key === '' && $auto_detected) {
|
||||
$settings_url = admin_url('admin.php?page=esi-peppol-settings');
|
||||
$notices[] = [
|
||||
'message' => sprintf(
|
||||
/* translators: %s: URL vers la page de réglages */
|
||||
__('Aucun champ TVA client n\'a été détecté automatiquement. Vous pouvez utiliser l\'outil de détection dans <a href="%s">ESI Peppol > Configuration</a> pour rechercher manuellement les champs TVA dans vos commandes.', 'esi_peppol'),
|
||||
esc_url($settings_url)
|
||||
),
|
||||
'type' => 'warning'
|
||||
];
|
||||
return sprintf(
|
||||
/* translators: %s: URL vers la page de réglages */
|
||||
__('Aucun champ TVA client n\'a été détecté automatiquement. Vous pouvez utiliser l\'outil de détection dans <a href="%s">ESI Peppol > Configuration</a> pour rechercher manuellement les champs TVA dans vos commandes.', 'esi_peppol'),
|
||||
esc_url($settings_url)
|
||||
);
|
||||
}
|
||||
|
||||
$vat_number = (string) get_option('woocommerce_store_vat_number', '');
|
||||
|
||||
// Si le numéro de TVA est renseigné, on ne vérifie pas les autres notices liées au TVA
|
||||
if ($vat_number === '') {
|
||||
$api_key = (string) get_option('esi_peppol_api_key', '');
|
||||
$password = (string) get_option('esi_peppol_password', '');
|
||||
|
||||
if ($api_key === '' && $password === '') {
|
||||
// Invitation initiale
|
||||
$notices[] = [
|
||||
'message' => __(
|
||||
'Pour finaliser la mise en place de Peppol, pensez à renseigner le numéro de TVA de la boutique dans WooCommerce > Réglages > Général.',
|
||||
'esi_peppol'
|
||||
),
|
||||
'type' => 'info'
|
||||
];
|
||||
} else {
|
||||
// Rappel après configuration du connecteur
|
||||
$notices[] = [
|
||||
'message' => __(
|
||||
'Votre connecteur ESI Peppol est configuré, mais le numéro de TVA de la boutique n\'est pas encore renseigné dans WooCommerce > Réglages > Général. Sans ce numéro, les factures Peppol risquent d\'être incomplètes.',
|
||||
'esi_peppol'
|
||||
),
|
||||
'type' => 'warning'
|
||||
];
|
||||
}
|
||||
if ($vat_number !== '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $notices;
|
||||
$api_key = (string) get_option('esi_peppol_api_key', '');
|
||||
$password = (string) get_option('esi_peppol_password', '');
|
||||
|
||||
if ($api_key === '' && $password === '') {
|
||||
// Invitation initiale
|
||||
return __(
|
||||
'Pour finaliser la mise en place de Peppol, pensez à renseigner le numéro de TVA de la boutique dans WooCommerce > Réglages > Général.',
|
||||
'esi_peppol'
|
||||
);
|
||||
}
|
||||
|
||||
// Rappel après configuration du connecteur
|
||||
return __(
|
||||
'Votre connecteur ESI Peppol est configuré, mais le numéro de TVA de la boutique n\'est pas encore renseigné dans WooCommerce > Réglages > Général. Sans ce numéro, les factures Peppol risquent d\'être incomplètes.',
|
||||
'esi_peppol'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1123,146 +1237,27 @@ class PEPPOL_Plugin {
|
||||
return;
|
||||
}
|
||||
|
||||
$vat_notices = self::get_vat_notice_message();
|
||||
$vat_notice_message = self::get_vat_notice_message();
|
||||
|
||||
if (empty($vat_notices)) {
|
||||
if (empty($vat_notice_message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($vat_notices as $notice) {
|
||||
$notice_class = 'notice-' . $notice['type'];
|
||||
echo '<div class="notice ' . \esc_attr($notice_class) . '"><p>' .
|
||||
\wp_kses_post($notice['message']) .
|
||||
'</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie l'état de configuration des settings ESI Peppol
|
||||
*
|
||||
* @return array Tableau avec l'état de chaque étape du setup
|
||||
*/
|
||||
public static function check_settings_status(): array {
|
||||
$status = [
|
||||
'vat_number' => [
|
||||
'completed' => false,
|
||||
'value' => '',
|
||||
'label' => __('Numéro de TVA de la boutique', 'esi_peppol'),
|
||||
'description' => __('Le numéro de TVA de votre boutique doit être renseigné dans WooCommerce > Réglages > Général', 'esi_peppol'),
|
||||
'action_url' => admin_url('admin.php?page=wc-settings&tab=general'),
|
||||
'action_label' => __('Configurer dans WooCommerce', 'esi_peppol'),
|
||||
],
|
||||
'vat_field' => [
|
||||
'completed' => false,
|
||||
'value' => '',
|
||||
'label' => __('Champ TVA client', 'esi_peppol'),
|
||||
'description' => __('Le champ utilisé pour récupérer le numéro de TVA du client dans les commandes', 'esi_peppol'),
|
||||
'action_url' => admin_url('admin.php?page=esi-peppol-settings'),
|
||||
'action_label' => __('Détecter le champ TVA', 'esi_peppol'),
|
||||
],
|
||||
'invoice_number_format' => [
|
||||
'completed' => false,
|
||||
'value' => '',
|
||||
'label' => __('Format num facture', 'esi_peppol'),
|
||||
'description' => __('Format du numéro de facture (backup si aucun format détecté automatiquement)', 'esi_peppol'),
|
||||
'action_url' => admin_url('admin.php?page=esi-peppol-settings'),
|
||||
'action_label' => __('Configurer le format', 'esi_peppol'),
|
||||
],
|
||||
'api_credentials' => [
|
||||
'completed' => false,
|
||||
'value' => '',
|
||||
'label' => __('Identifiants API', 'esi_peppol'),
|
||||
'description' => __('Votre clé API et mot de passe pour vous connecter à la plateforme ESI Peppol', 'esi_peppol'),
|
||||
'action_url' => admin_url('admin.php?page=esi-peppol-settings'),
|
||||
'action_label' => __('Configurer les identifiants', 'esi_peppol'),
|
||||
],
|
||||
'webhook' => [
|
||||
'completed' => true, // Toujours disponible
|
||||
'value' => '',
|
||||
'label' => __('URL Webhook', 'esi_peppol'),
|
||||
'description' => __('URL à configurer dans votre profil d\'entreprise sur la plateforme', 'esi_peppol'),
|
||||
'action_url' => admin_url('admin.php?page=esi-peppol-settings'),
|
||||
'action_label' => __('Voir l\'URL webhook', 'esi_peppol'),
|
||||
],
|
||||
'email' => [
|
||||
'completed' => false,
|
||||
'optional' => true,
|
||||
'value' => '',
|
||||
'label' => __('Email de notification', 'esi_peppol'),
|
||||
'description' => __('Adresse email pour recevoir les notifications en cas d\'erreur d\'envoi des factures', 'esi_peppol'),
|
||||
'action_url' => admin_url('admin.php?page=esi-peppol-settings'),
|
||||
'action_label' => __('Configurer l\'email', 'esi_peppol'),
|
||||
],
|
||||
];
|
||||
|
||||
// Vérifier le numéro de TVA de la boutique
|
||||
$vat_number = (string) get_option('woocommerce_store_vat_number', '');
|
||||
if ($vat_number !== '') {
|
||||
$status['vat_number']['completed'] = true;
|
||||
$status['vat_number']['value'] = $vat_number;
|
||||
$api_key = (string) \get_option('esi_peppol_api_key', '');
|
||||
$password = (string) \get_option('esi_peppol_password', '');
|
||||
$vat_field_key = (string) \get_option('esi_peppol_vat_field_key', '');
|
||||
$auto_detected = (bool) \get_option('esi_peppol_vat_field_auto_detected', false);
|
||||
|
||||
// Si aucun champ TVA détecté, utiliser notice-warning
|
||||
if ($vat_field_key === '' && $auto_detected) {
|
||||
$notice_class = 'notice-warning';
|
||||
} else {
|
||||
$notice_class = ($api_key === '' && $password === '') ? 'notice-info' : 'notice-warning';
|
||||
}
|
||||
|
||||
// Vérifier le champ TVA client
|
||||
$vat_field_key = (string) get_option('esi_peppol_vat_field_key', '');
|
||||
if ($vat_field_key !== '') {
|
||||
$status['vat_field']['completed'] = true;
|
||||
$status['vat_field']['value'] = $vat_field_key;
|
||||
}
|
||||
|
||||
// Vérifier le format num TVA (détecté ou backup)
|
||||
$detected_format = '';
|
||||
if (\ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_has_wpo_invoice_numbers()) {
|
||||
$detected_format = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_wpo_invoice_number_format_label();
|
||||
}
|
||||
$backup_format = (string) get_option('esi_peppol_invoice_number_format', '');
|
||||
if ($backup_format === '') {
|
||||
$backup_format = (string) get_option('esi_peppol_vat_number_format', '');
|
||||
}
|
||||
if ($detected_format !== '') {
|
||||
$status['invoice_number_format']['completed'] = true;
|
||||
$status['invoice_number_format']['value'] = sprintf(
|
||||
__('Format détecté : %s', 'esi_peppol'),
|
||||
$detected_format
|
||||
);
|
||||
} elseif ($backup_format !== '') {
|
||||
$status['invoice_number_format']['completed'] = true;
|
||||
$status['invoice_number_format']['value'] = $backup_format;
|
||||
}
|
||||
|
||||
// Vérifier les identifiants API
|
||||
$api_key = (string) get_option('esi_peppol_api_key', '');
|
||||
$password = (string) get_option('esi_peppol_password', '');
|
||||
if ($api_key !== '' && $password !== '') {
|
||||
$status['api_credentials']['completed'] = true;
|
||||
$status['api_credentials']['value'] = substr($api_key, 0, 8) . '...'; // Masquer partiellement
|
||||
}
|
||||
|
||||
// Récupérer l'URL webhook
|
||||
if (class_exists(\ESI_PEPPOL\controllers\PEPPOL_Webhook_controller::class)) {
|
||||
$webhook_url = \ESI_PEPPOL\controllers\PEPPOL_Webhook_controller::get_webhook_url();
|
||||
$status['webhook']['value'] = $webhook_url;
|
||||
}
|
||||
|
||||
// Vérifier l'email
|
||||
$email = (string) get_option('esi_peppol_email', '');
|
||||
if ($email !== '') {
|
||||
$status['email']['completed'] = true;
|
||||
$status['email']['value'] = $email;
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
private static function normalize_invoice_number_format(string $format): string {
|
||||
$format = trim($format);
|
||||
if ($format === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$format = str_replace('{number}', '', $format);
|
||||
$format = trim($format);
|
||||
|
||||
return $format . '{number}';
|
||||
echo '<div class="notice ' . \esc_attr($notice_class) . '"><p>' .
|
||||
\wp_kses_post($vat_notice_message) .
|
||||
'</p></div>';
|
||||
}
|
||||
|
||||
public static function write_log($message, $level = 'INFO') {
|
||||
|
||||
@ -46,19 +46,12 @@ class PEPPOL_Woocommerce_controller {
|
||||
public static function post_payment(int $order_id): void {
|
||||
$order_id = (int) $order_id;
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ($order_id <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = \wc_get_order($order_id);
|
||||
|
||||
$is_invoice_exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->prefix}esi_peppol_invoices WHERE order_id = %d", $order_id));
|
||||
if ($is_invoice_exists > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$order instanceof \WC_Order) {
|
||||
return;
|
||||
}
|
||||
@ -98,9 +91,6 @@ class PEPPOL_Woocommerce_controller {
|
||||
return;
|
||||
}
|
||||
|
||||
// Générer un numéro de facture de secours si nécessaire
|
||||
PEPPOL_peppol_controller::ensure_invoice_number_meta($order);
|
||||
|
||||
// Construire le payload JSON à partir de la commande
|
||||
$payload = PEPPOL_peppol_controller::build_payload_from_order($order);
|
||||
|
||||
|
||||
@ -187,114 +187,6 @@ 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.
|
||||
@ -394,198 +286,7 @@ class PEPPOL_Woo_Helper {
|
||||
}
|
||||
}
|
||||
|
||||
// Si aucun champ n'a été trouvé dans les groupes billing/shipping,
|
||||
// chercher dans toutes les meta keys contenant un pattern
|
||||
if (empty($vat_fields_found)) {
|
||||
foreach ($order->get_meta_data() as $meta) {
|
||||
$k = (string) $meta->key;
|
||||
$k_lower = strtolower($k);
|
||||
|
||||
// Vérifier si la clé contient un pattern TVA
|
||||
$found = false;
|
||||
foreach ($patterns as $p) {
|
||||
if (strpos($k_lower, $p) !== false) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$v = $meta->value;
|
||||
if (is_string($v)) {
|
||||
$v = trim($v);
|
||||
}
|
||||
if (!empty($v)) {
|
||||
if (!isset($vat_fields_found[$k])) {
|
||||
$vat_fields_found[$k] = [
|
||||
'key' => $k,
|
||||
'value' => $v,
|
||||
'group' => 'other', // Groupe "other" pour les champs hors billing/shipping
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return array_values($vat_fields_found);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne les infos de paiement utiles pour Peppol.
|
||||
*
|
||||
* @param \WC_Order|int $order
|
||||
* @return array{payment_method:string,payment_method_title:string,payment_means_code:string,payment_terms:string}
|
||||
*/
|
||||
public static function esi_get_order_payment_data( $order ): array {
|
||||
if ( is_numeric( $order ) ) {
|
||||
$order = wc_get_order( $order );
|
||||
}
|
||||
|
||||
if ( ! $order instanceof \WC_Order ) {
|
||||
return [
|
||||
'payment_method' => '',
|
||||
'payment_method_title' => '',
|
||||
'payment_means_code' => '30',
|
||||
'payment_terms' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$payment_method = (string) $order->get_payment_method();
|
||||
$payment_method_title = (string) $order->get_payment_method_title();
|
||||
|
||||
// Valeur par defaut : virement (code 30)
|
||||
$payment_means_code = '30';
|
||||
|
||||
$map = [
|
||||
'bacs' => '30', // virement bancaire
|
||||
'cheque' => '20',
|
||||
'cod' => '10', // cash on delivery
|
||||
];
|
||||
if ( isset( $map[ $payment_method ] ) ) {
|
||||
$payment_means_code = $map[ $payment_method ];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'payment_method' => $payment_method,
|
||||
'payment_method_title' => $payment_method_title,
|
||||
'payment_means_code' => $payment_means_code,
|
||||
'payment_terms' => '',
|
||||
];
|
||||
|
||||
return apply_filters( 'esi_peppol_order_payment_data', $data, $order );
|
||||
}
|
||||
|
||||
public static function esi_is_wpo_wcpdf_installed(): bool {
|
||||
if (!function_exists('get_plugins')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
$plugins = get_plugins(); // [ 'dir/main.php' => headers... ]
|
||||
foreach ($plugins as $file => $data) {
|
||||
if ($file === 'woocommerce-pdf-invoices-packing-slips/woocommerce-pdf-invoices-packingslips.php') {
|
||||
return true;
|
||||
}
|
||||
// Optionnel : fallback par Name (plus fragile, dépend de la traduction / headers)
|
||||
if (!empty($data['Name']) && stripos($data['Name'], 'PDF Invoices') !== false) {
|
||||
// pas ultra fiable, mais utile en dernier recours
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* detecte les num de facture enregistré
|
||||
*/
|
||||
//wpo_wcpdf_documents_settings_invoice
|
||||
/*
|
||||
Array
|
||||
(
|
||||
[enabled] => 1
|
||||
[attach_to_email_ids] => Array
|
||||
(
|
||||
[customer_on_hold_order] => 1
|
||||
[customer_processing_order] => 1
|
||||
[customer_completed_order] => 1
|
||||
)
|
||||
|
||||
[my_account_buttons] => available
|
||||
[display_email] => 1
|
||||
[display_customer_notes] => 1
|
||||
[display_shipping_address] =>
|
||||
[display_number] => invoice_number
|
||||
[number_format] => Array
|
||||
(
|
||||
[prefix] => 2026-
|
||||
[suffix] =>
|
||||
[padding] =>
|
||||
)
|
||||
|
||||
[display_date] =>
|
||||
)*/
|
||||
|
||||
/**
|
||||
* Vérifie la présence de numéros de facture WPO WCPDF.
|
||||
* - Plugin installé
|
||||
* - Paramètres facture activés
|
||||
* - Au moins une commande avec meta _wcpdf_invoice_number
|
||||
*/
|
||||
public static function esi_has_wpo_invoice_numbers(): bool {
|
||||
if ( ! self::esi_is_wpo_wcpdf_installed() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'wc_get_orders' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$settings = get_option( 'wpo_wcpdf_documents_settings_invoice', [] );
|
||||
if ( empty( $settings ) || ! is_array( $settings ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( empty( $settings['enabled'] ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( empty( $settings['display_number'] ) || $settings['display_number'] !== 'invoice_number' ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$orders = wc_get_orders( [
|
||||
'limit' => 1,
|
||||
'status' => 'any',
|
||||
'meta_key' => '_wcpdf_invoice_number',
|
||||
'meta_compare' => 'EXISTS',
|
||||
'return' => 'ids',
|
||||
] );
|
||||
|
||||
return ! empty( $orders );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un libellé lisible du format des numéros WPO WCPDF.
|
||||
*/
|
||||
public static function esi_get_wpo_invoice_number_format_label(): string {
|
||||
$settings = get_option( 'wpo_wcpdf_documents_settings_invoice', [] );
|
||||
if ( empty( $settings ) || ! is_array( $settings ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$number_format = $settings['number_format'] ?? [];
|
||||
if ( ! is_array( $number_format ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$prefix = (string) ( $number_format['prefix'] ?? '' );
|
||||
$suffix = (string) ( $number_format['suffix'] ?? '' );
|
||||
$padding = (string) ( $number_format['padding'] ?? '' );
|
||||
|
||||
$format = $prefix . '{number}' . $suffix;
|
||||
if ( $padding !== '' ) {
|
||||
$format .= ' (padding: ' . $padding . ')';
|
||||
}
|
||||
|
||||
return $format;
|
||||
}
|
||||
|
||||
}
|
||||
@ -403,169 +403,3 @@ a.statut:visited {
|
||||
height: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* ====================================
|
||||
TIMELINE VERTICALE - DASHBOARD
|
||||
==================================== */
|
||||
|
||||
.esi-peppol-setup-guide {
|
||||
max-width: 900px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.step {
|
||||
position: relative;
|
||||
padding-left: 70px;
|
||||
padding-bottom: 40px;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
/* Dernier élément sans padding-bottom */
|
||||
.step:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Cercle numéroté (::before) */
|
||||
.step::before {
|
||||
content: attr(data-step);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
z-index: 2;
|
||||
|
||||
/* État par défaut : cercle blanc, bord gris, numéro gris */
|
||||
background-color: #ffffff;
|
||||
border: 3px solid #d1d5db;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* Ligne verticale (::after) */
|
||||
.step::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 22px; /* Centre du cercle (44px / 2) */
|
||||
top: 44px; /* Sous le cercle */
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
|
||||
/* État par défaut : ligne grise */
|
||||
background-color: #d1d5db;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Masquer la ligne sur le dernier élément */
|
||||
.step:last-child::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Carte de contenu */
|
||||
.step-card {
|
||||
background-color: #f3f4f6;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
padding: 20px 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step-card h3 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.step-card p {
|
||||
margin: 0 0 16px 0;
|
||||
color: #6b7280;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.step-card .step-value {
|
||||
display: inline-block;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.step-card .step-value strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.step-card .step-value code {
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.step-card .step-note {
|
||||
margin-top: 12px;
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/** ÉTAT COMPLÉTÉ : .is-done
|
||||
======================================== */
|
||||
|
||||
.step.is-done::before {
|
||||
/* Check ✓ ou numéro en bleu */
|
||||
background-color: #dbeafe;
|
||||
border-color: #3b82f6;
|
||||
color: #2563eb;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.step.is-done::after {
|
||||
/* Ligne bleue */
|
||||
background-color: #3b82f6;
|
||||
}
|
||||
|
||||
.step.is-done .step-card {
|
||||
/* Carte fond bleu, texte blanc */
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.step.is-done .step-card h3 {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.step.is-done .step-card p {
|
||||
color: #dbeafe;
|
||||
}
|
||||
|
||||
.step.is-done .step-card .step-value {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
.step.is-done .step-card .step-value strong {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.step.is-done .step-card .step-value code {
|
||||
color: #1e40af;
|
||||
background-color: #eff6ff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* Pour afficher un check ✓ sur les étapes complétées */
|
||||
.step.is-done[data-step="✓"]::before {
|
||||
font-size: 22px;
|
||||
content: "✓";
|
||||
}
|
||||
|
||||
@ -199,7 +199,6 @@
|
||||
}
|
||||
|
||||
// Toggle affichage du mot de passe sur la page de configuration
|
||||
|
||||
$('.esi-peppol-password-toggle').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
var $btn = $(this);
|
||||
@ -508,45 +507,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Copier l'URL webhook depuis le dashboard (avec data-copy-text)
|
||||
$(document).on('click', '.esi-peppol-copy-webhook-url[data-copy-text]', function (e) {
|
||||
e.preventDefault();
|
||||
var $btn = $(this);
|
||||
var text = $btn.attr('data-copy-text');
|
||||
|
||||
if (text) {
|
||||
// Utiliser l'API Clipboard moderne si disponible
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(function () {
|
||||
var originalText = $btn.text();
|
||||
$btn.text('Copié !');
|
||||
setTimeout(function () {
|
||||
$btn.text(originalText);
|
||||
}, 2000);
|
||||
}).catch(function (err) {
|
||||
console.error('Erreur lors de la copie:', err);
|
||||
});
|
||||
} else {
|
||||
// Fallback pour les anciens navigateurs
|
||||
try {
|
||||
var $temp = $('<textarea>');
|
||||
$('body').append($temp);
|
||||
$temp.val(text).select();
|
||||
document.execCommand('copy');
|
||||
$temp.remove();
|
||||
|
||||
var originalText = $btn.text();
|
||||
$btn.text('Copié !');
|
||||
setTimeout(function () {
|
||||
$btn.text(originalText);
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('Erreur lors de la copie:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Gestion de la détection des champs TVA
|
||||
var $detectVatBtn = $('#esi-peppol-detect-vat-fields');
|
||||
var $vatFieldsResult = $('#esi-peppol-vat-fields-result');
|
||||
|
||||
@ -1,299 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Exemple Timeline Verticale</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #1f2937;
|
||||
margin-bottom: 40px;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
/* ====================================
|
||||
TIMELINE VERTICALE
|
||||
==================================== */
|
||||
|
||||
.timeline {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.step {
|
||||
position: relative;
|
||||
padding-left: 70px;
|
||||
padding-bottom: 40px;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
/* Dernier élément sans padding-bottom */
|
||||
.step:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* Cercle numéroté (::before) */
|
||||
.step::before {
|
||||
content: attr(data-step);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
z-index: 2;
|
||||
|
||||
/* État par défaut : cercle blanc, bord gris, numéro gris */
|
||||
background-color: #ffffff;
|
||||
border: 3px solid #d1d5db;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* Ligne verticale (::after) */
|
||||
.step::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 22px; /* Centre du cercle (44px / 2) */
|
||||
top: 44px; /* Sous le cercle */
|
||||
bottom: 0;
|
||||
width: 3px;
|
||||
|
||||
/* État par défaut : ligne grise */
|
||||
background-color: #d1d5db;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Masquer la ligne sur le dernier élément */
|
||||
.step:last-child::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Carte de contenu */
|
||||
.step-card {
|
||||
background-color: #f3f4f6;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
padding: 20px 24px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.step-card h3 {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.step-card p {
|
||||
margin: 0 0 16px 0;
|
||||
color: #6b7280;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.step-card .step-value {
|
||||
display: inline-block;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.step-card .step-value strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.step-card .step-value code {
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.step-card .btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.step-card .btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
ÉTAT COMPLÉTÉ : .is-done
|
||||
======================================== */
|
||||
|
||||
.step.is-done::before {
|
||||
/* Check ✓ ou numéro en bleu */
|
||||
background-color: #dbeafe;
|
||||
border-color: #3b82f6;
|
||||
color: #2563eb;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.step.is-done::after {
|
||||
/* Ligne bleue */
|
||||
background-color: #3b82f6;
|
||||
}
|
||||
|
||||
.step.is-done .step-card {
|
||||
/* Carte fond bleu, texte blanc */
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
|
||||
}
|
||||
|
||||
.step.is-done .step-card h3 {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.step.is-done .step-card p {
|
||||
color: #dbeafe;
|
||||
}
|
||||
|
||||
.step.is-done .step-card .step-value {
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
border-color: #2563eb;
|
||||
}
|
||||
|
||||
.step.is-done .step-card .step-value strong {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
.step.is-done .step-card .step-value code {
|
||||
color: #1e40af;
|
||||
background-color: #eff6ff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/* Pour afficher un check ✓ sur les étapes complétées */
|
||||
.step.is-done[data-step="✓"]::before {
|
||||
font-size: 22px;
|
||||
content: "✓";
|
||||
}
|
||||
|
||||
.note {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding-top: 30px;
|
||||
border-top: 2px solid #e5e7eb;
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Timeline Verticale - Exemple</h1>
|
||||
|
||||
<div class="timeline">
|
||||
<!-- Étape 1: Complétée -->
|
||||
<div class="step is-done" data-step="✓">
|
||||
<div class="step-card">
|
||||
<h3>Configuration du connecteur</h3>
|
||||
<p>Le numéro de TVA de votre boutique doit être renseigné dans WooCommerce > Réglages > Général</p>
|
||||
<div class="step-value">
|
||||
<strong>Valeur configurée :</strong>
|
||||
<code>BE0431.066.713</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 2: Complétée -->
|
||||
<div class="step is-done" data-step="✓">
|
||||
<div class="step-card">
|
||||
<h3>Numéro de TVA de la boutique</h3>
|
||||
<p>Le champ utilisé pour récupérer le numéro de TVA du client dans les commandes</p>
|
||||
<div class="step-value">
|
||||
<strong>Champ détecté :</strong>
|
||||
<code>billing_vat_number</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 3: Non complétée -->
|
||||
<div class="step" data-step="3">
|
||||
<div class="step-card">
|
||||
<h3>Champ TVA client</h3>
|
||||
<p>Le champ utilisé pour récupérer le numéro de TVA du client dans les commandes</p>
|
||||
<p style="margin-bottom: 0; color: #ef4444; font-weight: 500;">Aucun champ détecté</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 4: Non complétée -->
|
||||
<div class="step" data-step="4">
|
||||
<div class="step-card">
|
||||
<h3>Identifiants API</h3>
|
||||
<p>Votre clé API et mot de passe pour vous connecter à la plateforme ESI Peppol</p>
|
||||
<p style="margin-bottom: 0; color: #ef4444; font-weight: 500;">Aucune clé API détectée</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 5: Non complétée -->
|
||||
<div class="step" data-step="5">
|
||||
<div class="step-card">
|
||||
<h3>URL Webhook</h3>
|
||||
<p>URL à configurer dans votre profil d'entreprise sur la plateforme</p>
|
||||
<div class="step-value">
|
||||
<strong>Configurez cette URL :</strong>
|
||||
<code>https://peppol.esi-web.be/webhook/callback</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 6: Non complétée (optionnelle) -->
|
||||
<div class="step" data-step="6">
|
||||
<div class="step-card">
|
||||
<h3>Email de notification (optionnel)</h3>
|
||||
<p>Adresse email pour recevoir les notifications en cas d'erreur d'envoi des factures</p>
|
||||
<a href="#" class="btn">Configurer un email</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="note">
|
||||
<p><strong>Structure CSS:</strong> .step avec ::before (cercle) et ::after (ligne)</p>
|
||||
<p><strong>États:</strong> Par défaut (gris) | .is-done (bleu avec check ✓)</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -6,160 +6,40 @@ if (!defined('ABSPATH')) {
|
||||
|
||||
/** @var string $settings_url */
|
||||
/** @var string $logs_url */
|
||||
|
||||
// Récupérer l'état des settings
|
||||
$settings_status = \ESI_PEPPOL\controllers\PEPPOL_Plugin::check_settings_status();
|
||||
?>
|
||||
|
||||
<div class="wrap esi-peppol-dashboard-wrap">
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e('ESI Peppol - Passerelle WooCommerce', 'esi_peppol'); ?></h1>
|
||||
|
||||
<p class="esi-peppol-dashboard-intro">
|
||||
<p>
|
||||
<?php esc_html_e(
|
||||
'Ce plugin permet d\'envoyer vos documents de facturation WooCommerce vers le réseau Peppol.
|
||||
Suivez les étapes ci-dessous pour configurer votre connexion.',
|
||||
Utilisez les boutons ci-dessous pour configurer la connexion et consulter l\'historique des échanges.',
|
||||
'esi_peppol'
|
||||
); ?>
|
||||
</p>
|
||||
|
||||
<!-- Setup guidé - Timeline -->
|
||||
<div class="esi-peppol-setup-guide">
|
||||
<h2 style="margin-bottom: 30px;"><?php esc_html_e('Configuration du connecteur', 'esi_peppol'); ?></h2>
|
||||
|
||||
<!-- Étape 1: Numéro TVA boutique -->
|
||||
<div class="step <?php echo $settings_status['vat_number']['completed'] ? 'is-done' : ''; ?>"
|
||||
data-step="<?php echo $settings_status['vat_number']['completed'] ? '✓' : '1'; ?>">
|
||||
<div class="step-card">
|
||||
<h3><?php echo esc_html($settings_status['vat_number']['label']); ?></h3>
|
||||
<p><?php echo esc_html($settings_status['vat_number']['description']); ?></p>
|
||||
<?php if ($settings_status['vat_number']['completed']) : ?>
|
||||
<div class="step-value">
|
||||
<strong><?php esc_html_e('Valeur configurée :', 'esi_peppol'); ?></strong>
|
||||
<code><?php echo esc_html($settings_status['vat_number']['value']); ?></code>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<a href="<?php echo esc_url($settings_status['vat_number']['action_url']); ?>" class="button button-primary">
|
||||
<?php echo esc_html($settings_status['vat_number']['action_label']); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
$vat_notice_message = \ESI_PEPPOL\controllers\PEPPOL_Plugin::get_vat_notice_message();
|
||||
if (!empty($vat_notice_message)) :
|
||||
$api_key = (string) get_option('esi_peppol_api_key', '');
|
||||
$password = (string) get_option('esi_peppol_password', '');
|
||||
$notice_class = ($api_key === '' && $password === '') ? 'notice-info' : 'notice-warning';
|
||||
?>
|
||||
<div class="notice <?php echo esc_attr($notice_class); ?>">
|
||||
<p><?php echo esc_html($vat_notice_message); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Étape 2: Champ TVA client -->
|
||||
<div class="step <?php echo $settings_status['vat_field']['completed'] ? 'is-done' : ''; ?>"
|
||||
data-step="<?php echo $settings_status['vat_field']['completed'] ? '✓' : '2'; ?>">
|
||||
<div class="step-card">
|
||||
<h3><?php echo esc_html($settings_status['vat_field']['label']); ?></h3>
|
||||
<p><?php echo esc_html($settings_status['vat_field']['description']); ?></p>
|
||||
<?php if ($settings_status['vat_field']['completed']) : ?>
|
||||
<div class="step-value">
|
||||
<strong><?php esc_html_e('Champ détecté :', 'esi_peppol'); ?></strong>
|
||||
<code><?php echo esc_html($settings_status['vat_field']['value']); ?></code>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<a href="<?php echo esc_url($settings_status['vat_field']['action_url']); ?>" class="button button-primary">
|
||||
<?php echo esc_html($settings_status['vat_field']['action_label']); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a href="<?php echo esc_url($settings_url); ?>" class="button button-primary button-hero">
|
||||
<?php esc_html_e('Configuration du connecteur', 'esi_peppol'); ?>
|
||||
</a>
|
||||
|
||||
<!-- Étape 3: Format num facture -->
|
||||
<div class="step <?php echo $settings_status['invoice_number_format']['completed'] ? 'is-done' : ''; ?>"
|
||||
data-step="<?php echo $settings_status['invoice_number_format']['completed'] ? '✓' : '3'; ?>">
|
||||
<div class="step-card">
|
||||
<h3><?php echo esc_html($settings_status['invoice_number_format']['label']); ?></h3>
|
||||
<p><?php echo esc_html($settings_status['invoice_number_format']['description']); ?></p>
|
||||
<?php if ($settings_status['invoice_number_format']['completed']) : ?>
|
||||
<div class="step-value">
|
||||
<strong><?php esc_html_e('Valeur :', 'esi_peppol'); ?></strong>
|
||||
<code><?php echo esc_html($settings_status['invoice_number_format']['value']); ?></code>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<a href="<?php echo esc_url($settings_status['invoice_number_format']['action_url']); ?>" class="button button-primary">
|
||||
<?php echo esc_html($settings_status['invoice_number_format']['action_label']); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 4: Identifiants API -->
|
||||
<div class="step <?php echo $settings_status['api_credentials']['completed'] ? 'is-done' : ''; ?>"
|
||||
data-step="<?php echo $settings_status['api_credentials']['completed'] ? '✓' : '4'; ?>">
|
||||
<div class="step-card">
|
||||
<h3><?php echo esc_html($settings_status['api_credentials']['label']); ?></h3>
|
||||
<p><?php echo esc_html($settings_status['api_credentials']['description']); ?></p>
|
||||
<?php if ($settings_status['api_credentials']['completed']) : ?>
|
||||
<div class="step-value">
|
||||
<strong><?php esc_html_e('Clé API configurée :', 'esi_peppol'); ?></strong>
|
||||
<code><?php echo esc_html($settings_status['api_credentials']['value']); ?></code>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<a href="<?php echo esc_url($settings_status['api_credentials']['action_url']); ?>" class="button button-primary">
|
||||
<?php echo esc_html($settings_status['api_credentials']['action_label']); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 5: Webhook -->
|
||||
<div class="step is-done" data-step="5">
|
||||
<div class="step-card">
|
||||
<h3><?php echo esc_html($settings_status['webhook']['label']); ?></h3>
|
||||
<p><?php echo esc_html($settings_status['webhook']['description']); ?></p>
|
||||
<div class="esi-peppol-webhook-display">
|
||||
<code class="esi-peppol-webhook-url"><?php echo esc_html($settings_status['webhook']['value']); ?></code>
|
||||
<button type="button" class="button button-small esi-peppol-copy-webhook-url" data-copy-text="<?php echo esc_attr($settings_status['webhook']['value']); ?>">
|
||||
<?php esc_html_e('Copier', 'esi_peppol'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<p class="step-note">
|
||||
<?php
|
||||
printf(
|
||||
wp_kses_post(
|
||||
/* translators: %s: URL de la documentation */
|
||||
__('Configurez cette URL dans votre profil d\'entreprise sur <a href="%s" target="_blank" rel="noopener noreferrer">https://peppol.esi-web.be/</a>', 'esi_peppol')
|
||||
),
|
||||
esc_url('https://peppol.esi-web.be/')
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Étape 6: Email (optionnel) -->
|
||||
<div class="step <?php echo $settings_status['email']['completed'] ? 'is-done' : ''; ?>"
|
||||
data-step="<?php echo $settings_status['email']['completed'] ? '✓' : '6'; ?>">
|
||||
<div class="step-card">
|
||||
<h3>
|
||||
<?php echo esc_html($settings_status['email']['label']); ?>
|
||||
<span style="font-size: 12px; font-weight: normal; opacity: 0.8;">(<?php esc_html_e('Optionnel', 'esi_peppol'); ?>)</span>
|
||||
</h3>
|
||||
<p><?php echo esc_html($settings_status['email']['description']); ?></p>
|
||||
<?php if ($settings_status['email']['completed']) : ?>
|
||||
<div class="step-value">
|
||||
<strong><?php esc_html_e('Email configuré :', 'esi_peppol'); ?></strong>
|
||||
<code><?php echo esc_html($settings_status['email']['value']); ?></code>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<a href="<?php echo esc_url($settings_status['email']['action_url']); ?>" class="button button-secondary">
|
||||
<?php echo esc_html($settings_status['email']['action_label']); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions rapides -->
|
||||
<div class="esi-peppol-dashboard-actions">
|
||||
<h2><?php esc_html_e('Actions rapides', 'esi_peppol'); ?></h2>
|
||||
<p>
|
||||
<a href="<?php echo esc_url($settings_url); ?>" class="button button-primary button-hero">
|
||||
<?php esc_html_e('Configuration avancée', 'esi_peppol'); ?>
|
||||
</a>
|
||||
<a href="<?php echo esc_url($logs_url); ?>" class="button button-secondary button-hero" style="margin-left: 10px;">
|
||||
<?php esc_html_e('Journal des envois Peppol', 'esi_peppol'); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<a href="<?php echo esc_url($logs_url); ?>" class="button button-secondary button-hero" style="margin-left: 10px;">
|
||||
<?php esc_html_e('Journal des envois Peppol', 'esi_peppol'); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ if ($logo_email_id) {
|
||||
<div class="wrap esi-peppol-settings-wrap">
|
||||
<h1><?php esc_html_e('Configuration ESI Peppol', 'esi_peppol'); ?></h1>
|
||||
|
||||
<!-- <p>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
wp_kses_post(
|
||||
@ -34,18 +34,19 @@ if ($logo_email_id) {
|
||||
esc_url('https://scrada.esiweb.pro/api-demo.html')
|
||||
);
|
||||
?>
|
||||
</p> -->
|
||||
</p>
|
||||
|
||||
<?php
|
||||
$vat_notices = \ESI_PEPPOL\controllers\PEPPOL_Plugin::get_vat_notice_message();
|
||||
if (!empty($vat_notices)) :
|
||||
foreach ($vat_notices as $notice) :
|
||||
$notice_class = 'notice-' . $notice['type'];
|
||||
?>
|
||||
<div class="notice <?php echo esc_attr($notice_class); ?>">
|
||||
<p><?php echo wp_kses_post($notice['message']); ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
$vat_notice_message = \ESI_PEPPOL\controllers\PEPPOL_Plugin::get_vat_notice_message();
|
||||
if (!empty($vat_notice_message)) :
|
||||
// Si les identifiants ESI Peppol sont déjà saisis, on passe en notice "warning"
|
||||
$api_key = (string) get_option('esi_peppol_api_key', '');
|
||||
$password = (string) get_option('esi_peppol_password', '');
|
||||
$notice_class = ($api_key === '' && $password === '') ? 'notice-info' : 'notice-warning';
|
||||
?>
|
||||
<div class="notice <?php echo esc_attr($notice_class); ?>">
|
||||
<p><?php echo esc_html($vat_notice_message); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($notice)) : ?>
|
||||
@ -193,54 +194,6 @@ if ($logo_email_id) {
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="esi_peppol_invoice_number_format"><?php esc_html_e('Format num facture', 'esi_peppol'); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<?php
|
||||
$backup_invoice_format = (string) get_option('esi_peppol_invoice_number_format', '');
|
||||
if ($backup_invoice_format === '') {
|
||||
$backup_invoice_format = (string) get_option('esi_peppol_vat_number_format', '');
|
||||
}
|
||||
$has_wpo_invoices = \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_has_wpo_invoice_numbers();
|
||||
$detected_format = $has_wpo_invoices
|
||||
? \ESI_PEPPOL\helpers\PEPPOL_Woo_Helper::esi_get_wpo_invoice_number_format_label()
|
||||
: '';
|
||||
?>
|
||||
<?php if ($detected_format !== '') : ?>
|
||||
<p style="margin: 0;">
|
||||
<strong><?php esc_html_e('Format détecté :', 'esi_peppol'); ?></strong>
|
||||
<code style="margin-left: 5px; padding: 2px 6px; background-color: #fff; border: 1px solid #c3c4c7;">
|
||||
<?php echo esc_html($detected_format); ?>
|
||||
</code>
|
||||
</p>
|
||||
<p class="description">
|
||||
<?php esc_html_e('Le format est détecté automatiquement via WPO WCPDF. Le champ de backup n\'est pas requis.', 'esi_peppol'); ?>
|
||||
</p>
|
||||
<?php else : ?>
|
||||
<input type="text"
|
||||
class="regular-text esi-peppol-invoice-format-input"
|
||||
id="esi_peppol_invoice_number_format"
|
||||
name="esi_peppol_invoice_number_format"
|
||||
value="<?php echo esc_attr($backup_invoice_format); ?>"
|
||||
/>
|
||||
<div class="esi-peppol-placeholder-buttons" style="margin-top: 8px;">
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{YYYY}">{YYYY}</button>
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{YY}">{YY}</button>
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{MM}">{MM}</button>
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{DD}">{DD}</button>
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{ORDER_ID}">{ORDER_ID}</button>
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{ORDER_NUMBER}">{ORDER_NUMBER}</button>
|
||||
<button type="button" class="button button-secondary esi-peppol-placeholder-btn" data-placeholder="{number}">{number}</button>
|
||||
</div>
|
||||
<p class="description">
|
||||
<?php esc_html_e('Format de secours si aucun format n\'est détecté automatiquement. Doit se terminer par {number}. Placeholders : {YYYY}, {YY}, {MM}, {DD}, {ORDER_ID}, {ORDER_NUMBER}, {number}.', 'esi_peppol'); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- <tr>
|
||||
<th scope="row">
|
||||
<label for="esi_peppol_logo_email"><?php esc_html_e('Logo Email', 'esi_peppol'); ?></label>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user