Plugin Directory

Changeset 3476201


Ignore:
Timestamp:
03/06/2026 09:21:00 AM (3 weeks ago)
Author:
webdigit
Message:

Release 0.4.0: orders list columns, sync button, error log modal. Also release 0.3.1 (review prompt, uninstall cleanup).

Location:
gestoo-connector-for-peppol-invoicing/trunk
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • gestoo-connector-for-peppol-invoicing/trunk/assets/css/admin.css

    r3474457 r3476201  
    6666    outline: 2px solid transparent;
    6767}
     68
     69/* =========================================================================
     70   Orders list columns – Peppol status badges
     71   ========================================================================= */
     72
     73.gestoo-peppol-badge {
     74    display: inline-block;
     75    padding: 2px 7px;
     76    border-radius: 3px;
     77    font-size: 11px;
     78    font-weight: 600;
     79    line-height: 1.6;
     80    white-space: nowrap;
     81    vertical-align: middle;
     82}
     83
     84.gestoo-peppol-badge--delivered {
     85    background: #d1f0e0;
     86    color: #145a32;
     87}
     88
     89.gestoo-peppol-badge--invoice-ok {
     90    background: #e3f2e1;
     91    color: #2d6a2d;
     92}
     93
     94.gestoo-peppol-badge--submitted {
     95    background: #d6eaf8;
     96    color: #154360;
     97}
     98
     99.gestoo-peppol-badge--pending {
     100    background: #fdebd0;
     101    color: #784212;
     102}
     103
     104.gestoo-peppol-badge--error {
     105    background: #fce8e6;
     106    color: #7b241c;
     107}
     108
     109.gestoo-peppol-badge--none {
     110    background: #f0f0f1;
     111    color: #50575e;
     112}
     113
     114/* Log info button (ℹ️) */
     115.gestoo-peppol-log-btn {
     116    background: none;
     117    border: none;
     118    cursor: pointer;
     119    padding: 0 3px;
     120    font-size: 14px;
     121    vertical-align: middle;
     122    opacity: .75;
     123}
     124
     125.gestoo-peppol-log-btn:hover {
     126    opacity: 1;
     127}
     128
     129/* Sync/retry button loading state */
     130.gestoo-peppol-sync-btn.gestoo-peppol-btn-loading {
     131    opacity: .6;
     132    cursor: wait;
     133}
     134
     135/* Inline error under sync button */
     136.gestoo-peppol-inline-error {
     137    display: block;
     138    color: #b32d2e;
     139    font-size: 11px;
     140    margin-top: 3px;
     141}
     142
     143/* =========================================================================
     144   Log modal
     145   ========================================================================= */
     146
     147#gestoo-peppol-log-modal {
     148    position: fixed;
     149    top: 0;
     150    left: 0;
     151    width: 100%;
     152    height: 100%;
     153    z-index: 160000; /* above WP admin bar (99999) */
     154}
     155
     156.gestoo-peppol-modal-overlay {
     157    position: absolute;
     158    top: 0;
     159    left: 0;
     160    width: 100%;
     161    height: 100%;
     162    background: rgba(0, 0, 0, .55);
     163    display: flex;
     164    align-items: center;
     165    justify-content: center;
     166}
     167
     168.gestoo-peppol-modal-box {
     169    position: relative;
     170    background: #fff;
     171    border-radius: 4px;
     172    box-shadow: 0 4px 24px rgba(0, 0, 0, .25);
     173    padding: 24px 28px 20px;
     174    width: 90%;
     175    max-width: 620px;
     176    max-height: 80vh;
     177    overflow-y: auto;
     178}
     179
     180.gestoo-peppol-modal-box h3 {
     181    margin: 0 0 14px;
     182    font-size: 16px;
     183    font-weight: 600;
     184    color: #1d2327;
     185}
     186
     187.gestoo-peppol-modal-close {
     188    position: absolute;
     189    top: 10px;
     190    right: 14px;
     191    background: none;
     192    border: none;
     193    font-size: 22px;
     194    line-height: 1;
     195    cursor: pointer;
     196    color: #646970;
     197    padding: 2px 6px;
     198}
     199
     200.gestoo-peppol-modal-close:hover {
     201    color: #1d2327;
     202}
     203
     204/* Last error notice inside modal */
     205.gestoo-peppol-last-error {
     206    background: #fce8e6;
     207    border-left: 3px solid #b32d2e;
     208    padding: 8px 10px;
     209    margin-bottom: 14px;
     210    font-size: 13px;
     211    color: #3c3c3c;
     212    border-radius: 0 3px 3px 0;
     213}
     214
     215/* Log table inside modal */
     216.gestoo-peppol-log-table {
     217    width: 100%;
     218    border-collapse: collapse;
     219    font-size: 13px;
     220}
     221
     222.gestoo-peppol-log-table thead th {
     223    text-align: left;
     224    padding: 6px 8px;
     225    border-bottom: 2px solid #dcdcde;
     226    color: #3c434a;
     227    font-weight: 600;
     228}
     229
     230.gestoo-peppol-log-table tbody tr:nth-child(odd) {
     231    background: #f6f7f7;
     232}
     233
     234.gestoo-peppol-log-table tbody td {
     235    padding: 6px 8px;
     236    border-bottom: 1px solid #f0f0f1;
     237    vertical-align: top;
     238    color: #3c434a;
     239}
     240
     241.gestoo-peppol-log-date {
     242    white-space: nowrap;
     243    color: #646970;
     244    font-size: 11px;
     245    width: 140px;
     246}
  • gestoo-connector-for-peppol-invoicing/trunk/gestoo-connector-for-peppol-invoicing.php

    r3476162 r3476201  
    44 * Plugin URI:        https://www.gestoo.be
    55 * Description:       WooCommerce to GestOO connector: create invoices and send via Peppol. Official invoicing stays in GestOO.
    6  * Version:           0.3.1
     6 * Version:           0.4.0
    77 * Requires at least: 6.0
    88 * Requires PHP:      7.4
     
    4242);
    4343
    44 define( 'GESTOO_PEPPOL_INVOICE_VERSION', '0.3.1' );
     44define( 'GESTOO_PEPPOL_INVOICE_VERSION', '0.4.0' );
    4545define( 'GESTOO_PEPPOL_INVOICE_PLUGIN_FILE', __FILE__ );
    4646define( 'GESTOO_PEPPOL_INVOICE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     
    120120    require_once GESTOO_PEPPOL_INVOICE_PLUGIN_DIR . 'admin/class-gestoo-peppol-admin-settings.php';
    121121    require_once GESTOO_PEPPOL_INVOICE_PLUGIN_DIR . 'admin/class-gestoo-peppol-order-meta-box.php';
     122    require_once GESTOO_PEPPOL_INVOICE_PLUGIN_DIR . 'admin/class-gestoo-peppol-order-list-columns.php';
    122123    require_once GESTOO_PEPPOL_INVOICE_PLUGIN_DIR . 'admin/class-gestoo-peppol-review-prompt.php';
    123124
     
    128129    Gestoo_Peppol_Admin_Settings::init();
    129130    Gestoo_Peppol_Order_Meta_Box::init();
     131    Gestoo_Peppol_Order_List_Columns::init();
    130132    Gestoo_Peppol_Review_Prompt::init();
    131133
  • gestoo-connector-for-peppol-invoicing/trunk/includes/class-gestoo-peppol-order-handler.php

    r3474457 r3476201  
    2626    private const META_LAST_ERROR     = '_gestoo_last_error';
    2727    private const META_SYNC_STATUS    = '_gestoo_sync_status'; // Values: pending, synced, error.
     28    private const META_ERROR_LOG      = '_gestoo_error_log';   // Array of {time, message, context}.
    2829
    2930    /**
     
    119120        $payload = self::build_invoice_payload( $order );
    120121        if ( null === $payload ) {
     122            $err_msg = __( 'Could not build payload (missing order data).', 'gestoo-connector-for-peppol-invoicing' );
    121123            $order->update_meta_data( self::META_SYNC_STATUS, 'error' );
    122             $order->update_meta_data( self::META_LAST_ERROR, __( 'Could not build payload (missing order data).', 'gestoo-connector-for-peppol-invoicing' ) );
     124            $order->update_meta_data( self::META_LAST_ERROR, $err_msg );
     125            self::append_error_log( $order, $err_msg, 'build_payload' );
    123126            $order->save();
    124127            return;
     
    128131
    129132        if ( ! $result['success'] ) {
     133            $err_msg = $result['message'] ?? __( 'Unknown error', 'gestoo-connector-for-peppol-invoicing' );
    130134            $order->update_meta_data( self::META_SYNC_STATUS, 'error' );
    131             $order->update_meta_data( self::META_LAST_ERROR, $result['message'] ?? __( 'Unknown error', 'gestoo-connector-for-peppol-invoicing' ) );
     135            $order->update_meta_data( self::META_LAST_ERROR, $err_msg );
     136            self::append_error_log( $order, $err_msg, 'create_invoice' );
    132137            $order->save();
    133138            return;
     
    137142        $invoice_id = (int) ( $data['invoice_id'] ?? 0 );
    138143        if ( 0 >= $invoice_id ) {
     144            $err_msg = __( 'Invalid API response (invoice_id missing).', 'gestoo-connector-for-peppol-invoicing' );
    139145            $order->update_meta_data( self::META_SYNC_STATUS, 'error' );
    140             $order->update_meta_data( self::META_LAST_ERROR, __( 'Invalid API response (invoice_id missing).', 'gestoo-connector-for-peppol-invoicing' ) );
     146            $order->update_meta_data( self::META_LAST_ERROR, $err_msg );
     147            self::append_error_log( $order, $err_msg, 'create_invoice' );
    141148            $order->save();
    142149            return;
     
    156163                $order->save();
    157164            } else {
     165
     166                $err_msg = $send_result['message'] ?? '';
    158167                $order->update_meta_data( self::META_PEPPOL_STATUS, 'error' );
    159                 $order->update_meta_data( self::META_PEPPOL_MESSAGE, $send_result['message'] ?? '' );
     168                $order->update_meta_data( self::META_PEPPOL_MESSAGE, $err_msg );
     169                if ( '' !== $err_msg ) {
     170                    self::append_error_log( $order, $err_msg, 'send_peppol' );
     171                }
    160172                $order->save();
    161173            }
     
    374386        return self::META_SYNC_STATUS;
    375387    }
     388
     389    /**
     390     * Meta key for error log.
     391     *
     392     * @return string
     393     */
     394    public static function meta_error_log(): string {
     395        return self::META_ERROR_LOG;
     396    }
     397
     398    /**
     399     * Append an entry to the order's error log (max 20 entries).
     400     * Caller must call $order->save() afterwards.
     401     *
     402     * @param WC_Order $order   Order object.
     403     * @param string   $message Error message.
     404     * @param string   $context Short context key (e.g. 'create_invoice', 'send_peppol').
     405     */
     406    public static function append_error_log( WC_Order $order, string $message, string $context = '' ): void {
     407        $log = $order->get_meta( self::META_ERROR_LOG );
     408        if ( ! is_array( $log ) ) {
     409            $log = [];
     410        }
     411        $log[] = [
     412            'time'    => current_time( 'mysql' ),
     413            'message' => $message,
     414            'context' => $context,
     415        ];
     416        if ( count( $log ) > 20 ) {
     417            $log = array_slice( $log, -20 );
     418        }
     419        $order->update_meta_data( self::META_ERROR_LOG, $log );
     420    }
     421
     422    /**
     423     * Smart sync: create invoice in GestOO if not yet created, or refresh Peppol status if already created.
     424     * Idempotent: calling multiple times is safe (GestOO handles deduplication via idempotency_key).
     425     *
     426     * @param WC_Order $order Order to sync.
     427     * @return array{ success: bool, message: string }
     428     */
     429    public static function sync_order_now( WC_Order $order ): array {
     430        $client = self::get_api_client();
     431        if ( null === $client ) {
     432            return [
     433                'success' => false,
     434                'message' => __( 'GestOO API not configured.', 'gestoo-connector-for-peppol-invoicing' ),
     435            ];
     436        }
     437
     438        $invoice_id = (int) $order->get_meta( self::META_INVOICE_ID );
     439
     440        if ( $invoice_id <= 0 ) {
     441            // No invoice yet → try to create it.
     442            if ( ! self::has_tax_rates_configured() ) {
     443                return [
     444                    'success' => false,
     445                    'message' => __( 'VAT rates must be configured in WooCommerce to generate Peppol invoices.', 'gestoo-connector-for-peppol-invoicing' ),
     446                ];
     447            }
     448            $payload = self::build_invoice_payload( $order );
     449            if ( null === $payload ) {
     450                return [
     451                    'success' => false,
     452                    'message' => __( 'Insufficient order data (missing billing email).', 'gestoo-connector-for-peppol-invoicing' ),
     453                ];
     454            }
     455            $result = $client->create_invoice( $payload );
     456            if ( ! $result['success'] ) {
     457                $msg = $result['message'] ?? __( 'Unknown error', 'gestoo-connector-for-peppol-invoicing' );
     458                $order->update_meta_data( self::META_SYNC_STATUS, 'error' );
     459                $order->update_meta_data( self::META_LAST_ERROR, $msg );
     460                self::append_error_log( $order, $msg, 'create_invoice' );
     461                $order->save();
     462                return [ 'success' => false, 'message' => $msg ];
     463            }
     464            $data       = $result['data'] ?? [];
     465            $invoice_id = (int) ( $data['invoice_id'] ?? 0 );
     466            if ( $invoice_id <= 0 ) {
     467                $msg = __( 'Invalid API response (invoice_id missing).', 'gestoo-connector-for-peppol-invoicing' );
     468                $order->update_meta_data( self::META_SYNC_STATUS, 'error' );
     469                $order->update_meta_data( self::META_LAST_ERROR, $msg );
     470                self::append_error_log( $order, $msg, 'create_invoice' );
     471                $order->save();
     472                return [ 'success' => false, 'message' => $msg ];
     473            }
     474            $order->update_meta_data( self::META_INVOICE_ID, $invoice_id );
     475            $order->update_meta_data( self::META_INVOICE_NUMBER, $data['invoice_number'] ?? '' );
     476            $order->update_meta_data( self::META_SYNC_STATUS, 'synced' );
     477            $order->delete_meta_data( self::META_LAST_ERROR );
     478            $order->save();
     479            if ( self::is_auto_send_peppol() ) {
     480                $send = $client->send_peppol( $invoice_id );
     481                if ( $send['success'] && isset( $send['data']['peppol_status'] ) ) {
     482                    $order->update_meta_data( self::META_PEPPOL_STATUS, $send['data']['peppol_status'] );
     483                    $order->update_meta_data( self::META_PEPPOL_MESSAGE, $send['data']['peppol_message'] ?? '' );
     484                    $order->save();
     485                } else {
     486                    $msg = $send['message'] ?? __( 'Peppol send error.', 'gestoo-connector-for-peppol-invoicing' );
     487                    $order->update_meta_data( self::META_PEPPOL_STATUS, 'error' );
     488                    $order->update_meta_data( self::META_PEPPOL_MESSAGE, $msg );
     489                    self::append_error_log( $order, $msg, 'send_peppol' );
     490                    $order->save();
     491                }
     492            }
     493            return [ 'success' => true, 'message' => __( 'Invoice created.', 'gestoo-connector-for-peppol-invoicing' ) ];
     494        }
     495
     496        // Invoice already exists → refresh Peppol status from GestOO.
     497        self::refresh_peppol_status( $order, $client );
     498        return [ 'success' => true, 'message' => __( 'Status refreshed.', 'gestoo-connector-for-peppol-invoicing' ) ];
     499    }
    376500}
  • gestoo-connector-for-peppol-invoicing/trunk/readme.txt

    r3476162 r3476201  
    22
    33Contributors: webdigit
    4 Tags: woocommerce, peppol, invoice, e-invoicing, ubl, b2b, gestoo
     4Tags: woocommerce, peppol, invoice, e-invoicing
    55Requires at least: 6.0
    66Tested up to: 6.9
    7 Stable tag: 0.3.1
     7Stable tag: 0.4.0
    88Requires PHP: 7.4
    99Requires Plugins: woocommerce
     
    8080== Changelog ==
    8181
     82= 0.4.0 =
     83* Added Peppol status columns in the WooCommerce orders list (HPOS + legacy compatible).
     84* Added smart sync/retry button in the orders list: creates invoice if missing, refreshes Peppol status if already sent.
     85* Added event log modal (ℹ button) showing timestamped error history per order.
     86* Stored error log (up to 20 entries) in order meta for diagnostics.
     87
    8288= 0.3.1 =
    8389* Added review prompt (J+30 after activation) to help users rate the plugin.
     
    105111== Upgrade Notice ==
    106112
     113= 0.4.0 =
     114Adds Peppol status columns and sync/retry button in the orders list. Safe to update.
     115
    107116= 0.3.1 =
    108117Adds review prompt and uninstall cleanup. Safe to update.
Note: See TracChangeset for help on using the changeset viewer.