'; const COLOR_WHITE = '#ffffff'; const COLOR_DARK = '#1e293b'; /** * Instance de la classe principale. * * @var mixed */ private mixed $admin; /** * Delegue pour la construction du tableau des produits. * * @var OrderProductTableRenderer */ private OrderProductTableRenderer $table_renderer; /** * Constructeur. * * @param mixed $admin Classe principale du plugin. */ public function __construct( mixed $admin ) { $this->admin = $admin; $this->table_renderer = new OrderProductTableRenderer(); } /** * Remplace les variables WooCommerce dans le contenu. * * @param string $content Contenu HTML brut. * @param object $order Commande WooCommerce. * @return string */ public function replace_order_variables( string $content, object $order ): string { $billing = $this->format_address( $order, 'billing' ); $shipping = $this->format_address( $order, 'shipping' ); $order_status = $order->get_status(); $doc_type = $this->detect_document_type( $order_status ); $doc_label = $this->get_document_type_label( $doc_type ); $vars = $this->build_order_variable_values( $order, $billing, $shipping, $doc_type, $doc_label ); foreach ( array( array( '{', '}' ), array( '{{', '}}' ), array( '[', ']' ) ) as list( $pre, $suf ) ) { $keys = array_map( fn( $k ) => $pre . $k . $suf, array_keys( $vars ) ); $content = str_replace( $keys, array_values( $vars ), $content ); } // Balise speciale uniquement en accolades simples. $content = str_replace( '{order_items_table}', $this->generate_order_products_table( $order, 'default' ), $content ); return $content; } /** * Detecte le type de document selon le statut de commande. * * @param string $order_status Statut WooCommerce. * @return string */ private function detect_document_type( string $order_status ): string { $normalized = strtolower( $order_status ); if ( in_array( $normalized, array( 'pending', 'processing', 'on-hold' ), true ) ) { return 'devis'; } return 'facture'; } /** * Retourne le label du type de document. * * @param string $doc_type Type de document. * @return string */ private function get_document_type_label( string $doc_type ): string { return 'devis' === $doc_type ? __( 'Devis', 'advanced-pdf-invoice-builder' ) : __( 'Facture', 'advanced-pdf-invoice-builder' ); } /** * Construit la map des valeurs de variables de commande. * * @param mixed $order Commande WooCommerce. * @param string $billing Adresse de facturation formatee. * @param string $shipping Adresse de livraison formatee. * @param string $doc_type Type de document. * @param string $doc_label Label du type de document. * @return array */ private function build_order_variable_values( $order, string $billing, string $shipping, string $doc_type, string $doc_label ): array { $price_formatter = fn( $value ) => function_exists( 'wc_price' ) ? wc_price( (float) $value ) : '$' . number_format( (float) $value, 2 ); $order_date = $order->get_date_created(); $order_date_only = $order_date ? $order_date->date( 'd/m/Y' ) : gmdate( 'd/m/Y' ); $order_date_time = $order_date ? $order_date->date( 'd/m/Y H:i:s' ) : gmdate( 'd/m/Y H:i:s' ); $billing_address_fallback = empty( $billing ) ? __( 'Adresse de facturation non disponible', 'advanced-pdf-invoice-builder' ) : $billing; $shipping_address_fallback = empty( $shipping ) ? __( 'Adresse de livraison non disponible', 'advanced-pdf-invoice-builder' ) : $shipping; return array( 'order_id' => $order->get_id(), 'order_number' => $order->get_order_number(), 'order_date' => $order_date_only, 'order_date_time' => $order_date_time, 'customer_name' => trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ), 'customer_first_name' => $order->get_billing_first_name(), 'customer_last_name' => $order->get_billing_last_name(), 'customer_email' => $order->get_billing_email(), 'customer_phone' => $order->get_billing_phone(), 'billing_company' => $order->get_billing_company(), 'billing_first_name' => $order->get_billing_first_name(), 'billing_last_name' => $order->get_billing_last_name(), 'billing_address_1' => $order->get_billing_address_1(), 'billing_address_2' => $order->get_billing_address_2(), 'billing_city' => $order->get_billing_city(), 'billing_state' => $order->get_billing_state(), 'billing_postcode' => $order->get_billing_postcode(), 'billing_country' => $order->get_billing_country(), 'billing_address' => $billing_address_fallback, 'complete_customer_info' => $this->format_complete_customer_info( $order ), 'complete_billing_address' => $billing_address_fallback, 'shipping_first_name' => $order->get_shipping_first_name(), 'shipping_last_name' => $order->get_shipping_last_name(), 'shipping_company' => $order->get_shipping_company(), 'shipping_address_1' => $order->get_shipping_address_1(), 'shipping_address_2' => $order->get_shipping_address_2(), 'shipping_city' => $order->get_shipping_city(), 'shipping_state' => $order->get_shipping_state(), 'shipping_postcode' => $order->get_shipping_postcode(), 'shipping_country' => $order->get_shipping_country(), 'shipping_address' => $shipping_address_fallback, 'total' => $price_formatter( $order->get_total() ), 'subtotal' => $price_formatter( $order->get_subtotal() ), 'tax' => $price_formatter( $order->get_total_tax() ), 'shipping_total' => $price_formatter( $order->get_shipping_total() ), 'discount_total' => $price_formatter( $order->get_discount_total() ), 'payment_method' => $order->get_payment_method_title(), 'order_status' => function_exists( 'wc_get_order_status_name' ) ? wc_get_order_status_name( $order->get_status() ) : $order->get_status(), 'currency' => $order->get_currency(), 'document_type' => $doc_type, 'document_type_label' => $doc_label, ); } /** * Formate les informations completes du client. * * @param object $order Commande WooCommerce. * @return string */ public function format_complete_customer_info( object $order ): string { $info = array(); // Nom complet. $full_name = trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ); if ( ! empty( $full_name ) ) { $info[] = $full_name; } // Societe. $company = $order->get_billing_company(); if ( ! empty( $company ) ) { $info[] = $company; } // Adresse complete. $billing_address = $this->format_address( $order, 'billing' ); if ( ! empty( $billing_address ) ) { $info[] = $billing_address; } // Email. $email = $order->get_billing_email(); if ( ! empty( $email ) ) { $info[] = 'Email: ' . $email; } // Telephone. $phone = $order->get_billing_phone(); if ( ! empty( $phone ) ) { $info[] = __( 'Telephone: ', 'advanced-pdf-invoice-builder' ) . $phone; } return implode( "\n", $info ); } /** * Formate les informations de societe. * * @return string */ public function format_complete_company_info(): string { $company_info = pdfib_get_option( 'pdfib_company_info', '' ); if ( ! empty( $company_info ) ) { return $company_info; } $parts = array(); $company_name = \get_bloginfo( 'name' ); if ( ! empty( $company_name ) ) { $parts[] = $company_name; } $address_parts = $this->build_store_address_parts(); if ( ! empty( $address_parts ) ) { $parts = array_merge( $parts, $address_parts ); } $email = \get_bloginfo( 'admin_email' ); if ( ! empty( $email ) ) { $parts[] = 'Email: ' . $email; } if ( ! empty( $parts ) ) { return implode( "\n", $parts ); } return __( "Votre Societe SARL\n123 Rue de l'Entreprise\n75001 Paris\nFrance\nTel: 01 23 45 67 89\nEmail: contact@votresociete.com", 'advanced-pdf-invoice-builder' ); } /** * Construit les parties d'adresse du magasin WooCommerce. * * @return string[] */ private function build_store_address_parts(): array { $address_parts = array(); $address1 = \get_option( 'woocommerce_store_address' ); $address2 = \get_option( 'woocommerce_store_address_2' ); $city = \get_option( 'woocommerce_store_city' ); $postcode = \get_option( 'woocommerce_store_postcode' ); $country = \get_option( 'woocommerce_store_country' ); if ( ! empty( $address1 ) ) { $address_parts[] = $address1; } if ( ! empty( $address2 ) ) { $address_parts[] = $address2; } $city_line = array(); if ( ! empty( $postcode ) ) { $city_line[] = $postcode; } if ( ! empty( $city ) ) { $city_line[] = $city; } if ( ! empty( $city_line ) ) { $address_parts[] = implode( ' ', $city_line ); } if ( ! empty( $country ) ) { $wc = function_exists( 'WC' ) ? \WC() : null; $countries = $wc ? $wc->countries->get_countries() : array(); $address_parts[] = isset( $countries[ $country ] ) ? $countries[ $country ] : $country; } return $address_parts; } /** * Genere le HTML pour une commande. * * @param object $order Commande WooCommerce. * @param mixed $template_data Donnees du template. * @return mixed */ public function generate_order_html( object $order, mixed $template_data ): mixed { return $this->admin->generate_unified_html( $template_data, $order ); } /** * Genere le HTML a partir d'un template sans commande. * * @param mixed $template Donnees du template. * @return mixed */ public function generate_html_from_template_data( mixed $template ): mixed { return $this->admin->generate_unified_html( $template, null ); } /** * Remplace les variables WooCommerce dans une structure imbriquee. * * @param mixed $template_data Structure de template. * @param mixed $woocommerce_data Variables a injecter. * @return mixed */ public function replace_woocommerce_variables( mixed $template_data, mixed $woocommerce_data ): mixed { $processed_data = $template_data; // Fonction recursive pour remplacer les variables dans toutes les profondeurs. $replace_vars = function ( $data ) use ( $woocommerce_data, &$replace_vars ) { if ( is_array( $data ) ) { $result = array(); foreach ( $data as $key => $value ) { $result[ $key ] = $replace_vars( $value ); } return $result; } if ( is_string( $data ) ) { // Remplacer les variables du type {order_number}, {customer_name}, etc. $replaced = $data; foreach ( $woocommerce_data as $var => $value ) { $replaced = str_replace( '{' . $var . '}', $value, $replaced ); } return $replaced; } return $data; }; return $replace_vars( $processed_data ); } /** * Formate une adresse sans declencher des chargements inutiles. * * @param object $order Commande WooCommerce. * @param string $type Type d'adresse: billing ou shipping. * @return string */ private function format_address( object $order, string $type = 'billing' ): string { $is_billing = 'billing' === $type; $address_parts = array(); $company = $is_billing ? $order->get_billing_company() : $order->get_shipping_company(); if ( ! empty( $company ) ) { $address_parts[] = $company; } $full_name = trim( ( $is_billing ? $order->get_billing_first_name() : $order->get_shipping_first_name() ) . ' ' . ( $is_billing ? $order->get_billing_last_name() : $order->get_shipping_last_name() ) ); if ( ! empty( $full_name ) ) { $address_parts[] = $full_name; } $address_1 = $is_billing ? $order->get_billing_address_1() : $order->get_shipping_address_1(); if ( ! empty( $address_1 ) ) { $address_parts[] = $address_1; } $address_2 = $is_billing ? $order->get_billing_address_2() : $order->get_shipping_address_2(); if ( ! empty( $address_2 ) ) { $address_parts[] = $address_2; } $city_line = trim( ( $is_billing ? $order->get_billing_city() : $order->get_shipping_city() ) . ' ' . ( $is_billing ? $order->get_billing_postcode() : $order->get_shipping_postcode() ) ); if ( ! empty( $city_line ) ) { $address_parts[] = $city_line; } $country = $this->get_country_name( $is_billing ? $order->get_billing_country() : $order->get_shipping_country() ); if ( ! empty( $country ) ) { $address_parts[] = $country; } return implode( "\n", $address_parts ); } /** * Retourne le nom du pays depuis son code. * * @param string $country_code Code pays ISO. * @return string */ private function get_country_name( string $country_code ): string { if ( ! defined( 'WC_VERSION' ) || ! $country_code ) { return $country_code; } // Utiliser get_option au lieu de WC()->countries pour eviter l'autoloading. $countries = \get_option( 'woocommerce_countries', array() ); if ( empty( $countries ) ) { $countries = \get_option( 'woocommerce_allowed_countries', array() ); } return isset( $countries[ $country_code ] ) ? $countries[ $country_code ] : $country_code; } /** * Genere le HTML unifie pour un template. * * @param mixed $template Donnees du template. * @param mixed $order Commande optionnelle. * @return mixed */ public function generate_unified_html( mixed $template, mixed $order = null ): mixed { // Cette methode restera deleguee a la classe principale. return $this->admin->generate_unified_html( $template, $order ); } /** * Genere le tableau des produits de la commande. * * @param object $order Commande WooCommerce. * @param string $table_style Style du tableau. * @param mixed $element Element de configuration optionnel. * @return string */ public function generate_order_products_table( object $order, string $table_style = 'default', mixed $element = null ): string { return $this->table_renderer->generate_order_products_table( $order, $table_style, $element ); } }