Changeset 3463326
- Timestamp:
- 02/17/2026 10:18:40 AM (14 hours ago)
- Location:
- woopop-electronic-invoice-free/trunk
- Files:
-
- 1 added
- 52 edited
-
addon/for/cozmos/inc/filters.php (modified) (1 diff)
-
addon/for/cozmos/inc/filtersFront.php (modified) (1 diff)
-
addon/for/cozmos/src/Functions/Utils.php (modified) (1 diff)
-
addon/for/cozmos/vendor/composer/installed.php (modified) (2 diffs)
-
addon/for/pmpro/inc/filtersAlways.php (modified) (1 diff)
-
addon/for/pmpro/src/Functions/Utils.php (modified) (1 diff)
-
addon/for/pmpro/vendor/composer/installed.php (modified) (2 diffs)
-
addon/to/aruba/languages/waf-it_IT.mo (modified) (previous)
-
addon/to/aruba/languages/waf-it_IT.po (modified) (10 diffs)
-
addon/to/aruba/src/Functions/Api.php (modified) (12 diffs)
-
addon/to/aruba/src/Jobs.php (modified) (2 diffs)
-
addon/to/aruba/vendor/composer/installed.php (modified) (2 diffs)
-
addon/to/fattureincloud-stock/vendor/composer/installed.php (modified) (2 diffs)
-
addon/to/fattureincloud/inc/filtersAdmin.php (modified) (1 diff)
-
addon/to/fattureincloud/inc/filtersFront.php (modified) (1 diff)
-
addon/to/fattureincloud/languages/wfc-it_IT.mo (modified) (previous)
-
addon/to/fattureincloud/languages/wfc-it_IT.po (modified) (25 diffs)
-
addon/to/fattureincloud/src/Functions/Api.php (modified) (14 diffs)
-
addon/to/fattureincloud/src/Jobs.php (modified) (1 diff)
-
addon/to/fattureincloud/vendor/composer/installed.php (modified) (2 diffs)
-
addon/to/sdi-pec/languages/popsdipec-it_IT.mo (modified) (previous)
-
addon/to/sdi-pec/languages/popsdipec-it_IT.po (modified) (16 diffs)
-
addon/to/sdi-pec/src/Functions/Api.php (modified) (12 diffs)
-
addon/to/sdi-pec/src/Jobs.php (modified) (2 diffs)
-
addon/to/sdi-pec/vendor/composer/installed.php (modified) (2 diffs)
-
assets/js/admin.js (modified) (1 diff)
-
assets/js/admin.min.js (modified) (1 diff)
-
changelog.txt (modified) (1 diff)
-
inc/filtersAdmin.php (modified) (1 diff)
-
inc/localizeScripts.php (modified) (1 diff)
-
inc/wc/filtersAdmin.php (modified) (1 diff)
-
inc/wc/filtersFront.php (modified) (1 diff)
-
index.php (modified) (2 diffs)
-
languages/el-inv-es_ES.mo (modified) (previous)
-
languages/el-inv-es_ES.po (modified) (2 diffs)
-
languages/el-inv-fr_FR.mo (modified) (previous)
-
languages/el-inv-fr_FR.po (modified) (2 diffs)
-
languages/el-inv-it_IT.mo (modified) (previous)
-
languages/el-inv-it_IT.po (modified) (3 diffs)
-
readme.md (modified) (2 diffs)
-
readme.txt (modified) (2 diffs)
-
requires.php (modified) (1 diff)
-
src/Admin/XmlOrderListTable.php (modified) (1 diff)
-
src/Functions/CloudApi.php (modified) (2 diffs)
-
src/Functions/Invoice.php (modified) (9 diffs)
-
src/Functions/Lock.php (added)
-
src/Functions/Utils.php (modified) (1 diff)
-
src/Functions/Webhooks.php (modified) (7 diffs)
-
src/Integrations.php (modified) (3 diffs)
-
src/InvoiceApi.php (modified) (26 diffs)
-
src/PeppolApi.php (modified) (11 diffs)
-
src/WooCommerce/Fields/InvoiceFields.php (modified) (4 diffs)
-
vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
woopop-electronic-invoice-free/trunk/addon/for/cozmos/inc/filters.php
r3320156 r3463326 137 137 ), 138 138 ), 139 'filter' => array() 139 'filter' => array( 140 array( 141 'filter' => 'wc_el_inv-next_number_already_assigned', 142 'callback' => 'WooPoPCozmosLabsPMS\\Functions\\Utils::cozmosNextNumberAlreadyAssigned', 143 'priority' => 20, 144 'accepted_args' => 3, 145 ), 146 array( 147 'filter' => 'wc_el_inv-next_number_last_assigned_for_series', 148 'callback' => 'WooPoPCozmosLabsPMS\\Functions\\Utils::cozmosLastAssignedNumberForSeries', 149 'priority' => 20, 150 'accepted_args' => 2, 151 ), 152 ) 140 153 ), 141 154 ); -
woopop-electronic-invoice-free/trunk/addon/for/cozmos/inc/filtersFront.php
r3341137 r3463326 214 214 'accepted_args' => 1, 215 215 ), 216 array( 217 'filter' => 'wc_el_inv-next_number_already_assigned', 218 'callback' => 'WooPoPCozmosLabsPMS\\Functions\\Utils::cozmosNextNumberAlreadyAssigned', 219 'priority' => 20, 220 'accepted_args' => 3, 221 ), 222 array( 223 'filter' => 'wc_el_inv-next_number_last_assigned_for_series', 224 'callback' => 'WooPoPCozmosLabsPMS\\Functions\\Utils::cozmosLastAssignedNumberForSeries', 225 'priority' => 20, 226 'accepted_args' => 2, 227 ), 216 228 ), 217 229 ), -
woopop-electronic-invoice-free/trunk/addon/for/cozmos/src/Functions/Utils.php
r3248026 r3463326 3 3 namespace WooPoPCozmosLabsPMS\Functions; 4 4 5 use WcElectronInvoice\Providers\OrderQuery; 6 5 7 defined( 'ABSPATH' ) || die( 'Not allowed.' ); 6 8 7 class Utils {} 9 class Utils { 10 /** 11 * Extend duplicate-check lookup for Cozmos numeration. 12 * 13 * @param bool $exists 14 * @param int $number 15 * @param string $optionKeyName 16 * 17 * @return bool 18 */ 19 public static function cozmosNextNumberAlreadyAssigned( $exists, $number, $optionKeyName ) { 20 if ( $exists || (int) $number < 1 ) { 21 return (bool) $exists; 22 } 23 24 $lastAssigned = self::cozmosLastAssignedNumberForSeries( 0, $optionKeyName ); 25 26 return ( $lastAssigned > 0 && $lastAssigned === (int) $number ); 27 } 28 29 /** 30 * Contribute Cozmos last assigned number for current series. 31 * 32 * @param int $lastAssigned 33 * @param string $optionKeyName 34 * 35 * @return int 36 */ 37 public static function cozmosLastAssignedNumberForSeries( $lastAssigned, $optionKeyName ) { 38 if ( 'number_next_invoice' !== $optionKeyName && 'number_next_receipt' !== $optionKeyName ) { 39 return (int) $lastAssigned; 40 } 41 42 try { 43 $provider = OrderQuery::instance()->get_provider( 'cozmoslabspms' ); 44 if ( ! $provider || ! method_exists( $provider, 'getOrders' ) ) { 45 return (int) $lastAssigned; 46 } 47 48 $limit = (int) apply_filters( 49 'wc_el_inv-next_number_recent_provider_orders_limit', 50 200, 51 'cozmoslabspms', 52 $optionKeyName, 53 0 54 ); 55 if ( $limit < 1 ) { 56 $limit = 1; 57 } 58 59 $orders = $provider->getOrders( [ 60 'status' => [ 'completed', 'processing' ], 61 'limit' => $limit, 62 'orderby' => 'date', 63 'order' => 'DESC', 64 ] ); 65 if ( ! is_array( $orders ) ) { 66 return (int) $lastAssigned; 67 } 68 69 $seriesContext = \WcElectronInvoice\Functions\getNextNumberSeriesContext( $optionKeyName ); 70 foreach ( $orders as $order ) { 71 if ( ! is_object( $order ) || ! method_exists( $order, 'get_meta' ) ) { 72 continue; 73 } 74 75 $invoiceNumber = (int) $order->get_meta( 'order_number_invoice', true ); 76 if ( $invoiceNumber < 1 ) { 77 continue; 78 } 79 80 $choiceType = (string) $order->get_meta( '_billing_choice_type', true ); 81 $isReceipt = ( 'receipt' === $choiceType ); 82 if ( 'number_next_receipt' === $optionKeyName && ! $isReceipt ) { 83 continue; 84 } 85 if ( 'number_next_invoice' === $optionKeyName && $isReceipt ) { 86 continue; 87 } 88 89 if ( \WcElectronInvoice\Functions\isOrderInNextNumberSeries( $order, $seriesContext ) ) { 90 return max( (int) $lastAssigned, $invoiceNumber ); 91 } 92 93 return (int) $lastAssigned; 94 } 95 } catch ( \Throwable $e ) { 96 return (int) $lastAssigned; 97 } 98 99 return (int) $lastAssigned; 100 } 101 } -
woopop-electronic-invoice-free/trunk/addon/for/cozmos/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woopop-electronic-invoice-free/trunk/addon/for/pmpro/inc/filtersAlways.php
r3418379 r3463326 122 122 ), 123 123 ), 124 'filter' => array(), 124 'filter' => array( 125 array( 126 'filter' => 'wc_el_inv-next_number_already_assigned', 127 'callback' => 'POPxPMPro\\Functions\\Utils::pmproNextNumberAlreadyAssigned', 128 'priority' => 20, 129 'accepted_args' => 3, 130 ), 131 array( 132 'filter' => 'wc_el_inv-next_number_last_assigned_for_series', 133 'callback' => 'POPxPMPro\\Functions\\Utils::pmproLastAssignedNumberForSeries', 134 'priority' => 20, 135 'accepted_args' => 2, 136 ), 137 ), 125 138 ), 126 139 ); -
woopop-electronic-invoice-free/trunk/addon/for/pmpro/src/Functions/Utils.php
r3409381 r3463326 189 189 190 190 return max( 0, $netAmount ); 191 } 192 193 /** 194 * Extend duplicate-check lookup for PMPro numeration. 195 * 196 * @param bool $exists 197 * @param int $number 198 * @param string $optionKeyName 199 * 200 * @return bool 201 */ 202 public static function pmproNextNumberAlreadyAssigned( $exists, $number, $optionKeyName ) { 203 if ( $exists || (int) $number < 1 ) { 204 return (bool) $exists; 205 } 206 207 $lastAssigned = self::pmproLastAssignedNumberForSeries( 0, $optionKeyName ); 208 209 return ( $lastAssigned > 0 && $lastAssigned === (int) $number ); 210 } 211 212 /** 213 * Contribute PMPro last assigned number for current series. 214 * 215 * @param int $lastAssigned 216 * @param string $optionKeyName 217 * 218 * @return int 219 */ 220 public static function pmproLastAssignedNumberForSeries( $lastAssigned, $optionKeyName ) { 221 if ( 'number_next_invoice' !== $optionKeyName && 'number_next_receipt' !== $optionKeyName ) { 222 return (int) $lastAssigned; 223 } 224 225 try { 226 $provider = OrderQuery::instance()->get_provider( 'pmpro' ); 227 if ( ! $provider || ! method_exists( $provider, 'getOrders' ) ) { 228 return (int) $lastAssigned; 229 } 230 231 $limit = (int) apply_filters( 232 'wc_el_inv-next_number_recent_provider_orders_limit', 233 200, 234 'pmpro', 235 $optionKeyName, 236 0 237 ); 238 if ( $limit < 1 ) { 239 $limit = 1; 240 } 241 242 $orders = $provider->getOrders( [ 243 'status' => [ 'completed', 'processing' ], 244 'limit' => $limit, 245 'orderby' => 'date', 246 'order' => 'DESC', 247 ] ); 248 if ( ! is_array( $orders ) ) { 249 return (int) $lastAssigned; 250 } 251 252 $seriesContext = \WcElectronInvoice\Functions\getNextNumberSeriesContext( $optionKeyName ); 253 foreach ( $orders as $order ) { 254 if ( ! is_object( $order ) || ! method_exists( $order, 'get_meta' ) ) { 255 continue; 256 } 257 258 $invoiceNumber = (int) $order->get_meta( 'order_number_invoice', true ); 259 if ( $invoiceNumber < 1 ) { 260 continue; 261 } 262 263 $choiceType = (string) $order->get_meta( '_billing_choice_type', true ); 264 $isReceipt = ( 'receipt' === $choiceType ); 265 if ( 'number_next_receipt' === $optionKeyName && ! $isReceipt ) { 266 continue; 267 } 268 if ( 'number_next_invoice' === $optionKeyName && $isReceipt ) { 269 continue; 270 } 271 272 if ( \WcElectronInvoice\Functions\isOrderInNextNumberSeries( $order, $seriesContext ) ) { 273 return max( (int) $lastAssigned, $invoiceNumber ); 274 } 275 276 return (int) $lastAssigned; 277 } 278 } catch ( \Throwable $e ) { 279 return (int) $lastAssigned; 280 } 281 282 return (int) $lastAssigned; 191 283 } 192 284 -
woopop-electronic-invoice-free/trunk/addon/for/pmpro/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woopop-electronic-invoice-free/trunk/addon/to/aruba/languages/waf-it_IT.po
r3253418 r3463326 3 3 "Project-Id-Version: POP to Aruba\n" 4 4 "POT-Creation-Date: 2025-03-08 10:54+0100\n" 5 "PO-Revision-Date: 202 5-03-08 10:54+0100\n"5 "PO-Revision-Date: 2026-02-16 13:58+0100\n" 6 6 "Last-Translator: Alfio <[email protected]>\n" 7 7 "Language-Team: \n" … … 11 11 "Content-Transfer-Encoding: 8bit\n" 12 12 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 "X-Generator: Poedit 3. 5\n"13 "X-Generator: Poedit 3.8\n" 14 14 "X-Poedit-Basepath: ..\n" 15 "X-Poedit-KeywordsList: __;_e;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c;" 16 "_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;esc_attr__;esc_attr_e;" 17 "esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 15 "X-Poedit-KeywordsList: " 16 "__;_e;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c;_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 18 17 "X-Poedit-SearchPathExcluded-3: assets\n" 19 18 "X-Poedit-SourceCharset: UTF-8\n" … … 34 33 msgstr "Fatture Aruba azioni" 35 34 36 #: inc/localizeScripts.php:40 src/Functions/Api.php:507 37 #: src/Functions/Api.php:647 35 #: inc/localizeScripts.php:40 src/Functions/Api.php:507 src/Functions/Api.php:647 38 36 msgid "Message:" 39 37 msgstr "Messaggio:" … … 147 145 msgstr "Richiesta aggiornamento token non valida" 148 146 149 #: src/Functions/Api.php:409 src/Functions/Api.php:495 150 #: src/Functions/Api.php: 503 src/Functions/Api.php:643147 #: src/Functions/Api.php:409 src/Functions/Api.php:495 src/Functions/Api.php:503 148 #: src/Functions/Api.php:643 151 149 msgid "Oops, something is wrong!" 152 150 msgstr "Ops, qualcosa non va!" … … 166 164 #: src/Functions/Api.php:505 src/Functions/Api.php:645 167 165 msgid "" 168 "The invoice could not be created, we recommend that you check your order " 169 "data and try again" 170 msgstr "" 171 "Impossibile creare la fattura, ti consigliamo di controllare i dati " 172 "dell’ordine e riprovare" 166 "The invoice could not be created, we recommend that you check your order data and try again" 167 msgstr "" 168 "Impossibile creare la fattura, ti consigliamo di controllare i dati dell’ordine e riprovare" 173 169 174 170 #: src/Functions/Api.php:508 src/Functions/Api.php:648 … … 188 184 msgstr "Errore di caricamento della fattura!" 189 185 190 #: src/Functions/Api.php:627 src/Functions/Api.php:817 191 #: src/Functions/Api.php:1100 src/Jobs.php:474 src/Jobs.php:530 192 #: src/Jobs.php:598 186 #: src/Functions/Api.php:627 src/Functions/Api.php:817 src/Functions/Api.php:1100 187 #: src/Jobs.php:474 src/Jobs.php:530 src/Jobs.php:598 193 188 msgid "Unauthorized: the Token seems to be invalid, update it!" 194 189 msgstr "Non autorizzato: il Token sembra non essere valido, aggiornalo!" … … 240 235 #: src/Functions/Utils.php:258 241 236 msgid "WooPOP Addon: You do not have the permissions to manage the add-on" 242 msgstr "" 243 "WooPOP Addon: Non si dispone dei permessi per gestire il componente " 244 "aggiuntivo" 237 msgstr "WooPOP Addon: Non si dispone dei permessi per gestire il componente aggiuntivo" 245 238 246 239 #: src/Functions/Utils.php:267 … … 270 263 #: src/Functions/Utils.php:315 271 264 msgid "" 272 "Enable automatic sending of invoices for completed orders not sent in the "273 " last 7 days. (work2 times a day)"274 msgstr "" 275 "Abilita l'invio automatico delle fatture per gli ordini completati e non "276 " inviati negli ultimi7 giorni. (invia 2 volte al giorno)"265 "Enable automatic sending of invoices for completed orders not sent in the last 7 days. (work " 266 "2 times a day)" 267 msgstr "" 268 "Abilita l'invio automatico delle fatture per gli ordini completati e non inviati negli ultimi " 269 "7 giorni. (invia 2 volte al giorno)" 277 270 278 271 #: src/Functions/Utils.php:321 … … 310 303 #: src/Functions/Utils.php:367 311 304 msgid "Enter this code in wp-config.php to activate the event log." 312 msgstr "" 313 "Inserisci questo codice in wp-config.php per attivare il registro eventi." 305 msgstr "Inserisci questo codice in wp-config.php per attivare il registro eventi." 314 306 315 307 #: src/Functions/Utils.php:377 … … 369 361 msgid "The PDF invoice has already been sent manually" 370 362 msgstr "La fattura PDF è già stata inviata manualmente" 363 364 msgid "Action skipped: another upload process is already running for this order." 365 msgstr "Azione saltata: un altro processo di caricamento è già in esecuzione per questo ordine." -
woopop-electronic-invoice-free/trunk/addon/to/aruba/src/Functions/Api.php
r3409381 r3463326 434 434 $scheduledCronJob = get_option('waf_scheduled_events'); 435 435 $checkSent = \WcElectronInvoice\Functions\getPostMeta('_invoice_sent', null, $orderID, true, 'order', $provider); 436 if ('on' === $scheduledCronJob && 'feCronJobUploadRequest' === $ajaxAction && 'sent' === $checkSent) { 436 if ('on' === $scheduledCronJob && 437 \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) && 438 'sent' === $checkSent 439 ) { 437 440 return; 438 441 } … … 442 445 443 446 // auto refreshOAuthToken 444 if ( $ajaxAction !== 'feCronJobUploadRequest'&&447 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($ajaxAction, ['feCronJobUploadRequest']) && 445 448 (! $apiData->expireAccessToken || (int)$apiData->expireAccessToken < time()) 446 449 ) { … … 471 474 $isXmlFromRemoTe = false; 472 475 $validXML = false; 473 if('feCronJobUploadRequest' !== $ajaxAction) { 476 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($ajaxAction, 477 ['feCronJobUploadRequest'])) { 474 478 $url = esc_url_raw(add_query_arg([ 475 479 CreateXml::LIST_TYPE => $orderID, … … 500 504 // Not valid ? 501 505 if (! $isXml->valid) { 506 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('aruba', 'xml_invalid'); 502 507 $noXmlResponse = array( 503 508 'title' => __('Oops, something is wrong!', WP_FATT_ARUBA_TEXTDOMAIN), … … 505 510 'xml' => $isXml->xml, 506 511 'message' => __('Request error: xml not valid', WP_FATT_ARUBA_TEXTDOMAIN), 507 'code' => 501,512 'code' => $uploadError['code'], 508 513 ); 509 514 … … 524 529 } 525 530 531 $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('aruba', 'upload_invoice', (int)$orderID, 532 (string)$provider, (string)$choiceType); 533 $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision( 534 \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0), 535 \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) 536 ); 537 if (! $lockDecision['continue']) { 538 if ($lockDecision['respond_error']) { 539 $lockError = \WcElectronInvoice\Functions\mapLockBusyError('aruba', 'upload_invoice'); 540 wp_send_json(array( 541 'message' => __('Action skipped: another upload process is already running for this order.', 542 WP_FATT_ARUBA_TEXTDOMAIN), 543 'orderID' => $orderID, 544 'code' => $lockError['code'], 545 )); 546 die(); 547 } 548 549 return; 550 } 551 526 552 try { 527 553 // XML in Base64 528 554 $xmlB64 = base64_encode($xml); 529 555 530 $body = array( 531 'dataFile' => $xmlB64, 532 'credential' => '', 533 'domain' => '', 534 'senderPIVA' => '', 535 'skipExtraSchema' => false, 536 ); 556 $body = \WcElectronInvoice\Functions\buildArubaUploadPayload($xmlB64); 537 557 538 558 $baseUri = getActiveApi()->apiWsUrl; … … 556 576 // Synchronous Controls 557 577 // @see https://fatturazioneelettronica.aruba.it/apidoc/docs.html#synchronous-checks 558 if ("0000" !== $responseData->errorCode && $responseData->uploadFileName === '') { 578 if ("0000" !== $responseData->errorCode || 579 ! \WcElectronInvoice\Functions\canMarkInvoiceSentWithUploadFileName($responseData->uploadFileName) 580 ) { 581 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('aruba', 'upload_failed'); 559 582 $responseArgs = array( 560 583 'body' => $responseData, 561 'message' => $responseData->errorDescription, 584 'message' => ! empty($responseData->errorDescription) ? $responseData->errorDescription : __('Invoice upload error!', 585 WP_FATT_ARUBA_TEXTDOMAIN), 562 586 'orderID' => $orderID, 563 587 'jobForceSignIn' => $jobForceSignIn, 564 588 'requestURI' => $uri, 565 'code' => $ response->getStatusCode(),589 'code' => $uploadError['code'], 566 590 ); 567 591 } else { 568 592 $order->update_meta('waf-upload_file_name', $responseData->uploadFileName); 569 $order->update_meta('_invoice_sent', 'sent'); 593 if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithUploadFileName($responseData->uploadFileName)) { 594 $order->update_meta('_invoice_sent', 'sent'); 595 } 570 596 // Save 571 597 $order->save(); … … 584 610 } 585 611 } else { 612 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('aruba', 'upload_failed'); 586 613 $responseArgs = array( 587 614 'body' => $responseData, … … 590 617 'jobForceSignIn' => $jobForceSignIn, 591 618 'requestURI' => $uri, 592 'code' => $ response->getStatusCode(),619 'code' => $uploadError['code'], 593 620 ); 594 621 } … … 619 646 // Cron Job return data 620 647 if ('on' === $scheduledCronJob && 621 'feCronJobUploadRequest' === $ajaxAction&&648 \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) && 622 649 'sent' !== $checkSent 623 650 ) { 651 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 624 652 return (object)array( 625 653 'fileName' => property_exists($responseData, 'uploadFileName') ? $responseData->uploadFileName : null, … … 628 656 } 629 657 658 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 630 659 wp_send_json($responseArgs); 631 660 die(); … … 661 690 true)); 662 691 692 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 663 693 wp_send_json($dataException); 664 694 die(); -
woopop-electronic-invoice-free/trunk/addon/to/aruba/src/Jobs.php
r3426093 r3463326 302 302 $data = getActiveApi(); 303 303 if (! $data->active) { 304 wp_clear_scheduled_hook('twiceDailyWooPoPToFattureArubaJobsScheduledUpload'); 304 305 return; 305 306 } … … 368 369 } 369 370 }); 371 } else { 372 wp_clear_scheduled_hook('twiceDailyWooPoPToFattureArubaJobsScheduledUpload'); 370 373 } 371 374 } -
woopop-electronic-invoice-free/trunk/addon/to/aruba/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud-stock/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud/inc/filtersAdmin.php
r3382415 r3463326 113 113 array( 114 114 'filter' => 'woocommerce_order_status_changed', 115 'callback' => 'WooPoPToFattureInCloud\\Jobs::changeStatus', 115 'callback' => function ($orderID, $from, $to) { 116 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_FIC)) { 117 return; 118 } 119 120 \WooPoPToFattureInCloud\Jobs::changeStatus($orderID, $from, $to); 121 }, 116 122 'priority' => 10, 117 123 'accepted_args' => 3, -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud/inc/filtersFront.php
r3369717 r3463326 52 52 'woocommerce_order_status_processing', 53 53 ), 54 'callback' => 'WooPoPToFattureInCloud\\Jobs::autoCompleted', 54 'callback' => function ($orderID) { 55 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_FIC)) { 56 return; 57 } 58 59 \WooPoPToFattureInCloud\Jobs::autoCompleted($orderID); 60 }, 55 61 'priority' => 10, 56 62 'accepted_args' => 1, -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud/languages/wfc-it_IT.po
r3382415 r3463326 3 3 "Project-Id-Version: POP to Fattureincloud\n" 4 4 "POT-Creation-Date: 2025-10-02 12:08+0200\n" 5 "PO-Revision-Date: 202 5-10-02 12:08+0200\n"5 "PO-Revision-Date: 2026-02-16 13:58+0100\n" 6 6 "Last-Translator: Alfio <[email protected]>\n" 7 7 "Language-Team: \n" … … 11 11 "Content-Transfer-Encoding: 8bit\n" 12 12 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 13 "X-Generator: Poedit 3. 7\n"13 "X-Generator: Poedit 3.8\n" 14 14 "X-Poedit-Basepath: ..\n" 15 15 "X-Poedit-KeywordsList: " … … 37 37 msgstr "Caricare su Fatture in Cloud per sincronizzare il numero di fattura" 38 38 39 #: inc/localizeScripts.php:40 src/Functions/Api.php:1269 40 #: src/Functions/Api.php:2616 39 #: inc/localizeScripts.php:40 src/Functions/Api.php:1269 src/Functions/Api.php:2616 41 40 msgid "Message:" 42 41 msgstr "Messaggio:" … … 53 52 msgid "Time out, click Connect again to generate another verification code" 54 53 msgstr "" 55 "Tempo scaduto, fare di nuovo click su Connetti per generare un altro codice " 56 "di verificawhi" 54 "Tempo scaduto, fare di nuovo click su Connetti per generare un altro codice di verificawhi" 57 55 58 56 #: src/Api.php:347 src/Api.php:410 src/Api.php:556 src/Functions/Api.php:499 59 #: src/Functions/Api.php:2429 src/Functions/Api.php:2588 60 #: src/Functions/Api.php:2 718 src/Functions/Api.php:2991 src/Jobs.php:48957 #: src/Functions/Api.php:2429 src/Functions/Api.php:2588 src/Functions/Api.php:2718 58 #: src/Functions/Api.php:2991 src/Jobs.php:489 61 59 msgid "Unauthorized: the Token seems to be invalid, update it!" 62 60 msgstr "Non autorizzato: il Token sembra non essere valido, aggiornalo!" … … 92 90 #: src/Filters.php:168 93 91 msgid "" 94 "Permissions allow you to create invoices or orders, *credit notes, *clients "95 " and *receipts(optionals) on Fatture in Cloud"96 msgstr "" 97 "I permessi permettono di creare fatture oppure ordini, *note di credito, "98 "* clienti e *ricevute (opzionali) su Fatture in Cloud"92 "Permissions allow you to create invoices or orders, *credit notes, *clients and *receipts " 93 "(optionals) on Fatture in Cloud" 94 msgstr "" 95 "I permessi permettono di creare fatture oppure ordini, *note di credito, *clienti e " 96 "*ricevute (opzionali) su Fatture in Cloud" 99 97 100 98 #: src/Filters.php:173 src/Filters.php:294 … … 188 186 #: src/Filters.php:329 189 187 msgid "" 190 "(*) If you do not connect the company you will not be able to send invoices " 191 "through the Fatture In Cloud API" 192 msgstr "" 193 "(*) Se non colleghi l’azienda non potrai inviare fatture tramite l’API " 194 "Fatture In Cloud " 188 "(*) If you do not connect the company you will not be able to send invoices through the " 189 "Fatture In Cloud API" 190 msgstr "" 191 "(*) Se non colleghi l’azienda non potrai inviare fatture tramite l’API Fatture In Cloud " 195 192 196 193 #: src/Filters.php:346 src/Filters.php:359 … … 208 205 #: src/Filters.php:375 209 206 msgid "select the payment gateway and a payment account for each method" 210 msgstr "" 211 "selezionare il gateway di pagamento e un conto di pagamento per ciascun " 212 "metodo" 207 msgstr "selezionare il gateway di pagamento e un conto di pagamento per ciascun metodo" 213 208 214 209 #: src/Filters.php:396 … … 313 308 msgstr "Errore nel caricamento. controllare i dati e riprovare" 314 309 315 #: src/Functions/Api.php:191 src/Functions/Api.php:254 316 #: src/Functions/Api.php:387 src/Functions/Api.php:419 317 #: src/Functions/Api.php:498 src/Functions/Api.php:513 318 #: src/Functions/Api.php:567 src/Functions/Api.php:596 319 #: src/Functions/Api.php:628 src/Functions/Api.php:747 320 #: src/Functions/Api.php:817 src/Functions/Api.php:830 321 #: src/Functions/Api.php:842 src/Functions/Api.php:1023 322 #: src/Functions/Api.php:1040 src/Functions/Api.php:1068 323 #: src/Functions/Api.php:1108 src/Functions/Api.php:1123 324 #: src/Functions/Api.php:1142 src/Functions/Api.php:1197 325 #: src/Functions/Api.php:1211 src/Functions/Api.php:1257 326 #: src/Functions/Api.php:1265 src/Functions/Api.php:1514 327 #: src/Functions/Api.php:1521 src/Functions/Api.php:1537 328 #: src/Functions/Api.php:2131 src/Functions/Api.php:2152 329 #: src/Functions/Api.php:2277 src/Functions/Api.php:2369 330 #: src/Functions/Api.php:2428 src/Functions/Api.php:2443 331 #: src/Functions/Api.php:2467 src/Functions/Api.php:2480 332 #: src/Functions/Api.php:2587 src/Functions/Api.php:2602 333 #: src/Functions/Api.php:2612 src/Functions/Api.php:2717 334 #: src/Functions/Api.php:2732 src/Functions/Api.php:2808 335 #: src/Functions/Api.php:2990 src/Functions/Api.php:3016 310 #: src/Functions/Api.php:191 src/Functions/Api.php:254 src/Functions/Api.php:387 311 #: src/Functions/Api.php:419 src/Functions/Api.php:498 src/Functions/Api.php:513 312 #: src/Functions/Api.php:567 src/Functions/Api.php:596 src/Functions/Api.php:628 313 #: src/Functions/Api.php:747 src/Functions/Api.php:817 src/Functions/Api.php:830 314 #: src/Functions/Api.php:842 src/Functions/Api.php:1023 src/Functions/Api.php:1040 315 #: src/Functions/Api.php:1068 src/Functions/Api.php:1108 src/Functions/Api.php:1123 316 #: src/Functions/Api.php:1142 src/Functions/Api.php:1197 src/Functions/Api.php:1211 317 #: src/Functions/Api.php:1257 src/Functions/Api.php:1265 src/Functions/Api.php:1514 318 #: src/Functions/Api.php:1521 src/Functions/Api.php:1537 src/Functions/Api.php:2131 319 #: src/Functions/Api.php:2152 src/Functions/Api.php:2277 src/Functions/Api.php:2369 320 #: src/Functions/Api.php:2428 src/Functions/Api.php:2443 src/Functions/Api.php:2467 321 #: src/Functions/Api.php:2480 src/Functions/Api.php:2587 src/Functions/Api.php:2602 322 #: src/Functions/Api.php:2612 src/Functions/Api.php:2717 src/Functions/Api.php:2732 323 #: src/Functions/Api.php:2808 src/Functions/Api.php:2990 src/Functions/Api.php:3016 336 324 #: src/Functions/Api.php:3200 337 325 msgid "Oops, something is wrong!" … … 397 385 msgid "Invalid Settings for Mapping Payment account whit method" 398 386 msgstr "" 399 "Impostazioni non valide per la mappatura del conto di pagamento e del metodo " 400 "di pagamento" 387 "Impostazioni non valide per la mappatura del conto di pagamento e del metodo di pagamento" 401 388 402 389 #: src/Functions/Api.php:656 … … 406 393 #: src/Functions/Api.php:707 407 394 msgid "Mapping Payment Gateway whit method ID set successfully!" 408 msgstr "" 409 "Mappatura del Gateway di pagamento con l’ID del metodo impostato con " 410 "successo!" 395 msgstr "Mappatura del Gateway di pagamento con l’ID del metodo impostato con successo!" 411 396 412 397 #: src/Functions/Api.php:748 src/Functions/Api.php:818 … … 462 447 #: src/Functions/Api.php:1199 463 448 msgid "" 464 "You cannot send the credit note because the reference to the linked document " 465 "is\n" 466 " missing. (NB: before sending the credit note, the " 467 "invoice must be sent)." 468 msgstr "" 469 "Non è possibile inviare la nota di credito perché manca il riferimento al " 470 "documento collegato\n" 471 " (NB: prima di inviare la nota di credito, è necessario " 472 "inviare la fattura)." 449 "You cannot send the credit note because the reference to the linked document is\n" 450 " missing. (NB: before sending the credit note, the invoice must be sent)." 451 msgstr "" 452 "Non è possibile inviare la nota di credito perché manca il riferimento al documento " 453 "collegato\n" 454 " (NB: prima di inviare la nota di credito, è necessario inviare la " 455 "fattura)." 473 456 474 457 #: src/Functions/Api.php:1213 475 458 msgid "You cannot send a credit note from an order set to receive a receipt" 476 459 msgstr "" 477 "Non è possibile inviare una nota di credito da un ordine impostato per " 478 "ricevere una ricevuta" 460 "Non è possibile inviare una nota di credito da un ordine impostato per ricevere una ricevuta" 479 461 480 462 #: src/Functions/Api.php:1260 … … 482 464 msgstr "Errore di richiesta: xml non valido" 483 465 484 #: src/Functions/Api.php:1266 src/Functions/Api.php:2370 485 #: src/Functions/Api.php:2 468 src/Functions/Api.php:2613466 #: src/Functions/Api.php:1266 src/Functions/Api.php:2370 src/Functions/Api.php:2468 467 #: src/Functions/Api.php:2613 486 468 msgid "Order: " 487 469 msgstr "Ordine: " 488 470 489 #: src/Functions/Api.php:1267 src/Functions/Api.php:2371 490 #: src/Functions/Api.php:2469 src/Functions/Api.php:2614 491 msgid "" 492 "The invoice could not be created, we recommend that you check your order " 493 "data and try again" 494 msgstr "" 495 "Non è stato possibile creare la fattura, si consiglia di controllare i dati " 496 "dell'ordine e riprovare" 497 498 #: src/Functions/Api.php:1270 src/Functions/Api.php:2373 499 #: src/Functions/Api.php:2471 471 #: src/Functions/Api.php:1267 src/Functions/Api.php:2371 src/Functions/Api.php:2469 472 #: src/Functions/Api.php:2614 473 msgid "" 474 "The invoice could not be created, we recommend that you check your order data and try again" 475 msgstr "" 476 "Non è stato possibile creare la fattura, si consiglia di controllare i dati dell'ordine e " 477 "riprovare" 478 479 #: src/Functions/Api.php:1270 src/Functions/Api.php:2373 src/Functions/Api.php:2471 500 480 msgid "Errors:" 501 481 msgstr "Errori:" 502 482 503 #: src/Functions/Api.php:1331 src/Functions/Api.php:1333 504 #: src/Functions/Api.php:1336 483 #: src/Functions/Api.php:1331 src/Functions/Api.php:1333 src/Functions/Api.php:1336 505 484 msgid "Shipping: " 506 485 msgstr "Spedizione: " … … 511 490 512 491 #: src/Functions/Api.php:1396 513 msgid "" 514 "The payment method of the order has been excluded from sending to Fatture in " 515 "Cloud" 516 msgstr "" 517 "Il metodo di pagamento dell'ordine è stato escluso dall'invio a Fatture in " 518 "Cloud" 492 msgid "The payment method of the order has been excluded from sending to Fatture in Cloud" 493 msgstr "Il metodo di pagamento dell'ordine è stato escluso dall'invio a Fatture in Cloud" 519 494 520 495 #: src/Functions/Api.php:1406 … … 592 567 #: src/Functions/Api.php:2896 593 568 msgid "" 594 "Error in generating the PDF, download it from your reserved area, or ask the " 595 "seller for it" 596 msgstr "" 597 "Errore nel generare il PDF, scaricarlo dalla propria area riservata o " 598 "richiederlo al venditore" 569 "Error in generating the PDF, download it from your reserved area, or ask the seller for it" 570 msgstr "" 571 "Errore nel generare il PDF, scaricarlo dalla propria area riservata o richiederlo al " 572 "venditore" 599 573 600 574 #: src/Functions/Api.php:2936 … … 613 587 #: src/Functions/Utils.php:258 614 588 msgid "WooPOP Addon: You do not have the permissions to manage the add-on" 615 msgstr "" 616 "WooPOP Addon: Non si dispone dei permessi per gestire il componente " 617 "aggiuntivo" 589 msgstr "WooPOP Addon: Non si dispone dei permessi per gestire il componente aggiuntivo" 618 590 619 591 #: src/Functions/Utils.php:287 … … 635 607 #: src/Functions/Utils.php:301 636 608 msgid "" 637 "Go to <b>settings > balance account</b> and create a balance account, eg: "638 " <b>\"Standardaccount\"</b>"639 msgstr "" 640 "Vai su <b>impostazioni > conto saldo</b> e crea un conto saldo, ad esempio: "641 " <b>”Contostandard”</b> "609 "Go to <b>settings > balance account</b> and create a balance account, eg: <b>\"Standard " 610 "account\"</b>" 611 msgstr "" 612 "Vai su <b>impostazioni > conto saldo</b> e crea un conto saldo, ad esempio: <b>”Conto " 613 "standard”</b> " 642 614 643 615 #: src/Functions/Utils.php:303 644 616 msgid "" 645 "Go to <b>settings > payment methods</b> and create these basic methods: "646 " <b>(Wire transfer,Check, Cash, Payment card)</b>"647 msgstr "" 648 "Vai su <b>impostazioni > metodi di pagamento</b> e crea questi metodi di "649 " base:<b>(Bonifico, Assegno, Contanti, Carta di pagamento)</b>"617 "Go to <b>settings > payment methods</b> and create these basic methods: <b>(Wire transfer, " 618 "Check, Cash, Payment card)</b>" 619 msgstr "" 620 "Vai su <b>impostazioni > metodi di pagamento</b> e crea questi metodi di base: " 621 "<b>(Bonifico, Assegno, Contanti, Carta di pagamento)</b>" 650 622 651 623 #: src/Functions/Utils.php:308 … … 655 627 #: src/Functions/Utils.php:310 656 628 msgid "" 657 "<b>By clicking on Connect</b>, a popup will appear with a <b>code and a "658 " link</b> to authorize the connection. When you click, a page will open where "659 " you will have to authorize the connection by entering the code in the popup"660 " <u>(do not close the popup until theprocedure is complete)</u>"661 msgstr "" 662 "<b>Facendo clic su Connetti</b>, apparirà un popup con un <b>codice e un "663 " link</b> per autorizzare la connessione. Facendo clic, si aprirà una pagina"664 " in cui si dovrà autorizzare la connessione inserendo il codice presente nel "665 " popup <u>(non chiudere il popup fino alcompletamento della procedura)</u>"629 "<b>By clicking on Connect</b>, a popup will appear with a <b>code and a link</b> to " 630 "authorize the connection. When you click, a page will open where you will have to authorize " 631 "the connection by entering the code in the popup <u>(do not close the popup until the " 632 "procedure is complete)</u>" 633 msgstr "" 634 "<b>Facendo clic su Connetti</b>, apparirà un popup con un <b>codice e un link</b> per " 635 "autorizzare la connessione. Facendo clic, si aprirà una pagina in cui si dovrà autorizzare " 636 "la connessione inserendo il codice presente nel popup <u>(non chiudere il popup fino al " 637 "completamento della procedura)</u>" 666 638 667 639 #: src/Functions/Utils.php:312 668 640 msgid "" 669 "Once the connection procedure with Fatture In Cloud is finished, <b>go back " 670 "to this page</b> where you will find the answer of <b>\"Access Token " 671 "successful!\"</b>" 672 msgstr "" 673 "Una volta terminata la procedura di connessione con Fatture In Cloud, " 674 "<b>torna in questa pagina</b> dove troverai la risposta di <b>”Token di " 675 "accesso riuscito!”</b>" 641 "Once the connection procedure with Fatture In Cloud is finished, <b>go back to this page</" 642 "b> where you will find the answer of <b>\"Access Token successful!\"</b>" 643 msgstr "" 644 "Una volta terminata la procedura di connessione con Fatture In Cloud, <b>torna in questa " 645 "pagina</b> dove troverai la risposta di <b>”Token di accesso riuscito!”</b>" 676 646 677 647 #: src/Functions/Utils.php:318 678 648 msgid "" 679 "Si consiglia di verificare con molta attenzione che i dati per la " 680 "Fatturazione elettronica inviati a\n" 681 " Fattura in Cloud siano corretti. L'autore del plugin " 682 "declina ogni responsabilità per errori o mancanze nella generazione della " 683 "Fattura elettronica." 649 "Si consiglia di verificare con molta attenzione che i dati per la Fatturazione elettronica " 650 "inviati a\n" 651 " Fattura in Cloud siano corretti. L'autore del plugin declina ogni " 652 "responsabilità per errori o mancanze nella generazione della Fattura elettronica." 684 653 msgstr "" 685 654 … … 689 658 690 659 #: src/Functions/Utils.php:328 691 msgid "" 692 "Every time the plugin is deactivated, the authentication data is removed." 693 msgstr "" 694 "Ogni volta che il plugin viene disattivato, i dati di autenticazione vengono " 695 "rimossi." 660 msgid "Every time the plugin is deactivated, the authentication data is removed." 661 msgstr "Ogni volta che il plugin viene disattivato, i dati di autenticazione vengono rimossi." 696 662 697 663 #: src/Functions/Utils.php:330 … … 725 691 #: src/Functions/Utils.php:363 726 692 msgid "" 727 "Enable automatic sending of invoices for completed orders not sent in the "728 " last 7 days.(work 2 times a day)"729 msgstr "" 730 "Abilita l'invio automatico delle fatture per gli ordini completati e non "731 " inviati negliultimi 7 giorni. (invia 2 volte al giorno)"693 "Enable automatic sending of invoices for completed orders not sent in the last 7 days. " 694 "(work 2 times a day)" 695 msgstr "" 696 "Abilita l'invio automatico delle fatture per gli ordini completati e non inviati negli " 697 "ultimi 7 giorni. (invia 2 volte al giorno)" 732 698 733 699 #: src/Functions/Utils.php:368 … … 737 703 #: src/Functions/Utils.php:369 738 704 msgid "" 739 "if active, the invoice / receipt documents will be created but the invoices "740 " will not besent to the SDI"741 msgstr "" 742 "se attivo, i documenti fattura/ricevuta saranno creati ma le fatture non "743 " verranno inviateallo SDI"705 "if active, the invoice / receipt documents will be created but the invoices will not be " 706 "sent to the SDI" 707 msgstr "" 708 "se attivo, i documenti fattura/ricevuta saranno creati ma le fatture non verranno inviate " 709 "allo SDI" 744 710 745 711 #: src/Functions/Utils.php:375 … … 756 722 757 723 #: src/Functions/Utils.php:396 758 msgid "" 759 "Use the \"Fatture in Cloud\" PDF document as an attachment to the Invoice " 760 "email" 761 msgstr "" 762 "Utilizza il documento PDF di “Fatture in Cloud” come allegato all’e-mail di " 763 "Fatturazione" 724 msgid "Use the \"Fatture in Cloud\" PDF document as an attachment to the Invoice email" 725 msgstr "" 726 "Utilizza il documento PDF di “Fatture in Cloud” come allegato all’e-mail di Fatturazione" 764 727 765 728 #: src/Functions/Utils.php:402 … … 768 731 769 732 #: src/Functions/Utils.php:407 770 msgid "" 771 "Enables the insertion of the notes inserted during the checkout phase on the " 772 "invoice" 773 msgstr "" 774 "Abilita inserimento in fattura delle note inserite durante la fase di " 775 "checkout" 733 msgid "Enables the insertion of the notes inserted during the checkout phase on the invoice" 734 msgstr "Abilita inserimento in fattura delle note inserite durante la fase di checkout" 776 735 777 736 #: src/Functions/Utils.php:413 … … 789 748 #: src/Functions/Utils.php:428 790 749 msgid "" 791 "Revenue centers can be used to group revenue related to a particular "792 " activity. (for example\"e-commerce\")"793 msgstr "" 794 "I centri di ricavo possono essere utilizzati per raggruppare le entrate "795 " relative a unaparticolare attività. (ad esempio \"e-commerce\")"750 "Revenue centers can be used to group revenue related to a particular activity. (for example " 751 "\"e-commerce\")" 752 msgstr "" 753 "I centri di ricavo possono essere utilizzati per raggruppare le entrate relative a una " 754 "particolare attività. (ad esempio \"e-commerce\")" 796 755 797 756 #: src/Functions/Utils.php:435 … … 829 788 #: src/Functions/Utils.php:468 830 789 msgid "Enter this code in wp-config.php to activate the event log." 831 msgstr "" 832 "Inserisci questo codice nel wp-config.php per attivare il registro degli " 833 "eventi." 790 msgstr "Inserisci questo codice nel wp-config.php per attivare il registro degli eventi." 834 791 835 792 #: src/Functions/Utils.php:477 … … 886 843 msgstr "Vuoi imbrogliare eh?" 887 844 845 msgid "Action skipped: another create process is already running for this order." 846 msgstr "Azione saltata: un altro processo di creazione è già in esecuzione per questo ordine." 847 848 msgid "Action not allowed: another integration channel is active." 849 msgstr "Azione non consentita: un altro canale di integrazione è attivo." 850 851 msgid "Missing issued document id, create the invoice first." 852 msgstr "ID documento emesso mancante, crea prima la fattura." 853 854 msgid "Missing document id parameter for send request." 855 msgstr "Parametro ID documento mancante nella richiesta di invio." 856 857 msgid "Invalid document id for send request." 858 msgstr "ID documento non valido per la richiesta di invio." 859 860 msgid "Action skipped: another send process is already running for this order." 861 msgstr "Azione saltata: un altro processo di invio è già in esecuzione per questo ordine." 862 888 863 #~ msgid "Sended" 889 864 #~ msgstr "Inviato" -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud/src/Functions/Api.php
r3460146 r3463326 34 34 use GuzzleHttp\Exception\GuzzleException; 35 35 use WcElectronInvoice\Admin\Settings\OptionPage; 36 use WcElectronInvoice\Integrations; 36 37 use WcElectronInvoice\WooCommerce\Fields\GeneralFields; 37 38 use WcElectronInvoice\WooCommerce\Fields\TaxFields; … … 1085 1086 $jobEvent = filterInput($_POST, 'fcCreateInvoiceJobEvent', FILTER_UNSAFE_RAW); 1086 1087 1088 if (! Integrations::isInvoiceChannelActive(Integrations::CHANNEL_FIC)) { 1089 if (! $jobEvent) { 1090 wp_send_json(array( 1091 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 1092 'message' => esc_html__('Action not allowed: another integration channel is active.', 1093 WP_FATT_CLOUD_TEXTDOMAIN), 1094 'code' => 422, 1095 )); 1096 die(); 1097 } 1098 1099 return; 1100 } 1101 1087 1102 // auto refreshOAuthToken 1088 1103 if (! $jobEvent && ! getActiveApi()->expireAccessToken || (int)getActiveApi()->expireAccessToken < time()) { … … 1116 1131 } 1117 1132 1118 // Return if order not completed and processing status 1119 if ('processing' !== $order->get_status() && 1120 'completed' !== $order->get_status() 1121 ) { 1133 $statusAllowed = in_array((string)$order->get_status(), ['processing', 'completed'], true); 1134 $disableInvoiceNumber = \WcElectronInvoice\Functions\disableInvoiceOnOrderTotalZero($order); 1135 $createDecision = \WcElectronInvoice\Functions\evaluateFicCreateDecision( 1136 $statusAllowed, 1137 true === apply_filters('wfc-create-invoice_send_invoice_zero', $disableInvoiceNumber, $order->get_total()) 1138 ); 1139 1140 if (! $createDecision['allowed'] && 'status_not_allowed' === $createDecision['reason']) { 1141 $createError = \WcElectronInvoice\Functions\mapFicCreateDecisionError($createDecision['reason']); 1122 1142 wp_send_json(array( 1123 1143 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 1124 1144 'body' => null, 1125 1145 'message' => sprintf(__("OrderID %s - not completed or not processing status."), $order->get_id()), 1126 'code' => 501,1146 'code' => $createError['code'], 1127 1147 )); 1128 1148 // Log … … 1133 1153 } 1134 1154 1135 // Return if total is 0 1136 $disableInvoiceNumber = \WcElectronInvoice\Functions\disableInvoiceOnOrderTotalZero($order); 1137 if (true === apply_filters('wfc-create-invoice_send_invoice_zero', 1138 $disableInvoiceNumber, 1139 $order->get_total() 1140 )) { 1155 if (! $createDecision['allowed'] && 'total_zero_not_allowed' === $createDecision['reason']) { 1156 $createError = \WcElectronInvoice\Functions\mapFicCreateDecisionError($createDecision['reason']); 1141 1157 wp_send_json(array( 1142 1158 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 1143 1159 'body' => null, 1144 1160 'message' => __('The order total is 0 (zero) you cannot send the invoice', WP_FATT_CLOUD_TEXTDOMAIN), 1145 'code' => 501,1161 'code' => $createError['code'], 1146 1162 )); 1147 1163 // Log … … 1643 1659 $dutyValue = $order->get_total() > floatval('77.47') ? 2 : 0; 1644 1660 } 1661 1662 $lockKey = null; 1645 1663 1646 1664 try { … … 2094 2112 } 2095 2113 2096 $body['data']['show_totals'] = 'all'; 2097 $body['data']['show_paypal_button'] = false; 2098 $body['data']['show_notification_button'] = false; 2099 $body['data']['accompanying_invoice'] = false; 2100 $body['data']['show_payment_method'] = true; 2101 $body['data']['show_payments'] = true; 2114 $body['data'] = array_merge( 2115 $body['data'], 2116 \WcElectronInvoice\Functions\buildFicDocumentDisplayOptions() 2117 ); 2102 2118 if (false === apply_filters('wfc-disabled_data_date_document', false, $jobEvent)) { 2103 2119 // data: data di emissione documento 2104 2120 $body['data']['date'] = $docDateNow->format('Y-m-d'); 2105 2121 } 2106 // od_date: documento originario (operazione per fatture, data fattura collegata per NC) 2107 $body['data']['ei_data']['od_date'] = $odDateValue; 2108 2109 // invoice_date: per NC è la data della fattura stornata; per le fatture impostiamo la data emissione 2110 if ('credit_note' === $documentType && $linkedInvDate) { 2111 $body['data']['ei_data']['invoice_date'] = $linkedInvDate; 2112 } else { 2113 $body['data']['ei_data']['invoice_date'] = $docDateNow->format('Y-m-d'); 2114 } 2115 2116 if ('credit_note' === $documentType) { 2117 $body['data']['ei_data']['invoice_number'] = $creditNoteLinkedInvoiceNumber; 2118 } 2119 2120 if ($iban) { 2121 $body['data']['ei_data']['bank_iban'] = $iban; 2122 $body['data']['ei_data']['bank_name'] = $bankName; 2123 $body['data']['ei_data']['bank_beneficiary'] = $accountName; 2124 } 2122 $invoiceDate = ('credit_note' === $documentType && $linkedInvDate) ? $linkedInvDate : $docDateNow->format('Y-m-d'); 2123 $eiInvoiceNumber = 'credit_note' === $documentType ? (string)$creditNoteLinkedInvoiceNumber : null; 2124 $bankData = $iban ? [ 2125 'iban' => $iban, 2126 'name' => $bankName, 2127 'beneficiary' => $accountName, 2128 ] : null; 2129 2130 $body['data']['ei_data'] = array_merge( 2131 $body['data']['ei_data'], 2132 \WcElectronInvoice\Functions\buildFicEiDataPayload( 2133 (string)$odDateValue, 2134 (string)$invoiceDate, 2135 $eiInvoiceNumber, 2136 $bankData 2137 ) 2138 ); 2125 2139 2126 2140 // Log … … 2154 2168 } 2155 2169 2170 $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('fic', 'create_invoice', (int)$orderID, 2171 (string)$provider, (string)$documentType); 2172 $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision( 2173 \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0), 2174 (bool)$jobEvent 2175 ); 2176 if (! $lockDecision['continue']) { 2177 if ($lockDecision['respond_error']) { 2178 $lockError = \WcElectronInvoice\Functions\mapLockBusyError('fic', 'create_invoice'); 2179 wp_send_json(array( 2180 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 2181 'message' => esc_html__('Action skipped: another create process is already running for this order.', 2182 WP_FATT_CLOUD_TEXTDOMAIN), 2183 'code' => $lockError['code'], 2184 )); 2185 die(); 2186 } 2187 2188 return; 2189 } 2190 2156 2191 // Check issued document exist 2157 2192 $documentID = \WcElectronInvoice\Functions\getPostMeta("wfc-issued_document_id", null, $orderID, true, 2158 2193 'order', $provider); 2159 if (isset($documentID) && '' !== $documentID) { 2194 $createDecision = \WcElectronInvoice\Functions\evaluateFicCreateDecision(true, false, $documentID); 2195 if (! $createDecision['allowed'] && 'already_created' === $createDecision['reason']) { 2160 2196 if (! $jobEvent) { 2197 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2198 $createError = \WcElectronInvoice\Functions\mapFicCreateDecisionError($createDecision['reason']); 2161 2199 wp_send_json(array( 2162 2200 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 2163 2201 'message' => esc_html__('The document has already been created', WP_FATT_CLOUD_TEXTDOMAIN), 2164 'code' => 422,2202 'code' => $createError['code'], 2165 2203 )); 2166 2204 \WooPoPToFattureInCloud\Functions\log("OrderID {$order->get_id()} - The document has already been created: {$documentID}" . "\n"); 2167 2205 die(); 2168 2206 } else { 2207 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2169 2208 return; 2170 2209 } … … 2428 2467 2429 2468 if (! $jobEvent) { 2469 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2430 2470 wp_send_json($issuedDocumentResponse); 2431 2471 die(); 2432 2472 } else { 2473 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2433 2474 return true; 2434 2475 } … … 2482 2523 sendEmailToAdminForInvoiceCreateError($emailBody, $orderID); 2483 2524 2525 if (! empty($lockKey)) { 2526 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2527 } 2484 2528 wp_send_json($dataException); 2485 2529 die(); … … 2789 2833 $id = filterInput($_POST, 'id', FILTER_VALIDATE_INT); 2790 2834 $provider = filterInput($_POST, 'provider', FILTER_UNSAFE_RAW); 2835 $docID = (int)$docID; 2836 2837 if (! Integrations::isInvoiceChannelActive(Integrations::CHANNEL_FIC)) { 2838 if (! $jobEvent) { 2839 wp_send_json(array( 2840 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 2841 'message' => esc_html__('Action not allowed: another integration channel is active.', 2842 WP_FATT_CLOUD_TEXTDOMAIN), 2843 'code' => 422, 2844 )); 2845 die(); 2846 } 2847 2848 return; 2849 } 2791 2850 2792 2851 $order = \WcElectronInvoice\Providers\OrderQuery::instance()->getProviderOrder($id, $provider); … … 2811 2870 } 2812 2871 2813 // Invoice sent check 2814 $checkSent = \WcElectronInvoice\Functions\getPostMeta('_invoice_sent', null, $id, true, 'order', $provider); 2815 if (isset($checkSent) && 'sent' === $checkSent) { 2872 $issuedDocumentID = (int)\WcElectronInvoice\Functions\getPostMeta('wfc-issued_document_id', null, $id, true, 2873 'order', 2874 $provider); 2875 $issuedSendDocument = \WcElectronInvoice\Functions\getPostMeta('wfc-issued_send_document', null, $id, true, 2876 'order', 2877 $provider); 2878 $checkSent = \WcElectronInvoice\Functions\getPostMeta('_invoice_sent', null, $id, true, 'order', 2879 $provider); 2880 $sendDecision = \WcElectronInvoice\Functions\evaluateFicSendDecision( 2881 (int)$docID, 2882 (int)$issuedDocumentID, 2883 $issuedSendDocument, 2884 is_string($checkSent) ? $checkSent : null 2885 ); 2886 $alreadySentOnFic = \WcElectronInvoice\Functions\normalizeBooleanMeta($issuedSendDocument); 2887 2888 if (! $sendDecision['allowed']) { 2889 $sendError = \WcElectronInvoice\Functions\mapFicSendDecisionError($sendDecision['reason']); 2890 if ('doc_id_mismatch' === $sendDecision['reason']) { 2891 \WooPoPToFattureInCloud\Functions\log('sendInvoice - docID mismatch, abort send: ' . print_r([ 2892 'docID' => $docID, 2893 'issuedDocumentID' => $issuedDocumentID, 2894 'orderID' => $id, 2895 ], true)); 2896 } 2897 2816 2898 if (! $jobEvent) { 2899 $message = esc_html__('Action not allowed.', WP_FATT_CLOUD_TEXTDOMAIN); 2900 if ('missing_issued_document' === $sendDecision['reason']) { 2901 $message = esc_html__('Missing issued document id, create the invoice first.', WP_FATT_CLOUD_TEXTDOMAIN); 2902 } elseif ('missing_doc_id' === $sendDecision['reason']) { 2903 $message = esc_html__('Missing document id parameter for send request.', WP_FATT_CLOUD_TEXTDOMAIN); 2904 } elseif ('doc_id_mismatch' === $sendDecision['reason']) { 2905 $message = esc_html__('Invalid document id for send request.', WP_FATT_CLOUD_TEXTDOMAIN); 2906 } elseif ('already_sent_fic' === $sendDecision['reason'] || 'already_sent_meta' === $sendDecision['reason']) { 2907 $message = esc_html__('The document has already been sent', WP_FATT_CLOUD_TEXTDOMAIN); 2908 } 2909 2817 2910 wp_send_json(array( 2818 2911 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 2819 'message' => esc_html__('The document has already been sent', WP_FATT_CLOUD_TEXTDOMAIN),2820 'code' => 422,2912 'message' => $message, 2913 'code' => $sendError['code'], 2821 2914 )); 2822 2915 die(); 2823 } else { 2824 return; 2825 } 2916 } 2917 2918 return; 2919 } 2920 2921 $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('fic', 'send_invoice', (int)$id, (string)$provider, 2922 (string)$docID); 2923 $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision( 2924 \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0), 2925 (bool)$jobEvent 2926 ); 2927 if (! $lockDecision['continue']) { 2928 if ($lockDecision['respond_error']) { 2929 $lockError = \WcElectronInvoice\Functions\mapLockBusyError('fic', 'send_invoice'); 2930 wp_send_json(array( 2931 'title' => __('Oops, something is wrong!', WP_FATT_CLOUD_TEXTDOMAIN), 2932 'message' => esc_html__('Action skipped: another send process is already running for this order.', 2933 WP_FATT_CLOUD_TEXTDOMAIN), 2934 'code' => $lockError['code'], 2935 )); 2936 die(); 2937 } 2938 2939 return; 2826 2940 } 2827 2941 … … 2840 2954 2841 2955 $orderID = $order->get_id(); 2842 if ($sendEInvoice->valid()) { 2956 if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithFicSend((bool)$sendEInvoice->valid(), (int)$docID, 2957 $alreadySentOnFic)) { 2843 2958 // Log 2844 2959 \WooPoPToFattureInCloud\Functions\log('sendInvoice Valid : ' . print_r($sendEInvoice->valid(), true)); … … 2989 3104 2990 3105 if (! $jobEvent) { 3106 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2991 3107 wp_send_json($response); 2992 3108 die(); 2993 3109 } else { 3110 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 2994 3111 return true; 2995 3112 } … … 3036 3153 \WooPoPToFattureInCloud\Functions\log(print_r($dataException, true)); 3037 3154 3155 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 3038 3156 wp_send_json($dataException); 3039 3157 die(); -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud/src/Jobs.php
r3370892 r3463326 516 516 $data = getActiveApi(); 517 517 if (! $data->active) { 518 wp_clear_scheduled_hook('twiceDailyWooPoPToFattureInCloudJobsScheduledUpload'); 518 519 return; 519 520 } -
woopop-electronic-invoice-free/trunk/addon/to/fattureincloud/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woopop-electronic-invoice-free/trunk/addon/to/sdi-pec/languages/popsdipec-it_IT.po
r3253418 r3463326 4 4 "Project-Id-Version: POP to SdI\n" 5 5 "POT-Creation-Date: 2025-03-08 10:54+0100\n" 6 "PO-Revision-Date: 202 5-03-08 10:54+0100\n"6 "PO-Revision-Date: 2026-02-16 13:57+0100\n" 7 7 "Last-Translator: \n" 8 8 "Language-Team: \n" … … 12 12 "Content-Transfer-Encoding: 8bit\n" 13 13 "Plural-Forms: nplurals=2; plural=(n != 1);\n" 14 "X-Generator: Poedit 3. 5\n"14 "X-Generator: Poedit 3.8\n" 15 15 "X-Poedit-Basepath: ..\n" 16 "X-Poedit-KeywordsList: __;_e;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c;" 17 "_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;esc_attr__;esc_attr_e;" 18 "esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 16 "X-Poedit-KeywordsList: " 17 "__;_e;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c;_nc:4c,1,2;_x:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;_ex:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n" 19 18 "X-Poedit-SearchPathExcluded-3: assets\n" 20 19 "X-Poedit-SourceCharset: UTF-8\n" … … 51 50 msgstr "Azioni Fatture SdI PEC" 52 51 53 #: inc/localizeScripts.php:40 src/Functions/Api.php:318 54 #: src/Functions/Api.php:4 29 src/Functions/Api.php:46552 #: inc/localizeScripts.php:40 src/Functions/Api.php:318 src/Functions/Api.php:429 53 #: src/Functions/Api.php:465 55 54 msgid "Message:" 56 55 msgstr "Messaggio:" … … 94 93 #: src/Filters.php:104 95 94 msgid "" 96 "Set the expiration date for the password used for authentication. This field " 97 "is optional. Make sure to create an Application Password required for two-" 98 "factor authentication." 99 msgstr "" 100 "Imposta la data di scadenza per la password utilizzata per l’autenticazione. " 101 "Questo campo è facoltativo. Assicurati di creare una password " 102 "dell’applicazione richiesta per l’autenticazione a due fattori." 95 "Set the expiration date for the password used for authentication. This field is optional. " 96 "Make sure to create an Application Password required for two-factor authentication." 97 msgstr "" 98 "Imposta la data di scadenza per la password utilizzata per l’autenticazione. Questo campo è " 99 "facoltativo. Assicurati di creare una password dell’applicazione richiesta per " 100 "l’autenticazione a due fattori." 103 101 104 102 #: src/Filters.php:106 … … 130 128 msgstr "Salvato. Credenziali non valide o configurazione errata." 131 129 132 #: src/Functions/Api.php:193 src/Functions/Api.php:306 133 #: src/Functions/Api.php:314 src/Functions/Api.php:355 134 #: src/Functions/Api.php:425 src/Functions/Api.php:454 130 #: src/Functions/Api.php:193 src/Functions/Api.php:306 src/Functions/Api.php:314 131 #: src/Functions/Api.php:355 src/Functions/Api.php:425 src/Functions/Api.php:454 135 132 #: src/Functions/Api.php:461 136 133 msgid "Oops, something is wrong!" … … 157 154 msgstr "Errore di richiesta: xml non valido" 158 155 159 #: src/Functions/Api.php:315 src/Functions/Api.php:356 160 #: src/Functions/Api.php:4 26 src/Functions/Api.php:462156 #: src/Functions/Api.php:315 src/Functions/Api.php:356 src/Functions/Api.php:426 157 #: src/Functions/Api.php:462 161 158 msgid "Order: " 162 159 msgstr "Ordine: " … … 164 161 #: src/Functions/Api.php:316 src/Functions/Api.php:427 165 162 msgid "" 166 "The invoice could not be created, we recommend that you check your order " 167 "data and try again" 168 msgstr "" 169 "Impossibile creare la fattura, ti consigliamo di controllare i dati " 170 "dell’ordine e riprovare" 163 "The invoice could not be created, we recommend that you check your order data and try again" 164 msgstr "" 165 "Impossibile creare la fattura, ti consigliamo di controllare i dati dell’ordine e riprovare" 171 166 172 167 #: src/Functions/Api.php:319 src/Functions/Api.php:466 … … 179 174 180 175 #: src/Functions/Api.php:350 181 msgid "" 182 "You are in development mode set the constant WP_FATT_SDI_DEV_PEC_TO for " 183 "testing" 184 msgstr "" 185 "Sei in modalità di sviluppo, impostare la costante WP_FATT_SDI_DEV_PEC_TO " 186 "per i test" 176 msgid "You are in development mode set the constant WP_FATT_SDI_DEV_PEC_TO for testing" 177 msgstr "Sei in modalità di sviluppo, impostare la costante WP_FATT_SDI_DEV_PEC_TO per i test" 187 178 188 179 #: src/Functions/Api.php:357 189 180 #, php-format 190 181 msgid "" 191 "The invoice could not be send because sender or receiver is not a valid "192 " email addresses: %s-> %s"193 msgstr "" 194 "Non è stato possibile inviare la fattura perché il mittente o il "195 " destinatario non sonoindirizzi email validi: %s -> %s"182 "The invoice could not be send because sender or receiver is not a valid email addresses: %s " 183 "-> %s" 184 msgstr "" 185 "Non è stato possibile inviare la fattura perché il mittente o il destinatario non sono " 186 "indirizzi email validi: %s -> %s" 196 187 197 188 #: src/Functions/Api.php:398 … … 223 214 #: src/Functions/Api.php:463 224 215 msgid "The invoice could not be sent because PhpMailer throws an exception" 225 msgstr "" 226 "Non è stato possibile inviare la fattura perché PhpMailer genera un’eccezione" 216 msgstr "Non è stato possibile inviare la fattura perché PhpMailer genera un’eccezione" 227 217 228 218 #: src/Functions/Api.php:515 … … 232 222 #: src/Functions/Utils.php:359 233 223 msgid "WooPOP Addon: You do not have the permissions to manage the add-on" 234 msgstr "" 235 "WooPOP Addon: Non si dispone dei permessi per gestire il componente " 236 "aggiuntivo" 224 msgstr "WooPOP Addon: Non si dispone dei permessi per gestire il componente aggiuntivo" 237 225 238 226 #: src/Functions/Utils.php:368 … … 262 250 #: src/Functions/Utils.php:415 263 251 msgid "" 264 "Enable automatic sending of invoices for completed orders not sent in the "265 " last 7 days. (work2 times a day)"266 msgstr "" 267 "Abilita l'invio automatico delle fatture per gli ordini completati e non "268 " inviati negli ultimi7 giorni. (funziona 2 volte al giorno)"252 "Enable automatic sending of invoices for completed orders not sent in the last 7 days. (work " 253 "2 times a day)" 254 msgstr "" 255 "Abilita l'invio automatico delle fatture per gli ordini completati e non inviati negli ultimi " 256 "7 giorni. (funziona 2 volte al giorno)" 269 257 270 258 #: src/Functions/Utils.php:421 … … 277 265 278 266 #: src/Functions/Utils.php:428 279 msgid "" 280 "Se hai ricevuto un indirizzo email destintario differente, puoi sostituirlo " 281 "qui" 267 msgid "Se hai ricevuto un indirizzo email destintario differente, puoi sostituirlo qui" 282 268 msgstr "" 283 269 … … 288 274 #: src/Functions/Utils.php:435 289 275 msgid "Set the email with the constant WP_FATT_SDI_DEV_PEC_TO" 290 msgstr "" 291 "Sei in modalità di sviluppo, impostare la costante WP_FATT_SDI_DEV_PEC_TO " 292 "per i test" 276 msgstr "Sei in modalità di sviluppo, impostare la costante WP_FATT_SDI_DEV_PEC_TO per i test" 293 277 294 278 #: src/Functions/Utils.php:441 … … 322 306 #: src/Functions/Utils.php:480 323 307 msgid "Enter this code in wp-config.php to activate the event log." 324 msgstr "" 325 "Inserisci questo codice nel wp-config.php per attivare il registro degli " 326 "eventi." 308 msgstr "Inserisci questo codice nel wp-config.php per attivare il registro degli eventi." 327 309 328 310 #: src/Functions/Utils.php:490 … … 366 348 #, php-format 367 349 msgid "" 368 "This is a reminder that your password will expire on %s. Please update it to "369 " avoidinterruptions in the PEC invoice sending system."370 msgstr "" 371 "Questo è un promemoria che la tua password scadrà il %s. Aggiornala per "372 " evitare interruzioninel sistema di invio delle fatture PEC."350 "This is a reminder that your password will expire on %s. Please update it to avoid " 351 "interruptions in the PEC invoice sending system." 352 msgstr "" 353 "Questo è un promemoria che la tua password scadrà il %s. Aggiornala per evitare interruzioni " 354 "nel sistema di invio delle fatture PEC." 373 355 374 356 #: src/Functions/Utils.php:738 375 msgid "" 376 "If your password expires, the system for sending invoices via PEC will stop " 377 "functioning." 378 msgstr "" 379 "Se la tua password scade, il sistema di invio delle fatture tramite PEC " 380 "smetterà di funzionare." 357 msgid "If your password expires, the system for sending invoices via PEC will stop functioning." 358 msgstr "" 359 "Se la tua password scade, il sistema di invio delle fatture tramite PEC smetterà di " 360 "funzionare." 381 361 382 362 #: src/Functions/Utils.php:740 … … 387 367 msgid "Cheatin’ huh?" 388 368 msgstr "Vuoi imbrogliare eh?" 369 370 msgid "Action skipped: another upload process is already running for this order." 371 msgstr "Azione saltata: un altro processo di caricamento è già in esecuzione per questo ordine." 372 373 msgid "Invoice send error: empty invoice file name!" 374 msgstr "Errore invio fattura: nome file fattura vuoto!" -
woopop-electronic-invoice-free/trunk/addon/to/sdi-pec/src/Functions/Api.php
r3366302 r3463326 221 221 $checkSent = \WcElectronInvoice\Functions\getPostMeta('_invoice_sent', null, $orderID, true, 'order', 222 222 $provider); 223 if ('on' === $scheduledCronJob && 'feCronJobUploadRequest' === $ajaxAction && 'sent' === $checkSent) { 223 if ('on' === $scheduledCronJob && 224 \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) && 225 'sent' === $checkSent 226 ) { 224 227 return; 225 228 } … … 239 242 ) 240 243 ) { 241 if ('feCronJobUploadRequest' !== $ajaxAction) { 244 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($ajaxAction, 245 ['feCronJobUploadRequest'])) { 242 246 wp_send_json(array( 243 247 'title' => __('Payment method excluded for sending.', WP_FATT_SDI_PEC_TEXTDOMAIN), … … 284 288 $isXmlFromRemoTe = false; 285 289 $validXML = false; 286 if ('feCronJobUploadRequest' !== $ajaxAction) { 290 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($ajaxAction, 291 ['feCronJobUploadRequest'])) { 287 292 $url = esc_url_raw(add_query_arg([ 288 293 CreateXml::LIST_TYPE => $orderID, … … 313 318 // Not valid ? 314 319 if (! $isXml->valid) { 320 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 'xml_invalid'); 315 321 $noXmlResponse = array( 316 322 'title' => __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN), … … 318 324 'xml' => $isXml->xml, 319 325 'message' => __('Request error: xml not valid', WP_FATT_SDI_PEC_TEXTDOMAIN), 320 'code' => 501,326 'code' => $uploadError['code'], 321 327 ); 322 328 … … 351 357 ! \PHPMailer\PHPMailer\PHPMailer::validateAddress($smtpPecTo) 352 358 ) { 359 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 360 'invalid_email_recipient'); 353 361 $noSentResponse = array( 354 362 'message' => __('Sender or recipient not valid!', WP_FATT_SDI_PEC_TEXTDOMAIN), 355 363 'orderID' => $orderID, 356 'code' => 501,364 'code' => $uploadError['code'], 357 365 ); 358 366 … … 380 388 } 381 389 390 $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('sdi_pec', 'upload_invoice', (int)$orderID, 391 (string)$provider, (string)$choiceType); 392 $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision( 393 \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0), 394 \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) 395 ); 396 if (! $lockDecision['continue']) { 397 if ($lockDecision['respond_error']) { 398 $lockError = \WcElectronInvoice\Functions\mapLockBusyError('sdi_pec', 'upload_invoice'); 399 wp_send_json(array( 400 'message' => __('Action skipped: another upload process is already running for this order.', 401 WP_FATT_SDI_PEC_TEXTDOMAIN), 402 'orderID' => $orderID, 403 'code' => $lockError['code'], 404 )); 405 die(); 406 } 407 408 return; 409 } 410 382 411 try { 383 412 $mail = new \PHPMailer\PHPMailer\PHPMailer(true); … … 427 456 428 457 if (! $mail->send()) { 458 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 'upload_failed'); 429 459 $noSentResponse = array( 430 460 'message' => __('Invoice send error!', WP_FATT_SDI_PEC_TEXTDOMAIN), 431 461 'orderID' => $orderID, 432 'code' => 501,462 'code' => $uploadError['code'], 433 463 ); 434 464 … … 445 475 true)); 446 476 477 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 447 478 wp_send_json($noSentResponse); 448 479 die(); 449 480 } 450 481 451 $order->update_meta('_invoice_sent', 'sent'); 452 $order->save(); 453 454 $responseArgs = array( 455 'message' => sprintf('%s:%s - %s', 456 __('Invoice', WP_FATT_SDI_PEC_TEXTDOMAIN), 457 $fileName, 458 __('Invoice sent successfully!', WP_FATT_SDI_PEC_TEXTDOMAIN) 459 ), 460 'orderID' => $orderID, 461 'code' => 200, 462 ); 482 if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithUploadFileName($fileName)) { 483 $order->update_meta('_invoice_sent', 'sent'); 484 $order->save(); 485 486 $responseArgs = array( 487 'message' => sprintf('%s:%s - %s', 488 __('Invoice', WP_FATT_SDI_PEC_TEXTDOMAIN), 489 $fileName, 490 __('Invoice sent successfully!', WP_FATT_SDI_PEC_TEXTDOMAIN) 491 ), 492 'orderID' => $orderID, 493 'code' => 200, 494 ); 495 } else { 496 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 497 'empty_upload_filename'); 498 $responseArgs = array( 499 'message' => __('Invoice send error: empty invoice file name!', WP_FATT_SDI_PEC_TEXTDOMAIN), 500 'orderID' => $orderID, 501 'code' => $uploadError['code'], 502 ); 503 } 463 504 } catch (\Exception $e) { 505 $uploadError = \WcElectronInvoice\Functions\mapUploadPreconditionError('sdi_pec', 'upload_failed'); 464 506 $noSentResponse = array( 465 507 'title' => __('Oops, something is wrong!', WP_FATT_SDI_PEC_TEXTDOMAIN), 466 508 'errors' => [$e->getMessage()], 467 509 'message' => __('Request error: invoice could not be sent', WP_FATT_SDI_PEC_TEXTDOMAIN), 468 'code' => 501,510 'code' => $uploadError['code'], 469 511 ); 470 512 … … 481 523 \WooPoPToSdIPec\Functions\log(print_r($noSentResponse, true)); 482 524 525 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 483 526 wp_send_json($noSentResponse); 484 527 die(); … … 509 552 // Cron Job return data 510 553 if ('on' === $scheduledCronJob && 511 'feCronJobUploadRequest' === $ajaxAction&&554 \WcElectronInvoice\Functions\isBackgroundExecutionMode($ajaxAction, ['feCronJobUploadRequest']) && 512 555 'sent' !== $checkSent 513 556 ) { 557 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 514 558 return (object)array( 515 559 'fileName' => $fileName, … … 518 562 } 519 563 564 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 520 565 wp_send_json($responseArgs); 521 566 die(); -
woopop-electronic-invoice-free/trunk/addon/to/sdi-pec/src/Jobs.php
r3370892 r3463326 276 276 $data = getActiveApi(); 277 277 if (! $data->smtpConnected) { 278 wp_clear_scheduled_hook('twiceDailyWooPoPToSdIPecJobsScheduledUpload'); 278 279 return; 279 280 } … … 343 344 } 344 345 }); 346 } else { 347 wp_clear_scheduled_hook('twiceDailyWooPoPToSdIPecJobsScheduledUpload'); 345 348 } 346 349 } -
woopop-electronic-invoice-free/trunk/addon/to/sdi-pec/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../', -
woopop-electronic-invoice-free/trunk/assets/js/admin.js
r3444920 r3463326 1568 1568 }); 1569 1569 1570 /** 1571 * Native timeout pending actions - confirm UUID 1572 */ 1573 document.querySelectorAll('.sdi_pop_actions a.api-action.confirm-timeout-uuid, .peppol_pop_actions a.api-action.confirm-timeout-uuid').forEach(function (btn) { 1574 btn.addEventListener('click', function (e) { 1575 e.preventDefault(); 1576 e.stopImmediatePropagation(); 1577 1578 const provider = btn.dataset.provider; 1579 const id = btn.dataset.id; 1580 const channel = btn.dataset.channel; 1581 const documentType = btn.dataset.documentType || 'invoice'; 1582 const wrapper = btn.closest('.sdi_pop_actions, .peppol_pop_actions'); 1583 const uuidInput = wrapper ? wrapper.querySelector('.native-timeout-uuid-input') : null; 1584 const uuid = uuidInput ? uuidInput.value.trim() : ''; 1585 1586 if (!uuid) { 1587 showToastAndReload(wc_el_inv_admin.timeout_uuid_required, null, 'error'); 1588 return; 1589 } 1590 1591 this.setAttribute('disabled', 'disabled'); 1592 1593 let loader = document.getElementById('loading-overlay'); 1594 if (!loader) { 1595 loader = document.createElement('span'); 1596 loader.id = 'loading-overlay'; 1597 document.body.appendChild(loader); 1598 } 1599 loader.classList.remove('hidden'); 1600 loader.classList.add('loading'); 1601 1602 fetch(ajaxurl, { 1603 method: 'POST', 1604 headers: {'Content-Type': 'application/x-www-form-urlencoded'}, 1605 body: new URLSearchParams({ 1606 action: 'confirmNativeSendWithUuid', 1607 provider: provider, 1608 id: id, 1609 channel: channel, 1610 document_type: documentType, 1611 uuid: uuid 1612 }) 1613 }) 1614 .then(response => response.json()) 1615 .then(data => { 1616 if ('dev' === wc_el_inv_admin.mode) { 1617 console.log('Response:', data); 1618 } 1619 showToastAndReload(data.data.message, data.data.raw, data.success ? 'success' : 'error', 6000); 1620 }) 1621 .catch(error => { 1622 console.error('AJAX error:', error); 1623 showToastAndReload(error.message, null, 'error'); 1624 }).finally(() => { 1625 this.removeAttribute('disabled'); 1626 loader.classList.remove('loading'); 1627 loader.classList.add('hidden'); 1628 }); 1629 }); 1630 }); 1631 1632 /** 1633 * Native timeout pending actions - cancel timeout 1634 */ 1635 document.querySelectorAll('.sdi_pop_actions a.api-action.cancel-timeout-pending, .peppol_pop_actions a.api-action.cancel-timeout-pending').forEach(function (btn) { 1636 btn.addEventListener('click', function (e) { 1637 e.preventDefault(); 1638 e.stopImmediatePropagation(); 1639 1640 if (!window.confirm(wc_el_inv_admin.timeout_cancel_confirm)) { 1641 return; 1642 } 1643 1644 const provider = btn.dataset.provider; 1645 const id = btn.dataset.id; 1646 const channel = btn.dataset.channel; 1647 1648 this.setAttribute('disabled', 'disabled'); 1649 1650 let loader = document.getElementById('loading-overlay'); 1651 if (!loader) { 1652 loader = document.createElement('span'); 1653 loader.id = 'loading-overlay'; 1654 document.body.appendChild(loader); 1655 } 1656 loader.classList.remove('hidden'); 1657 loader.classList.add('loading'); 1658 1659 fetch(ajaxurl, { 1660 method: 'POST', 1661 headers: {'Content-Type': 'application/x-www-form-urlencoded'}, 1662 body: new URLSearchParams({ 1663 action: 'cancelNativeTimeoutPending', 1664 provider: provider, 1665 id: id, 1666 channel: channel 1667 }) 1668 }) 1669 .then(response => response.json()) 1670 .then(data => { 1671 if ('dev' === wc_el_inv_admin.mode) { 1672 console.log('Response:', data); 1673 } 1674 showToastAndReload(data.data.message, data.data.raw, data.success ? 'success' : 'error', 6000); 1675 }) 1676 .catch(error => { 1677 console.error('AJAX error:', error); 1678 showToastAndReload(error.message, null, 'error'); 1679 }).finally(() => { 1680 this.removeAttribute('disabled'); 1681 loader.classList.remove('loading'); 1682 loader.classList.add('hidden'); 1683 }); 1684 }); 1685 }); 1686 1570 1687 }); 1571 1688 -
woopop-electronic-invoice-free/trunk/assets/js/admin.min.js
r3444920 r3463326 24 24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 25 25 */ 26 !function(e,t,n){function o(e,t,n){var o=document.createElement("a");o.setAttribute("id",e),o.setAttribute("href","javascript:;"),o.innerHTML='<i class="dashicons dashicons-edit"></i>',t.insertAdjacentElement(n,o)}function i(e,t,o){var i=document.createElement("button");i.setAttribute("id",e),i.setAttribute("name",e),i.setAttribute("class","button"),i.innerText=n.text_save,t.insertAdjacentElement(o,i)}function a(e,t,n){var o=document.createElement("a");o.setAttribute("id",e),o.setAttribute("href","javascript:;"),o.innerHTML='<i class="dashicons dashicons-no"></i>',t.insertAdjacentElement("beforeend",o)}function r(e){var t=e.split(/[^0-9]/),n=t[0]+"-"+t[1]+"-"+t[2]+"T"+t[3]+":"+t[4]+":"+t[5]+"+0000";return new Date(n.toString()).getTime()/1e3}function d(e){var t,n=null,o=null,i=null,a=document.getElementById("date_in"),d=document.getElementById("date_out"),c=document.getElementById("filter_type"),s=document.getElementById("filter_state"),l=document.getElementById("filter_provider");if(c&&(n=c.options[c.selectedIndex].value),s&&(o=s.options[s.selectedIndex].value),l&&(i=l.options[l.selectedIndex].value),a.value||d.value){var u=a.value+"T00:00:00",_=d.value+"T23:59:59";u=u.split(" - ").map((function(e){return r(e)})).join(" - "),_=_.split(" - ").map((function(e){return r(e)})).join(" - "),a.value&&""===d.value&&(t=e+"&date_in="+u),""===a.value&&d.value&&(t=e+"&date_out="+_),a.value&&d.value&&(t=e+"&date_in="+u+"&date_out="+_)}else t=e;n&&(t=t+"&filter_type="+n),o&&(t=t+"&filter_state="+o),i&&(t=t+"&filter_provider="+i);if(e&&e.indexOf("filtered")>-1){var m={};if(e.replace(/[?&]+([^=&]+)=([^&]*)/gi,(function(e,t,n){m[t]=n})),"no"===m.filtered)return e}return t}function c(e,t){var n,o=t;return window.location.href.indexOf(e)>-1&&(o=(n={},window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,(function(e,t,o){n[t]=o})),n)[e]),o}function s(e,t,o,i){i||(i=4e3),"dev"===n.mode&&(console.log("message",e),console.log("raw",t));const a=document.createElement("div");a.className="toast "+o;let r="";"error"===o&&(r+='<span class="close" title="close">X</span>'),r+=e;let d="";t?.response?.data?.violations?t.response.data.violations.forEach((e=>{d+="<hr><b>",d+=`- ${n.sdi_notice_path}: ${e.propertyPath}\n`,d+="</b>",d+=`- ${n.sdi_notice_content}: ${e.propertyContent}\n`,d+=`- ${n.sdi_notice_message}: ${e.message}\n`})):t?.detail?.detail?d+=`- ${t.detail.detail}\n`:t?.error&&(d+=`- ${t.error}\n`),d&&(r+="<small>",r+="\n"+n.sdi_notice_details+":\n",r+=d,r+="</small>"),a.innerHTML=r.replace(/\n/g,"<br>"),window.requestAnimationFrame((()=>{document.body.appendChild(a),a.style.opacity="1","error"===o?a.querySelector(".close").addEventListener("click",(function(){a.remove(),location.reload()})):setTimeout((()=>{a.remove(),"success"!==o&&"notify"!==o||location.reload()}),i)}))}document.addEventListener("scroll",(function(){const e=document.querySelector(".wc_el_inv__header");e&&(window.scrollY>20?e.classList.add("is-scrolled"):e.classList.remove("is-scrolled"))})),window.addEventListener("load",(function(){var r=t("#store_invoice-description");r&&t(r).prev().addClass("store_invoice_title");var s,l,u,_,m, v,p,f,y,b,h,g,w,E,A=t("#store_invoice_transmitter-description");A&&t(A).prev().addClass("store_invoice_transmitter_title");t("a.edit_address").on("click",(function(e){t(".order_data_column > p").addClass("hide")})),s=c("tab"),(l=document.querySelectorAll("#toplevel_page_wc_el_inv-options-page ul.wp-submenu li a"))&&l.forEach((function(e){e.parentElement.classList.contains("wp-first-item")&&e.parentElement.remove(),e.parentElement.classList.remove("current"),-1!==e.getAttribute("href").indexOf(s)&&e.parentElement.classList.add("current")})),function(){var e=document.getElementById("wc_el_inv-settings-number_next_invoice");if(e){""!==e.value&&e.setAttribute("disabled","disabled"),o("edit_invoice_next_number",e,"afterend");var t=document.getElementById("edit_invoice_next_number");t.addEventListener("click",(function(){t.hasAttribute("readonly")||(t.style.display="none",e.removeAttribute("disabled"),i("save_invoice_next_number",e,"afterend"))}))}}(),function(){var e=document.getElementById("wc_el_inv-settings-number_next_receipt");if(e){""!==e.value&&e.setAttribute("disabled","disabled"),o("edit_receipt_next_number",e,"afterend");var t=document.getElementById("edit_receipt_next_number");t.addEventListener("click",(function(){t.hasAttribute("readonly")||(t.style.display="none",e.removeAttribute("disabled"),i("save_receipt_next_number",e,"afterend"))}))}}(),function(){var e=document.getElementById("wc_el_inv-settings-number_next_credit_note");if(e){""!==e.value&&e.setAttribute("disabled","disabled"),o("edit_credit_note_next_number",e,"afterend");var t=document.getElementById("edit_credit_note_next_number");t.addEventListener("click",(function(){t.hasAttribute("readonly")||(t.style.display="none",e.removeAttribute("disabled"),i("save_credit_note_next_number",e,"afterend"))}));var n=document.getElementById("wc_el_inv-settings-number_next_credit_note_receipt");if(n){""!==n.value&&n.setAttribute("disabled","disabled"),o("edit_credit_note_receipt_next_number",n,"afterend");var a=document.getElementById("edit_credit_note_receipt_next_number");a.addEventListener("click",(function(){t.hasAttribute("readonly")||(a.style.display="none",n.removeAttribute("disabled"),i("save_credit_note_receipt_next_number",n,"afterend"))}))}}}(),function(){var t=document.querySelectorAll(".wc_el_inv-order_fields");if(0!==t.length){e.forEach(t,(function(e){""!==e.value&&e.setAttribute("disabled","disabled")}));var n=document.querySelector(".wc_el_inv__general-order h3"),i=document.querySelector(".wc_el_inv__general-order--hidden-fields"),r=document.querySelector(".wc_el_inv__general-order--text-data");if(n&&i&&r){o("edit_invoice_next_number",n,"beforeend");var d=document.getElementById("edit_invoice_next_number");a("close_invoice_next_number",n);var c=document.getElementById("close_invoice_next_number");c.style.display="none",d.addEventListener("click",(function(){i.style.display="block",r.style.display="none",this.style.display="none",c.style.display="block",e.forEach(t,(function(e){e.removeAttribute("disabled")}))})),c.addEventListener("click",(function(){i.style.display="none",r.style.display="block",c.style.display="none",d.style.display="",e.forEach(t,(function(e){e.setAttribute("disabled","disabled")}))}))}}}(),u=document.querySelectorAll(".wc_el_inv__refund-invoice[data-order_refund_id]"),0!==(_=document.querySelectorAll(".wc_el_inv-order_fields")).length&&(e.forEach(_,(function(e){""!==e.value&&e.setAttribute("disabled","disabled")})),e.forEach(u,(function(t,n){var i=t.querySelector(".wc_el_inv__refund-invoice td h3"),r=t.querySelector(".wc_el_inv__refund-invoice--hidden-fields"),d=t.querySelector(".wc_el_inv__refund-invoice--text-data");if(i&&r&&d){o("edit_refund_invoice_next_number-"+n,i,"beforeend");var c=document.getElementById("edit_refund_invoice_next_number-"+n);a("close_refund_invoice_next_number-"+n,i);var s=document.getElementById("close_refund_invoice_next_number-"+n);s.style.display="none",s.addEventListener("click",(function(){r.style.display="none",d.style.display="block",s.style.display="none",c.style.display="",e.forEach(_,(function(e){e.setAttribute("disabled","disabled")}))})),c.addEventListener("click",(function(){r.style.display="block",d.style.display="none",this.style.display="none",s.style.display="block",e.forEach(_,(function(e){e.removeAttribute("disabled")}))}))}}))),function(){var t=[document.querySelector(".save-all-csv")];if([]!==t){var n=document.querySelector(".filter");if(n){var o=n.getAttribute("href");n.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var t=d(o);t&&(window.location=t)}))}t&&e.forEach(t,(function(e){e&&e.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var t=d(e.target.href);window.open(t,"_blank")}))}))}}(),m=document.querySelector("#woocommerce-order-items .inside .refund-items"),(v=document.querySelectorAll("#order_refunds .actions .mark_trigger"))&&e.forEach(v,(function(e){e.classList.contains("mark_as_sent")&&m&&(m.setAttribute("disabled",!0),m.innerText=n.refund_item_disabled_text)})),p=document.querySelector("#woocommerce-order-items .inside input#refund_amount"),f=document.querySelector(".wc_el_inv__general-order .actions .mark_trigger"),p&&f&&f.classList.contains("mark_as_sent")&&(p.setAttribute("readonly",!0),p.insertAdjacentHTML("afterend",'<p id="readonly-info">'+n.refund_amount_read_only_info_text+"</p>")),y=document.getElementById("wc_el_inv_order_search"),b=document.querySelector(".wc_el_inv_order_search_trigger"),y&&b&&(y.addEventListener("change",(function(e){e.preventDefault(),e.stopImmediatePropagation(),"dev"===n.mode&&console.log("search:",y.value),""!==y.value?window.location=b.href+"&order_search="+y.value:window.location=b.href+"&order_search"})),b.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation(),"dev"===n.mode&&console.log("search:",y.value),""===y.value?alert(n.search_by_id):window.location=b.href+"&order_search="+y.value}))),(h=document.getElementById("action_bulk"))&&h.addEventListener("change",(function(t){var o=t.target.value,i=document.querySelectorAll('input[name="pop-invoice[]"]'),a=!1;if(e.forEach(i,(function(e){!0===e.checked&&(a=e.checked)})),a||""===o){var r=!1;if("sent"===o?window.confirm(n.invoice_sent_confirm)&&(r=!0):"no_sent"===o&&window.confirm(n.invoice_undo_confirm)&&(r=!0),r){var d=document.getElementById("wp-list-table-invoice-form");if(d){var c=document.createElement("input");c.setAttribute("type","hidden"),c.setAttribute("id","bulk-sent"),c.setAttribute("name","bulk-sent"),c.setAttribute("value",o),d.appendChild(c),d.submit()}}}else alert(n.bulk_invoice_cb)})),(g=document.querySelectorAll(".doc-type-input"))&&e.forEach(g,(function(e){e.addEventListener("change",(function(e){this.parentElement.parentElement.parentElement.querySelectorAll(".choice_type--current")[0].setAttribute("value",this.value)}))})),(w=document.querySelectorAll(".action-endpoint"))&&e.forEach(w,(function(e){e.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var t=this.parentElement.querySelector(".choice_type--current"),n=this.href;t&&(n=n+"&choice_type="+t.value),window.open(n)}))})),function(){if("undefined"!=typeof inlineEditPost){var e=inlineEditPost.edit;inlineEditPost.edit=function(n){e.apply(this,arguments);var o=0;if("object"==typeof n&&(o=parseInt(this.getId(n))),o>0){var i=t("#edit-"+o),a=t("#post-"+o),r=!!t(".column-reverse_charge > *",a).prop("checked");t(':input[name="active_reverse_charge"]',i).prop("checked",r)}}}}(),function(){var e=document.querySelectorAll(".nav_section_advanced#wc_output_fields_nav ul li a");function t(t){e.forEach((function(e){e.getAttribute("href")==="#"+t?e.classList.add("active"):e.classList.remove("active")}));var n=document.querySelector(".wc_el_inv-form .form-table tr.import_collections"),o=document.querySelector(".wc_el_inv-form .form-table tr.export_collections"),i=document.querySelector(".wc_el_inv-form .form-table tr.reverse_charge"),a=document.querySelector(".wc_el_inv-form .form-table tr.automatic_sending"),r=document.querySelector(".wc_el_inv-form .form-table tr.pop_webhook");"reverse-charge"===t&&(o&&(o.style.display="none"),n&&(n.style.display="none"),i&&(i.style.display="table-row"),a&&(a.style.display="none"),r&&(r.style.display="none")),"import-export"===t&&(o&&(o.style.display="table-row"),n&&(n.style.display="table-row"),i&&(i.style.display="none"),a&&(a.style.display="none"),r&&(r.style.display="none")),"automatic-sending"===t&&(o&&(o.style.display="none"),n&&(n.style.display="none"),i&&(i.style.display="none"),a&&(a.style.display="table-row"),r&&(r.style.display="none")),"webhook"===t&&(o&&(o.style.display="none"),n&&(n.style.display="none"),i&&(i.style.display="none"),a&&(a.style.display="none"),r&&(r.style.display="table-row"))}e&&("free"===n.user_level?t("automatic-sending"):"IT"!==n.shop_country?t("webhook"):"#reverse-charge"!==window.location.hash&&window.location.hash?"#import-export"===window.location.hash?t("import-export"):"#automatic-sending"===window.location.hash?t("automatic-sending"):"#webhook"===window.location.hash&&t("webhook"):t("reverse-charge"),e.forEach((function(n){n.addEventListener("click",(function(o){e.forEach((function(e){e.classList.remove("active")})),n.classList.add("active"),t(o.target.hash.replace("#",""))}))})))}(),(E=document.querySelectorAll(".wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_digits input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .reset_numerations input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .suffix_invoice input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .suffix_year input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .suffix_year_format input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_invoice input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_receipt input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .numeration_credit_note input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_credit_note input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .numeration_credit_note_receipt input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_credit_note_receipt input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_invoice_next_number,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_receipt_next_number,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_credit_note_next_number,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_credit_note_receipt_next_number"))&&E.forEach((function(e){e.setAttribute("disabled","disabled"),e.setAttribute("readonly","readonly")})),function(){var e=document.querySelectorAll(".rc-nature-select");function t(e,t){var o=e.options[e.selectedIndex].text.split(" - "),i=2===o.length?o[0]:"",a=2===o.length?o[1]:"",r=null;("N7"===i&&(a="IVA assolta in altro stato UE (vendite a distanza ex art. 40 c. 3 e 4 e art. 41 c. 1 lett. b, DL 331/93; prestazione di servizi di telecomunicazioni, teleradiodiffusione ed elettronici ex art. 7-sexies lett. f, g, art. 74-sexies DPR 633/72)"),"wc_el_inv-ue_private_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-ue_private_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-ue_company_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-ue_company_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-ue_company_no_vies_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-ue_company_no_vies_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-extra_ue_private_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-extra_ue_private_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-extra_ue_company_nature_code"===e.getAttribute("id")&&(r=document.getElementById("wc_el_inv-extra_ue_company_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))),r)&&(r.closest("tr").style.display="Personalizzato"===a?"table-row":"none");"dev"===n.mode&&console.log("CODE:",i,"REF:",a)}e.length>0&&e.forEach((function(e){t(e,!1),e.addEventListener("change",(function(n){t(e,!0)}))}))}(),t(document).ajaxComplete((function(){var e=document.querySelector("#woocommerce-order-items .inside input#refund_amount"),t=document.querySelector(".wc_el_inv__general-order .actions .mark_trigger");e&&t&&(!e.hasAttribute("readonly")&&t.classList.contains("mark_as_sent")?(e.setAttribute("readonly",!0),e.insertAdjacentHTML("afterend",'<p id="readonly-info">'+n.refund_amount_read_only_info_text+"</p>")):e.hasAttribute("readonly")&&!t.classList.contains("mark_as_sent")&&e.removeAttribute("readonly"))}))})),document.body.classList.add("disable-clicks"),document.addEventListener("DOMContentLoaded",(function(){var o;(o=document.querySelectorAll(".mark_trigger"))&&e.forEach(o,(function(e){e.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var o=!1;this.classList.contains("mark_as_sent")?window.confirm(n.invoice_sent_confirm)&&(o=!0):this.classList.contains("mark_undo")&&window.confirm(n.invoice_undo_confirm)&&(o=!0),o&&t.ajax({url:n.ajax_url,method:"POST",cache:!1,data:{action:"markInvoice",action_url:this.href,nonce:n.ajax_nonce},beforeSend:function(){}.bind(this),complete:function(e,t){"dev"===n.mode&&console.log(e,t)}.bind(this),error:function(e,t,n){console.warn("markInvoice "+n,t)}.bind(this),success:function(e){console.log(e),window.location.reload()}.bind(this)})}))})),document.body.classList.remove("disable-clicks")})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".sdi_pop_actions a.api-action.create").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".sdi_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;this.setAttribute("disabled","disabled");let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"createAndSendInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".sdi_pop_actions a.api-action.notifications").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".sdi_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"notificationsInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,null,e.success?"notify":"error")})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".sdi_pop_actions a.api-action.preserve").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".sdi_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"preserveInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,null,e.success?"preserve":"error")})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".peppol_pop_actions a.api-action.create").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".peppol_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;this.setAttribute("disabled","disabled");let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"createAndSendUblInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".peppol_pop_actions a.api-action.state").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".peppol_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"stateInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,null,e.success?"success":"notify")})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{d.classList.remove("loading"),d.classList.add("hidden")}))}))}))})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".pop_to_webhook_actions a.api-action.send-webhook").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".pop_to_webhook_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id;this.setAttribute("disabled","disabled");let r=document.getElementById("loading-overlay");r||(r=document.createElement("span"),r.id="loading-overlay",document.body.appendChild(r)),r.classList.remove("hidden"),r.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"sendWebhook",method:"ajax",provider:i,id:a})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),r.classList.remove("loading"),r.classList.add("hidden")}))}))}))}));let l=0,u=n.count_filtered;function _(e){localStorage.setItem("downloadOffset",e),localStorage.setItem("downloadStarted","1")}function m(){localStorage.removeItem("downloadOffset"),localStorage.removeItem("downloadStarted")}function v(){l>=u?m():t.ajax({url:n.ajax_url,method:"POST",data:{action:"processFileBatch",offset:l,limit:5},success:function(e){if(e.success){e.data.total&&(u=e.data.total),l+=5,l>u&&(l=u),_(l),t(".pop-progress span").remove();let n=Math.min(l/u*100,100);t("#progress-bar").css("width",n+"%").text(Math.round(n)+"%"),e.data.done?(m(),t(this).removeClass("disabled"),t("#progress-bar").css("width","100%").text("100%"),window.location.href=e.data.zip_url):v()}else alert("Errore: "+e.data.message)}})}t(document).ready((function(){const e={offset:parseInt(localStorage.getItem("downloadOffset")||"0"),started:"1"===localStorage.getItem("downloadStarted")};e.started&&(l=e.offset,v()),t("#start-download-btn").on("click",(function(e){t(this).addClass("disabled"),e.preventDefault(),e.stopImmediatePropagation(),t(".pop-progress span").text(n.start_download),l=0,_(l),t("#progress-bar").css("width","0%"),v()}))}));let p=document.getElementById("change_api_key");p&&p.addEventListener("click",(()=>{let e=document.querySelector(".wc_el_inv-form table.form-table .license_key");if(!e)return;let t=e.classList.contains("hidden");e.classList.toggle("hidden");let n=e.querySelector('td label[for="wc_el_inv-settings-license_key"] input[type="text"]');n&&(t?n.removeAttribute("readonly"):n.setAttribute("readonly","readonly"),document.getElementById("save_license_key")||i("save_license_key",n,"afterend"))}))}(window._,window.jQuery,window.wc_el_inv_admin);26 !function(e,t,n){function o(e,t,n){var o=document.createElement("a");o.setAttribute("id",e),o.setAttribute("href","javascript:;"),o.innerHTML='<i class="dashicons dashicons-edit"></i>',t.insertAdjacentElement(n,o)}function i(e,t,o){var i=document.createElement("button");i.setAttribute("id",e),i.setAttribute("name",e),i.setAttribute("class","button"),i.innerText=n.text_save,t.insertAdjacentElement(o,i)}function a(e,t,n){var o=document.createElement("a");o.setAttribute("id",e),o.setAttribute("href","javascript:;"),o.innerHTML='<i class="dashicons dashicons-no"></i>',t.insertAdjacentElement("beforeend",o)}function r(e){var t=e.split(/[^0-9]/),n=t[0]+"-"+t[1]+"-"+t[2]+"T"+t[3]+":"+t[4]+":"+t[5]+"+0000";return new Date(n.toString()).getTime()/1e3}function d(e){var t,n=null,o=null,i=null,a=document.getElementById("date_in"),d=document.getElementById("date_out"),c=document.getElementById("filter_type"),s=document.getElementById("filter_state"),l=document.getElementById("filter_provider");if(c&&(n=c.options[c.selectedIndex].value),s&&(o=s.options[s.selectedIndex].value),l&&(i=l.options[l.selectedIndex].value),a.value||d.value){var u=a.value+"T00:00:00",_=d.value+"T23:59:59";u=u.split(" - ").map((function(e){return r(e)})).join(" - "),_=_.split(" - ").map((function(e){return r(e)})).join(" - "),a.value&&""===d.value&&(t=e+"&date_in="+u),""===a.value&&d.value&&(t=e+"&date_out="+_),a.value&&d.value&&(t=e+"&date_in="+u+"&date_out="+_)}else t=e;n&&(t=t+"&filter_type="+n),o&&(t=t+"&filter_state="+o),i&&(t=t+"&filter_provider="+i);if(e&&e.indexOf("filtered")>-1){var m={};if(e.replace(/[?&]+([^=&]+)=([^&]*)/gi,(function(e,t,n){m[t]=n})),"no"===m.filtered)return e}return t}function c(e,t){var n,o=t;return window.location.href.indexOf(e)>-1&&(o=(n={},window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi,(function(e,t,o){n[t]=o})),n)[e]),o}function s(e,t,o,i){i||(i=4e3),"dev"===n.mode&&(console.log("message",e),console.log("raw",t));const a=document.createElement("div");a.className="toast "+o;let r="";"error"===o&&(r+='<span class="close" title="close">X</span>'),r+=e;let d="";t?.response?.data?.violations?t.response.data.violations.forEach((e=>{d+="<hr><b>",d+=`- ${n.sdi_notice_path}: ${e.propertyPath}\n`,d+="</b>",d+=`- ${n.sdi_notice_content}: ${e.propertyContent}\n`,d+=`- ${n.sdi_notice_message}: ${e.message}\n`})):t?.detail?.detail?d+=`- ${t.detail.detail}\n`:t?.error&&(d+=`- ${t.error}\n`),d&&(r+="<small>",r+="\n"+n.sdi_notice_details+":\n",r+=d,r+="</small>"),a.innerHTML=r.replace(/\n/g,"<br>"),window.requestAnimationFrame((()=>{document.body.appendChild(a),a.style.opacity="1","error"===o?a.querySelector(".close").addEventListener("click",(function(){a.remove(),location.reload()})):setTimeout((()=>{a.remove(),"success"!==o&&"notify"!==o||location.reload()}),i)}))}document.addEventListener("scroll",(function(){const e=document.querySelector(".wc_el_inv__header");e&&(window.scrollY>20?e.classList.add("is-scrolled"):e.classList.remove("is-scrolled"))})),window.addEventListener("load",(function(){var r=t("#store_invoice-description");r&&t(r).prev().addClass("store_invoice_title");var s,l,u,_,m,p,v,f,y,h,b,g,w,E,A=t("#store_invoice_transmitter-description");A&&t(A).prev().addClass("store_invoice_transmitter_title");t("a.edit_address").on("click",(function(e){t(".order_data_column > p").addClass("hide")})),s=c("tab"),(l=document.querySelectorAll("#toplevel_page_wc_el_inv-options-page ul.wp-submenu li a"))&&l.forEach((function(e){e.parentElement.classList.contains("wp-first-item")&&e.parentElement.remove(),e.parentElement.classList.remove("current"),-1!==e.getAttribute("href").indexOf(s)&&e.parentElement.classList.add("current")})),function(){var e=document.getElementById("wc_el_inv-settings-number_next_invoice");if(e){""!==e.value&&e.setAttribute("disabled","disabled"),o("edit_invoice_next_number",e,"afterend");var t=document.getElementById("edit_invoice_next_number");t.addEventListener("click",(function(){t.hasAttribute("readonly")||(t.style.display="none",e.removeAttribute("disabled"),i("save_invoice_next_number",e,"afterend"))}))}}(),function(){var e=document.getElementById("wc_el_inv-settings-number_next_receipt");if(e){""!==e.value&&e.setAttribute("disabled","disabled"),o("edit_receipt_next_number",e,"afterend");var t=document.getElementById("edit_receipt_next_number");t.addEventListener("click",(function(){t.hasAttribute("readonly")||(t.style.display="none",e.removeAttribute("disabled"),i("save_receipt_next_number",e,"afterend"))}))}}(),function(){var e=document.getElementById("wc_el_inv-settings-number_next_credit_note");if(e){""!==e.value&&e.setAttribute("disabled","disabled"),o("edit_credit_note_next_number",e,"afterend");var t=document.getElementById("edit_credit_note_next_number");t.addEventListener("click",(function(){t.hasAttribute("readonly")||(t.style.display="none",e.removeAttribute("disabled"),i("save_credit_note_next_number",e,"afterend"))}));var n=document.getElementById("wc_el_inv-settings-number_next_credit_note_receipt");if(n){""!==n.value&&n.setAttribute("disabled","disabled"),o("edit_credit_note_receipt_next_number",n,"afterend");var a=document.getElementById("edit_credit_note_receipt_next_number");a.addEventListener("click",(function(){t.hasAttribute("readonly")||(a.style.display="none",n.removeAttribute("disabled"),i("save_credit_note_receipt_next_number",n,"afterend"))}))}}}(),function(){var t=document.querySelectorAll(".wc_el_inv-order_fields");if(0!==t.length){e.forEach(t,(function(e){""!==e.value&&e.setAttribute("disabled","disabled")}));var n=document.querySelector(".wc_el_inv__general-order h3"),i=document.querySelector(".wc_el_inv__general-order--hidden-fields"),r=document.querySelector(".wc_el_inv__general-order--text-data");if(n&&i&&r){o("edit_invoice_next_number",n,"beforeend");var d=document.getElementById("edit_invoice_next_number");a("close_invoice_next_number",n);var c=document.getElementById("close_invoice_next_number");c.style.display="none",d.addEventListener("click",(function(){i.style.display="block",r.style.display="none",this.style.display="none",c.style.display="block",e.forEach(t,(function(e){e.removeAttribute("disabled")}))})),c.addEventListener("click",(function(){i.style.display="none",r.style.display="block",c.style.display="none",d.style.display="",e.forEach(t,(function(e){e.setAttribute("disabled","disabled")}))}))}}}(),u=document.querySelectorAll(".wc_el_inv__refund-invoice[data-order_refund_id]"),0!==(_=document.querySelectorAll(".wc_el_inv-order_fields")).length&&(e.forEach(_,(function(e){""!==e.value&&e.setAttribute("disabled","disabled")})),e.forEach(u,(function(t,n){var i=t.querySelector(".wc_el_inv__refund-invoice td h3"),r=t.querySelector(".wc_el_inv__refund-invoice--hidden-fields"),d=t.querySelector(".wc_el_inv__refund-invoice--text-data");if(i&&r&&d){o("edit_refund_invoice_next_number-"+n,i,"beforeend");var c=document.getElementById("edit_refund_invoice_next_number-"+n);a("close_refund_invoice_next_number-"+n,i);var s=document.getElementById("close_refund_invoice_next_number-"+n);s.style.display="none",s.addEventListener("click",(function(){r.style.display="none",d.style.display="block",s.style.display="none",c.style.display="",e.forEach(_,(function(e){e.setAttribute("disabled","disabled")}))})),c.addEventListener("click",(function(){r.style.display="block",d.style.display="none",this.style.display="none",s.style.display="block",e.forEach(_,(function(e){e.removeAttribute("disabled")}))}))}}))),function(){var t=[document.querySelector(".save-all-csv")];if([]!==t){var n=document.querySelector(".filter");if(n){var o=n.getAttribute("href");n.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var t=d(o);t&&(window.location=t)}))}t&&e.forEach(t,(function(e){e&&e.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var t=d(e.target.href);window.open(t,"_blank")}))}))}}(),m=document.querySelector("#woocommerce-order-items .inside .refund-items"),(p=document.querySelectorAll("#order_refunds .actions .mark_trigger"))&&e.forEach(p,(function(e){e.classList.contains("mark_as_sent")&&m&&(m.setAttribute("disabled",!0),m.innerText=n.refund_item_disabled_text)})),v=document.querySelector("#woocommerce-order-items .inside input#refund_amount"),f=document.querySelector(".wc_el_inv__general-order .actions .mark_trigger"),v&&f&&f.classList.contains("mark_as_sent")&&(v.setAttribute("readonly",!0),v.insertAdjacentHTML("afterend",'<p id="readonly-info">'+n.refund_amount_read_only_info_text+"</p>")),y=document.getElementById("wc_el_inv_order_search"),h=document.querySelector(".wc_el_inv_order_search_trigger"),y&&h&&(y.addEventListener("change",(function(e){e.preventDefault(),e.stopImmediatePropagation(),"dev"===n.mode&&console.log("search:",y.value),""!==y.value?window.location=h.href+"&order_search="+y.value:window.location=h.href+"&order_search"})),h.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation(),"dev"===n.mode&&console.log("search:",y.value),""===y.value?alert(n.search_by_id):window.location=h.href+"&order_search="+y.value}))),(b=document.getElementById("action_bulk"))&&b.addEventListener("change",(function(t){var o=t.target.value,i=document.querySelectorAll('input[name="pop-invoice[]"]'),a=!1;if(e.forEach(i,(function(e){!0===e.checked&&(a=e.checked)})),a||""===o){var r=!1;if("sent"===o?window.confirm(n.invoice_sent_confirm)&&(r=!0):"no_sent"===o&&window.confirm(n.invoice_undo_confirm)&&(r=!0),r){var d=document.getElementById("wp-list-table-invoice-form");if(d){var c=document.createElement("input");c.setAttribute("type","hidden"),c.setAttribute("id","bulk-sent"),c.setAttribute("name","bulk-sent"),c.setAttribute("value",o),d.appendChild(c),d.submit()}}}else alert(n.bulk_invoice_cb)})),(g=document.querySelectorAll(".doc-type-input"))&&e.forEach(g,(function(e){e.addEventListener("change",(function(e){this.parentElement.parentElement.parentElement.querySelectorAll(".choice_type--current")[0].setAttribute("value",this.value)}))})),(w=document.querySelectorAll(".action-endpoint"))&&e.forEach(w,(function(e){e.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var t=this.parentElement.querySelector(".choice_type--current"),n=this.href;t&&(n=n+"&choice_type="+t.value),window.open(n)}))})),function(){if("undefined"!=typeof inlineEditPost){var e=inlineEditPost.edit;inlineEditPost.edit=function(n){e.apply(this,arguments);var o=0;if("object"==typeof n&&(o=parseInt(this.getId(n))),o>0){var i=t("#edit-"+o),a=t("#post-"+o),r=!!t(".column-reverse_charge > *",a).prop("checked");t(':input[name="active_reverse_charge"]',i).prop("checked",r)}}}}(),function(){var e=document.querySelectorAll(".nav_section_advanced#wc_output_fields_nav ul li a");function t(t){e.forEach((function(e){e.getAttribute("href")==="#"+t?e.classList.add("active"):e.classList.remove("active")}));var n=document.querySelector(".wc_el_inv-form .form-table tr.import_collections"),o=document.querySelector(".wc_el_inv-form .form-table tr.export_collections"),i=document.querySelector(".wc_el_inv-form .form-table tr.reverse_charge"),a=document.querySelector(".wc_el_inv-form .form-table tr.automatic_sending"),r=document.querySelector(".wc_el_inv-form .form-table tr.pop_webhook");"reverse-charge"===t&&(o&&(o.style.display="none"),n&&(n.style.display="none"),i&&(i.style.display="table-row"),a&&(a.style.display="none"),r&&(r.style.display="none")),"import-export"===t&&(o&&(o.style.display="table-row"),n&&(n.style.display="table-row"),i&&(i.style.display="none"),a&&(a.style.display="none"),r&&(r.style.display="none")),"automatic-sending"===t&&(o&&(o.style.display="none"),n&&(n.style.display="none"),i&&(i.style.display="none"),a&&(a.style.display="table-row"),r&&(r.style.display="none")),"webhook"===t&&(o&&(o.style.display="none"),n&&(n.style.display="none"),i&&(i.style.display="none"),a&&(a.style.display="none"),r&&(r.style.display="table-row"))}e&&("free"===n.user_level?t("automatic-sending"):"IT"!==n.shop_country?t("webhook"):"#reverse-charge"!==window.location.hash&&window.location.hash?"#import-export"===window.location.hash?t("import-export"):"#automatic-sending"===window.location.hash?t("automatic-sending"):"#webhook"===window.location.hash&&t("webhook"):t("reverse-charge"),e.forEach((function(n){n.addEventListener("click",(function(o){e.forEach((function(e){e.classList.remove("active")})),n.classList.add("active"),t(o.target.hash.replace("#",""))}))})))}(),(E=document.querySelectorAll(".wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_digits input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .reset_numerations input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .suffix_invoice input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .suffix_year input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .suffix_year_format input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_invoice input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_receipt input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .numeration_credit_note input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_credit_note input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .numeration_credit_note_receipt input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table .number_next_credit_note_receipt input,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_invoice_next_number,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_receipt_next_number,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_credit_note_next_number,\n.wc_el_inv-fic-addon.current-setting_section_numeration .form-table #edit_credit_note_receipt_next_number"))&&E.forEach((function(e){e.setAttribute("disabled","disabled"),e.setAttribute("readonly","readonly")})),function(){var e=document.querySelectorAll(".rc-nature-select");function t(e,t){var o=e.options[e.selectedIndex].text.split(" - "),i=2===o.length?o[0]:"",a=2===o.length?o[1]:"",r=null;("N7"===i&&(a="IVA assolta in altro stato UE (vendite a distanza ex art. 40 c. 3 e 4 e art. 41 c. 1 lett. b, DL 331/93; prestazione di servizi di telecomunicazioni, teleradiodiffusione ed elettronici ex art. 7-sexies lett. f, g, art. 74-sexies DPR 633/72)"),"wc_el_inv-ue_private_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-ue_private_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-ue_company_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-ue_company_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-ue_company_no_vies_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-ue_company_no_vies_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-extra_ue_private_nature_code"===e.getAttribute("id")?(r=document.getElementById("wc_el_inv-extra_ue_private_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))):"wc_el_inv-extra_ue_company_nature_code"===e.getAttribute("id")&&(r=document.getElementById("wc_el_inv-extra_ue_company_normative_reference"),t&&("Personalizzato"===a?(r.setAttribute("value",""),r.value=""):(r.setAttribute("value",a),r.value=a))),r)&&(r.closest("tr").style.display="Personalizzato"===a?"table-row":"none");"dev"===n.mode&&console.log("CODE:",i,"REF:",a)}e.length>0&&e.forEach((function(e){t(e,!1),e.addEventListener("change",(function(n){t(e,!0)}))}))}(),t(document).ajaxComplete((function(){var e=document.querySelector("#woocommerce-order-items .inside input#refund_amount"),t=document.querySelector(".wc_el_inv__general-order .actions .mark_trigger");e&&t&&(!e.hasAttribute("readonly")&&t.classList.contains("mark_as_sent")?(e.setAttribute("readonly",!0),e.insertAdjacentHTML("afterend",'<p id="readonly-info">'+n.refund_amount_read_only_info_text+"</p>")):e.hasAttribute("readonly")&&!t.classList.contains("mark_as_sent")&&e.removeAttribute("readonly"))}))})),document.body.classList.add("disable-clicks"),document.addEventListener("DOMContentLoaded",(function(){var o;(o=document.querySelectorAll(".mark_trigger"))&&e.forEach(o,(function(e){e.addEventListener("click",(function(e){e.preventDefault(),e.stopImmediatePropagation();var o=!1;this.classList.contains("mark_as_sent")?window.confirm(n.invoice_sent_confirm)&&(o=!0):this.classList.contains("mark_undo")&&window.confirm(n.invoice_undo_confirm)&&(o=!0),o&&t.ajax({url:n.ajax_url,method:"POST",cache:!1,data:{action:"markInvoice",action_url:this.href,nonce:n.ajax_nonce},beforeSend:function(){}.bind(this),complete:function(e,t){"dev"===n.mode&&console.log(e,t)}.bind(this),error:function(e,t,n){console.warn("markInvoice "+n,t)}.bind(this),success:function(e){console.log(e),window.location.reload()}.bind(this)})}))})),document.body.classList.remove("disable-clicks")})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".sdi_pop_actions a.api-action.create").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".sdi_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;this.setAttribute("disabled","disabled");let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"createAndSendInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".sdi_pop_actions a.api-action.notifications").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".sdi_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"notificationsInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,null,e.success?"notify":"error")})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".sdi_pop_actions a.api-action.preserve").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".sdi_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"preserveInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,null,e.success?"preserve":"error")})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".peppol_pop_actions a.api-action.create").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".peppol_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;this.setAttribute("disabled","disabled");let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"createAndSendUblInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".peppol_pop_actions a.api-action.state").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".peppol_pop_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id,r=o.dataset.uuid;let d=document.getElementById("loading-overlay");d||(d=document.createElement("span"),d.id="loading-overlay",document.body.appendChild(d)),d.classList.remove("hidden"),d.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"stateInvoice",method:"ajax",provider:i,id:a,uuid:r})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,null,e.success?"success":"notify")})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{d.classList.remove("loading"),d.classList.add("hidden")}))}))})),document.querySelectorAll(".sdi_pop_actions a.api-action.confirm-timeout-uuid, .peppol_pop_actions a.api-action.confirm-timeout-uuid").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.dataset.provider,i=e.dataset.id,a=e.dataset.channel,r=e.dataset.documentType||"invoice",d=e.closest(".sdi_pop_actions, .peppol_pop_actions"),c=d?d.querySelector(".native-timeout-uuid-input"):null,l=c?c.value.trim():"";if(!l)return void s(n.timeout_uuid_required,null,"error");this.setAttribute("disabled","disabled");let u=document.getElementById("loading-overlay");u||(u=document.createElement("span"),u.id="loading-overlay",document.body.appendChild(u)),u.classList.remove("hidden"),u.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"confirmNativeSendWithUuid",provider:o,id:i,channel:a,document_type:r,uuid:l})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),u.classList.remove("loading"),u.classList.add("hidden")}))}))})),document.querySelectorAll(".sdi_pop_actions a.api-action.cancel-timeout-pending, .peppol_pop_actions a.api-action.cancel-timeout-pending").forEach((function(e){e.addEventListener("click",(function(t){if(t.preventDefault(),t.stopImmediatePropagation(),!window.confirm(n.timeout_cancel_confirm))return;const o=e.dataset.provider,i=e.dataset.id,a=e.dataset.channel;this.setAttribute("disabled","disabled");let r=document.getElementById("loading-overlay");r||(r=document.createElement("span"),r.id="loading-overlay",document.body.appendChild(r)),r.classList.remove("hidden"),r.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"cancelNativeTimeoutPending",provider:o,id:i,channel:a})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),r.classList.remove("loading"),r.classList.add("hidden")}))}))}))})),document.addEventListener("DOMContentLoaded",(function(){document.querySelectorAll(".pop_to_webhook_actions a.api-action.send-webhook").forEach((function(e){e.addEventListener("click",(function(t){t.preventDefault(),t.stopImmediatePropagation();const o=e.closest(".pop_to_webhook_actions").querySelector("a.api-action");if(!o)return;const i=o.dataset.provider,a=o.dataset.id;this.setAttribute("disabled","disabled");let r=document.getElementById("loading-overlay");r||(r=document.createElement("span"),r.id="loading-overlay",document.body.appendChild(r)),r.classList.remove("hidden"),r.classList.add("loading"),fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"sendWebhook",method:"ajax",provider:i,id:a})}).then((e=>e.json())).then((e=>{"dev"===n.mode&&console.log("Response:",e),s(e.data.message,e.data.raw,e.success?"success":"error",6e3)})).catch((e=>{console.error("AJAX error:",e),s(e.message,null,"error")})).finally((()=>{this.removeAttribute("disabled"),r.classList.remove("loading"),r.classList.add("hidden")}))}))}))}));let l=0,u=n.count_filtered;function _(e){localStorage.setItem("downloadOffset",e),localStorage.setItem("downloadStarted","1")}function m(){localStorage.removeItem("downloadOffset"),localStorage.removeItem("downloadStarted")}function p(){l>=u?m():t.ajax({url:n.ajax_url,method:"POST",data:{action:"processFileBatch",offset:l,limit:5},success:function(e){if(e.success){e.data.total&&(u=e.data.total),l+=5,l>u&&(l=u),_(l),t(".pop-progress span").remove();let n=Math.min(l/u*100,100);t("#progress-bar").css("width",n+"%").text(Math.round(n)+"%"),e.data.done?(m(),t(this).removeClass("disabled"),t("#progress-bar").css("width","100%").text("100%"),window.location.href=e.data.zip_url):p()}else alert("Errore: "+e.data.message)}})}t(document).ready((function(){const e={offset:parseInt(localStorage.getItem("downloadOffset")||"0"),started:"1"===localStorage.getItem("downloadStarted")};e.started&&(l=e.offset,p()),t("#start-download-btn").on("click",(function(e){t(this).addClass("disabled"),e.preventDefault(),e.stopImmediatePropagation(),t(".pop-progress span").text(n.start_download),l=0,_(l),t("#progress-bar").css("width","0%"),p()}))}));let v=document.getElementById("change_api_key");v&&v.addEventListener("click",(()=>{let e=document.querySelector(".wc_el_inv-form table.form-table .license_key");if(!e)return;let t=e.classList.contains("hidden");e.classList.toggle("hidden");let n=e.querySelector('td label[for="wc_el_inv-settings-license_key"] input[type="text"]');n&&(t?n.removeAttribute("readonly"):n.setAttribute("readonly","readonly"),document.getElementById("save_license_key")||i("save_license_key",n,"afterend"))}))}(window._,window.jQuery,window.wc_el_inv_admin); -
woopop-electronic-invoice-free/trunk/changelog.txt
r3460902 r3463326 1 = 6.7.0 - 17/02/2026 = 2 Added: DB advisory lock helpers based on MySQL/MariaDB GET_LOCK/RELEASE_LOCK to prevent concurrent duplicate create/send/upload actions. 3 Fixed: Fatture in Cloud send flow now requires explicit docID and wfc-issued_send_document=false; removed implicit docID fallback from stored document meta. 4 Fixed: Fatture in Cloud create flow now uses atomic lock guard to avoid duplicate issued documents under concurrent Ajax/Cron execution. 5 Fixed: Added channel guards to SDI/PEPPOL/FIC status callbacks so native/addon flows run only when their integration channel is effectively active. 6 Fixed: Hardened _invoice_sent writes across channels (SDI native, PEPPOL, Aruba, SDI via PEC, FIC) by requiring minimum success/coherence conditions before setting sent. 7 Fixed: Added recurring cron cleanup for inactive/disabled upload schedules (FIC, Aruba, SDI via PEC) to avoid orphan scheduled events. 8 Fixed: Fatture table invoice number rendering now only shows dismiss icon when both order_number_invoice and formatted number are missing, with recovery from formatted where possible. 9 Added: Native SDI/PEPPOL timeout-pending flow with explicit admin recovery actions in order list (Confirm UUID / Cancel timeout). 10 Added: New admin AJAX actions confirmNativeSendWithUuid and cancelNativeTimeoutPending to finalize or clear timeout-pending state safely. 11 Added: Native timeout helper set (meta keys/channel prefix/timeout detection) and centralized timeout pending metadata management. 12 Fixed: SDI and PEPPOL create/send flows now mark timeout-pending on timeout-like provider responses and clear pending state on successful UUID save. 13 Fixed: SDI and PEPPOL create/send error responses now include readable provider detail extracted from raw payload data when available. 14 Change: Timeout pending UUID input in native actions UI is now full-width with explicit spacing before recovery buttons. 15 Fixed: getNextInvoiceNumber now aligns stale next numeration values to max(next, last_assigned+1) for the active series, preventing duplicates and backward numbering. 16 Added: series-aware duplicate check now uses WooCommerce order queries (HPOS compatible), comparing type/prefix/year context against the latest assigned order. 17 Added: provider-level series hooks for PMPro and Cozmos addons via wc_el_inv-next_number_already_assigned and wc_el_inv-next_number_last_assigned_for_series. 18 Added: Extended unit test coverage for timeout helpers, native error detail extraction, resolver hardening edge cases, and wrapper/adapter behavior. 19 Fixed: Resolver hardening for invalid filter outputs (channels status/priority), empty priorities, non-string priority entries, and normalized shop country handling. 20 1 21 = 6.6.2 - 13/02/2026 = 2 22 Fixed: WooCommerce Blocks checkout now reliably saves POP additional billing fields into order meta during checkout completion. -
woopop-electronic-invoice-free/trunk/inc/filtersAdmin.php
r3444920 r3463326 434 434 'priority' => 10, 435 435 ), 436 array( 437 'filter' => 'wp_ajax_confirmNativeSendWithUuid', 438 'callback' => '\\WcElectronInvoice\\InvoiceApi::confirmNativeSendWithUuid', 439 'priority' => 10, 440 ), 441 array( 442 'filter' => 'wp_ajax_cancelNativeTimeoutPending', 443 'callback' => '\\WcElectronInvoice\\InvoiceApi::cancelNativeTimeoutPending', 444 'priority' => 10, 445 ), 436 446 437 447 /** -
woopop-electronic-invoice-free/trunk/inc/localizeScripts.php
r3460902 r3463326 130 130 WC_EL_INV_TEXTDOMAIN 131 131 ), 132 'timeout_uuid_required' => esc_html__( 133 'UUID is required.', 134 WC_EL_INV_TEXTDOMAIN 135 ), 136 'timeout_cancel_confirm' => esc_html__( 137 'Confirm only if POP support has explicitly verified that the document was NOT created/sent. This action re-enables send and may create duplicates if verification is incorrect.', 138 WC_EL_INV_TEXTDOMAIN 139 ), 132 140 ), 133 141 'wc_el_inv_integration' => array( -
woopop-electronic-invoice-free/trunk/inc/wc/filtersAdmin.php
r3382415 r3463326 282 282 'filter' => 'woocommerce_order_status_changed', 283 283 'callback' => function ($orderID, $from, $to) { 284 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_SDIPOP)) { 285 return; 286 } 287 284 288 \WcElectronInvoice\InvoiceApi::changeStatus($orderID, $from, $to); 289 }, 290 'priority' => 10, 291 'accepted_args' => 3, 292 ), 293 294 /** 295 * PEPPOL via POP 296 * Cron Jobs on order status change 297 */ 298 array( 299 'filter' => 'woocommerce_order_status_changed', 300 'callback' => function ($orderID, $from, $to) { 301 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_PEPPOL)) { 302 return; 303 } 304 305 \WcElectronInvoice\PeppolApi::changeStatus($orderID, $from, $to); 285 306 }, 286 307 'priority' => 10, -
woopop-electronic-invoice-free/trunk/inc/wc/filtersFront.php
r3460902 r3463326 235 235 ), 236 236 'callback' => function ($orderID) { 237 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_SDIPOP)) { 238 return; 239 } 240 237 241 \WcElectronInvoice\InvoiceApi::autoCompleted($orderID); 242 }, 243 'priority' => 10, 244 'accepted_args' => 1, 245 ), 246 247 /** 248 * PEPPOL via POP 249 * Cron Jobs on order auto completed 250 */ 251 array( 252 'filter' => array( 253 'woocommerce_payment_complete', 254 'woocommerce_payment_complete_order_status_completed', 255 'woocommerce_payment_complete_order_status_processing', 256 'woocommerce_order_status_completed', 257 'woocommerce_order_status_processing', 258 ), 259 'callback' => function ($orderID) { 260 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_PEPPOL)) { 261 return; 262 } 263 264 \WcElectronInvoice\PeppolApi::autoCompleted($orderID); 238 265 }, 239 266 'priority' => 10, -
woopop-electronic-invoice-free/trunk/index.php
r3460902 r3463326 7 7 * Description: POP automatically configures your e-commerce to comply with European tax regulations. Your e-commerce can generate electronic invoices in XML format and, thanks to our APIs, automatically transmit them to your accounting software and tax authorities. 8 8 * 9 * Version: 6. 6.29 * Version: 6.7.0 10 10 * Author: POP 11 11 * Author URI: https://popapi.io/ … … 52 52 define('WC_EL_INV_NAME', 'POP Electronic Invoice'); 53 53 define('WC_EL_INV_TEXTDOMAIN', 'el-inv'); 54 define('WC_EL_INV_VERSION', '6. 6.2');54 define('WC_EL_INV_VERSION', '6.7.0'); 55 55 define('WC_EL_INV_VERSION_CLASS', str_replace('.', '_', WC_EL_INV_VERSION)); 56 56 define('WC_EL_INV_PLUGIN_DIR', basename(plugin_dir_path(__FILE__))); -
woopop-electronic-invoice-free/trunk/languages/el-inv-es_ES.po
r3460146 r3463326 3 3 "Project-Id-Version: POP Electronic Invoice\n" 4 4 "POT-Creation-Date: 2025-12-11 10:11+0100\n" 5 "PO-Revision-Date: 2026-02-1 1 11:00+0100\n"5 "PO-Revision-Date: 2026-02-16 13:57+0100\n" 6 6 "Last-Translator: \n" 7 7 "Language-Team: Angelo Giammarresi - [email protected]\n" … … 3317 3317 msgstr "Referencia normativa para el reverse charge guardada con este pedido." 3318 3318 3319 msgid "Action not allowed: another integration channel is active." 3320 msgstr "Acción no permitida: hay otro canal de integración activo." 3321 3322 msgid "Action skipped: another send/create process is already running for this order." 3323 msgstr "Acción omitida: ya se está ejecutando otro proceso de envío/creación para este pedido." 3324 3319 3325 #~ msgid "Scopri come" 3320 3326 #~ msgstr "Descubre cómo" -
woopop-electronic-invoice-free/trunk/languages/el-inv-fr_FR.po
r3460146 r3463326 3 3 "Project-Id-Version: POP Electronic Invoice\n" 4 4 "POT-Creation-Date: 2025-12-11 10:08+0100\n" 5 "PO-Revision-Date: 2026-02-1 1 11:00+0100\n"5 "PO-Revision-Date: 2026-02-16 13:57+0100\n" 6 6 "Last-Translator: Automatically generated\n" 7 7 "Language-Team: none\n" … … 3400 3400 msgid "Invoice delivered successfully" 3401 3401 msgstr "Facture livrée avec succès" 3402 3403 msgid "Action not allowed: another integration channel is active." 3404 msgstr "Action non autorisée : un autre canal d’intégration est actif." 3405 3406 msgid "Action skipped: another send/create process is already running for this order." 3407 msgstr "Action ignorée : un autre processus d’envoi/création est déjà en cours pour cette commande." -
woopop-electronic-invoice-free/trunk/languages/el-inv-it_IT.po
r3460146 r3463326 3 3 "Project-Id-Version: POP Electronic Invoice\n" 4 4 "POT-Creation-Date: 2025-12-11 10:09+0100\n" 5 "PO-Revision-Date: 2026-02-1 1 11:00+0100\n"5 "PO-Revision-Date: 2026-02-16 13:57+0100\n" 6 6 "Last-Translator: Alfio <[email protected]>\n" 7 7 "Language-Team: \n" … … 2899 2899 #: src/Xml/BuildXml.php:189 2900 2900 msgid "State" 2901 msgstr " Provincia"2901 msgstr "Stato" 2902 2902 2903 2903 #: src/Utils/Countries.php:1121 src/Utils/Countries.php:1200 … … 3432 3432 msgid "Invoice delivered successfully" 3433 3433 msgstr "Fattura consegnata con successo" 3434 3435 msgid "Action not allowed: another integration channel is active." 3436 msgstr "Azione non consentita: un altro canale di integrazione è attivo." 3437 3438 msgid "Action skipped: another send/create process is already running for this order." 3439 msgstr "Azione saltata: un altro processo di invio/creazione è già in esecuzione per questo ordine." 3434 3440 3435 3441 #~ msgid "" -
woopop-electronic-invoice-free/trunk/readme.md
r3461234 r3463326 4 4 * **Requires at least:** 4.6 5 5 * **Tested up to:** 6.9 6 * **Stable tag:** 6. 6.26 * **Stable tag:** 6.7.0 7 7 * **Requires PHP:** 5.6 8 8 * **License:** GPLv2 or later … … 179 179 180 180 ## Changelog 181 = 6.7.0 - 17/02/2026 = 182 * Add: DB advisory lock helpers based on MySQL/MariaDB GET_LOCK/RELEASE_LOCK to prevent concurrent duplicate create/send/upload actions. 183 * Fix: Fatture in Cloud send flow now requires explicit docID and wfc-issued_send_document=false; removed implicit docID fallback from stored document meta. 184 * Fix: Fatture in Cloud create flow now uses atomic lock guard to avoid duplicate issued documents under concurrent Ajax/Cron execution. 185 * Fix: Added channel guards to SDI/PEPPOL/FIC status callbacks so native/addon flows run only when their integration channel is effectively active. 186 * Fix: Hardened _invoice_sent writes across channels (SDI native, PEPPOL, Aruba, SDI via PEC, FIC) by requiring minimum success/coherence conditions before setting sent. 187 * Fix: Added recurring cron cleanup for inactive/disabled upload schedules (FIC, Aruba, SDI via PEC) to avoid orphan scheduled events. 188 * Fix: Fatture table invoice number rendering now only shows dismiss icon when both order_number_invoice and formatted number are missing, with recovery from formatted where possible. 189 * Add: Native SDI/PEPPOL timeout-pending flow with explicit admin recovery actions in order list (Confirm UUID / Cancel timeout). 190 * Add: New admin AJAX actions confirmNativeSendWithUuid and cancelNativeTimeoutPending to finalize or clear timeout-pending state safely. 191 * Add: Native timeout helper set (meta keys/channel prefix/timeout detection) and centralized timeout pending metadata management. 192 * Fix: SDI and PEPPOL create/send flows now mark timeout-pending on timeout-like provider responses and clear pending state on successful UUID save. 193 * Fix: SDI and PEPPOL create/send error responses now include readable provider detail extracted from raw payload data when available. 194 * Change: Timeout pending UUID input in native actions UI is now full-width with explicit spacing before recovery buttons. 195 * Fix: getNextInvoiceNumber now aligns stale next numeration values to max(next, last_assigned+1) for the active series, preventing duplicates and backward numbering. 196 * Add: series-aware duplicate check now uses WooCommerce order queries (HPOS compatible), comparing type/prefix/year context against the latest assigned order. 197 * Add: provider-level series hooks for PMPro and Cozmos addons via wc_el_inv-next_number_already_assigned and wc_el_inv-next_number_last_assigned_for_series. 198 * Add: Extended unit test coverage for timeout helpers, native error detail extraction, resolver hardening edge cases, and wrapper/adapter behavior. 199 * Fix: Resolver hardening for invalid filter outputs (channels status/priority), empty priorities, non-string priority entries, and normalized shop country handling. 200 181 201 ### 6.6.2 - 13/02/2026 182 202 * Fix: WooCommerce Blocks checkout now reliably saves POP additional billing fields into order meta during checkout completion. -
woopop-electronic-invoice-free/trunk/readme.txt
r3461234 r3463326 4 4 Requires at least: 4.6 5 5 Tested up to: 6.9 6 Stable tag: 6. 6.26 Stable tag: 6.7.0 7 7 Requires PHP: 5.6 8 8 License: GPLv2 or later … … 179 179 180 180 == Changelog == 181 = 6.7.0 - 17/02/2026 = 182 * Add: DB advisory lock helpers based on MySQL/MariaDB GET_LOCK/RELEASE_LOCK to prevent concurrent duplicate create/send/upload actions. 183 * Fix: Fatture in Cloud send flow now requires explicit docID and wfc-issued_send_document=false; removed implicit docID fallback from stored document meta. 184 * Fix: Fatture in Cloud create flow now uses atomic lock guard to avoid duplicate issued documents under concurrent Ajax/Cron execution. 185 * Fix: Added channel guards to SDI/PEPPOL/FIC status callbacks so native/addon flows run only when their integration channel is effectively active. 186 * Fix: Hardened _invoice_sent writes across channels (SDI native, PEPPOL, Aruba, SDI via PEC, FIC) by requiring minimum success/coherence conditions before setting sent. 187 * Fix: Added recurring cron cleanup for inactive/disabled upload schedules (FIC, Aruba, SDI via PEC) to avoid orphan scheduled events. 188 * Fix: Fatture table invoice number rendering now only shows dismiss icon when both order_number_invoice and formatted number are missing, with recovery from formatted where possible. 189 * Add: Native SDI/PEPPOL timeout-pending flow with explicit admin recovery actions in order list (Confirm UUID / Cancel timeout). 190 * Add: New admin AJAX actions confirmNativeSendWithUuid and cancelNativeTimeoutPending to finalize or clear timeout-pending state safely. 191 * Add: Native timeout helper set (meta keys/channel prefix/timeout detection) and centralized timeout pending metadata management. 192 * Fix: SDI and PEPPOL create/send flows now mark timeout-pending on timeout-like provider responses and clear pending state on successful UUID save. 193 * Fix: SDI and PEPPOL create/send error responses now include readable provider detail extracted from raw payload data when available. 194 * Change: Timeout pending UUID input in native actions UI is now full-width with explicit spacing before recovery buttons. 195 * Fix: getNextInvoiceNumber now aligns stale next numeration values to max(next, last_assigned+1) for the active series, preventing duplicates and backward numbering. 196 * Add: series-aware duplicate check now uses WooCommerce order queries (HPOS compatible), comparing type/prefix/year context against the latest assigned order. 197 * Add: provider-level series hooks for PMPro and Cozmos addons via wc_el_inv-next_number_already_assigned and wc_el_inv-next_number_last_assigned_for_series. 198 * Add: Extended unit test coverage for timeout helpers, native error detail extraction, resolver hardening edge cases, and wrapper/adapter behavior. 199 * Fix: Resolver hardening for invalid filter outputs (channels status/priority), empty priorities, non-string priority entries, and normalized shop country handling. 200 181 201 = 6.6.2 - 13/02/2026 = 182 202 * Fix: WooCommerce Blocks checkout now reliably saves POP additional billing fields into order meta during checkout completion. -
woopop-electronic-invoice-free/trunk/requires.php
r3323757 r3463326 31 31 32 32 require_once \WcElectronInvoice\Plugin::getPluginDirPath('/src/Functions/Utils.php'); 33 require_once \WcElectronInvoice\Plugin::getPluginDirPath('/src/Functions/Lock.php'); 33 34 require_once \WcElectronInvoice\Plugin::getPluginDirPath('/src/Functions/Conditionals.php'); 34 35 require_once \WcElectronInvoice\Plugin::getPluginDirPath('/src/Functions/Invoice.php'); -
woopop-electronic-invoice-free/trunk/src/Admin/XmlOrderListTable.php
r3445528 r3463326 747 747 748 748 $invoiceNumber = $order->get_order_number_invoice(); 749 if (empty($invoiceNumber) ) {749 if (empty($invoiceNumber) && empty($formattedNumber)) { 750 750 // Not invoice number 751 751 return '<span class="color_red dashicons dashicons-dismiss"></span>'; -
woopop-electronic-invoice-free/trunk/src/Functions/CloudApi.php
r3382415 r3463326 186 186 $method = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null; 187 187 188 if (('createAndSendInvoice' === $action || 'createAndSendUblInvoice' === $action) && ('cronjob' === $method || 'ajax' === $method)) { 188 if ( 189 ('createAndSendInvoice' === $action || 'createAndSendUblInvoice' === $action) && 190 \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'ajax']) 191 ) { 189 192 $response = json_decode($body); 190 193 $responseIntegrationViaPop = $response; … … 327 330 $action = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null; 328 331 $method = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null; 329 if (('createAndSendInvoice' === $action || 'createAndSendUblInvoice' === $action) && ('cronjob' === $method || 'ajax' === $method)) { 332 if ( 333 ('createAndSendInvoice' === $action || 'createAndSendUblInvoice' === $action) && 334 \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'ajax']) 335 ) { 330 336 return json_encode($responseIntegrationViaPop); 331 337 } -
woopop-electronic-invoice-free/trunk/src/Functions/Invoice.php
r3460146 r3463326 51 51 $maxAttempts = 5; // How many times to retry in case of conflict 52 52 $attempt = 0; 53 $scanLimit = (int)apply_filters('wc_el_inv-next_number_scan_limit', 50, $optionKeyName); 54 if ($scanLimit < 1) { 55 $scanLimit = 1; 56 } 53 57 54 58 while ($attempt < $maxAttempts) { … … 98 102 if ($currentValue < 1) { 99 103 $currentValue = 1; 104 } 105 106 $lastAssigned = getLastAssignedInvoiceNumberForCurrentSeries($optionKeyName); 107 /** 108 * Allow providers/integrations to contribute last assigned number for the active series. 109 * 110 * @param int $lastAssigned 111 * @param string $optionKeyName 112 */ 113 $lastAssigned = (int)apply_filters('wc_el_inv-next_number_last_assigned_for_series', $lastAssigned, $optionKeyName); 114 if ($lastAssigned > 0 && $currentValue <= $lastAssigned) { 115 $currentValue = $lastAssigned + 1; 116 } 117 118 $checked = 0; 119 while ( 120 $checked < $scanLimit 121 && 122 isInvoiceNumberAlreadyAssigned($currentValue, $optionKeyName) 123 ) { 124 $currentValue++; 125 $checked++; 126 } 127 128 if ( 129 $checked >= $scanLimit 130 && 131 isInvoiceNumberAlreadyAssigned($currentValue, $optionKeyName) 132 ) { 133 error_log("[WcElectronInvoice] Unable to align next invoice number for {$optionKeyName}: scan limit reached at {$currentValue}"); 134 135 return false; 100 136 } 101 137 … … 135 171 136 172 /** 173 * Check if candidate invoice number is already assigned. 174 * 175 * @param int $number 176 * @param string $optionKeyName 177 * 178 * @return bool 179 */ 180 function isInvoiceNumberAlreadyAssigned(int $number, string $optionKeyName): bool 181 { 182 if ($number < 1) { 183 return false; 184 } 185 186 global $wpdb; 187 188 $exists = false; 189 190 if ('number_next_invoice' === $optionKeyName || 'number_next_receipt' === $optionKeyName) { 191 $lastAssigned = getLastAssignedInvoiceNumberForCurrentSeries($optionKeyName); 192 /** 193 * Allow providers/integrations to contribute last assigned number for the active series. 194 * 195 * @param int $lastAssigned 196 * @param string $optionKeyName 197 */ 198 $lastAssigned = (int)apply_filters('wc_el_inv-next_number_last_assigned_for_series', $lastAssigned, $optionKeyName); 199 $exists = ($lastAssigned > 0 && $lastAssigned === $number); 200 201 if (! $exists && isset($wpdb) && is_object($wpdb) && isset($wpdb->postmeta, $wpdb->posts) && ! function_exists('wc_get_orders')) { 202 $typeWhere = ''; 203 if ('number_next_receipt' === $optionKeyName) { 204 $typeWhere = " AND type_meta.meta_value = 'receipt' "; 205 } else { 206 $typeWhere = " AND (type_meta.meta_id IS NULL OR type_meta.meta_value = '' OR type_meta.meta_value = 'invoice') "; 207 } 208 209 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 210 $exists = (bool)$wpdb->get_var( 211 $wpdb->prepare( 212 "SELECT inv_meta.meta_id 213 FROM {$wpdb->postmeta} inv_meta 214 INNER JOIN {$wpdb->posts} posts ON posts.ID = inv_meta.post_id 215 LEFT JOIN {$wpdb->postmeta} type_meta ON type_meta.post_id = posts.ID AND type_meta.meta_key = '_billing_choice_type' 216 WHERE inv_meta.meta_key = 'order_number_invoice' 217 AND CAST(inv_meta.meta_value AS UNSIGNED) = %d 218 AND posts.post_type = 'shop_order' 219 AND posts.post_status <> 'trash' 220 {$typeWhere} 221 LIMIT 1", 222 $number 223 ) 224 ); 225 } 226 } 227 228 /** 229 * Filter duplicate-number lookup so integrations/providers can extend check. 230 * 231 * @param bool $exists 232 * @param int $number 233 * @param string $optionKeyName 234 */ 235 return (bool)apply_filters('wc_el_inv-next_number_already_assigned', $exists, $number, $optionKeyName); 236 } 237 238 /** 239 * Resolve last assigned invoice number for the current WooCommerce series context. 240 * 241 * @param string $optionKeyName 242 * 243 * @return int 244 */ 245 function getLastAssignedInvoiceNumberForCurrentSeries(string $optionKeyName): int 246 { 247 if (! function_exists('wc_get_orders') || ! function_exists('wc_get_order')) { 248 return 0; 249 } 250 251 $recentOrdersLimit = (int)apply_filters('wc_el_inv-next_number_recent_orders_limit', 200, $optionKeyName, 0); 252 if ($recentOrdersLimit < 1) { 253 $recentOrdersLimit = 1; 254 } 255 256 $ordersArgs = [ 257 'type' => 'shop_order', 258 'status' => 'any', 259 'limit' => $recentOrdersLimit, 260 'return' => 'ids', 261 'orderby' => 'date', 262 'order' => 'DESC', 263 ]; 264 265 /** 266 * Filter recent-orders query used to detect duplicate numbers. 267 * 268 * @param array $ordersArgs 269 * @param string $optionKeyName 270 * @param int $number 271 */ 272 $ordersArgs = apply_filters('wc_el_inv-next_number_recent_orders_args', $ordersArgs, $optionKeyName, 0); 273 274 $seriesContext = getNextNumberSeriesContext($optionKeyName); 275 $recentOrderIDs = wc_get_orders($ordersArgs); 276 if (! is_array($recentOrderIDs)) { 277 return 0; 278 } 279 280 foreach ($recentOrderIDs as $orderID) { 281 $order = is_object($orderID) ? $orderID : wc_get_order($orderID); 282 if (! is_object($order) || ! method_exists($order, 'get_meta')) { 283 continue; 284 } 285 286 $invoiceNumber = (int)$order->get_meta('order_number_invoice'); 287 if ($invoiceNumber < 1) { 288 continue; 289 } 290 291 $choiceType = (string)$order->get_meta('_billing_choice_type'); 292 $isReceipt = ('receipt' === $choiceType); 293 if ('number_next_receipt' === $optionKeyName && ! $isReceipt) { 294 continue; 295 } 296 if ('number_next_invoice' === $optionKeyName && $isReceipt) { 297 continue; 298 } 299 300 return isOrderInNextNumberSeries($order, $seriesContext) ? $invoiceNumber : 0; 301 } 302 303 return 0; 304 } 305 306 /** 307 * Build current series context (type/prefix/year token) for next-number alignment. 308 * 309 * @param string $optionKeyName 310 * 311 * @return array{numeration_type:string,prefix:string,suffix_year_enabled:bool,suffix_year_format:string,year_token:string} 312 */ 313 function getNextNumberSeriesContext(string $optionKeyName): array 314 { 315 $numerationType = ('number_next_receipt' === $optionKeyName) ? 'receipt' : 'invoice'; 316 $options = OptionPage::init(); 317 318 $prefix = (string)$options->getOptions("prefix_{$numerationType}_number"); 319 $prefix = (string)apply_filters('wc_el_inv-next_number_series_prefix', $prefix, $optionKeyName, $numerationType); 320 321 $suffixYearEnabled = ('on' === (string)$options->getOptions('suffix_year_invoice_number')); 322 $suffixYearFormat = (string)$options->getOptions('suffix_year_format_invoice_number'); 323 if ('' === $suffixYearFormat) { 324 $suffixYearFormat = 'Y'; 325 } 326 327 $yearToken = $suffixYearEnabled ? date($suffixYearFormat) : ''; 328 329 return [ 330 'numeration_type' => $numerationType, 331 'prefix' => $prefix, 332 'suffix_year_enabled' => $suffixYearEnabled, 333 'suffix_year_format' => $suffixYearFormat, 334 'year_token' => $yearToken, 335 ]; 336 } 337 338 /** 339 * Check whether an order belongs to the active next-number series. 340 * 341 * @param mixed $order 342 * @param array $seriesContext 343 * 344 * @return bool 345 */ 346 function isOrderInNextNumberSeries($order, array $seriesContext): bool 347 { 348 if (! is_object($order)) { 349 return false; 350 } 351 352 $formattedPrefix = ''; 353 if ( 354 method_exists($order, 'get_meta') 355 && 356 method_exists($order, 'get_type') 357 && 358 method_exists($order, 'get_id') 359 ) { 360 $formatted = (string)$order->get_meta("{$order->get_type()}_formatted_number_{$order->get_id()}"); 361 if ('' === $formatted) { 362 $formatted = (string)$order->get_meta("shop_order_formatted_number_{$order->get_id()}"); 363 } 364 if (preg_match('/^\D*/u', $formatted, $matches)) { 365 $formattedPrefix = (string)$matches[0]; 366 } 367 } 368 369 if ((string)$seriesContext['prefix'] !== $formattedPrefix) { 370 return false; 371 } 372 373 if (! empty($seriesContext['suffix_year_enabled'])) { 374 $seriesYear = ''; 375 $date = null; 376 if (method_exists($order, 'get_date_completed')) { 377 $date = $order->get_date_completed(); 378 } 379 if (! $date && method_exists($order, 'get_date_created')) { 380 $date = $order->get_date_created(); 381 } 382 if ($date instanceof \DateTimeInterface) { 383 $seriesYear = $date->format((string)$seriesContext['suffix_year_format']); 384 } 385 if ($seriesYear !== (string)$seriesContext['year_token']) { 386 return false; 387 } 388 } 389 390 return true; 391 } 392 393 /** 137 394 * removeDuplicateMeta 138 395 * … … 257 514 $orderInvoiceNumber = $order->get_meta('order_number_invoice') ?? null; 258 515 if (! $orderInvoiceNumber) { 516 $recoveredNumber = recoverOrderInvoiceNumberFromFormatted($order, $numerationType); 517 if ($recoveredNumber > 0) { 518 $order->update_meta_data('order_number_invoice', $recoveredNumber); 519 setFormattedInvoiceNumber($order, $numerationType, true); 520 $order->save(); 521 $orderInvoiceNumber = $recoveredNumber; 522 } 523 } 524 if (! $orderInvoiceNumber) { 259 525 // Get next invoice number option. 260 526 $invoiceNumber = (int)getNextInvoiceNumber("number_next_{$numerationType}"); … … 272 538 $numerationType = apply_filters('wc_el_inv-invoice_number_next_numeration_type', $numerationType, $order); 273 539 274 if ( (! $checkSent || 'no_sent' === $checkSent) && (! $orderInvoiceNumber)) {540 if (! $orderInvoiceNumber) { 275 541 // Set invoice number for order. 276 542 $order->update_meta_data('order_number_invoice', $invoiceNumber); … … 374 640 $orderInvoiceNumber = $order->get_meta('order_number_invoice'); 375 641 if (! $orderInvoiceNumber) { 642 $recoveredNumber = recoverOrderInvoiceNumberFromFormatted($order, $numerationType); 643 if ($recoveredNumber > 0) { 644 $order->update_meta_data('order_number_invoice', $recoveredNumber); 645 setFormattedInvoiceNumber($order, $numerationType, true); 646 $order->save(); 647 $orderInvoiceNumber = $recoveredNumber; 648 } 649 } 650 if (! $orderInvoiceNumber) { 376 651 // Get next invoice number option. 377 652 $invoiceNumber = (int)getNextInvoiceNumber("number_next_{$numerationType}"); … … 388 663 $numerationType = apply_filters('wc_el_inv-invoice_number_next_numeration_type', $numerationType, $order); 389 664 390 if ( (! $checkSent || 'no_sent' === $checkSent) && (! $orderInvoiceNumber)) {665 if (! $orderInvoiceNumber) { 391 666 // Set invoice number for order. 392 667 $order->update_meta_data('order_number_invoice', $invoiceNumber); … … 635 910 636 911 /** 912 * Try to recover the raw invoice number from an existing formatted number. 913 * 914 * @param \WC_Order $order 915 * @param string $numerationType 916 * 917 * @return int 918 */ 919 function recoverOrderInvoiceNumberFromFormatted($order, $numerationType): int 920 { 921 $formattedNumber = $order->get_meta("{$order->get_type()}_formatted_number_{$order->get_id()}") ?? null; 922 if (! $formattedNumber) { 923 return 0; 924 } 925 926 $normalized = apply_filters('wc_el_inv-normalize_formatted_number', $formattedNumber, $order); 927 if (is_numeric($normalized) && (int)$normalized > 0) { 928 return (int)$normalized; 929 } 930 931 $candidate = (string)$formattedNumber; 932 $options = OptionPage::init(); 933 934 $prefix = $options->getOptions("prefix_{$numerationType}_number"); 935 $prefix = apply_filters("wc_el_inv-formatted_number_prefix_{$numerationType}", $prefix, $order); 936 if (is_string($prefix) && '' !== $prefix && str_starts_with($candidate, $prefix)) { 937 $candidate = substr($candidate, strlen($prefix)); 938 } 939 940 $suffix = $options->getOptions('suffix_invoice_number'); 941 $suffixYear = $options->getOptions('suffix_year_invoice_number'); 942 $suffixYearFormat = $options->getOptions('suffix_year_format_invoice_number') ?: 'Y'; 943 if ('on' === $suffixYear) { 944 $created = $order->has_status('completed') ? $order->get_date_completed() : $order->get_date_created(); 945 if (! $created instanceof \DateTimeInterface) { 946 $created = new \DateTime(); 947 } 948 $suffix = "/" . $created->format($suffixYearFormat); 949 } else { 950 $suffix = isset($suffix) && '' !== $suffix ? $suffix : ''; 951 } 952 $suffix = apply_filters('wc_el_inv-formatted_number_suffix_number', $suffix, $order); 953 if (is_string($suffix) && '' !== $suffix && str_ends_with($candidate, $suffix)) { 954 $candidate = substr($candidate, 0, -strlen($suffix)); 955 } 956 957 $candidate = trim($candidate); 958 959 if (ctype_digit($candidate) && (int)$candidate > 0) { 960 return (int)$candidate; 961 } 962 963 return 0; 964 } 965 966 /** 637 967 * setCreditNoteFallBackNumeration 638 968 * … … 685 1015 686 1016 return false; 1017 } 1018 1019 /** 1020 * Evaluate whether FIC send action can continue. 1021 * 1022 * @param int $docID 1023 * @param int $issuedDocumentID 1024 * @param mixed $issuedSendDocument 1025 * @param string|null $invoiceSent 1026 * 1027 * @return array{allowed:bool,reason:string} 1028 */ 1029 function evaluateFicSendDecision( 1030 int $docID, 1031 int $issuedDocumentID, 1032 $issuedSendDocument, 1033 ?string $invoiceSent 1034 ): array { 1035 if ($issuedDocumentID <= 0) { 1036 return ['allowed' => false, 'reason' => 'missing_issued_document']; 1037 } 1038 1039 if (normalizeBooleanMeta($issuedSendDocument)) { 1040 return ['allowed' => false, 'reason' => 'already_sent_fic']; 1041 } 1042 1043 if ($docID <= 0) { 1044 return ['allowed' => false, 'reason' => 'missing_doc_id']; 1045 } 1046 1047 if ($docID !== $issuedDocumentID) { 1048 return ['allowed' => false, 'reason' => 'doc_id_mismatch']; 1049 } 1050 1051 if ('sent' === (string)$invoiceSent) { 1052 return ['allowed' => false, 'reason' => 'already_sent_meta']; 1053 } 1054 1055 return ['allowed' => true, 'reason' => 'ok']; 1056 } 1057 1058 /** 1059 * Evaluate whether FIC create action can continue. 1060 * 1061 * @param bool $statusAllowed 1062 * @param bool $invoiceBlockedOnZero 1063 * @param mixed $existingDocumentID 1064 * 1065 * @return array{allowed:bool,reason:string} 1066 */ 1067 function evaluateFicCreateDecision( 1068 bool $statusAllowed, 1069 bool $invoiceBlockedOnZero, 1070 $existingDocumentID = null 1071 ): array { 1072 if (! $statusAllowed) { 1073 return ['allowed' => false, 'reason' => 'status_not_allowed']; 1074 } 1075 1076 if ($invoiceBlockedOnZero) { 1077 return ['allowed' => false, 'reason' => 'total_zero_not_allowed']; 1078 } 1079 1080 if (! empty($existingDocumentID)) { 1081 return ['allowed' => false, 'reason' => 'already_created']; 1082 } 1083 1084 return ['allowed' => true, 'reason' => 'ok']; 1085 } 1086 1087 /** 1088 * Evaluate behavior when a DB lock is required for an action. 1089 * 1090 * @param bool $lockAcquired 1091 * @param bool $isBackgroundJob 1092 * 1093 * @return array{continue:bool,respond_error:bool,reason:string} 1094 */ 1095 function evaluateLockBusyDecision(bool $lockAcquired, bool $isBackgroundJob): array 1096 { 1097 if ($lockAcquired) { 1098 return [ 1099 'continue' => true, 1100 'respond_error' => false, 1101 'reason' => 'ok', 1102 ]; 1103 } 1104 1105 if ($isBackgroundJob) { 1106 return [ 1107 'continue' => false, 1108 'respond_error' => false, 1109 'reason' => 'lock_busy_background_skip', 1110 ]; 1111 } 1112 1113 return [ 1114 'continue' => false, 1115 'respond_error' => true, 1116 'reason' => 'lock_busy_foreground_error', 1117 ]; 1118 } 1119 1120 /** 1121 * Map FIC send decision reason to normalized error metadata. 1122 * 1123 * @param string $reason 1124 * 1125 * @return array{code:int,message_key:string} 1126 */ 1127 function mapFicSendDecisionError(string $reason): array 1128 { 1129 $map = [ 1130 'missing_issued_document' => ['code' => 422, 'message_key' => 'missing_issued_document'], 1131 'missing_doc_id' => ['code' => 422, 'message_key' => 'missing_doc_id'], 1132 'doc_id_mismatch' => ['code' => 422, 'message_key' => 'doc_id_mismatch'], 1133 'already_sent_fic' => ['code' => 422, 'message_key' => 'already_sent'], 1134 'already_sent_meta' => ['code' => 422, 'message_key' => 'already_sent'], 1135 ]; 1136 1137 if (isset($map[$reason])) { 1138 return $map[$reason]; 1139 } 1140 1141 return ['code' => 422, 'message_key' => 'action_not_allowed']; 1142 } 1143 1144 /** 1145 * Map FIC create decision reason to normalized error metadata. 1146 * 1147 * @param string $reason 1148 * 1149 * @return array{code:int,message_key:string} 1150 */ 1151 function mapFicCreateDecisionError(string $reason): array 1152 { 1153 $map = [ 1154 'status_not_allowed' => ['code' => 501, 'message_key' => 'status_not_allowed'], 1155 'total_zero_not_allowed' => ['code' => 501, 'message_key' => 'total_zero_not_allowed'], 1156 'already_created' => ['code' => 422, 'message_key' => 'already_created'], 1157 ]; 1158 1159 if (isset($map[$reason])) { 1160 return $map[$reason]; 1161 } 1162 1163 return ['code' => 422, 'message_key' => 'action_not_allowed']; 1164 } 1165 1166 /** 1167 * Map lock-busy context to normalized error metadata. 1168 * 1169 * @param string $channel 1170 * @param string $action 1171 * 1172 * @return array{code:int,message_key:string} 1173 */ 1174 function mapLockBusyError(string $channel, string $action): array 1175 { 1176 $channel = strtolower(trim($channel)); 1177 $action = strtolower(trim($action)); 1178 $key = "{$channel}:{$action}"; 1179 1180 $map = [ 1181 'fic:create_invoice' => ['code' => 409, 'message_key' => 'action_skipped_create'], 1182 'fic:send_invoice' => ['code' => 409, 'message_key' => 'action_skipped_send'], 1183 'sdi:create_and_send' => ['code' => 409, 'message_key' => 'action_skipped_send_create'], 1184 'peppol:create_and_send' => ['code' => 409, 'message_key' => 'action_skipped_send_create'], 1185 'aruba:upload_invoice' => ['code' => 409, 'message_key' => 'action_skipped_upload'], 1186 'sdi_pec:upload_invoice' => ['code' => 409, 'message_key' => 'action_skipped_upload'], 1187 ]; 1188 1189 if (isset($map[$key])) { 1190 return $map[$key]; 1191 } 1192 1193 return ['code' => 409, 'message_key' => 'action_skipped']; 1194 } 1195 1196 /** 1197 * Map SDI/PEPPOL native create/send precondition reason to normalized metadata. 1198 * 1199 * @param string $channel 1200 * @param string $reason 1201 * 1202 * @return array{code:int,message_key:string} 1203 */ 1204 function mapNativeCreateAndSendPreconditionError(string $channel, string $reason): array 1205 { 1206 $channel = strtolower(trim($channel)); 1207 $reason = strtolower(trim($reason)); 1208 1209 $map = [ 1210 'channel_mismatch' => ['code' => 422, 'message_key' => 'action_not_allowed_channel'], 1211 'integration_inactive' => ['code' => 422, 'message_key' => 'action_not_allowed'], 1212 'action_invalid' => ['code' => 422, 'message_key' => 'action_not_valid'], 1213 'uuid_already_exists' => ['code' => 422, 'message_key' => 'uuid_exists'], 1214 'response_uuid_missing' => ['code' => 422, 'message_key' => 'response_uuid_missing'], 1215 ]; 1216 1217 if ('sdi' !== $channel && 'peppol' !== $channel) { 1218 return ['code' => 422, 'message_key' => 'action_not_allowed']; 1219 } 1220 1221 if (isset($map[$reason])) { 1222 return $map[$reason]; 1223 } 1224 1225 return ['code' => 422, 'message_key' => 'action_not_allowed']; 1226 } 1227 1228 /** 1229 * Map Aruba/SDI-PEC upload precondition reason to normalized metadata. 1230 * 1231 * @param string $channel 1232 * @param string $reason 1233 * 1234 * @return array{code:int,message_key:string} 1235 */ 1236 function mapUploadPreconditionError(string $channel, string $reason): array 1237 { 1238 $channel = strtolower(trim($channel)); 1239 $reason = strtolower(trim($reason)); 1240 1241 if ('aruba' !== $channel && 'sdi_pec' !== $channel) { 1242 return ['code' => 501, 'message_key' => 'upload_error']; 1243 } 1244 1245 $map = [ 1246 'xml_invalid' => ['code' => 501, 'message_key' => 'xml_invalid'], 1247 'invalid_email_recipient' => ['code' => 501, 'message_key' => 'invalid_email_recipient'], 1248 'upload_failed' => ['code' => 501, 'message_key' => 'upload_failed'], 1249 'empty_upload_filename' => ['code' => 501, 'message_key' => 'empty_upload_filename'], 1250 ]; 1251 1252 if (isset($map[$reason])) { 1253 return $map[$reason]; 1254 } 1255 1256 return ['code' => 501, 'message_key' => 'upload_error']; 1257 } 1258 1259 /** 1260 * Build Aruba upload payload in a deterministic format. 1261 * 1262 * @param string $xmlBase64 1263 * 1264 * @return array<string,mixed> 1265 */ 1266 function buildArubaUploadPayload(string $xmlBase64): array 1267 { 1268 return [ 1269 'dataFile' => $xmlBase64, 1270 'credential' => '', 1271 'domain' => '', 1272 'senderPIVA' => '', 1273 'skipExtraSchema' => false, 1274 ]; 1275 } 1276 1277 /** 1278 * Build integration metadata for native create/send XML generation. 1279 * 1280 * @param string $channel 1281 * 1282 * @return array<string,mixed> 1283 */ 1284 function buildNativeCreateIntegrationMeta(string $channel): array 1285 { 1286 $channel = strtolower(trim($channel)); 1287 1288 if ('sdi' === $channel) { 1289 return [ 1290 'integration' => [ 1291 'use' => 'sdi-via-pop', 1292 'action' => 'create', 1293 ], 1294 ]; 1295 } 1296 1297 if ('peppol' === $channel) { 1298 return [ 1299 'integration' => [ 1300 'use' => 'peppol-via-pop', 1301 'action' => 'create', 1302 ], 1303 ]; 1304 } 1305 1306 return []; 1307 } 1308 1309 /** 1310 * Build stable FIC document display options. 1311 * 1312 * @return array<string,mixed> 1313 */ 1314 function buildFicDocumentDisplayOptions(): array 1315 { 1316 return [ 1317 'show_totals' => 'all', 1318 'show_paypal_button' => false, 1319 'show_notification_button' => false, 1320 'accompanying_invoice' => false, 1321 'show_payment_method' => true, 1322 'show_payments' => true, 1323 ]; 1324 } 1325 1326 /** 1327 * Build FIC ei_data payload with optional fields. 1328 * 1329 * @param string $odDate 1330 * @param string $invoiceDate 1331 * @param string|null $creditNoteInvoiceNumber 1332 * @param array|null $bankData ['iban' => string, 'name' => string, 'beneficiary' => string] 1333 * 1334 * @return array<string,mixed> 1335 */ 1336 function buildFicEiDataPayload( 1337 string $odDate, 1338 string $invoiceDate, 1339 ?string $creditNoteInvoiceNumber = null, 1340 ?array $bankData = null 1341 ): array { 1342 $payload = [ 1343 'od_date' => $odDate, 1344 'invoice_date' => $invoiceDate, 1345 ]; 1346 1347 if (is_string($creditNoteInvoiceNumber) && '' !== trim($creditNoteInvoiceNumber)) { 1348 $payload['invoice_number'] = $creditNoteInvoiceNumber; 1349 } 1350 1351 if (is_array($bankData) && ! empty($bankData['iban'])) { 1352 $payload['bank_iban'] = (string)$bankData['iban']; 1353 $payload['bank_name'] = (string)($bankData['name'] ?? ''); 1354 $payload['bank_beneficiary'] = (string)($bankData['beneficiary'] ?? ''); 1355 } 1356 1357 return $payload; 1358 } 1359 1360 /** 1361 * Check if current execution mode is a background one. 1362 * 1363 * @param string|null $mode 1364 * @param array $backgroundModes 1365 * 1366 * @return bool 1367 */ 1368 function isBackgroundExecutionMode(?string $mode, array $backgroundModes = ['cronjob', 'feCronJobUploadRequest']): bool 1369 { 1370 if (! is_string($mode) || '' === $mode) { 1371 return false; 1372 } 1373 1374 return in_array($mode, $backgroundModes, true); 1375 } 1376 1377 /** 1378 * Normalize meta/option values to boolean consistently. 1379 * 1380 * @param mixed $value 1381 * 1382 * @return bool 1383 */ 1384 function normalizeBooleanMeta($value): bool 1385 { 1386 return filter_var($value, FILTER_VALIDATE_BOOLEAN); 1387 } 1388 1389 /** 1390 * Resolve integration document type from order type. 1391 * 1392 * @param string $orderType 1393 * 1394 * @return string 1395 */ 1396 function resolveDocumentTypeFromOrderType(string $orderType): string 1397 { 1398 return 'shop_order' === $orderType ? 'invoice' : 'credit_note'; 1399 } 1400 1401 /** 1402 * Check if current execution should return immediate (foreground) errors. 1403 * 1404 * @param string|null $mode 1405 * @param array $backgroundModes 1406 * 1407 * @return bool 1408 */ 1409 function shouldRespondWithImmediateError( 1410 ?string $mode, 1411 array $backgroundModes = ['cronjob', 'feCronJobUploadRequest'] 1412 ): bool { 1413 return ! isBackgroundExecutionMode($mode, $backgroundModes); 1414 } 1415 1416 /** 1417 * Build UUID meta key for native integrations. 1418 * 1419 * @param string $channelPrefix 1420 * @param string $documentType 1421 * 1422 * @return string 1423 */ 1424 function buildUuidMetaKey(string $channelPrefix, string $documentType): string 1425 { 1426 $channelPrefix = trim($channelPrefix); 1427 $documentType = trim($documentType); 1428 1429 return "{$channelPrefix}-{$documentType}_uuid"; 1430 } 1431 1432 /** 1433 * Build notifications meta key for native integrations. 1434 * 1435 * @param string $channelPrefix 1436 * @param string $documentType 1437 * 1438 * @return string 1439 */ 1440 function buildNotificationsMetaKey(string $channelPrefix, string $documentType): string 1441 { 1442 $channelPrefix = trim($channelPrefix); 1443 $documentType = trim($documentType); 1444 1445 return "{$channelPrefix}-{$documentType}_notifications"; 1446 } 1447 1448 /** 1449 * Build meta key used to mark native timeout pending verification. 1450 * 1451 * @return string 1452 */ 1453 function buildNativeTimeoutPendingMetaKey(): string 1454 { 1455 return '_native_timeout_pending_verification'; 1456 } 1457 1458 /** 1459 * Build meta key for native timeout pending channel. 1460 * 1461 * @return string 1462 */ 1463 function buildNativeTimeoutPendingChannelMetaKey(): string 1464 { 1465 return '_native_timeout_pending_channel'; 1466 } 1467 1468 /** 1469 * Build meta key for native timeout pending timestamp. 1470 * 1471 * @return string 1472 */ 1473 function buildNativeTimeoutPendingTimestampMetaKey(): string 1474 { 1475 return '_native_timeout_pending_at'; 1476 } 1477 1478 /** 1479 * Build meta key for native timeout pending document type. 1480 * 1481 * @return string 1482 */ 1483 function buildNativeTimeoutPendingDocumentTypeMetaKey(): string 1484 { 1485 return '_native_timeout_pending_document_type'; 1486 } 1487 1488 /** 1489 * Resolve native channel meta prefix from logical channel name. 1490 * 1491 * @param string $channel 1492 * 1493 * @return string 1494 */ 1495 function resolveNativeChannelPrefix(string $channel): string 1496 { 1497 $channel = strtolower(trim($channel)); 1498 if ('sdi' === $channel) { 1499 return 'sdi_pop'; 1500 } 1501 if ('peppol' === $channel) { 1502 return 'peppol_pop'; 1503 } 1504 1505 return ''; 1506 } 1507 1508 /** 1509 * Detect timeout-like failures in mixed error payloads (string/array/object). 1510 * 1511 * @param mixed $value 1512 * 1513 * @return bool 1514 */ 1515 function isLikelyTimeoutError($value): bool 1516 { 1517 if (is_string($value)) { 1518 $haystack = strtolower($value); 1519 $needles = [ 1520 'timed out', 1521 'timeout', 1522 'curl error 28', 1523 'operation timed out', 1524 'connection timed out', 1525 'http request failed', 1526 ]; 1527 1528 foreach ($needles as $needle) { 1529 if (false !== strpos($haystack, $needle)) { 1530 return true; 1531 } 1532 } 1533 1534 return false; 1535 } 1536 1537 if (is_array($value) || is_object($value)) { 1538 $encoded = function_exists('wp_json_encode') ? wp_json_encode($value) : json_encode($value); 1539 if (is_string($encoded) && '' !== $encoded) { 1540 return isLikelyTimeoutError($encoded); 1541 } 1542 } 1543 1544 return false; 1545 } 1546 1547 /** 1548 * Extract readable native integration error detail from mixed raw payloads. 1549 * 1550 * @param mixed $raw 1551 * 1552 * @return string 1553 */ 1554 function extractNativeErrorDetail($raw): string 1555 { 1556 $status = ''; 1557 $detail = ''; 1558 $title = ''; 1559 $msg = ''; 1560 1561 if (is_object($raw)) { 1562 $status = isset($raw->code) ? (string)$raw->code : ''; 1563 $msg = isset($raw->message) && is_string($raw->message) ? trim($raw->message) : ''; 1564 1565 if (isset($raw->data) && is_object($raw->data)) { 1566 $status = '' !== $status ? $status : (isset($raw->data->status) ? (string)$raw->data->status : ''); 1567 $detail = isset($raw->data->detail) && is_string($raw->data->detail) ? trim($raw->data->detail) : ''; 1568 $title = isset($raw->data->title) && is_string($raw->data->title) ? trim($raw->data->title) : ''; 1569 } 1570 } elseif (is_array($raw)) { 1571 $status = isset($raw['code']) ? (string)$raw['code'] : ''; 1572 $msg = isset($raw['message']) && is_string($raw['message']) ? trim($raw['message']) : ''; 1573 1574 if (isset($raw['data']) && is_array($raw['data'])) { 1575 $status = '' !== $status ? $status : (isset($raw['data']['status']) ? (string)$raw['data']['status'] : ''); 1576 $detail = isset($raw['data']['detail']) && is_string($raw['data']['detail']) ? trim($raw['data']['detail']) : ''; 1577 $title = isset($raw['data']['title']) && is_string($raw['data']['title']) ? trim($raw['data']['title']) : ''; 1578 } 1579 } 1580 1581 $genericMessages = [ 1582 'risposta non valida', 1583 'invalid response', 1584 'an error occurred', 1585 ]; 1586 1587 $msgLower = strtolower($msg); 1588 $titleLower = strtolower($title); 1589 foreach ($genericMessages as $genericMessage) { 1590 if ($msgLower === $genericMessage) { 1591 $msg = ''; 1592 } 1593 if ($titleLower === $genericMessage) { 1594 $title = ''; 1595 } 1596 } 1597 1598 $bestText = $detail ?: ($msg ?: $title); 1599 if ('' === $bestText) { 1600 return ''; 1601 } 1602 1603 return '' !== $status ? "[{$status}] {$bestText}" : $bestText; 1604 } 1605 1606 /** 1607 * Minimal consistency check for marking invoice as sent using a UUID. 1608 * 1609 * @param mixed $uuid 1610 * 1611 * @return bool 1612 */ 1613 function canMarkInvoiceSentWithUuid($uuid): bool 1614 { 1615 return is_string($uuid) && '' !== trim($uuid); 1616 } 1617 1618 /** 1619 * Minimal consistency check for marking invoice as sent using an upload filename. 1620 * 1621 * @param mixed $uploadFileName 1622 * 1623 * @return bool 1624 */ 1625 function canMarkInvoiceSentWithUploadFileName($uploadFileName): bool 1626 { 1627 return is_string($uploadFileName) && '' !== trim($uploadFileName); 1628 } 1629 1630 /** 1631 * Minimal consistency check for marking invoice as sent in FIC flow. 1632 * 1633 * @param bool $responseValid 1634 * @param int $docID 1635 * @param mixed $alreadySentOnFic 1636 * 1637 * @return bool 1638 */ 1639 function canMarkInvoiceSentWithFicSend(bool $responseValid, int $docID, $alreadySentOnFic = false): bool 1640 { 1641 return $responseValid && 1642 $docID > 0 && 1643 ! normalizeBooleanMeta($alreadySentOnFic); 687 1644 } 688 1645 -
woopop-electronic-invoice-free/trunk/src/Functions/Utils.php
r3409381 r3463326 1146 1146 $action = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null; 1147 1147 $method = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null; 1148 if (('createAndSendInvoice' === $action || 'createAndSendUblInvoice' === $action) && ('cronjob' === $method || 'ajax' === $method)) { 1148 if ( 1149 ('createAndSendInvoice' === $action || 'createAndSendUblInvoice' === $action) && 1150 \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob', 'ajax']) 1151 ) { 1149 1152 return $createXml->create($buildXml->xmlData); 1150 1153 } -
woopop-electronic-invoice-free/trunk/src/Functions/Webhooks.php
r3341137 r3463326 152 152 $action = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null; 153 153 $method = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null; 154 $shouldRespond = \WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob']); 154 155 155 156 if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'growth') || ! $usePoPWebhookId) { 156 if ( 'cronjob' !== $method) {157 if ($shouldRespond) { 157 158 wp_send_json_error([ 158 159 'message' => esc_html__('Action not allowed.', … … 166 167 // I check the action parameter if it does not match I get an error 167 168 if ('sendWebhook' !== $action) { 168 if ( 'cronjob' !== $method) {169 if ($shouldRespond) { 169 170 wp_send_json_error([ 170 171 'message' => esc_html__('Action not valid.', … … 195 196 ) { 196 197 197 if ( 'cronjob' !== $method) {198 if ($shouldRespond) { 198 199 wp_send_json_error([ 199 200 'message' => sprintf(esc_html__('Invalid action: Webhook has already been sent, please delete "%s" data to resend', … … 237 238 ])); 238 239 239 if ( 'cronjob' !== $method) {240 if ($shouldRespond) { 240 241 wp_send_json_error([ 241 242 'message' => esc_html__('Webhook Response > Invalid Json.', … … 264 265 true) . "\n"); 265 266 266 if ( 'cronjob' !== $method) {267 if ($shouldRespond) { 267 268 wp_send_json_error([ 268 269 'message' => $response->message, … … 276 277 } 277 278 278 if ( 'cronjob' !== $method) {279 if ($shouldRespond) { 279 280 wp_send_json_success([ 280 281 'message' => $response->message, … … 337 338 function webHookScheduledCron( 338 339 int $orderID, 339 string $from = null,340 string $to = null,340 ?string $from = null, 341 ?string $to = null, 341 342 string $provider = 'woocommerce' 342 343 ): bool { -
woopop-electronic-invoice-free/trunk/src/Integrations.php
r3460146 r3463326 9 9 use function WcElectronInvoice\Functions\getActiveAddon; 10 10 use function WcElectronInvoice\Functions\getListKeyValue; 11 use WcElectronInvoice\WooCommerce\Fields\GeneralFields; 11 12 12 13 if (! defined('ABSPATH')) { … … 19 20 class Integrations 20 21 { 22 public const CHANNEL_NONE = 'none'; 23 public const CHANNEL_FIC = 'fic'; 24 public const CHANNEL_ARUBA = 'aruba'; 25 public const CHANNEL_SDIPEC = 'sdi_pec'; 26 public const CHANNEL_SDIPOP = 'sdi_pop'; 27 public const CHANNEL_PEPPOL = 'peppol'; 28 21 29 /** 22 30 * $addonPlugins … … 90 98 91 99 return false; 100 } 101 102 /** 103 * Resolve the active invoicing channel. 104 * 105 * The result is deterministic and exclusive: 106 * one channel or "none". 107 * 108 * @return string 109 */ 110 public static function resolveActiveInvoiceChannel(): string 111 { 112 $channels = [ 113 self::CHANNEL_FIC => (bool)get_option('wc_el_inv-addon-fattureincloud'), 114 self::CHANNEL_ARUBA => (bool)get_option('wc_el_inv-addon-aruba'), 115 self::CHANNEL_SDIPEC => (bool)get_option('wc_el_inv-addon-sdi-pec'), 116 self::CHANNEL_SDIPOP => (bool)get_option('wc_el_inv-addon-sdi-via-pop', false) && 117 'active' === get_option('wc_el_inv-active_sdipop_integration'), 118 self::CHANNEL_PEPPOL => (bool)get_option('wc_el_inv-addon-peppol-via-pop', false) && 119 'active' === get_option('wc_el_inv-active_peppol_integration'), 120 ]; 121 122 $defaultChannels = $channels; 123 $channels = apply_filters('wc_el_inv-invoice_channels_status', $channels); 124 if (! is_array($channels)) { 125 $channels = $defaultChannels; 126 } 127 128 if (! empty($channels[self::CHANNEL_SDIPOP]) && ! empty($channels[self::CHANNEL_PEPPOL])) { 129 $shopCountry = strtoupper(trim((string)GeneralFields::getGeneralInvoiceOptionCountryState())); 130 131 if (in_array($shopCountry, ['IT', 'SM'], true)) { 132 $channels[self::CHANNEL_PEPPOL] = false; 133 } elseif (defined('WC_EL_INV_UBL_COUNTRIES') && 134 is_array(WC_EL_INV_UBL_COUNTRIES) && 135 in_array($shopCountry, WC_EL_INV_UBL_COUNTRIES, true) 136 ) { 137 $channels[self::CHANNEL_SDIPOP] = false; 138 } else { 139 $channels[self::CHANNEL_PEPPOL] = false; 140 } 141 } 142 143 $defaultPriority = [ 144 self::CHANNEL_FIC, 145 self::CHANNEL_ARUBA, 146 self::CHANNEL_SDIPEC, 147 self::CHANNEL_SDIPOP, 148 self::CHANNEL_PEPPOL, 149 ]; 150 151 $priority = apply_filters('wc_el_inv-invoice_channel_priority', $defaultPriority); 152 153 if (! is_array($priority) || empty($priority)) { 154 $priority = $defaultPriority; 155 } 156 157 foreach ($priority as $channel) { 158 if (! is_string($channel) || '' === $channel) { 159 continue; 160 } 161 if (isset($channels[$channel]) && (bool)$channels[$channel]) { 162 return (string)$channel; 163 } 164 } 165 166 return self::CHANNEL_NONE; 167 } 168 169 /** 170 * Check if the provided channel is the resolved active one. 171 * 172 * @param string $channel 173 * 174 * @return bool 175 */ 176 public static function isInvoiceChannelActive(string $channel): bool 177 { 178 return self::resolveActiveInvoiceChannel() === $channel; 179 } 180 181 /** 182 * Check if at least one invoicing channel is active. 183 * 184 * @return bool 185 */ 186 public static function hasAnyInvoiceChannelActive(): bool 187 { 188 return self::CHANNEL_NONE !== self::resolveActiveInvoiceChannel(); 92 189 } 93 190 -
woopop-electronic-invoice-free/trunk/src/InvoiceApi.php
r3409381 r3463326 37 37 38 38 return (object)[ 39 'active_plugin_integration' => $integrationPluginActive,39 'active_plugin_integration' => (bool) $integrationPluginActive, 40 40 'active_integration' => 'active' === $sdiViaPOP, 41 41 'active_signature' => 'active' === $sdiViaPOPSignature, … … 167 167 168 168 // Check if uuid exist 169 $uuid = $order->get_meta( 'sdi_pop-invoice_uuid');169 $uuid = $order->get_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', 'invoice')); 170 170 if ($uuid) { 171 171 // Log … … 251 251 $checkSent = $order->get_meta("_invoice_sent", true); 252 252 // Check if uuid exist 253 $uuid = $order->get_meta( 'sdi_pop-invoice_uuid');253 $uuid = $order->get_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', 'invoice')); 254 254 // Check WC_Order instance 255 255 $wcOrderClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order'); … … 331 331 } 332 332 333 $uuid = \WcElectronInvoice\Functions\getPostMeta("sdi_pop-invoice_uuid", null, $id, true, 'order', 333 $uuid = \WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', 334 'invoice'), null, $id, true, 'order', 334 335 $provider) ?: null; 335 336 … … 337 338 $provider) ?: null; 338 339 339 $notificationsData = \WcElectronInvoice\Functions\getPostMeta("sdi_pop-invoice_notifications", null, $id, true, 340 $notificationsData = \WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 341 'invoice'), null, $id, true, 340 342 'order', 341 343 $provider) ?: null; … … 344 346 'order', 345 347 $provider) ?: null; 348 $timeoutPending = \WcElectronInvoice\Functions\normalizeBooleanMeta( 349 \WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey(), 350 null, 351 $id, 352 true, 353 'order', 354 $provider) 355 ); 356 $timeoutChannel = (string)(\WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey(), 357 null, 358 $id, 359 true, 360 'order', 361 $provider) ?: ''); 362 $timeoutDocType = (string)(\WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey(), 363 null, 364 $id, 365 true, 366 'order', 367 $provider) ?: 'invoice'); 368 $orderNumberInvoice = (string)(\WcElectronInvoice\Functions\getPostMeta('order_number_invoice', 369 null, 370 $id, 371 true, 372 'order', 373 $provider) ?: ''); 346 374 347 375 $send = __('Send invoice', WC_EL_INV_TEXTDOMAIN); … … 352 380 $output .= '<div class="sdi_pop_actions">'; 353 381 $output .= sprintf('<hr><h4>%s:</h4>', __('SdI native actions', WC_EL_INV_TEXTDOMAIN)); 382 if ($timeoutPending && 'sdi' === $timeoutChannel) { 383 $output .= sprintf( 384 '<div class="pop-notice notice-warning timeout-native-pending"><p>%s</p><p>%s%s</p></div>', 385 esc_html__('Send not confirmed due to provider timeout. Do not resend: verify with POP support and enter document UUID.', 386 WC_EL_INV_TEXTDOMAIN), 387 esc_html__('Invoice number: ', WC_EL_INV_TEXTDOMAIN), 388 esc_html('' !== $orderNumberInvoice ? $orderNumberInvoice : 'N/A') 389 ); 390 $output .= sprintf( 391 '<input type="text" class="native-timeout-uuid-input" style="display:block;width:100%%;min-width:100%%;margin:0 0 8px 0;" id="native_timeout_uuid_%1$s" placeholder="%2$s" />', 392 esc_attr($id), 393 esc_attr__('Enter verified UUID', WC_EL_INV_TEXTDOMAIN) 394 ); 395 $output .= sprintf( 396 '<a href="javascript:;" data-provider="%1$s" data-id="%2$s" data-channel="sdi" data-document-type="%3$s" class="api-action confirm-timeout-uuid button button-primary" id="api_confirm_timeout_uuid_%2$s">%4$s</a>', 397 esc_attr($provider), 398 esc_attr($id), 399 esc_attr($timeoutDocType), 400 esc_html__('Confirm UUID', WC_EL_INV_TEXTDOMAIN) 401 ); 402 $output .= sprintf( 403 '<a href="javascript:;" data-provider="%1$s" data-id="%2$s" data-channel="sdi" class="api-action cancel-timeout-pending button button-secondary" id="api_cancel_timeout_%2$s">%3$s</a>', 404 esc_attr($provider), 405 esc_attr($id), 406 esc_html__('Cancel timeout', WC_EL_INV_TEXTDOMAIN) 407 ); 408 $output .= '</div>'; 409 410 return $output; 411 } 412 354 413 if ($uuid) { 355 414 // Created … … 440 499 $method = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null; 441 500 501 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_SDIPOP)) { 502 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 503 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi', 504 'channel_mismatch'); 505 wp_send_json_error([ 506 'message' => esc_html__('Action not allowed: another integration channel is active.', 507 WC_EL_INV_TEXTDOMAIN), 508 'code' => $preconditionError['code'], 509 ]); 510 } 511 512 return; 513 } 514 442 515 if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'growth') || 443 516 (! $getSdiPOPIntegrationData->active_plugin_integration || 444 517 ! $getSdiPOPIntegrationData->active_integration) 445 518 ) { 446 if ('cronjob' !== $method) { 519 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 520 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi', 521 'integration_inactive'); 447 522 wp_send_json_error([ 448 523 'message' => esc_html__('Action not allowed.', 449 524 WC_EL_INV_TEXTDOMAIN), 525 'code' => $preconditionError['code'], 450 526 ]); 451 527 } … … 456 532 // I check the action parameter if it does not match I get an error 457 533 if ('createAndSendInvoice' !== $action) { 458 if ('cronjob' !== $method) { 534 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 535 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi', 536 'action_invalid'); 459 537 wp_send_json_error([ 460 538 'message' => esc_html__('Action not valid.', 461 539 WC_EL_INV_TEXTDOMAIN), 540 'code' => $preconditionError['code'], 462 541 ]); 463 542 } … … 467 546 468 547 $order = OrderQuery::instance()->getProviderOrder((int)$id, $provider); 469 $type = $order->get_type() === 'shop_order' ? 'invoice' : 'credit_note';470 $metaKey = "sdi_pop-{$type}_uuid";548 $type = \WcElectronInvoice\Functions\resolveDocumentTypeFromOrderType((string)$order->get_type()); 549 $metaKey = \WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', $type); 471 550 472 551 $uuid = $order->get_meta($metaKey); … … 476 555 \WcElectronInvoice\Functions\log("createAndSendInvoice > UUID exist: {$uuid}" . "\n"); 477 556 478 if ('cronjob' !== $method) { 557 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 558 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi', 559 'uuid_already_exists'); 479 560 wp_send_json_error([ 480 561 'message' => esc_html__("createAndSendInvoice - UUID exist: {$uuid}", 481 562 WC_EL_INV_TEXTDOMAIN), 563 'code' => $preconditionError['code'], 482 564 ]); 483 565 } … … 486 568 } 487 569 488 add_filter('wc_el_inv-params_filter_before_api_request', function ($params) { 489 $params['integration'] = [ 490 'use' => 'sdi-via-pop', 491 'action' => 'create', 492 ]; 493 494 return $params; 495 }); 496 497 $data = getXmlFromOrder($order); 498 $response = json_decode($data); 499 500 if (json_last_error() !== JSON_ERROR_NONE || ! is_object($response)) { 501 \WcElectronInvoice\Functions\log("createAndSendInvoice > check json response: Invalid response from the XML generator:" . "\n" . print_r($data, 502 true) . "\n"); 503 504 if ('cronjob' !== $method) { 570 $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('sdi', 'create_and_send', (int)$order->get_id(), 571 (string)$provider, $metaKey); 572 $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision( 573 \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0), 574 \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob']) 575 ); 576 if (! $lockDecision['continue']) { 577 if ($lockDecision['respond_error']) { 578 $lockError = \WcElectronInvoice\Functions\mapLockBusyError('sdi', 'create_and_send'); 505 579 wp_send_json_error([ 506 'message' => esc_html__(' createAndSendInvoice > check json response: Invalid response from the XML generator.',580 'message' => esc_html__('Action skipped: another send/create process is already running for this order.', 507 581 WC_EL_INV_TEXTDOMAIN), 508 ' raw' => $data,582 'code' => $lockError['code'], 509 583 ]); 510 584 } … … 513 587 } 514 588 515 if ( 516 ! property_exists($response, 'message') || 517 ! property_exists($response, 'data') || 518 ! property_exists($response, 'xml_string') 519 ) { 520 \WcElectronInvoice\Functions\log("createAndSendInvoice > check json property: Invalid response from the XML generator:" . "\n" . print_r($response, 521 true) . "\n"); 522 523 if ('cronjob' !== $method) { 524 wp_send_json_error([ 525 'message' => esc_html__('createAndSendInvoice > check json property: Invalid response from the XML generator.', 526 WC_EL_INV_TEXTDOMAIN), 527 'check' => 'json property', 528 'raw' => $response, 529 ]); 530 } 531 532 return; 533 } 534 535 if (! $response->success) { 536 \WcElectronInvoice\Functions\log("createAndSendInvoice > check success: Invalid response from the XML generator" . "\n" . print_r($response, 537 true) . "\n"); 538 539 if ('cronjob' !== $method) { 540 wp_send_json_error([ 541 'message' => esc_html__('createAndSendInvoice > check success: Invalid response from the XML generator.', 542 WC_EL_INV_TEXTDOMAIN), 543 'check' => 'success', 544 'raw' => $response, 545 ]); 546 } 547 548 return; 549 } 550 551 if ('credit_note' === $type && $order->get_parent_id()) { 552 $parent = OrderQuery::instance()->getProviderOrder((int)$order->get_parent_id(), $provider); 553 if ($parent) { 554 $parent->update_meta($metaKey, $response->data->uuid); 555 } 556 } else { 557 $order->update_meta($metaKey, $response->data->uuid); 558 } 559 560 $order->update_meta('_invoice_sent', 'sent'); 561 562 if ('cronjob' !== $method) { 589 try { 590 add_filter('wc_el_inv-params_filter_before_api_request', function ($params) { 591 $integrationMeta = \WcElectronInvoice\Functions\buildNativeCreateIntegrationMeta('sdi'); 592 if (! empty($integrationMeta['integration'])) { 593 $params['integration'] = $integrationMeta['integration']; 594 } 595 596 return $params; 597 }); 598 599 $data = getXmlFromOrder($order); 600 $response = json_decode($data); 601 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($data)) { 602 self::markNativeTimeoutPending($order, 'sdi', $type); 603 } 604 605 if (json_last_error() !== JSON_ERROR_NONE || ! is_object($response)) { 606 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($data)) { 607 self::markNativeTimeoutPending($order, 'sdi', $type); 608 } 609 \WcElectronInvoice\Functions\log("createAndSendInvoice > check json response: Invalid response from the XML generator:" . "\n" . print_r($data, 610 true) . "\n"); 611 612 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 613 wp_send_json_error([ 614 'message' => esc_html__('createAndSendInvoice > check json response: Invalid response from the XML generator.', 615 WC_EL_INV_TEXTDOMAIN), 616 'raw' => $data, 617 ]); 618 } 619 620 return; 621 } 622 623 if ( 624 ! property_exists($response, 'message') || 625 ! property_exists($response, 'data') || 626 ! property_exists($response, 'xml_string') 627 ) { 628 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($response)) { 629 self::markNativeTimeoutPending($order, 'sdi', $type); 630 } 631 \WcElectronInvoice\Functions\log("createAndSendInvoice > check json property: Invalid response from the XML generator:" . "\n" . print_r($response, 632 true) . "\n"); 633 634 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 635 wp_send_json_error([ 636 'message' => esc_html__('createAndSendInvoice > check json property: Invalid response from the XML generator.', 637 WC_EL_INV_TEXTDOMAIN), 638 'check' => 'json property', 639 'raw' => $response, 640 ]); 641 } 642 643 return; 644 } 645 646 if (! $response->success) { 647 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($response)) { 648 self::markNativeTimeoutPending($order, 'sdi', $type); 649 } 650 \WcElectronInvoice\Functions\log("createAndSendInvoice > check success: Invalid response from the XML generator" . "\n" . print_r($response, 651 true) . "\n"); 652 653 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 654 $message = esc_html__('createAndSendInvoice > check success: Invalid response from the XML generator.', 655 WC_EL_INV_TEXTDOMAIN); 656 $detail = \WcElectronInvoice\Functions\extractNativeErrorDetail($response); 657 if ('' !== $detail) { 658 $message .= ' ' . $detail; 659 } 660 wp_send_json_error([ 661 'message' => $message, 662 'check' => 'success', 663 'raw' => $response, 664 ]); 665 } 666 667 return; 668 } 669 670 $responseUuid = property_exists($response->data, 'uuid') ? (string)$response->data->uuid : ''; 671 if (! \WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($responseUuid)) { 672 \WcElectronInvoice\Functions\log("createAndSendInvoice > check uuid: Invalid response from the XML generator" . "\n" . print_r($response, 673 true) . "\n"); 674 675 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 676 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('sdi', 677 'response_uuid_missing'); 678 $message = esc_html__('createAndSendInvoice > check uuid: Invalid response from the XML generator.', 679 WC_EL_INV_TEXTDOMAIN); 680 $detail = \WcElectronInvoice\Functions\extractNativeErrorDetail($response); 681 if ('' !== $detail) { 682 $message .= ' ' . $detail; 683 } 684 wp_send_json_error([ 685 'message' => $message, 686 'check' => 'uuid', 687 'raw' => $response, 688 'code' => $preconditionError['code'], 689 ]); 690 } 691 692 return; 693 } 694 695 if ('credit_note' === $type && $order->get_parent_id()) { 696 $parent = OrderQuery::instance()->getProviderOrder((int)$order->get_parent_id(), $provider); 697 if ($parent) { 698 $parent->update_meta($metaKey, $responseUuid); 699 } 700 } else { 701 $order->update_meta($metaKey, $responseUuid); 702 } 703 704 if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($responseUuid)) { 705 $order->update_meta('_invoice_sent', 'sent'); 706 self::clearNativeTimeoutPending($order); 707 } 708 } finally { 709 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 710 } 711 712 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 563 713 wp_send_json_success([ 564 714 'message' => $response->message, 565 715 'provider' => $provider, 566 716 'order_id' => $id, 567 'uuid' => property_exists($response->data, 'uuid') ? $response->data->uuid : null,717 'uuid' => $responseUuid, 568 718 'xml' => $response->xml_string, 569 719 ]); … … 573 723 574 724 // Notify 575 if ($uuid = property_exists($response->data, 'uuid') ? $response->data->uuid : null) {725 if ($uuid = $responseUuid) { 576 726 $time = self::jobTiming('notify'); 577 727 $nextScheduled = wp_next_scheduled( … … 628 778 ! $getSdiPOPIntegrationData->active_integration) 629 779 ) { 630 if ( 'cronjob' !== $method) {780 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 631 781 wp_send_json_error([ 632 782 'message' => esc_html__('Action not allowed.', … … 640 790 // I check the action parameter if it does not match I get an error 641 791 if ('notificationsInvoice' !== $action) { 642 if ( 'cronjob' !== $method) {792 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 643 793 wp_send_json_error([ 644 794 'message' => esc_html__('Action not valid.', … … 651 801 652 802 $order = OrderQuery::instance()->getProviderOrder($id, $provider); 653 $notificationsMeta = $order->get_meta('sdi_pop-invoice_notifications'); 803 $notificationsMeta = $order->get_meta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 804 'invoice')); 654 805 655 806 if ('' === $uuid) { 656 807 \WcElectronInvoice\Functions\log("notificationsInvoice - check uuid: Missing uuid parameter" . "\n"); 657 808 658 if ( 'cronjob' !== $method) {809 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 659 810 wp_send_json_error([ 660 811 'message' => esc_html__('notificationsInvoice - check uuid: Missing uuid parameter', … … 670 821 \WcElectronInvoice\Functions\log("notificationsInvoice - check notifications for uuid: Exist notifications for uuid" . "\n"); 671 822 672 if ( 'cronjob' !== $method) {823 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 673 824 wp_send_json_error([ 674 825 'message' => esc_html__('notificationsInvoice - check notifications for uuid: Exist notifications for uuid', … … 892 1043 ]; 893 1044 894 if ( 'cronjob' === $method) {1045 if (\WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob'])) { 895 1046 // If no notification is present, I reschedule the cron for the request 896 1047 $time = self::jobTiming('notify'); … … 911 1062 912 1063 if (! empty($notificationsMeta)) { 913 $order->update_meta('sdi_pop-invoice_notifications', $notificationsMeta); 914 } 915 916 if ('cronjob' !== $method) { 1064 $order->update_meta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 'invoice'), 1065 $notificationsMeta); 1066 } 1067 1068 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 917 1069 wp_send_json_success([ 918 1070 'message' => $message, … … 962 1114 ! $getSdiPOPIntegrationData->active_integration) 963 1115 ) { 964 if ( 'cronjob' !== $method) {1116 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 965 1117 wp_send_json_error([ 966 1118 'message' => esc_html__('Action not allowed.', … … 974 1126 // I check the action parameter if it does not match I get an error 975 1127 if ('preserveInvoice' !== $action) { 976 if ( 'cronjob' !== $method) {1128 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 977 1129 wp_send_json_error([ 978 1130 'message' => esc_html__('Action not valid.', … … 989 1141 \WcElectronInvoice\Functions\log("check uuid > Missing uuid parameter" . "\n"); 990 1142 991 if ( 'cronjob' !== $method) {1143 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 992 1144 wp_send_json_error([ 993 1145 'message' => esc_html__('Missing uuid parameter', WC_EL_INV_TEXTDOMAIN), … … 1011 1163 */ 1012 1164 if ($getSdiPOPIntegrationData->active_storage) { 1013 $notificationsData = \WcElectronInvoice\Functions\getPostMeta("sdi_pop-invoice_notifications", null, $id, 1165 $notificationsData = \WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 1166 'invoice'), null, $id, 1014 1167 true, 1015 1168 'order', … … 1084 1237 1085 1238 // Update notifications meta data 1086 $notificationsStored = $order->get_meta('sdi_pop-invoice_notifications'); 1239 $notificationsStored = $order->get_meta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 1240 'invoice')); 1087 1241 if (! empty($notificationsMeta)) { 1088 1242 $notificationsMeta = array_merge($notificationsStored, $notificationsMeta); 1089 $order->update_meta('sdi_pop-invoice_notifications', $notificationsMeta); 1090 } 1091 1092 if ('cronjob' !== $method) { 1243 $order->update_meta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 'invoice'), 1244 $notificationsMeta); 1245 } 1246 1247 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 1093 1248 wp_send_json_success([ 1094 1249 'message' => $message, … … 1115 1270 return $preserve; 1116 1271 } 1272 1273 /** 1274 * Confirm native (SDI/PEPPOL) timeout-pending send by manual UUID input. 1275 * 1276 * @return void 1277 */ 1278 public static function confirmNativeSendWithUuid() 1279 { 1280 $provider = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'provider') ?? ''); 1281 $id = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? ''); 1282 $channel = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'channel') ?? ''); 1283 $uuid = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'uuid') ?? ''); 1284 $action = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null; 1285 1286 if ('confirmNativeSendWithUuid' !== $action) { 1287 wp_send_json_error([ 1288 'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN), 1289 ]); 1290 1291 return; 1292 } 1293 1294 if (! in_array($channel, ['sdi', 'peppol'], true)) { 1295 wp_send_json_error([ 1296 'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN), 1297 ]); 1298 1299 return; 1300 } 1301 1302 if (! \WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($uuid)) { 1303 wp_send_json_error([ 1304 'message' => esc_html__('UUID is required.', WC_EL_INV_TEXTDOMAIN), 1305 ]); 1306 1307 return; 1308 } 1309 1310 $order = OrderQuery::instance()->getProviderOrder((int)$id, $provider); 1311 if (! $order) { 1312 wp_send_json_error([ 1313 'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN), 1314 ]); 1315 1316 return; 1317 } 1318 1319 $pending = \WcElectronInvoice\Functions\normalizeBooleanMeta( 1320 $order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey()) 1321 ); 1322 $pendingChannel = (string)$order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey()); 1323 if (! $pending || $pendingChannel !== $channel) { 1324 wp_send_json_error([ 1325 'message' => esc_html__('Timeout verification is not pending for this channel.', WC_EL_INV_TEXTDOMAIN), 1326 ]); 1327 1328 return; 1329 } 1330 1331 $documentType = \WcElectronInvoice\Functions\resolveDocumentTypeFromOrderType((string)$order->get_type()); 1332 $channelPrefix = \WcElectronInvoice\Functions\resolveNativeChannelPrefix($channel); 1333 if ('' === $channelPrefix) { 1334 wp_send_json_error([ 1335 'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN), 1336 ]); 1337 1338 return; 1339 } 1340 1341 $metaKey = \WcElectronInvoice\Functions\buildUuidMetaKey($channelPrefix, $documentType); 1342 if ('credit_note' === $documentType && $order->get_parent_id()) { 1343 $parent = OrderQuery::instance()->getProviderOrder((int)$order->get_parent_id(), $provider); 1344 if ($parent) { 1345 $parent->update_meta($metaKey, $uuid); 1346 $parent->save(); 1347 } 1348 } else { 1349 $order->update_meta($metaKey, $uuid); 1350 } 1351 1352 $order->update_meta('_invoice_sent', 'sent'); 1353 $order->update_meta('_invoice_sent_timestamp', time()); 1354 self::clearNativeTimeoutPending($order); 1355 $order->save(); 1356 1357 wp_send_json_success([ 1358 'message' => esc_html__('UUID saved and invoice marked as sent.', WC_EL_INV_TEXTDOMAIN), 1359 'uuid' => $uuid, 1360 'channel' => $channel, 1361 ]); 1362 } 1363 1364 /** 1365 * Cancel native timeout pending state and re-enable send action. 1366 * 1367 * @return void 1368 */ 1369 public static function cancelNativeTimeoutPending() 1370 { 1371 $provider = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'provider') ?? ''); 1372 $id = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'id') ?? ''); 1373 $channel = sanitize_text_field(\WcElectronInvoice\Functions\filterInput($_POST, 'channel') ?? ''); 1374 $action = \WcElectronInvoice\Functions\filterInput($_POST, 'action') ?? null; 1375 1376 if ('cancelNativeTimeoutPending' !== $action) { 1377 wp_send_json_error([ 1378 'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN), 1379 ]); 1380 1381 return; 1382 } 1383 1384 if (! in_array($channel, ['sdi', 'peppol'], true)) { 1385 wp_send_json_error([ 1386 'message' => esc_html__('Action not valid.', WC_EL_INV_TEXTDOMAIN), 1387 ]); 1388 1389 return; 1390 } 1391 1392 $order = OrderQuery::instance()->getProviderOrder((int)$id, $provider); 1393 if (! $order) { 1394 wp_send_json_error([ 1395 'message' => esc_html__('Order not found.', WC_EL_INV_TEXTDOMAIN), 1396 ]); 1397 1398 return; 1399 } 1400 1401 $pending = \WcElectronInvoice\Functions\normalizeBooleanMeta( 1402 $order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey()) 1403 ); 1404 $pendingChannel = (string)$order->get_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey()); 1405 if (! $pending || $pendingChannel !== $channel) { 1406 wp_send_json_error([ 1407 'message' => esc_html__('Timeout verification is not pending for this channel.', WC_EL_INV_TEXTDOMAIN), 1408 ]); 1409 1410 return; 1411 } 1412 1413 self::clearNativeTimeoutPending($order); 1414 $order->save(); 1415 1416 wp_send_json_success([ 1417 'message' => esc_html__('Timeout status removed. Send action is enabled again.', WC_EL_INV_TEXTDOMAIN), 1418 'channel' => $channel, 1419 ]); 1420 } 1421 1422 /** 1423 * Store timeout pending verification metadata on order. 1424 * 1425 * @param mixed $order 1426 * @param string $channel 1427 * @param string $documentType 1428 * 1429 * @return void 1430 */ 1431 public static function markNativeTimeoutPending($order, string $channel, string $documentType = 'invoice'): void 1432 { 1433 if (! is_object($order) || ! method_exists($order, 'update_meta')) { 1434 return; 1435 } 1436 1437 $channel = strtolower(trim($channel)); 1438 if (! in_array($channel, ['sdi', 'peppol'], true)) { 1439 return; 1440 } 1441 1442 $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey(), 'yes'); 1443 $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey(), $channel); 1444 $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey(), $documentType); 1445 $order->update_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingTimestampMetaKey(), time()); 1446 $order->save(); 1447 } 1448 1449 /** 1450 * Clear timeout pending verification metadata on order. 1451 * 1452 * @param mixed $order 1453 * 1454 * @return void 1455 */ 1456 public static function clearNativeTimeoutPending($order): void 1457 { 1458 if (! is_object($order) || ! method_exists($order, 'delete_meta')) { 1459 return; 1460 } 1461 1462 $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey()); 1463 $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey()); 1464 $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey()); 1465 $order->delete_meta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingTimestampMetaKey()); 1466 $order->save(); 1467 } 1117 1468 } -
woopop-electronic-invoice-free/trunk/src/PeppolApi.php
r3444920 r3463326 37 37 38 38 return (object)[ 39 'active_plugin_integration' => $integrationPluginActive ?? null,40 'peppol_integration' => $peppolIntegration ?? null,39 'active_plugin_integration' => (bool) $integrationPluginActive, 40 'peppol_integration' => 'active' === $peppolIntegration, 41 41 'identifier_scheme' => $idScheme ?? null, 42 42 'identifier_value' => $idValue ?? null, … … 90 90 91 91 // Check if uuid exist 92 $uuid = $order->get_meta( 'peppol_pop-invoice_uuid');92 $uuid = $order->get_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('peppol_pop', 'invoice')); 93 93 if ($uuid) { 94 94 // Log … … 173 173 $checkSent = $order->get_meta("_invoice_sent", true); 174 174 // Check if uuid exist 175 $uuid = $order->get_meta( 'peppol_pop-invoice_uuid');175 $uuid = $order->get_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('peppol_pop', 'invoice')); 176 176 // Check WC_Order instance 177 177 $wcOrderClass = \WcElectronInvoice\Functions\wcOrderClassName($order, '\WC_Order'); … … 279 279 $checkSent = \WcElectronInvoice\Functions\getPostMeta("_invoice_sent", null, $id, true, 'order', 280 280 $provider) ?: null; 281 282 $uuid = \WcElectronInvoice\Functions\getPostMeta("peppol_pop-invoice_uuid", null, $id, true, 'order', 281 $timeoutPending = \WcElectronInvoice\Functions\normalizeBooleanMeta( 282 \WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingMetaKey(), 283 null, 284 $id, 285 true, 286 'order', 287 $provider) 288 ); 289 $timeoutChannel = (string)(\WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingChannelMetaKey(), 290 null, 291 $id, 292 true, 293 'order', 294 $provider) ?: ''); 295 $timeoutDocType = (string)(\WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildNativeTimeoutPendingDocumentTypeMetaKey(), 296 null, 297 $id, 298 true, 299 'order', 300 $provider) ?: 'invoice'); 301 $orderNumberInvoice = (string)(\WcElectronInvoice\Functions\getPostMeta('order_number_invoice', 302 null, 303 $id, 304 true, 305 'order', 306 $provider) ?: ''); 307 308 $uuid = \WcElectronInvoice\Functions\getPostMeta(\WcElectronInvoice\Functions\buildUuidMetaKey('peppol_pop', 309 'invoice'), null, $id, true, 'order', 283 310 $provider) ?: null; 284 311 285 312 $output .= '<div class="peppol_pop_actions">'; 286 313 $output .= sprintf('<hr><h4>%s:</h4>', __('PEPPOL native actions', WC_EL_INV_TEXTDOMAIN)); 314 if ($timeoutPending && 'peppol' === $timeoutChannel) { 315 $output .= sprintf( 316 '<div class="pop-notice notice-warning timeout-native-pending"><p>%s</p><p>%s%s</p></div>', 317 esc_html__('Send not confirmed due to provider timeout. Do not resend: verify with POP support and enter document UUID.', 318 WC_EL_INV_TEXTDOMAIN), 319 esc_html__('Invoice number: ', WC_EL_INV_TEXTDOMAIN), 320 esc_html('' !== $orderNumberInvoice ? $orderNumberInvoice : 'N/A') 321 ); 322 $output .= sprintf( 323 '<input type="text" class="native-timeout-uuid-input" style="display:block;width:100%%;min-width:100%%;margin:0 0 8px 0;" id="native_timeout_uuid_%1$s" placeholder="%2$s" />', 324 esc_attr($id), 325 esc_attr__('Enter verified UUID', WC_EL_INV_TEXTDOMAIN) 326 ); 327 $output .= sprintf( 328 '<a href="javascript:;" data-provider="%1$s" data-id="%2$s" data-channel="peppol" data-document-type="%3$s" class="api-action confirm-timeout-uuid button button-primary" id="api_confirm_timeout_uuid_%2$s">%4$s</a>', 329 esc_attr($provider), 330 esc_attr($id), 331 esc_attr($timeoutDocType), 332 esc_html__('Confirm UUID', WC_EL_INV_TEXTDOMAIN) 333 ); 334 $output .= sprintf( 335 '<a href="javascript:;" data-provider="%1$s" data-id="%2$s" data-channel="peppol" class="api-action cancel-timeout-pending button button-secondary" id="api_cancel_timeout_%2$s">%3$s</a>', 336 esc_attr($provider), 337 esc_attr($id), 338 esc_html__('Cancel timeout', WC_EL_INV_TEXTDOMAIN) 339 ); 340 $output .= '</div>'; 341 342 return $output; 343 } 344 287 345 if ($uuid || 'sent' === $checkSent) { 288 346 // Created and notify … … 321 379 $method = \WcElectronInvoice\Functions\filterInput($_POST, 'method') ?? null; 322 380 381 if (! \WcElectronInvoice\Integrations::isInvoiceChannelActive(\WcElectronInvoice\Integrations::CHANNEL_PEPPOL)) { 382 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 383 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('peppol', 384 'channel_mismatch'); 385 wp_send_json_error([ 386 'message' => esc_html__('Action not allowed: another integration channel is active.', 387 WC_EL_INV_TEXTDOMAIN), 388 'code' => $preconditionError['code'], 389 ]); 390 } 391 392 return; 393 } 394 323 395 if (! \WcElectronInvoice\Wizard::isUserLevelGte($level, 'growth') || 324 396 (! $getPEPPOLPOPIntegrationData->active_plugin_integration || 325 397 ! $getPEPPOLPOPIntegrationData->peppol_integration) 326 398 ) { 327 if ('cronjob' !== $method) { 399 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 400 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('peppol', 401 'integration_inactive'); 328 402 wp_send_json_error([ 329 403 'message' => esc_html__('Action not allowed.', 330 404 WC_EL_INV_TEXTDOMAIN), 405 'code' => $preconditionError['code'], 331 406 ]); 332 407 } … … 337 412 // I check the action parameter if it does not match I get an error 338 413 if ('createAndSendUblInvoice' !== $action) { 339 if ('cronjob' !== $method) { 414 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 415 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('peppol', 416 'action_invalid'); 340 417 wp_send_json_error([ 341 418 'message' => esc_html__('Action not valid.', 342 419 WC_EL_INV_TEXTDOMAIN), 420 'code' => $preconditionError['code'], 343 421 ]); 344 422 } … … 348 426 349 427 $order = OrderQuery::instance()->getProviderOrder((int)$id, $provider); 350 $type = $order->get_type() === 'shop_order' ? 'invoice' : 'credit_note';351 352 $metaKey = "peppol_pop-{$type}_uuid";428 $type = \WcElectronInvoice\Functions\resolveDocumentTypeFromOrderType((string)$order->get_type()); 429 430 $metaKey = \WcElectronInvoice\Functions\buildUuidMetaKey('peppol_pop', $type); 353 431 354 432 $uuid = $order->get_meta($metaKey); … … 358 436 \WcElectronInvoice\Functions\log("createAndSendUblInvoice - UUID exist: {$uuid}" . "\n"); 359 437 360 if ('cronjob' !== $method) { 438 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 439 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('peppol', 440 'uuid_already_exists'); 361 441 wp_send_json_error([ 362 442 'message' => esc_html__("createAndSendUblInvoice - UUID exist: {$uuid}", 363 443 WC_EL_INV_TEXTDOMAIN), 444 'code' => $preconditionError['code'], 364 445 ]); 365 446 } … … 368 449 } 369 450 370 add_filter('wc_el_inv-params_filter_before_api_request', function ($params) { 371 $params['integration'] = [ 372 'use' => 'peppol-via-pop', 373 'action' => 'create', 374 ]; 375 376 return $params; 377 }); 378 379 $data = getXmlFromOrder($order); 380 $response = json_decode($data); 381 382 if (json_last_error() !== JSON_ERROR_NONE || ! is_object($response)) { 383 \WcElectronInvoice\Functions\log("createAndSendUblInvoice - check json response: Invalid response from the XML generator:" . "\n" . print_r($data, 384 true) . "\n"); 385 386 if ('cronjob' !== $method) { 451 $lockKey = \WcElectronInvoice\Functions\buildDbLockKey('peppol', 'create_and_send', (int)$order->get_id(), 452 (string)$provider, $metaKey); 453 $lockDecision = \WcElectronInvoice\Functions\evaluateLockBusyDecision( 454 \WcElectronInvoice\Functions\acquireDbLock($lockKey, 0), 455 \WcElectronInvoice\Functions\isBackgroundExecutionMode($method, ['cronjob']) 456 ); 457 if (! $lockDecision['continue']) { 458 if ($lockDecision['respond_error']) { 459 $lockError = \WcElectronInvoice\Functions\mapLockBusyError('peppol', 'create_and_send'); 387 460 wp_send_json_error([ 388 'message' => esc_html__(' createAndSendUblInvoice - check json response: Invalid response from the XML generator.',461 'message' => esc_html__('Action skipped: another send/create process is already running for this order.', 389 462 WC_EL_INV_TEXTDOMAIN), 390 ' raw' => $data,463 'code' => $lockError['code'], 391 464 ]); 392 465 } … … 395 468 } 396 469 397 if ( 398 ! property_exists($response, 'message') || 399 ! property_exists($response, 'data') || 400 ! property_exists($response, 'xml_string') 401 ) { 402 \WcElectronInvoice\Functions\log("createAndSendUblInvoice - check json property: Invalid response from the XML generator:" . "\n" . print_r($response, 403 true) . "\n"); 404 405 if ('cronjob' !== $method) { 406 wp_send_json_error([ 407 'message' => esc_html__('createAndSendUblInvoice - check json property: Invalid response from the XML generator.', 408 WC_EL_INV_TEXTDOMAIN), 409 'check' => 'json property', 410 'raw' => $response, 411 ]); 412 } 413 414 return; 415 } 416 417 $success = filter_var($response->success ?? false, FILTER_VALIDATE_BOOLEAN); 418 $code = $response->code ?? 0; 419 $responseError = false; 420 $uuid = ! empty($response->data->uuid) ? $response->data->uuid : null; 421 if (! $success && $code !== 202) { 422 $message = esc_html__('createAndSendUblInvoice - check success: Invalid response from the XML generator.', 423 WC_EL_INV_TEXTDOMAIN); 424 425 // not uuid 426 if (! $uuid) { 427 $responseError = true; 428 } 429 430 // Violations 431 if (! empty($response->data->violations)) { 432 $messageViolations = ''; 433 $responseError = true; 434 $violations = (array)$response->data->violations; 435 if (! empty($violations)) { 436 $messages = array_map(function ($violation) { 437 return sprintf( 438 "Message: %s\nPropertyPath: %s\nCode: [%s]\n", 439 $violation->message ?? '', 440 $violation->propertyPath ?? '', 441 $violation->code ?? '', 442 ); 443 }, $violations); 444 445 $messageViolations = "\n\n" . implode("\n", $messages); 446 } 447 448 $message = $message . $messageViolations; 449 } 450 451 if ($responseError) { 470 try { 471 add_filter('wc_el_inv-params_filter_before_api_request', function ($params) { 472 $integrationMeta = \WcElectronInvoice\Functions\buildNativeCreateIntegrationMeta('peppol'); 473 if (! empty($integrationMeta['integration'])) { 474 $params['integration'] = $integrationMeta['integration']; 475 } 476 477 return $params; 478 }); 479 480 $data = getXmlFromOrder($order); 481 $response = json_decode($data); 482 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($data)) { 483 \WcElectronInvoice\InvoiceApi::markNativeTimeoutPending($order, 'peppol', $type); 484 } 485 486 if (json_last_error() !== JSON_ERROR_NONE || ! is_object($response)) { 487 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($data)) { 488 \WcElectronInvoice\InvoiceApi::markNativeTimeoutPending($order, 'peppol', $type); 489 } 490 \WcElectronInvoice\Functions\log("createAndSendUblInvoice - check json response: Invalid response from the XML generator:" . "\n" . print_r($data, 491 true) . "\n"); 492 493 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 494 wp_send_json_error([ 495 'message' => esc_html__('createAndSendUblInvoice - check json response: Invalid response from the XML generator.', 496 WC_EL_INV_TEXTDOMAIN), 497 'raw' => $data, 498 ]); 499 } 500 501 return; 502 } 503 504 if ( 505 ! property_exists($response, 'message') || 506 ! property_exists($response, 'data') || 507 ! property_exists($response, 'xml_string') 508 ) { 509 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($response)) { 510 \WcElectronInvoice\InvoiceApi::markNativeTimeoutPending($order, 'peppol', $type); 511 } 512 \WcElectronInvoice\Functions\log("createAndSendUblInvoice - check json property: Invalid response from the XML generator:" . "\n" . print_r($response, 513 true) . "\n"); 514 515 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 516 wp_send_json_error([ 517 'message' => esc_html__('createAndSendUblInvoice - check json property: Invalid response from the XML generator.', 518 WC_EL_INV_TEXTDOMAIN), 519 'check' => 'json property', 520 'raw' => $response, 521 ]); 522 } 523 524 return; 525 } 526 527 $success = filter_var($response->success ?? false, FILTER_VALIDATE_BOOLEAN); 528 $code = $response->code ?? 0; 529 $responseError = false; 530 $uuid = ! empty($response->data->uuid) ? $response->data->uuid : null; 531 if (! $success && $code !== 202) { 532 $message = esc_html__('createAndSendUblInvoice - check success: Invalid response from the XML generator.', 533 WC_EL_INV_TEXTDOMAIN); 534 $detail = \WcElectronInvoice\Functions\extractNativeErrorDetail($response); 535 if ('' !== $detail) { 536 $message .= ' ' . $detail; 537 } 538 539 // not uuid 540 if (! $uuid) { 541 $responseError = true; 542 } 543 544 // Violations 545 if (! empty($response->data->violations)) { 546 $messageViolations = ''; 547 $responseError = true; 548 $violations = (array)$response->data->violations; 549 if (! empty($violations)) { 550 $messages = array_map(function ($violation) { 551 return sprintf( 552 "Message: %s\nPropertyPath: %s\nCode: [%s]\n", 553 $violation->message ?? '', 554 $violation->propertyPath ?? '', 555 $violation->code ?? '', 556 ); 557 }, $violations); 558 559 $messageViolations = "\n\n" . implode("\n", $messages); 560 } 561 562 $message = $message . $messageViolations; 563 } 564 565 if ($responseError) { 566 if (\WcElectronInvoice\Functions\isLikelyTimeoutError($response)) { 567 \WcElectronInvoice\InvoiceApi::markNativeTimeoutPending($order, 'peppol', $type); 568 } 569 \WcElectronInvoice\Functions\log( 570 "createAndSendUblInvoice - check success: Invalid response from the XML generator" . "\n" . 571 wp_json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n" 572 ); 573 574 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 575 wp_send_json_error([ 576 'message' => $message, 577 'check' => 'success', 578 'raw' => $response, 579 ]); 580 } 581 582 return; 583 } 584 } 585 586 if (! \WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($uuid)) { 452 587 \WcElectronInvoice\Functions\log( 453 "createAndSendUblInvoice - check success: Invalid response from the XML generator" . "\n" .588 "createAndSendUblInvoice - check uuid: Invalid response from the XML generator" . "\n" . 454 589 wp_json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n" 455 590 ); 456 591 457 if ('cronjob' !== $method) { 592 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 593 $preconditionError = \WcElectronInvoice\Functions\mapNativeCreateAndSendPreconditionError('peppol', 594 'response_uuid_missing'); 458 595 wp_send_json_error([ 459 'message' => $message, 460 'check' => 'success', 596 'message' => esc_html__('createAndSendUblInvoice - check uuid: Invalid response from the XML generator.', 597 WC_EL_INV_TEXTDOMAIN), 598 'check' => 'uuid', 461 599 'raw' => $response, 600 'code' => $preconditionError['code'], 462 601 ]); 463 602 } … … 465 604 return; 466 605 } 467 } 468 469 if ('credit_note' === $type && $order->get_parent_id()) { 470 $parent = OrderQuery::instance()->getProviderOrder((int)$order->get_parent_id(), $provider); 471 if ($parent) { 472 $parent->update_meta($metaKey, $response->data->uuid); 473 } 474 } else { 475 $order->update_meta($metaKey, $response->data->uuid); 476 } 477 478 $order->update_meta('_invoice_sent', 'sent'); 479 480 if ('cronjob' !== $method) { 606 607 if ('credit_note' === $type && $order->get_parent_id()) { 608 $parent = OrderQuery::instance()->getProviderOrder((int)$order->get_parent_id(), $provider); 609 if ($parent) { 610 $parent->update_meta($metaKey, $uuid); 611 } 612 } else { 613 $order->update_meta($metaKey, $uuid); 614 } 615 616 if (\WcElectronInvoice\Functions\canMarkInvoiceSentWithUuid($uuid)) { 617 $order->update_meta('_invoice_sent', 'sent'); 618 \WcElectronInvoice\InvoiceApi::clearNativeTimeoutPending($order); 619 } 620 } finally { 621 \WcElectronInvoice\Functions\releaseDbLock($lockKey); 622 } 623 624 if (\WcElectronInvoice\Functions\shouldRespondWithImmediateError($method, ['cronjob'])) { 481 625 wp_send_json_success([ 482 626 'message' => $response->message, -
woopop-electronic-invoice-free/trunk/src/WooCommerce/Fields/InvoiceFields.php
r3460902 r3463326 933 933 if ($statusSdiViaPop->active_integration) { 934 934 // remove uuid and old notifications 935 $order->delete_meta( 'sdi_pop-invoice_uuid');936 $order->delete_meta( 'sdi_pop-invoice_notifications');935 $order->delete_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('sdi_pop', 'invoice')); 936 $order->delete_meta(\WcElectronInvoice\Functions\buildNotificationsMetaKey('sdi_pop', 'invoice')); 937 937 } 938 938 … … 940 940 if ($statusSdiViaPop->peppol_integration) { 941 941 // remove uuid 942 $order->delete_meta( 'peppol_pop-invoice_uuid');942 $order->delete_meta(\WcElectronInvoice\Functions\buildUuidMetaKey('peppol_pop', 'invoice')); 943 943 } 944 944 } … … 1197 1197 $invoiceNumberView = $number; 1198 1198 } 1199 $invoiceNumberDisplay = $invoiceNumberView ?? ($number ?: $invoiceNumber); 1199 1200 1200 1201 echo '<div class="wc_el_inv__general-order invoice-date">'; … … 1204 1205 esc_html__('Number', WC_EL_INV_TEXTDOMAIN), 1205 1206 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1206 $invoiceNumber View ?? $invoiceNumber,1207 $invoiceNumberDisplay, 1207 1208 esc_html__('Date', WC_EL_INV_TEXTDOMAIN), 1208 1209 esc_html__($dateCompleted->format('Y-m-d - H:i')) -
woopop-electronic-invoice-free/trunk/vendor/composer/installed.php
r3460902 r3463326 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',6 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' 09194be380794c1eca19b4ba745800898f9d0391',16 'reference' => 'cfce0241f8a719c93e1d6926bfcd08373376efc0', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../',
Note: See TracChangeset
for help on using the changeset viewer.