Plugin Directory

Changeset 3411298


Ignore:
Timestamp:
12/04/2025 06:16:00 PM (3 months ago)
Author:
jneshipping
Message:

Update Version to 1.2.1 - add shipping cost adjustment and bug fix

Location:
jne-shipping-official
Files:
11 edited
29 copied

Legend:

Unmodified
Added
Removed
  • jne-shipping-official/tags/1.2.1/admin/class-jne-woocommerce-admin.php

    r3399074 r3411298  
    6464    add_action('wp_ajax_jneshof_edit_customer', [$this, 'ajax_edit_customer']);
    6565    add_action('wp_ajax_jneshof_delete_customer', [$this, 'ajax_delete_customer']);
     66    add_action('wp_ajax_jneshof_get_provinces', [$this, 'ajax_get_provinces']);
     67    add_action('wp_ajax_jneshof_get_cities', [$this, 'ajax_get_cities']);
     68    add_action('wp_ajax_jneshof_get_districts', [$this, 'ajax_get_districts']);
     69    add_action('wp_ajax_jneshof_save_adjustment', [$this, 'ajax_save_adjustment']);
     70    add_action('wp_ajax_jneshof_delete_adjustment', [$this, 'ajax_delete_adjustment']);
    6671
    6772    add_action('admin_footer', [$this, 'hide_wp_footer']);
     
    113118    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-generate-awb.php';
    114119    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-dashboard.php';
     120    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-shipping-cost-adjustment.php';
    115121  }
    116122
     
    398404    add_submenu_page(
    399405      'jneshof-landing',
     406      __('Shipping Cost Adjustments', 'jne-shipping-official'),
     407      __('Shipping Cost Adjustments', 'jne-shipping-official'),
     408      'manage_options',
     409      'jneshof-shipping-cost-adjustments',
     410      [$this, 'display_shipping_cost_adjustments_page']
     411    );
     412    add_submenu_page(
     413      'jneshof-landing',
    400414      __('Bantuan', 'jne-shipping-official'),
    401415      __('Bantuan', 'jne-shipping-official'),
     
    449463  {
    450464    return include_once plugin_dir_path(__FILE__) . 'partials/jne-woocommerce-admin-dashboard-display.php';
     465  }
     466
     467  public function display_shipping_cost_adjustments_page()
     468  {
     469    return include_once plugin_dir_path(__FILE__) . 'partials/jne-woocommerce-admin-shipping-cost-adjustments-display.php';
    451470  }
    452471
     
    782801   * AJAX handler for deleting customer ID
    783802   */
     803  /**
     804   * AJAX handler for getting provinces
     805   */
     806  public function ajax_get_provinces()
     807  {
     808    if (!current_user_can('manage_options')) {
     809      wp_send_json_error('Insufficient permissions');
     810      return;
     811    }
     812
     813    $api = Jneshof_Woocommerce_API::get_instance();
     814    $provinces = $api->get_provinces();
     815
     816    if (is_wp_error($provinces)) {
     817      wp_send_json_error($provinces->get_error_message());
     818      return;
     819    }
     820
     821    wp_send_json_success($provinces);
     822  }
     823
     824  /**
     825   * AJAX handler for getting cities
     826   */
     827  public function ajax_get_cities()
     828  {
     829    if (!current_user_can('manage_options')) {
     830      wp_send_json_error('Insufficient permissions');
     831      return;
     832    }
     833
     834    $province_code = absint($_GET['province_code'] ?? 0);
     835    $page = absint($_GET['page'] ?? 1);
     836    $per_page = absint($_GET['per_page'] ?? 10);
     837    $search = sanitize_text_field($_GET['search'] ?? '');
     838
     839    if (empty($province_code)) {
     840      wp_send_json_error('Province code is required');
     841      return;
     842    }
     843
     844    $api = Jneshof_Woocommerce_API::get_instance();
     845    $response = $api->get_cities($province_code, $page, $per_page, $search);
     846
     847    if (is_wp_error($response)) {
     848      wp_send_json_error($response->get_error_message());
     849      return;
     850    }
     851
     852    wp_send_json_success($response);
     853  }
     854
     855  /**
     856   * AJAX handler for getting districts
     857   */
     858  public function ajax_get_districts()
     859  {
     860    if (!current_user_can('manage_options')) {
     861      wp_send_json_error('Insufficient permissions');
     862      return;
     863    }
     864
     865    $province_code = absint($_GET['province_code'] ?? 0);
     866    $city_name = sanitize_text_field($_GET['city_name'] ?? '');
     867    $page = absint($_GET['page'] ?? 1);
     868    $per_page = absint($_GET['per_page'] ?? 10);
     869    $search = sanitize_text_field($_GET['search'] ?? '');
     870
     871    if (empty($province_code) || empty($city_name)) {
     872      wp_send_json_error('Province code and city name are required');
     873      return;
     874    }
     875
     876    $api = Jneshof_Woocommerce_API::get_instance();
     877    $response = $api->get_districts($province_code, $city_name, $page, $per_page, $search);
     878
     879    if (is_wp_error($response)) {
     880      wp_send_json_error($response->get_error_message());
     881      return;
     882    }
     883
     884    wp_send_json_success($response);
     885  }
     886
     887  /**
     888   * AJAX handler for saving adjustment
     889   */
     890  public function ajax_save_adjustment()
     891  {
     892    if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'jneshof_adjustment_nonce')) {
     893      wp_send_json_error('Security check failed');
     894      return;
     895    }
     896
     897    if (!current_user_can('manage_options')) {
     898      wp_send_json_error('Insufficient permissions');
     899      return;
     900    }
     901
     902    $adjustment_handler = Jneshof_Admin_Shipping_Cost_Adjustment::get_instance();
     903
     904    $data = [
     905      'id' => sanitize_text_field(wp_unslash($_POST['id'] ?? '')),
     906      'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
     907      'adjustment_type' => sanitize_text_field(wp_unslash($_POST['adjustment_type'] ?? '')),
     908      'adjustment_value' => floatval($_POST['adjustment_value'] ?? 0),
     909      'is_active' => isset($_POST['is_active']) && (intval($_POST['is_active']) === 1) ? 1 : 0,
     910      'date_from' => sanitize_text_field(wp_unslash($_POST['date_from'] ?? '')),
     911      'date_to' => sanitize_text_field(wp_unslash($_POST['date_to'] ?? '')),
     912      'services' => isset($_POST['services']) ? array_map('sanitize_text_field', (array) $_POST['services']) : [],
     913      'provinces' => isset($_POST['provinces']) ? $this->sanitize_provinces($_POST['provinces']) : [],
     914      'cities' => isset($_POST['cities']) ? array_map('sanitize_text_field', (array) $_POST['cities']) : [],
     915      'districts' => isset($_POST['districts']) ? array_map('sanitize_text_field', (array) $_POST['districts']) : [],
     916    ];
     917
     918    $id = $adjustment_handler->save_adjustment($data);
     919   
     920    if ($id) {
     921      wp_send_json_success(['id' => $id, 'message' => 'Adjustment saved successfully']);
     922    } else {
     923      wp_send_json_error('Failed to save adjustment');
     924    }
     925  }
     926
     927  /**
     928   * AJAX handler for deleting adjustment
     929   */
     930  public function ajax_delete_adjustment()
     931  {
     932    if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'jneshof_adjustment_nonce')) {
     933      wp_send_json_error('Security check failed');
     934      return;
     935    }
     936
     937    if (!current_user_can('manage_options')) {
     938      wp_send_json_error('Insufficient permissions');
     939      return;
     940    }
     941
     942    $id = sanitize_text_field(wp_unslash($_POST['id'] ?? ''));
     943   
     944    if (empty($id)) {
     945      wp_send_json_error('Adjustment ID is required');
     946      return;
     947    }
     948
     949    $adjustment_handler = Jneshof_Admin_Shipping_Cost_Adjustment::get_instance();
     950    $deleted = $adjustment_handler->delete_adjustment($id);
     951
     952    if ($deleted) {
     953      wp_send_json_success(['message' => 'Adjustment deleted successfully']);
     954    } else {
     955      wp_send_json_error('Failed to delete adjustment');
     956    }
     957  }
     958
     959  /**
     960   * Sanitize provinces data
     961   */
     962  private function sanitize_provinces($provinces)
     963  {
     964    if (!is_array($provinces)) {
     965      return [];
     966    }
     967
     968    $sanitized = [];
     969    foreach ($provinces as $province) {
     970      if ($province === 'all') {
     971        $sanitized[] = 'all';
     972      } elseif (is_array($province)) {
     973        $sanitized[] = [
     974          'code' => absint($province['code'] ?? 0),
     975          'name' => sanitize_text_field($province['name'] ?? '')
     976        ];
     977      } elseif (is_string($province)) {
     978        $sanitized[] = sanitize_text_field($province);
     979      }
     980    }
     981    return $sanitized;
     982  }
     983
    784984  public function ajax_delete_customer()
    785985  {
  • jne-shipping-official/tags/1.2.1/admin/includes/class-jne-admin-generate-awb.php

    r3398965 r3411298  
    5858        }
    5959
     60        // Check if order already has tracking number
    6061        $jne_tracking_number = $order->getJNETrackingNumber();
    61         if (!$jne_tracking_number) {
    62           $skipped_orders[] = "Order #$order_id: No JNE tracking number";
     62        if (empty($jne_tracking_number)) {
     63          $skipped_orders[] = "Order #$order_id: No JNE tracking number. Please generate CNote first to create shipping label.";
    6364          continue;
    6465        }
     
    136137      }
    137138
     139      // Check if order already has tracking number
    138140      $jne_tracking_number = $order->getJNETrackingNumber();
    139       if (!$jne_tracking_number) {
    140         wp_die('JNE tracking number not found');
     141      if (empty($jne_tracking_number)) {
     142        wp_die('Order does not have a JNE tracking number. Please generate CNote first to create shipping label.');
    141143      }
    142144
  • jne-shipping-official/tags/1.2.1/admin/includes/class-jne-admin-order-list.php

    r3399074 r3411298  
    324324    }
    325325
     326    // Check if order already has tracking number
    326327    $jne_tracking_number = $order->getJNETrackingNumber();
    327     if(!$jne_tracking_number) {
    328       throw new Exception('JNE tracking number not found');
     328    if(empty($jne_tracking_number)) {
     329      throw new Exception('Order does not have a JNE tracking number. Please generate shipping label (AWB) first before requesting pickup.');
    329330    }
    330331
  • jne-shipping-official/tags/1.2.1/admin/includes/class-jne-admin-shipping-method.php

    r3398965 r3411298  
    22
    33if (!defined('ABSPATH')) {
    4     exit;
     4  exit;
     5}
     6
     7// Load adjustment handler if not already loaded
     8if (!class_exists('Jneshof_Admin_Shipping_Cost_Adjustment')) {
     9  require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-shipping-cost-adjustment.php';
    510}
    611
     
    148153
    149154          // Only add rate if price is valid
    150           if ($price > 0) {
     155          // Note: Shipping cost adjustments will be applied as cart fees (similar to insurance)
     156          if ($price >= 0) {
    151157            $this->add_rate([
    152158              'id' => $this->id . ':' . $original_service_code,
  • jne-shipping-official/tags/1.2.1/composer.json

    r3399074 r3411298  
    22    "name": "jne-shipping/jne-shipping-official",
    33    "description": "WordPress plugin integrated with WooCommerce for JNE shipping services",
    4     "version": "1.1.0",
     4    "version": "1.2.1",
    55    "type": "wordpress-plugin",
    66    "license": "GPL-2.0+",
  • jne-shipping-official/tags/1.2.1/includes/class-jne-woocommerce-api.php

    r3387194 r3411298  
    191191  }
    192192
    193   public function get_provinces()
    194   {
    195     $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
    196 
    197     return $this->request('/v1/jne/provinces', [], 'GET', [
    198       'Authorization' => $access_key
    199     ]);
    200   }
    201 
    202193  public function get_tariff_by_zipcode($destination_zipcode, $weight)
    203194  {
     
    253244
    254245  /**
     246   * Get provinces
     247   * @return array|WP_Error
     248   */
     249  public function get_provinces()
     250  {
     251    $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
     252
     253    $response = $this->request('/v1/jne/provinces', [], 'GET', [
     254      'Authorization' => $access_key
     255    ]);
     256
     257    if (is_wp_error($response)) {
     258      return [];
     259    }
     260
     261    return $response['data'] ?? [];
     262  }
     263
     264  /**
     265   * Get cities
     266   * @param int $province_code
     267   * @param int $page
     268   * @param int $per_page
     269   * @param string $search
     270   * @return array|WP_Error
     271   */
     272  public function get_cities($province_code, $page = 1, $per_page = 10, $search = '')
     273  {
     274    $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
     275
     276    $params = [
     277      'page' => $page,
     278      'per_page' => $per_page,
     279      'province_code' => $province_code
     280    ];
     281
     282    if (!empty($search)) {
     283      $params['search'] = $search;
     284    }
     285
     286    $query_string = '?' . http_build_query($params);
     287
     288    return $this->request('/v1/jne/cities' . $query_string, [], 'GET', [
     289      'Authorization' => $access_key
     290    ]);
     291  }
     292
     293  /**
     294   * Get districts
     295   * @param int $province_code
     296   * @param string $city_name
     297   * @param int $page
     298   * @param int $per_page
     299   * @param string $search
     300   * @return array|WP_Error
     301   */
     302  public function get_districts($province_code, $city_name, $page = 1, $per_page = 10, $search = '')
     303  {
     304    $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
     305
     306    $params = [
     307      'page' => $page,
     308      'per_page' => $per_page,
     309      'province_code' => $province_code,
     310      'city_name' => $city_name
     311    ];
     312
     313    if (!empty($search)) {
     314      $params['search'] = $search;
     315    }
     316
     317    $query_string = '?' . http_build_query($params);
     318
     319    return $this->request('/v1/jne/districts' . $query_string, [], 'GET', [
     320      'Authorization' => $access_key
     321    ]);
     322  }
     323
     324  /**
    255325   * Generate cnote
    256326   * @param array $data
  • jne-shipping-official/tags/1.2.1/jne-shipping-official.php

    r3399074 r3411298  
    2020 * Plugin Name:       JNE Shipping Official
    2121 * Description:       WordPress plugin integrated with WooCommerce for JNE shipping services
    22  * Version:           1.1.0
     22 * Version:           1.2.1
    2323 * Author:            PT. Tiki Jalur Nugraha Ekakurir
    2424 * Author URI:        https://jne.co.id/
     
    4848 * Rename this for your plugin and update it as you release new versions.
    4949 */
    50 define('JNESHOF_PLUGIN_VERSION', '1.1.0');
     50define('JNESHOF_PLUGIN_VERSION', '1.2.1');
    5151
    5252/**
  • jne-shipping-official/tags/1.2.1/public/class-jne-woocommerce-public.php

    r3387194 r3411298  
    6868        Jneshof_Checkout_Shipping_Insurance::get_instance();
    6969      }
     70      if (class_exists('Jneshof_Checkout_Shipping_Cost_Adjustment')) {
     71        Jneshof_Checkout_Shipping_Cost_Adjustment::get_instance();
     72      }
    7073      if (class_exists('Jneshof_Admin_Generate_Cnote')) {
    7174        Jneshof_Admin_Generate_Cnote::get_instance();
     
    7881    require_once plugin_dir_path(dirname(__FILE__)) . 'public/includes/jne-woocommerce-public-cod.php';
    7982    require_once plugin_dir_path(dirname(__FILE__)) . 'public/includes/jne-woocommerce-public-insurance.php';
     83    require_once plugin_dir_path(dirname(__FILE__)) . 'public/includes/jne-woocommerce-public-shipping-cost-adjustment.php';
    8084  }
    8185
  • jne-shipping-official/tags/1.2.1/readme.txt

    r3399074 r3411298  
    66Tested up to: 6.8
    77Requires PHP: 7.4
    8 Stable tag: 1.1.0
     8Stable tag: 1.2.1
    99License: GPL-2.0+
    1010License URI: http://www.gnu.org/licenses/gpl-2.0.txt
     
    9090
    9191== Changelog ==
     92
     93= 1.2.1 =
     94* Added validation to check if order has tracking number before requesting pickup
     95* Added validation to check if order has tracking number before generating shipping label (AWB)
     96* Improved error messages to guide users to generate CNote first when tracking number is missing
     97* Enhanced bulk AWB generation to skip orders without tracking numbers with clear error messages
     98
     99= 1.2.0 =
     100* Added Shipping Cost Adjustments feature - Create and manage adjustment profiles to modify shipping costs
     101* Support for multiple adjustment types: Free Shipping, Add/Subtract Fixed Amount, Add/Subtract Percentage
     102* Flexible condition matching based on Services, Provinces, Cities, and Districts
     103* Date range support for time-limited adjustments
     104* Active/Inactive toggle for each adjustment profile
     105* Dual list box interface for selecting cities and districts (add/remove mechanism)
     106* Automatic application of adjustments as cart fees
     107* Adjustment rates are displayed as "Shipping Cost Discount" or "Shipping Cost Addition" in cart/checkout
    92108
    93109= 1.1.0 =
  • jne-shipping-official/trunk/admin/class-jne-woocommerce-admin.php

    r3399074 r3411298  
    6464    add_action('wp_ajax_jneshof_edit_customer', [$this, 'ajax_edit_customer']);
    6565    add_action('wp_ajax_jneshof_delete_customer', [$this, 'ajax_delete_customer']);
     66    add_action('wp_ajax_jneshof_get_provinces', [$this, 'ajax_get_provinces']);
     67    add_action('wp_ajax_jneshof_get_cities', [$this, 'ajax_get_cities']);
     68    add_action('wp_ajax_jneshof_get_districts', [$this, 'ajax_get_districts']);
     69    add_action('wp_ajax_jneshof_save_adjustment', [$this, 'ajax_save_adjustment']);
     70    add_action('wp_ajax_jneshof_delete_adjustment', [$this, 'ajax_delete_adjustment']);
    6671
    6772    add_action('admin_footer', [$this, 'hide_wp_footer']);
     
    113118    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-generate-awb.php';
    114119    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-dashboard.php';
     120    require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-shipping-cost-adjustment.php';
    115121  }
    116122
     
    398404    add_submenu_page(
    399405      'jneshof-landing',
     406      __('Shipping Cost Adjustments', 'jne-shipping-official'),
     407      __('Shipping Cost Adjustments', 'jne-shipping-official'),
     408      'manage_options',
     409      'jneshof-shipping-cost-adjustments',
     410      [$this, 'display_shipping_cost_adjustments_page']
     411    );
     412    add_submenu_page(
     413      'jneshof-landing',
    400414      __('Bantuan', 'jne-shipping-official'),
    401415      __('Bantuan', 'jne-shipping-official'),
     
    449463  {
    450464    return include_once plugin_dir_path(__FILE__) . 'partials/jne-woocommerce-admin-dashboard-display.php';
     465  }
     466
     467  public function display_shipping_cost_adjustments_page()
     468  {
     469    return include_once plugin_dir_path(__FILE__) . 'partials/jne-woocommerce-admin-shipping-cost-adjustments-display.php';
    451470  }
    452471
     
    782801   * AJAX handler for deleting customer ID
    783802   */
     803  /**
     804   * AJAX handler for getting provinces
     805   */
     806  public function ajax_get_provinces()
     807  {
     808    if (!current_user_can('manage_options')) {
     809      wp_send_json_error('Insufficient permissions');
     810      return;
     811    }
     812
     813    $api = Jneshof_Woocommerce_API::get_instance();
     814    $provinces = $api->get_provinces();
     815
     816    if (is_wp_error($provinces)) {
     817      wp_send_json_error($provinces->get_error_message());
     818      return;
     819    }
     820
     821    wp_send_json_success($provinces);
     822  }
     823
     824  /**
     825   * AJAX handler for getting cities
     826   */
     827  public function ajax_get_cities()
     828  {
     829    if (!current_user_can('manage_options')) {
     830      wp_send_json_error('Insufficient permissions');
     831      return;
     832    }
     833
     834    $province_code = absint($_GET['province_code'] ?? 0);
     835    $page = absint($_GET['page'] ?? 1);
     836    $per_page = absint($_GET['per_page'] ?? 10);
     837    $search = sanitize_text_field($_GET['search'] ?? '');
     838
     839    if (empty($province_code)) {
     840      wp_send_json_error('Province code is required');
     841      return;
     842    }
     843
     844    $api = Jneshof_Woocommerce_API::get_instance();
     845    $response = $api->get_cities($province_code, $page, $per_page, $search);
     846
     847    if (is_wp_error($response)) {
     848      wp_send_json_error($response->get_error_message());
     849      return;
     850    }
     851
     852    wp_send_json_success($response);
     853  }
     854
     855  /**
     856   * AJAX handler for getting districts
     857   */
     858  public function ajax_get_districts()
     859  {
     860    if (!current_user_can('manage_options')) {
     861      wp_send_json_error('Insufficient permissions');
     862      return;
     863    }
     864
     865    $province_code = absint($_GET['province_code'] ?? 0);
     866    $city_name = sanitize_text_field($_GET['city_name'] ?? '');
     867    $page = absint($_GET['page'] ?? 1);
     868    $per_page = absint($_GET['per_page'] ?? 10);
     869    $search = sanitize_text_field($_GET['search'] ?? '');
     870
     871    if (empty($province_code) || empty($city_name)) {
     872      wp_send_json_error('Province code and city name are required');
     873      return;
     874    }
     875
     876    $api = Jneshof_Woocommerce_API::get_instance();
     877    $response = $api->get_districts($province_code, $city_name, $page, $per_page, $search);
     878
     879    if (is_wp_error($response)) {
     880      wp_send_json_error($response->get_error_message());
     881      return;
     882    }
     883
     884    wp_send_json_success($response);
     885  }
     886
     887  /**
     888   * AJAX handler for saving adjustment
     889   */
     890  public function ajax_save_adjustment()
     891  {
     892    if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'jneshof_adjustment_nonce')) {
     893      wp_send_json_error('Security check failed');
     894      return;
     895    }
     896
     897    if (!current_user_can('manage_options')) {
     898      wp_send_json_error('Insufficient permissions');
     899      return;
     900    }
     901
     902    $adjustment_handler = Jneshof_Admin_Shipping_Cost_Adjustment::get_instance();
     903
     904    $data = [
     905      'id' => sanitize_text_field(wp_unslash($_POST['id'] ?? '')),
     906      'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')),
     907      'adjustment_type' => sanitize_text_field(wp_unslash($_POST['adjustment_type'] ?? '')),
     908      'adjustment_value' => floatval($_POST['adjustment_value'] ?? 0),
     909      'is_active' => isset($_POST['is_active']) && (intval($_POST['is_active']) === 1) ? 1 : 0,
     910      'date_from' => sanitize_text_field(wp_unslash($_POST['date_from'] ?? '')),
     911      'date_to' => sanitize_text_field(wp_unslash($_POST['date_to'] ?? '')),
     912      'services' => isset($_POST['services']) ? array_map('sanitize_text_field', (array) $_POST['services']) : [],
     913      'provinces' => isset($_POST['provinces']) ? $this->sanitize_provinces($_POST['provinces']) : [],
     914      'cities' => isset($_POST['cities']) ? array_map('sanitize_text_field', (array) $_POST['cities']) : [],
     915      'districts' => isset($_POST['districts']) ? array_map('sanitize_text_field', (array) $_POST['districts']) : [],
     916    ];
     917
     918    $id = $adjustment_handler->save_adjustment($data);
     919   
     920    if ($id) {
     921      wp_send_json_success(['id' => $id, 'message' => 'Adjustment saved successfully']);
     922    } else {
     923      wp_send_json_error('Failed to save adjustment');
     924    }
     925  }
     926
     927  /**
     928   * AJAX handler for deleting adjustment
     929   */
     930  public function ajax_delete_adjustment()
     931  {
     932    if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'jneshof_adjustment_nonce')) {
     933      wp_send_json_error('Security check failed');
     934      return;
     935    }
     936
     937    if (!current_user_can('manage_options')) {
     938      wp_send_json_error('Insufficient permissions');
     939      return;
     940    }
     941
     942    $id = sanitize_text_field(wp_unslash($_POST['id'] ?? ''));
     943   
     944    if (empty($id)) {
     945      wp_send_json_error('Adjustment ID is required');
     946      return;
     947    }
     948
     949    $adjustment_handler = Jneshof_Admin_Shipping_Cost_Adjustment::get_instance();
     950    $deleted = $adjustment_handler->delete_adjustment($id);
     951
     952    if ($deleted) {
     953      wp_send_json_success(['message' => 'Adjustment deleted successfully']);
     954    } else {
     955      wp_send_json_error('Failed to delete adjustment');
     956    }
     957  }
     958
     959  /**
     960   * Sanitize provinces data
     961   */
     962  private function sanitize_provinces($provinces)
     963  {
     964    if (!is_array($provinces)) {
     965      return [];
     966    }
     967
     968    $sanitized = [];
     969    foreach ($provinces as $province) {
     970      if ($province === 'all') {
     971        $sanitized[] = 'all';
     972      } elseif (is_array($province)) {
     973        $sanitized[] = [
     974          'code' => absint($province['code'] ?? 0),
     975          'name' => sanitize_text_field($province['name'] ?? '')
     976        ];
     977      } elseif (is_string($province)) {
     978        $sanitized[] = sanitize_text_field($province);
     979      }
     980    }
     981    return $sanitized;
     982  }
     983
    784984  public function ajax_delete_customer()
    785985  {
  • jne-shipping-official/trunk/admin/includes/class-jne-admin-generate-awb.php

    r3398965 r3411298  
    5858        }
    5959
     60        // Check if order already has tracking number
    6061        $jne_tracking_number = $order->getJNETrackingNumber();
    61         if (!$jne_tracking_number) {
    62           $skipped_orders[] = "Order #$order_id: No JNE tracking number";
     62        if (empty($jne_tracking_number)) {
     63          $skipped_orders[] = "Order #$order_id: No JNE tracking number. Please generate CNote first to create shipping label.";
    6364          continue;
    6465        }
     
    136137      }
    137138
     139      // Check if order already has tracking number
    138140      $jne_tracking_number = $order->getJNETrackingNumber();
    139       if (!$jne_tracking_number) {
    140         wp_die('JNE tracking number not found');
     141      if (empty($jne_tracking_number)) {
     142        wp_die('Order does not have a JNE tracking number. Please generate CNote first to create shipping label.');
    141143      }
    142144
  • jne-shipping-official/trunk/admin/includes/class-jne-admin-order-list.php

    r3399074 r3411298  
    324324    }
    325325
     326    // Check if order already has tracking number
    326327    $jne_tracking_number = $order->getJNETrackingNumber();
    327     if(!$jne_tracking_number) {
    328       throw new Exception('JNE tracking number not found');
     328    if(empty($jne_tracking_number)) {
     329      throw new Exception('Order does not have a JNE tracking number. Please generate shipping label (AWB) first before requesting pickup.');
    329330    }
    330331
  • jne-shipping-official/trunk/admin/includes/class-jne-admin-shipping-method.php

    r3398965 r3411298  
    22
    33if (!defined('ABSPATH')) {
    4     exit;
     4  exit;
     5}
     6
     7// Load adjustment handler if not already loaded
     8if (!class_exists('Jneshof_Admin_Shipping_Cost_Adjustment')) {
     9  require_once plugin_dir_path(dirname(__FILE__)) . 'admin/includes/class-jne-admin-shipping-cost-adjustment.php';
    510}
    611
     
    148153
    149154          // Only add rate if price is valid
    150           if ($price > 0) {
     155          // Note: Shipping cost adjustments will be applied as cart fees (similar to insurance)
     156          if ($price >= 0) {
    151157            $this->add_rate([
    152158              'id' => $this->id . ':' . $original_service_code,
  • jne-shipping-official/trunk/composer.json

    r3399074 r3411298  
    22    "name": "jne-shipping/jne-shipping-official",
    33    "description": "WordPress plugin integrated with WooCommerce for JNE shipping services",
    4     "version": "1.1.0",
     4    "version": "1.2.1",
    55    "type": "wordpress-plugin",
    66    "license": "GPL-2.0+",
  • jne-shipping-official/trunk/includes/class-jne-woocommerce-api.php

    r3387194 r3411298  
    191191  }
    192192
    193   public function get_provinces()
    194   {
    195     $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
    196 
    197     return $this->request('/v1/jne/provinces', [], 'GET', [
    198       'Authorization' => $access_key
    199     ]);
    200   }
    201 
    202193  public function get_tariff_by_zipcode($destination_zipcode, $weight)
    203194  {
     
    253244
    254245  /**
     246   * Get provinces
     247   * @return array|WP_Error
     248   */
     249  public function get_provinces()
     250  {
     251    $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
     252
     253    $response = $this->request('/v1/jne/provinces', [], 'GET', [
     254      'Authorization' => $access_key
     255    ]);
     256
     257    if (is_wp_error($response)) {
     258      return [];
     259    }
     260
     261    return $response['data'] ?? [];
     262  }
     263
     264  /**
     265   * Get cities
     266   * @param int $province_code
     267   * @param int $page
     268   * @param int $per_page
     269   * @param string $search
     270   * @return array|WP_Error
     271   */
     272  public function get_cities($province_code, $page = 1, $per_page = 10, $search = '')
     273  {
     274    $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
     275
     276    $params = [
     277      'page' => $page,
     278      'per_page' => $per_page,
     279      'province_code' => $province_code
     280    ];
     281
     282    if (!empty($search)) {
     283      $params['search'] = $search;
     284    }
     285
     286    $query_string = '?' . http_build_query($params);
     287
     288    return $this->request('/v1/jne/cities' . $query_string, [], 'GET', [
     289      'Authorization' => $access_key
     290    ]);
     291  }
     292
     293  /**
     294   * Get districts
     295   * @param int $province_code
     296   * @param string $city_name
     297   * @param int $page
     298   * @param int $per_page
     299   * @param string $search
     300   * @return array|WP_Error
     301   */
     302  public function get_districts($province_code, $city_name, $page = 1, $per_page = 10, $search = '')
     303  {
     304    $access_key = Jneshof_Woocommerce_Configs::get_store_access_key();
     305
     306    $params = [
     307      'page' => $page,
     308      'per_page' => $per_page,
     309      'province_code' => $province_code,
     310      'city_name' => $city_name
     311    ];
     312
     313    if (!empty($search)) {
     314      $params['search'] = $search;
     315    }
     316
     317    $query_string = '?' . http_build_query($params);
     318
     319    return $this->request('/v1/jne/districts' . $query_string, [], 'GET', [
     320      'Authorization' => $access_key
     321    ]);
     322  }
     323
     324  /**
    255325   * Generate cnote
    256326   * @param array $data
  • jne-shipping-official/trunk/jne-shipping-official.php

    r3399074 r3411298  
    2020 * Plugin Name:       JNE Shipping Official
    2121 * Description:       WordPress plugin integrated with WooCommerce for JNE shipping services
    22  * Version:           1.1.0
     22 * Version:           1.2.1
    2323 * Author:            PT. Tiki Jalur Nugraha Ekakurir
    2424 * Author URI:        https://jne.co.id/
     
    4848 * Rename this for your plugin and update it as you release new versions.
    4949 */
    50 define('JNESHOF_PLUGIN_VERSION', '1.1.0');
     50define('JNESHOF_PLUGIN_VERSION', '1.2.1');
    5151
    5252/**
  • jne-shipping-official/trunk/public/class-jne-woocommerce-public.php

    r3387194 r3411298  
    6868        Jneshof_Checkout_Shipping_Insurance::get_instance();
    6969      }
     70      if (class_exists('Jneshof_Checkout_Shipping_Cost_Adjustment')) {
     71        Jneshof_Checkout_Shipping_Cost_Adjustment::get_instance();
     72      }
    7073      if (class_exists('Jneshof_Admin_Generate_Cnote')) {
    7174        Jneshof_Admin_Generate_Cnote::get_instance();
     
    7881    require_once plugin_dir_path(dirname(__FILE__)) . 'public/includes/jne-woocommerce-public-cod.php';
    7982    require_once plugin_dir_path(dirname(__FILE__)) . 'public/includes/jne-woocommerce-public-insurance.php';
     83    require_once plugin_dir_path(dirname(__FILE__)) . 'public/includes/jne-woocommerce-public-shipping-cost-adjustment.php';
    8084  }
    8185
  • jne-shipping-official/trunk/readme.txt

    r3399074 r3411298  
    66Tested up to: 6.8
    77Requires PHP: 7.4
    8 Stable tag: 1.1.0
     8Stable tag: 1.2.1
    99License: GPL-2.0+
    1010License URI: http://www.gnu.org/licenses/gpl-2.0.txt
     
    9090
    9191== Changelog ==
     92
     93= 1.2.1 =
     94* Added validation to check if order has tracking number before requesting pickup
     95* Added validation to check if order has tracking number before generating shipping label (AWB)
     96* Improved error messages to guide users to generate CNote first when tracking number is missing
     97* Enhanced bulk AWB generation to skip orders without tracking numbers with clear error messages
     98
     99= 1.2.0 =
     100* Added Shipping Cost Adjustments feature - Create and manage adjustment profiles to modify shipping costs
     101* Support for multiple adjustment types: Free Shipping, Add/Subtract Fixed Amount, Add/Subtract Percentage
     102* Flexible condition matching based on Services, Provinces, Cities, and Districts
     103* Date range support for time-limited adjustments
     104* Active/Inactive toggle for each adjustment profile
     105* Dual list box interface for selecting cities and districts (add/remove mechanism)
     106* Automatic application of adjustments as cart fees
     107* Adjustment rates are displayed as "Shipping Cost Discount" or "Shipping Cost Addition" in cart/checkout
    92108
    93109= 1.1.0 =
Note: See TracChangeset for help on using the changeset viewer.