id ) ) { return; } if ( ! empty( $screen->id ) && 'woocommerce_page_wc-settings' === $screen->id && ! empty( $_GET['section'] ) && POSTNL_SETTINGS_ID === wp_unslash( $_GET['section'] ) ) { wp_enqueue_script( 'postnl-admin-settings', POSTNL_WC_PLUGIN_DIR_URL . '/assets/js/admin-settings.js', array( 'jquery' ), POSTNL_WC_VERSION, true ); wp_enqueue_style( 'postnl-admin-settings-styles', POSTNL_WC_PLUGIN_DIR_URL . '/assets/css/admin-settings.css', array(), POSTNL_WC_VERSION ); } if ( ( 'shop_order' === $screen->id && 'post' === $screen->base ) || 'woocommerce_page_wc-orders' === $screen->id ) { wp_enqueue_style( 'postnl-admin-order-single', POSTNL_WC_PLUGIN_DIR_URL . '/assets/css/admin-order-single.css', array(), POSTNL_WC_VERSION ); wp_enqueue_script( 'postnl-admin-order-single', POSTNL_WC_PLUGIN_DIR_URL . '/assets/js/admin-order-single.js', array( 'jquery' ), POSTNL_WC_VERSION, true ); wp_enqueue_script( 'postnl-admin-shipment-track-trace', POSTNL_WC_PLUGIN_DIR_URL . '/assets/js/admin-shipment-track-trace.js', array( 'jquery' ), POSTNL_WC_VERSION, true ); wp_localize_script( 'postnl-admin-order-single', 'postnl_admin_order_obj', array( 'prefix' => $this->prefix, 'fields' => array_map( function ( $meta_field ) { return empty( $meta_field['id'] ) ? '' : $meta_field['id']; }, $this->meta_box_fields(), ), ) ); } } /** * Adding meta box in order admin page. * * @param String $post_type Post type for current admin page. * @param WP_POST|WC_Order $post_or_order_object Either WP_Post or WC_Order object. */ public function add_meta_box( $post_type, $post_or_order_object ) { $order = $this->init_order_object( $post_or_order_object ); if ( ! is_a( $order, 'WC_Order' ) || ! $this->is_postnl_shipping_method( $order ) ) { return; } // translators: %s will be replaced by service name. add_meta_box( 'woocommerce-shipment-postnl-label', esc_html__( 'Label & Tracking', 'postnl-for-woocommerce' ), array( $this, 'meta_box_html', ), Utils::get_order_screen_id(), 'side', 'high' ); } /** * Add value to meta box fields. * * @param WC_Order $order current order object. * * @return array */ public function add_meta_box_value( $order ) { if ( ! is_a( $order, 'WC_Order' ) ) { return array(); } $meta_fields = $this->meta_box_fields( $order ); $order_data = $order->get_meta( $this->meta_name ); $option_map = Mapping::option_available_list(); $from_country = Utils::get_base_country(); $to_country = $order->get_shipping_country(); $to_state = $order->get_shipping_state(); $destination = Utils::get_shipping_zone( $to_country, $to_state ); // The persisted backend collapses the 24h/48h choice onto the generic // 'letterbox' feature. Swap in the resolved variant so the override loop // below re-checks (and, once a label exists, locks) the correct checkbox // instead of re-checking the generic 24h box on top of the pre-selected // 48h box, which would show both letterbox variants as selected. if ( isset( $order_data['backend'] ) && is_array( $order_data['backend'] ) ) { $order_data['backend'] = $this->apply_letterbox_display_variant( $order_data['backend'], $order ); } foreach ( $meta_fields as $index => $field ) { if ( isset( $field['nonce'] ) && true === $field['nonce'] ) { continue; } $field_name = Utils::remove_prefix_field( $this->prefix, $field['id'] ); if ( ! empty( $order_data['frontend'][ $field_name ] ) ) { $meta_fields[ $index ]['value'] = $order_data['frontend'][ $field_name ]; } if ( isset( $order_data['backend'][ $field_name ] ) ) { if ( $this->have_label_file( $order ) ) { $meta_fields[ $index ]['custom_attributes']['disabled'] = 'disabled'; } $meta_fields[ $index ]['value'] = $order_data['backend'][ $field_name ]; } if ( isset( $option_map[ $from_country ][ $destination ] ) ) { $meta_fields[ $index ]['standard_feat'] = in_array( $field_name, $option_map[ $from_country ][ $destination ] ); } else { $meta_fields[ $index ]['standard_feat'] = false; } } return $meta_fields; } /** * Filter the fields to display the available fields only. * * @param Array $meta_fields Order meta fields. * @param Array $available_options Available fields based on the countries and chosen option in checkout page. * * @return array */ public function filter_available_fields( $meta_fields, $available_options ) { $meta_fields = array_filter( $meta_fields, function ( $field ) use ( $available_options ) { $field_name = Utils::remove_prefix_field( $this->prefix, $field['id'] ); if ( true === $field['standard_feat'] ) { return true; } if ( true === $field['const_field'] ) { return true; } return in_array( $field_name, $available_options, true ); } ); return $meta_fields; } /** * Get dropoff points information. * * @param WC_Order $order Order object. * * @return Array. */ public function get_pickup_points_info( $order ) { return $this->get_order_frontend_info( $order, 'dropoff_points_' ); } /** * Get delivery day information. * * @param WC_Order $order Order object. * * @return Array. */ public function get_delivery_day_info( $order ) { return $this->get_order_frontend_info( $order, 'delivery_day_' ); } /** * Generate the dropoff points html information. * * @param WC_Order $order Order object. */ public function generate_delivery_type_html( $order ) { if ( ! is_a( $order, 'WC_Order' ) ) { return; } $delivery_type = $this->get_delivery_type( $order ); if ( empty( $delivery_type ) ) { return; } ?>
$info_val ) { // Convert to the Dutch date format, falling back to the stored value // when it does not parse as Y-m-d so a malformed date cannot fatal the // whole order edit screen via date_format( false, ... ). $date_obj = date_create_from_format( 'Y-m-d', $info_val ); $dutch_date = ( false !== $date_obj ) ? date_format( $date_obj, 'd/m/Y' ) : $info_val; ?>
$info_val ) { switch ( $info_idx ) { case 'dropoff_points_date': $additional_text = esc_html__( 'Date:', 'postnl-for-woocommerce' ); break; case 'dropoff_points_time': $additional_text = esc_html__( 'Time:', 'postnl-for-woocommerce' ); break; default: $additional_text = ''; break; } ?>
settings->get_return_shipment_and_labels() && 'no' === $this->settings->get_return_shipment_and_labels_all() && 'NL' === $order->get_shipping_country() && 'NL' === Utils::get_base_country() && ! Utils::is_order_eligible_auto_letterbox( $order ) ) { ?>

is_return_function_activated( $order ) ) ? '' : 'style="display:none;"'; ?> >
is_return_function_activated( $order ) ) ? 'style="display:none;"' : ''; ?> >

