483 lines
17 KiB
PHP
483 lines
17 KiB
PHP
<?php
|
|
|
|
namespace ESI_PEPPOL\helpers;
|
|
|
|
class PEPPOL_Woo_Helper {
|
|
|
|
public static function my_get_item_tax_details( \WC_Order_Item $item ): array {
|
|
$taxes = $item->get_taxes(); // ['total' => [rate_id => amount], 'subtotal' => ...]
|
|
$out = [];
|
|
|
|
if ( ! empty( $taxes['total'] ) ) {
|
|
foreach ( $taxes['total'] as $rate_id => $tax_amount ) {
|
|
$tax_amount = floatval( $tax_amount );
|
|
if ( $tax_amount == 0 ) continue;
|
|
|
|
$rate = \WC_Tax::_get_tax_rate( $rate_id );
|
|
|
|
$out[] = [
|
|
'rate_id' => (int) $rate_id,
|
|
'percent' => floatval( $rate['tax_rate'] ),
|
|
'name' => $rate['tax_rate_name'],
|
|
'class' => $rate['tax_rate_class'], // 'standard', 'reduced-rate', etc.
|
|
'amount' => $tax_amount,
|
|
'compound'=> (bool) $rate['tax_rate_compound'],
|
|
];
|
|
}
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
public static function esi_get_order_vat_number( $order ) {
|
|
|
|
if ( is_numeric( $order ) ) {
|
|
$order = wc_get_order( $order );
|
|
}
|
|
|
|
if ( ! $order instanceof \WC_Order ) {
|
|
return '';
|
|
}
|
|
|
|
// 1) Clés les plus courantes (ajuste/complète si besoin)
|
|
$known_keys = [
|
|
'_billing_vat_number',
|
|
'billing_vat_number',
|
|
'_vat_number',
|
|
'vat_number',
|
|
'_billing_vat',
|
|
'billing_vat',
|
|
'_billing_tva',
|
|
'billing_tva',
|
|
'_tva',
|
|
'tva',
|
|
'eu_vat_number',
|
|
'_eu_vat_number',
|
|
'vat_id',
|
|
'_vat_id',
|
|
];
|
|
|
|
foreach ( $known_keys as $key ) {
|
|
$val = $order->get_meta( $key, true );
|
|
if ( ! empty( $val ) ) {
|
|
return is_string($val) ? trim($val) : $val;
|
|
}
|
|
}
|
|
|
|
// 2) Fallback: scan des meta keys contenant vat/tva
|
|
$vat_candidates = [];
|
|
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) ) {
|
|
$vat_candidates[$k] = $v;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si plusieurs candidats, tu peux choisir une règle
|
|
// Ici: si un seul => return, sinon priorise ceux qui contiennent "billing"
|
|
if ( count($vat_candidates) === 1 ) {
|
|
return reset($vat_candidates);
|
|
}
|
|
foreach ( $vat_candidates as $k => $v ) {
|
|
if ( stripos($k, 'billing') !== false ) {
|
|
return $v;
|
|
}
|
|
}
|
|
|
|
// 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,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param \WC_Order $order La commande à analyser
|
|
* @return array Tableau de champs TVA trouvés : ['key' => string, 'value' => string, 'group' => 'billing'|'shipping']
|
|
*/
|
|
public static function esi_detect_vat_fields_billing_shipping( \WC_Order $order ): array {
|
|
$vat_fields_found = [];
|
|
$patterns = ['vat', 'tva', 'btw', 'ust', 'mwst', 'piva', 'moms', 'mva'];
|
|
|
|
// Ordre de recherche : billing d'abord, puis shipping
|
|
$search_groups = ['billing', 'shipping'];
|
|
|
|
foreach ($search_groups as $group) {
|
|
// Chercher dans les meta keys du groupe (billing ou shipping)
|
|
foreach ($order->get_meta_data() as $meta) {
|
|
$k = (string) $meta->key;
|
|
$k_lower = strtolower($k);
|
|
|
|
// Vérifier que la clé commence par le groupe recherché (avec ou sans underscore)
|
|
$group_prefix = $group . '_';
|
|
$group_prefix_underscore = '_' . $group . '_';
|
|
if (strpos($k_lower, $group_prefix) !== 0 && strpos($k_lower, $group_prefix_underscore) !== 0) {
|
|
continue;
|
|
}
|
|
|
|
// 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' => $group,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Chercher aussi dans les champs de checkout du groupe
|
|
if (function_exists('WC') && WC()->checkout()) {
|
|
$fields = WC()->checkout()->get_checkout_fields();
|
|
$group_fields = $fields[$group] ?? [];
|
|
|
|
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) {
|
|
// Essayer avec et sans préfixe underscore
|
|
foreach ([$key, '_' . $key] as $meta_key) {
|
|
$val = $order->get_meta($meta_key, true);
|
|
if (!empty($val)) {
|
|
if (!isset($vat_fields_found[$meta_key])) {
|
|
$vat_fields_found[$meta_key] = [
|
|
'key' => $meta_key,
|
|
'value' => is_string($val) ? trim($val) : $val,
|
|
'group' => $group,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
} |