success = array(); $this->errors = array(); include( 'class-wcmp-rest.php' ); include( 'class-wcmp-api.php' ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'wp_ajax_wc_postnl', array($this, 'export' )); add_action( 'wp_ajax_wc_postnl_frontend', array($this, 'frontend_api_request' )); add_action( 'wp_ajax_nopriv_wc_postnl_frontend', array($this, 'frontend_api_request' )); } public function admin_notices () { if ( isset($_GET['postnl_done']) ) { // only do this when for the user that initiated this $action_return = get_option( 'wcpostnl_admin_notices' ); $print_queue = get_option( 'wcpostnl_print_queue', array() ); if (!empty($action_return)) { foreach ($action_return as $type => $message) { if (in_array($type, array('success','error'))) { if ( $type == 'success' && !empty($print_queue) ) { $print_queue_store = sprintf('', json_encode(array_keys($print_queue['order_ids']))); $print_queue_offset_store = sprintf('', $print_queue['offset']); // dequeue delete_option( 'wcpostnl_print_queue' ); } printf('
%s
%s%s
';var_dump($response);echo '';die(); if (isset($response['body']['data']['ids'])) { $ids = array_shift($response['body']['data']['ids']); $shipment_id = $ids['id']; $this->success[$order_id] = $shipment_id; $created_shipments[] = $shipment_id; $shipment = array ( 'shipment_id' => $shipment_id, ); // save shipment data in order meta $this->save_shipment_data( $order, $shipment ); // process directly setting if ( isset(WooCommerce_PostNL()->general_settings['process_directly']) || $process === true ) { // flush cache until WC issue #13439 is fixed https://github.com/woocommerce/woocommerce/issues/13439 if (method_exists($order, 'save')) { $order->save(); } $this->get_labels( (array) $order_id, 'url' ); $this->get_shipment_data( $shipment_id, $order ); } // status automation if ( isset(WooCommerce_PostNL()->general_settings['order_status_automation']) && !empty(WooCommerce_PostNL()->general_settings['automatic_order_status']) ) { $order->update_status( WooCommerce_PostNL()->general_settings['automatic_order_status'], __( 'PostNL shipment created:', 'woocommerce-postnl' ) ); } } else { $this->errors[$order_id] = __( 'Unknown error', 'woocommerce-postnl' ); } } catch (Exception $e) { $this->errors[$order_id] = $e->getMessage(); } } // store shipment ids from this export if (!empty($created_shipments)) { WCX_Order::update_meta_data( $order, '_postnl_last_shipment_ids', $created_shipments ); } } // echo '
';var_dump($this->success);echo '';die(); if (!empty($this->success)) { $return['success'] = sprintf(__( '%s shipments successfully exported to PostNL', 'woocommerce-postnl' ), count($this->success)); $return['success_ids'] = $this->success; } return $return; } public function get_shipment_labels( $shipment_ids, $order_ids = array(), $label_response_type = NULL, $offset = 0 ) { $return = array(); $this->log("*** Label request started ***"); $this->log("Shipment ID's: ".implode(', ', $shipment_ids)); try { $api = $this->init_api(); $params = array(); if(WooCommerce_PostNL()->general_settings['label_format'] == 'A4'){ if (!empty($offset) && is_numeric ($offset)) { $portrait_positions = array( 2, 4, 1, 3 ); // positions are defined on landscape, but paper is filled portrait-wise $params['positions'] = implode( ';', array_slice($portrait_positions,$offset) ); } $params['format'] = 'A4'; }else{ $params['format'] = 'A6'; } if (isset($label_response_type) && $label_response_type == 'url') { $response = $api->get_shipment_labels( $shipment_ids, $params, 'link' ); $this->log("API response:\n".var_export($response, true)); // var_dump( $response ); if (isset($response['body']['data']['pdfs']['url'])) { $url = untrailingslashit( $api->APIURL ) . $response['body']['data']['pdfs']['url']; $return['url'] = $url; } else { $this->errors[] = __( 'Unknown error', 'woocommerce-postnl' ); } } else { $response = $api->get_shipment_labels( $shipment_ids, $params, 'pdf' ); if (isset($response['body'])) { $this->log("PDF data received"); $pdf_data = $response['body']; $output_mode = isset(WooCommerce_PostNL()->general_settings['download_display'])?WooCommerce_PostNL()->general_settings['download_display']:''; if ( $output_mode == 'display' ) { $this->stream_pdf( $pdf_data, $order_ids ); } else { $this->download_pdf( $pdf_data, $order_ids ); } } else { $this->log("Unknown error, API response:\n".var_export($response, true)); $this->errors[] = __( 'Unknown error', 'woocommerce-postnl' ); } // echo '
';var_dump($response);echo '';die(); } } catch (Exception $e) { $this->errors[] = $e->getMessage(); } return $return; } public function get_labels( $order_ids, $label_response_type = NULL, $offset = 0 ) { $shipment_ids = $this->get_shipment_ids( $order_ids, array( 'only_last' => true ) ); if ( empty($shipment_ids) ) { $this->log("*** Failed label request (not exported yet) ***"); $this->errors[] = __( 'The selected orders have not been exported to PostNL yet!', 'woocommerce-postnl' ); return array(); } return $this->get_shipment_labels( $shipment_ids, $order_ids, $label_response_type, $offset ); } public function modal_dialog( $order_ids, $dialog ) { // check for JSON if (is_string($order_ids) && strpos($order_ids, '[') !== false ) { $order_ids = json_decode(stripslashes($order_ids)); } // cast as array for single exports $order_ids = (array) $order_ids; include('views/wcmp-bulk-options-form.php'); die(); } public function modal_success_page( $request, $result ) { include('views/wcmp-modal-result-page.php'); die(); } public function frontend_api_request() { // TODO: check nonce $params = $_REQUEST; // filter non API params $api_params = array( 'cc' => '', 'postal_code' => '', 'number' => '', 'carrier' => '', 'delivery_time' => '', 'delivery_date' => '', 'cutoff_time' => '', 'dropoff_days' => '', 'dropoff_delay' => '', 'deliverydays_window' => '', 'exclude_delivery_type' => '', ); $params = array_intersect_key($params, $api_params); $api = $this->init_api(); try { $response = $api->get_delivery_options( $params, true ); @header('Content-type: application/json; charset=utf-8'); echo $response['body']; } catch (Exception $e) { @header("HTTP/1.1 503 service unavailable"); } die(); } public function init_api () { // $user = WooCommerce_PostNL()->general_settings['api_username']; if ( !isset(WooCommerce_PostNL()->general_settings['api_key']) ) { return false; } $key = WooCommerce_PostNL()->general_settings['api_key']; $api = new WC_PostNL_API( $key ); return $api; } public function get_order_shipment_data( $order_ids, $type = 'standard' ) { foreach( $order_ids as $order_id ) { // get order $order = WCX::get_order( $order_id ); $shipment = array( 'recipient' => $this->get_recipient( $order ), 'options' => $this->get_options( $order ), 'carrier' => 1, // default to POSTNL for now ); if ( $pickup = $this->is_pickup( $order ) ) { // $pickup_time = array_shift($pickup['time']); // take first element in time array $shipment['pickup'] = array( 'postal_code' => $pickup['postal_code'], 'street' => $pickup['street'], 'city' => $pickup['city'], 'number' => $pickup['number'], 'location_name' => $pickup['location'], ); } $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' ); if ( $this->is_world_shipment_country( $shipping_country ) ) { $customs_declaration = $this->get_customs_declaration( $order ); $shipment['customs_declaration'] = $customs_declaration; $shipment['physical_properties'] = array( 'weight' => $customs_declaration['weight'], ); } /* disabled for now $concept_shipments = $this->get_shipment_ids( (array) $order_id, array( 'only_concepts' => true, 'only_last' => true ) ); if ( !empty($concept_shipments) ) { $shipment['id'] = array_pop($concept_shipments); } */ $shipments[] = $shipment; } return $shipments; } public function get_recipient( $order ) { $shipping_name = method_exists($order, 'get_formatted_shipping_full_name') ? $order->get_formatted_shipping_full_name() : trim( $order->shipping_first_name . ' ' . $order->shipping_last_name ); $address = array( 'cc' => (string) WCX_Order::get_prop( $order, 'shipping_country' ), 'city' => (string) WCX_Order::get_prop( $order, 'shipping_city' ), 'person' => $shipping_name, 'company' => (string) WCX_Order::get_prop( $order, 'shipping_company' ), 'email' => (string) WCX_Order::get_prop( $order, 'billing_email' ), 'phone' => isset(WooCommerce_PostNL()->export_defaults['connect_phone']) ? WCX_Order::get_prop( $order, 'billing_phone' ) : '', ); $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' ); if ( $shipping_country == 'NL' ) { // use billing address if old 'pakjegemak' (1.5.6 and older) if ( $pgaddress = WCX_Order::get_meta( $order, '_postnl_pgaddress' ) ) { $billing_name = method_exists($order, 'get_formatted_billing_full_name') ? $order->get_formatted_billing_full_name() : trim( $order->billing_first_name . ' ' . $order->billing_last_name ); $address_intl = array( 'city' => (string) WCX_Order::get_prop( $order, 'billing_city' ), 'person' => $billing_name, 'company' => (string) WCX_Order::get_prop( $order, 'billing_company' ), 'street' => (string) WCX_Order::get_meta( $order, '_billing_street_name' ), 'number' => (string) WCX_Order::get_meta( $order, '_billing_house_number' ), 'number_suffix' => (string) WCX_Order::get_meta( $order, '_billing_house_number_suffix' ), 'postal_code' => (string) WCX_Order::get_prop( $order, 'billing_postcode' ), ); } else { $address_intl = array( 'street' => (string) WCX_Order::get_meta( $order, '_shipping_street_name' ), 'number' => (string) WCX_Order::get_meta( $order, '_shipping_house_number' ), 'number_suffix' => (string) WCX_Order::get_meta( $order, '_shipping_house_number_suffix' ), 'postal_code' => (string) WCX_Order::get_prop( $order, 'shipping_postcode' ), ); } } else { $address_intl = array( 'postal_code' => (string) WCX_Order::get_prop( $order, 'shipping_postcode' ), 'street' => (string) WCX_Order::get_prop( $order, 'shipping_address_1' ), 'street_additional_info' => (string) WCX_Order::get_prop( $order, 'shipping_address_2' ), 'region' => (string) WCX_Order::get_prop( $order, 'shipping_state' ), ); } $address = array_merge( $address, $address_intl); return apply_filters( 'wc_postnl_recipient', $address, $order ); } public function get_options( $order ) { // parse description if (isset(WooCommerce_PostNL()->export_defaults['label_description'])) { $description = $this->replace_shortcodes( WooCommerce_PostNL()->export_defaults['label_description'], $order ); } else { $description = ''; } // use shipment options from order when available $shipment_options = WCX_Order::get_meta( $order, '_postnl_shipment_options' ); if (!empty($shipment_options)) { $emty_defaults = array( 'package_type' => 1, 'only_recipient' => 0, 'signature' => 0, 'return' => 0, 'label_description' => '', 'insured_amount' => 0, ); $options = array_merge($emty_defaults, $shipment_options); } else { if (isset(WooCommerce_PostNL()->export_defaults['insured']) && WooCommerce_PostNL()->export_defaults['insured_amount'] == '' && isset(WooCommerce_PostNL()->export_defaults['insured_amount_custom'])) { $insured_amount = WooCommerce_PostNL()->export_defaults['insured_amount_custom']; } elseif (isset(WooCommerce_PostNL()->export_defaults['insured']) && isset(WooCommerce_PostNL()->export_defaults['insured_amount'])) { $insured_amount = WooCommerce_PostNL()->export_defaults['insured_amount']; } else { $insured_amount = 0; } $options = array( 'package_type' => $this->get_package_type_for_order( $order ), 'only_recipient' => (isset(WooCommerce_PostNL()->export_defaults['only_recipient'])) ? 1 : 0, 'signature' => (isset(WooCommerce_PostNL()->export_defaults['signature'])) ? 1 : 0, 'return' => (isset(WooCommerce_PostNL()->export_defaults['return'])) ? 1 : 0, 'label_description' => $description, 'insured_amount' => $insured_amount, ); } // convert insurance option if ( !isset($options['insurance']) && isset($options['insured_amount']) ) { if ($options['insured_amount'] > 0) { $options['insurance'] = array( 'amount' => (int) $options['insured_amount'] * 100, 'currency' => 'EUR', ); } unset($options['insured_amount']); unset($options['insured']); } // set insurance amount to int if already set if (isset($options['insurance'])) { $options['insurance']['amount'] = (int) $options['insurance']['amount']; } // remove frontend insurance option values if (isset($options['insured_amount'])) { unset($options['insured_amount']); } if (isset($options['insured'])) { unset($options['insured']); } // load delivery options $postnl_delivery_options = WCX_Order::get_meta( $order, '_postnl_delivery_options' ); // set delivery type $options['delivery_type'] = $this->get_delivery_type( $order, $postnl_delivery_options ); // Options for Pickup and Pickup express delivery types: // always enable signature on receipt if ( $this->is_pickup( $order, $postnl_delivery_options ) ) { $options['signature'] = 1; } // delivery date (postponed delivery & pickup) if ($delivery_date = $this->get_delivery_date( $order, $postnl_delivery_options ) ) { $date_time = explode(' ', $delivery_date); // split date and time // only add if date is in the future $timestamp = strtotime($date_time[0]); if ( $timestamp < time() ) { $new_timestamp= $this->get_next_delivery_day($timestamp); $delivery_date = date( 'Y-m-d h:i:s', $new_timestamp ); } $options['delivery_date'] = $delivery_date; } // options signed & recipient only $postnl_signed = WCX_Order::get_meta( $order, '_postnl_signed' ); if (!empty($postnl__signed)) { $options['signature'] = 1; } $postnl_only_recipient = WCX_Order::get_meta( $order, '_postnl_only_recipient' ); if (!empty($postnl_only_recipient)) { $options['only_recipient'] = 1; } // allow prefiltering consignment data $options = apply_filters( 'wc_postnl_order_shipment_options', $options, $order ); // PREVENT ILLEGAL SETTINGS // convert numeric strings to int $int_options = array( 'package_type', 'delivery_type', 'only_recipient', 'signature', 'return' ); foreach ($options as $key => &$value) { if ( in_array($key, $int_options) ) { $value = (int) $value; } } // disable options for mailbox package and unpaid letter // echo '
';var_dump($package_type);echo '';die(); if ( $options['package_type'] != 1 ) { $illegal_options = array( 'delivery_type', 'only_recipient', 'signature', 'return', 'insurance', 'delivery_date' ); foreach ($options as $key => $option) { if (in_array($key, $illegal_options)) { unset($options[$key]); } } } return $options; } /** * @param int $timestamp * * @return false|string */ private function get_next_delivery_day($timestamp) { $weekDay = date('w', $timestamp); $new_timestamp = strtotime( '+1 day', $timestamp ); if ($weekDay == 0 || $weekDay == 1 || $new_timestamp < time() ) { $new_timestamp = $this->get_next_delivery_day( $new_timestamp ); } return $new_timestamp; } public function get_customs_declaration( $order ) { $weight = (int) round( $this->get_parcel_weight( $order ) * 1000 ); $invoice = $this->get_invoice_number( $order ); $contents = (int) ( (isset(WooCommerce_PostNL()->export_defaults['package_contents'])) ? WooCommerce_PostNL()->export_defaults['package_contents'] : 1 ); // Item defaults: // Classification $default_hs_code = (isset(WooCommerce_PostNL()->export_defaults['hs_code'])) ? WooCommerce_PostNL()->export_defaults['hs_code'] : ''; // Country (=shop base) $country = WC()->countries->get_base_country(); $items = array(); foreach ( $order->get_items() as $item_id => $item ) { $product = $order->get_product_from_item( $item ); if ( !empty( $product )) { // Description $description = $item['name']; // Amount $amount = (int) ( isset($item['qty']) ? $item['qty'] : 1 ); // Weight (total item weight in grams) $weight = (int) round( $this->get_item_weight_kg ( $item, $order ) * 1000 ); // Item value (in cents) $item_value = array( 'amount' => (int) round( ( $item['line_total'] + $item['line_tax'] ) * 100 ), 'currency' => WCX_Order::get_prop( $order, 'currency' ), ); // Classification / HS Code $classification = WCX_Product::get_meta( $product, '_postnl_hs_code', true ); if (empty($classification)) { $classification = $default_hs_code; } // add item to item list $items[] = compact( 'description', 'amount', 'weight', 'item_value', 'classification', 'country' ); } } return compact( 'weight', 'invoice', 'contents', 'items' ); } public function validate_shipments( $shipments, $output_errors = true ) { $missing_hs_codes = 0; foreach ($shipments as $key => $shipment) { // check customs declaration for HS codes if (isset($shipment['customs_declaration']) && !empty($shipment['customs_declaration']['items'])) { foreach ($shipment['customs_declaration']['items'] as $key => $item) { if (empty($item['classification'])) { unset($shipments[$key]); $missing_hs_codes++; break; } } } if ($output_errors === true && $missing_hs_codes > 0) { $this->errors[] = sprintf( __( '%d shipments missing HS codes - not exported.', 'woocommerce-postnl' ), $missing_hs_codes); } } return $shipments; } public function get_shipment_ids( $order_ids, $args ) { $shipment_ids = array(); foreach ($order_ids as $order_id) { $order = WCX::get_order( $order_id ); $order_shipments = WCX_Order::get_meta( $order, '_postnl_shipments' ); if (!empty($order_shipments)) { $order_shipment_ids = array(); // exclude concepts or only concepts foreach ( $order_shipments as $key => $shipment) { if (isset($args['exclude_concepts']) && empty($shipment['tracktrace'])) { continue; } if (isset($args['only_concepts']) && !empty($shipment['tracktrace'])) { continue; } $order_shipment_ids[] = $shipment['shipment_id']; } if (isset($args['only_last'])) { $last_shipment_ids = WCX_Order::get_meta( $order, '_postnl_last_shipment_ids' ); if ( !empty( $last_shipment_ids ) && is_array( $last_shipment_ids ) ) { foreach ($order_shipment_ids as $order_shipment_id) { if ( in_array($order_shipment_id, $last_shipment_ids ) ) { $shipment_ids[] = $order_shipment_id; } } } else { $shipment_ids[] = array_pop( $order_shipment_ids ); } } else { $shipment_ids[] = array_merge( $shipment_ids, $order_shipment_ids ); } } } return $shipment_ids; } public function save_shipment_data ( $order, $shipment ) { if ( empty($shipment) ) { return false; } $new_shipments = array(); $new_shipments[$shipment['shipment_id']] = $shipment; // don't store full shipment data // if (isset($shipment['shipment'])) { // unset($shipment['shipment']); // } if ( isset(WooCommerce_PostNL()->general_settings['keep_shipments']) ) { if ( $old_shipments = WCX_Order::get_meta( $order, '_postnl_shipments' ) ) { $shipments = $old_shipments; foreach ($new_shipments as $shipment_id => $shipment) { $shipments[$shipment_id] = $shipment; } } } $shipments = isset($shipments) ? $shipments : $new_shipments; WCX_Order::update_meta_data( $order, '_postnl_shipments', $shipments ); return; } public function get_package_type_from_shipping_method( $shipping_method, $shipping_class, $shipping_country ) { $package_type = 1; if (isset(WooCommerce_PostNL()->export_defaults['shipping_methods_package_types'])) { if ( strpos($shipping_method, "table_rate:") === 0 && class_exists('WC_Table_Rate_Shipping') ) { // Automattic / WooCommerce table rate // use full method = method_id:instance_id:rate_id $shipping_method_id = $shipping_method; } else { // non table rates if ( strpos($shipping_method, ':') !== false ) { // means we have method_id:instance_id $shipping_method = explode(':', $shipping_method); $shipping_method_id = $shipping_method[0]; $shipping_method_instance = $shipping_method[1]; } else { $shipping_method_id = $shipping_method; } // add class if we have one if (!empty($shipping_class)) { $shipping_method_id_class = "{$shipping_method_id}:{$shipping_class}"; } } foreach (WooCommerce_PostNL()->export_defaults['shipping_methods_package_types'] as $package_type_key => $package_type_shipping_methods ) { // check if we have a match with the predefined methods // fallback to bare method (without class) (if bare method also defined in settings) if (in_array($shipping_method_id, $package_type_shipping_methods) || (!empty($shipping_method_id_class) && in_array($shipping_method_id_class, $package_type_shipping_methods))) { $package_type = $package_type_key; break; } } } // disable mailbox package outside NL if ($shipping_country != 'NL' && $package_type == 2 ) { $package_type = 1; } return $package_type; } // determine appropriate package type for this order public function get_package_type_for_order( $order ) { $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' ); // get shipping methods from order $order_shipping_methods = $order->get_items('shipping'); if ( !empty( $order_shipping_methods ) ) { // we're taking the first (we're not handling multiple shipping methods as of yet) $order_shipping_method = array_shift($order_shipping_methods); $order_shipping_method = $order_shipping_method['method_id']; $order_shipping_class = WCX_Order::get_meta( $order, '_postnl_highest_shipping_class' ); if (empty($order_shipping_class)) { $order_shipping_class = $this->get_order_shipping_class( $order, $order_shipping_method ); } $package_type = $this->get_package_type_from_shipping_method( $order_shipping_method, $order_shipping_class, $shipping_country ); } // fallbacks if no match from previous if (!isset($package_type)) { if ((isset(WooCommerce_PostNL()->export_defaults['package_type']))) { $package_type = WooCommerce_PostNL()->export_defaults['package_type']; } else { $package_type = 1; // 1. package | 2. mailbox package | 3. letter } } // always parcel for Pickup and Pickup express delivery types. if ( $this->is_pickup( $order ) ) { $package_type = 1; } return $package_type; } public function get_package_types( $shipment_type = 'shipment' ) { $package_types = array( 1 => __( 'Parcel' , 'woocommerce-postnl' ), 2 => __( 'Mailbox package' , 'woocommerce-postnl' ), 3 => __( 'Unpaid letter' , 'woocommerce-postnl' ), ); if ( $shipment_type == 'return' ) { unset($package_types[2]); unset($package_types[3]); } return $package_types; } public function get_package_name( $package_type ) { $package_types = $this->get_package_types(); $package_name = isset($package_types[$package_type]) ? $package_types[$package_type] : __( 'Unknown' , 'woocommerce-postnl' ); return $package_name; } public function parse_errors( $errors ) { $parsed_errors = array(); foreach ($errors as $key => $error) { // check if we have an order_id if ($key > 10) { $parsed_errors[] = sprintf("%s %s: %s", __( 'Order', 'woocommerce-postnl' ), $key, $error ); } else { $parsed_errors[] = $error; } } if (count($parsed_errors) == 1) { $html = array_shift($parsed_errors); } else { foreach ($parsed_errors as &$parsed_error) { $parsed_error = "