get_shipping_country() && $this->settings->is_smart_return_enabled() ) { $check_for_barcode = empty( $this->get_backend_data( $order->get_ID() ) ); ?>

init_order_object( $post_or_order_object ); if ( ! is_a( $order, 'WC_Order' ) ) { return; } $form_class = ( $this->have_label_file( $order ) ) ? 'generated' : ''; if ( $this->have_backend_data( $order, 'create_return_label' ) ) { $form_class .= ' has-return'; } if ( $this->have_backend_data( $order, 'letterbox' ) ) { $form_class .= ' has-letterbox'; } $pickup_info = $this->get_pickup_points_info( $order ); $delivery_info = $this->get_delivery_day_info( $order ); $fields_with_value = $this->add_meta_box_value( $order ); $available_options = $this->get_available_options( $order ); $available_fields = $this->filter_available_fields( $fields_with_value, $available_options ); ?>
generate_delivery_type_html( $order ); ?> generate_delivery_date_html( $delivery_info ); ?> generate_pickup_points_html( $pickup_info ); ?>
activate_return_function_html( $order ); ?> send_smart_return_email_html( $order ); ?>
get_nonce_fields() ); if ( empty( $nonce_fields ) ) { throw new \Exception( esc_html__( 'Cannot find nonce field!', 'postnl-for-woocommerce' ) ); } // Check nonce before proceed. $nonce_result = check_ajax_referer( $this->nonce_key, $nonce_fields[0]['id'], false ); if ( false === $nonce_result ) { throw new \Exception( esc_html__( 'Nonce is invalid!', 'postnl-for-woocommerce' ) ); } $order_id = ! empty( $_REQUEST['order_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['order_id'] ) ) : 0; $result = $this->save_meta_value( $order_id, $_REQUEST ); $return_data = $result['saved_data']; $labels = $result['labels']; $tracking_note = $this->get_tracking_note( $order_id ); if ( ! empty( $tracking_note ) ) { $return_data = array_merge( $result['saved_data'], array( 'tracking_note' => $tracking_note, 'note_type' => $this->settings->is_woocommerce_email_enabled() ? 'customer' : 'private', ) ); } wp_send_json_success( $return_data ); } catch ( \Exception $e ) { wp_send_json_error( array( 'message' => $e->getMessage() ), ); } } /** * Delete meta data in order admin page using ajax. * * @throws \Exception Throw error for invalid nonce. */ public function delete_meta_data_ajax() { try { // Get array of nonce fields. $nonce_fields = array_values( $this->get_nonce_fields() ); if ( empty( $nonce_fields ) ) { throw new \Exception( esc_html__( 'Cannot find nonce field!', 'postnl-for-woocommerce' ) ); } // Check nonce before proceed. $nonce_result = check_ajax_referer( $this->nonce_key, $nonce_fields[0]['id'], false ); if ( false === $nonce_result ) { throw new \Exception( esc_html__( 'Nonce is invalid!', 'postnl-for-woocommerce' ) ); } $order_id = ! empty( $_REQUEST['order_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['order_id'] ) ) : 0; // Check if order id is really an ID from shop_order post type. $order = wc_get_order( $order_id ); if ( ! is_a( $order, 'WC_Order' ) ) { throw new \Exception( esc_html__( 'Order does not exist!', 'postnl-for-woocommerce' ) ); } $saved_data = $this->delete_meta_value( $order_id ); wp_send_json_success( $saved_data ); } catch ( \Exception $e ) { wp_send_json_error( array( 'message' => $e->getMessage() ), ); } } /** * Add house number field to the order address fields within the dashboard. * * @param array $fields address fields. * * @return array. */ public function admin_address_fields( $fields ) { $new_fields = array(); foreach ( $fields as $key => $field ) { if ( 'address_1' === $key ) { $new_fields['house_number'] = array( 'label' => __( 'House number', 'postnl-for-woocommerce' ), 'show' => false, ); } $new_fields[ $key ] = $field; } return $new_fields; } /** * Modify address and add house number. * * @param array $address Array of shipping address. * @param \WC_Order $order Order object. * @param String $type Address type. * * @return mixed */ public function add_house_number_to_address( $address, $order, $type = 'shipping' ) { if ( 'shipping' === $type ) { $house_number_meta = '_shipping_house_number'; } else { $house_number_meta = '_billing_house_number'; } $house_number = $order->get_meta( $house_number_meta ); if ( $house_number ) { $address['address_1'] .= ' ' . $house_number; } return $address; } /** * Add house number to the shipping address within the order. * * @param array $address Array of shipping address. * @param \WC_Order $order Order object. * * @return array */ public function display_shipping_house_number( $address, $order ) { return $this->add_house_number_to_address( $address, $order, 'shipping' ); } /** * Add house number to the billing address within the order. * * @param array $address Array of shipping address. * @param \WC_Order $order Order object. * * @return array */ public function display_billing_house_number( $address, $order ) { return $this->add_house_number_to_address( $address, $order, 'billing' ); } /** * Ajax action to activate return function. * * @return void */ public function postnl_activate_return_function() { try { if ( ! isset( $_POST['security'] ) ) { throw new \Exception( esc_html__( 'Cannot find nonce field!', 'postnl-for-woocommerce' ) ); } // Check nonce before proceed. if ( ! wp_verify_nonce( $_POST['security'], 'postnl_activate_return_function' ) ) { throw new \Exception( esc_html__( 'Nonce is invalid', 'postnl-for-woocommerce' ) ); } $order_id = ! empty( $_REQUEST['order_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['order_id'] ) ) : 0; // Check if order id is really an ID from shop_order post type. $order = wc_get_order( $order_id ); if ( ! is_a( $order, 'WC_Order' ) ) { throw new \Exception( esc_html__( 'Order does not exist!', 'postnl-for-woocommerce' ) ); } if ( $this->is_return_function_activated( $order ) ) { throw new \Exception( esc_html__( 'Already activated!', 'postnl-for-woocommerce' ) ); } $response = $this->service_factory()->return_label_service()->activate( (int) $order_id ); if ( ! empty( $response['successFulBarcodes'] ) && is_array( $response['successFulBarcodes'] ) ) { $order->update_meta_data( $this->is_return_activated_meta, 'yes' ); $order->save_meta_data(); wp_send_json_success(); return; } $error_message = esc_html__( 'Unknown error.', 'postnl-for-woocommerce' ); $error_items = array(); if ( isset( $response['errorsPerBarcode'] ) && is_array( $response['errorsPerBarcode'] ) ) { foreach ( $response['errorsPerBarcode'] as $barcode_errors ) { if ( ! isset( $barcode_errors['errors'] ) || ! is_array( $barcode_errors['errors'] ) ) { continue; } foreach ( $barcode_errors['errors'] as $error ) { if ( empty( $error['description'] ) ) { continue; } $error_items[] = sprintf( '
  • %s
  • ', esc_html( $error['description'] ) ); } } } if ( ! empty( $error_items ) ) { $error_message = ''; } // Translators: %s is the error message. throw new \Exception( sprintf( esc_html__( 'Error: %s', 'postnl-for-woocommerce' ), $error_message ) ); } catch ( \Exception $e ) { wp_send_json_error( array( 'message' => $e->getMessage() ), ); } } /** * Ajax action to send smart return email. * * @return void */ public function postnl_send_smart_return_email() { try { if ( ! isset( $_POST['security'] ) ) { throw new \Exception( esc_html__( 'Cannot find nonce field!', 'postnl-for-woocommerce' ) ); } // Check nonce before proceed. if ( ! wp_verify_nonce( $_POST['security'], 'postnl_send_smart_return_email' ) ) { throw new \Exception( esc_html__( 'Nonce is invalid', 'postnl-for-woocommerce' ) ); } $order_id = ! empty( $_REQUEST['order_id'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['order_id'] ) ) : 0; // Check if order id is really an ID from shop_order post type. $order = wc_get_order( $order_id ); if ( ! is_a( $order, 'WC_Order' ) ) { throw new \Exception( esc_html__( 'Order does not exist!', 'postnl-for-woocommerce' ) ); } $response = $this->service_factory()->smart_returns_service()->generate( $order ); $to = $order->get_billing_email(); $emails = WC()->mailer()->get_emails(); if ( empty( $emails ) || ! isset( $emails['WC_Smart_Return_Email'] ) ) { throw new \Exception( esc_html__( 'Email could not be sent', 'postnl-for-woocommerce' ) ); } $email = $emails['WC_Smart_Return_Email']; $email->recipient = $to; if ( 'v4' === ( $response['api_version'] ?? '' ) ) { // V4 retailPrint: show the printcode/barcode image in the email body, no PDF attachment. if ( empty( $response['content'] ) ) { throw new \Exception( esc_html__( 'Printcode could not be found', 'postnl-for-woocommerce' ) ); } $email->printcode_content = $response['content']; $email->printcode_mime = $response['mime'] ?? 'image/png'; $email->printcode_barcode = (string) ( $response['barcode'] ?? '' ); } else { // Legacy: pull the PrintcodeLabel PDF from the response and attach it. $printcode_label_content = null; if ( ! empty( $response['ResponseShipments'] ) ) { foreach ( $response['ResponseShipments'] as $shipment ) { if ( empty( $shipment['Labels'] ) ) { continue; } foreach ( $shipment['Labels'] as $label ) { if ( isset( $label['Labeltype'] ) && 'PrintcodeLabel' === $label['Labeltype'] ) { $printcode_label_content = $label['Content']; break 2; } } } } if ( ! $printcode_label_content ) { throw new \Exception( esc_html__( 'The Smart Return printcode could not be found.', 'postnl-for-woocommerce' ) ); } $upload_dir = wp_upload_dir(); $file_path = $upload_dir['path'] . '/printcode_label.pdf'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents, WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Writing the PostNL printcode PDF returned base64-encoded by the API. file_put_contents( $file_path, base64_decode( $printcode_label_content ) ); $email->attachment = $file_path; } $is_successful = $email->trigger( $order_id ); if ( $is_successful ) { wp_send_json_success( $to ); } throw new \Exception( esc_html__( 'Email could not be sent', 'postnl-for-woocommerce' ) ); } catch ( \Exception $e ) { wp_send_json_error( array( 'message' => $e->getMessage() ), ); } } /** * Add "18+" label beside adult products in the WooCommerce admin order page. * * @param int $item_id Order item ID. * @param WC_Order_Item $item Order item object. * * @return void */ public function add_adult_product_label_admin_order_page( int $item_id, WC_Order_Item $item ): void { if ( ! $item instanceof WC_Order_Item_Product ) { return; // Only handle product items. } $product = $item->get_product(); if ( ! $product ) { return; } $is_adult = Utils::is_adults_only_product( $product ); if ( $is_adult ) { echo '

    ' . esc_html__( '18+ Adults Only', 'postnl-for-woocommerce' ) . '

    '; } } }