Plugin Directory

Changeset 3459659


Ignore:
Timestamp:
02/12/2026 08:35:44 AM (7 weeks ago)
Author:
enituretechnology
Message:

1.1.1 - 2026-02-12

  • Update: Introduced bulk delete functionality for warehouse and dropship locations to simplify location management.
  • Update: Added product-level offset days functionality to allow more granular shipment timing control.

Ticket 23708293437

Location:
shipengine-shipping-quotes
Files:
34 edited
1 copied

Legend:

Unmodified
Added
Removed
  • shipengine-shipping-quotes/tags/1.1.1/admin/assets/en-uvs-admin.css

    r3439259 r3459659  
    6363}
    6464
    65 #en_uvs_quote_settings .en_uvs_shipment_day,
    66 #en_uvs_quote_settings .en_uvs_all_shipment {
    67     display: inline-block !important;
     65#en_uvs_quote_settings tr.en_uvs_all_shipment {
     66    display: inline-flex !important;
     67    align-items: baseline;
     68}
     69
     70#en_uvs_quote_settings .en_uvs_shipment_day {
     71    display: inline-block;
    6872}
    6973
     
    145149.en_quote_settings_sub_options th {
    146150    padding: 0 0 10px 15px;
    147     margin-left: 10px !important;
    148151    font-weight: 400;
    149152}
     
    358361    background: none !important;
    359362}
     363
     364.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"] {
     365    width: 100% !important;
     366    max-width: 100% !important;
     367}
     368.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"] {
     369    margin: 2px 0 0;
     370    padding: 5px;
     371}
     372.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_eniture_product_level_fulfillment_offset_days"] span.woocommerce-help-tip {
     373    float: right;
     374}
  • shipengine-shipping-quotes/tags/1.1.1/admin/assets/en-uvs-admin.js

    r3439259 r3459659  
    55    jQuery('#en_uvs_hazardous_material_settings').closest('table').attr('id', 'en_uvs_quote_settings');
    66    jQuery("#order_shipping_line_items .shipping .display_meta").css('display', 'none');
     7    jQuery("#en_uvs_fulfilment_offset_days").attr('maxlength', '2');
    78
    89    // Quote Settings Tab
     
    144145                'en_max_length': false,
    145146                'en_error_msg': 'Air hazardous material fee format should be 100.20 or 10%.',
    146             };
    147 
    148             en_validate_settings['#en_uvs_fulfilment_offset_days'] = {
    149                 'en_data_type': 'isNumeric',
    150                 'en_decimal': false,
    151                 'en_after_decimal': 0,
    152                 'en_add_percentage': false,
    153                 'en_minus_sign': false,
    154                 'en_max_length': 1,
    155                 'en_error_msg': 'Entered Days are not valid.',
    156147            };
    157148
     
    237228            }
    238229
     230            // fulfillment offset days validation
     231            if (!en_uvs_validate_fullfillment_offset_days()) return false;
    239232            // backup rates validation
    240233            if (!en_uvs_backup_rates_validations()) return false;
     
    687680    }
    688681}
     682
     683if (typeof en_uvs_validate_fullfillment_offset_days !== 'function') {
     684    function en_uvs_validate_fullfillment_offset_days() {
     685        const offset_days = jQuery("#en_uvs_fulfilment_offset_days").val();
     686        const number_regex = /^[0-9]+$/;
     687        let error_msg = '';
     688       
     689        if (offset_days != "" && offset_days < 1) error_msg = 'Fulfillment Offset Days must be greater than or equal to 1.';
     690        if (!error_msg && offset_days != "" && offset_days > 20) error_msg = 'Fulfillment Offset Days must be less than or equal to 20.';
     691        if (!error_msg && offset_days != "" && !number_regex.test(offset_days)) error_msg = 'Fulfillment Offset Days must be a number.';
     692
     693        if (error_msg) {
     694            jQuery('.en_uvs_plan_notice').after('<div class="notice notice-error en_settings_message"><p><strong>Error! </strong>' + error_msg + '</p></div>');
     695            jQuery('#en_settings_message').delay(200).animate({scrollTop: 0}, 1000);
     696            jQuery('html, body').animate({scrollTop: 0}, 'slow');
     697            return false;
     698        }
     699
     700        return true;
     701    }
     702}
  • shipengine-shipping-quotes/tags/1.1.1/admin/product/en-product-detail.php

    r3288959 r3459659  
    106106                    $custom_field = (isset($custom_field['id'])) ? $custom_field['id'] : '';
    107107                    $en_updated_product = (isset($_POST[$custom_field][$postId])) ? sanitize_text_field($_POST[$custom_field][$postId]) : '';
    108                     $en_updated_product = $custom_field == '_dropship_location' ?
    109                         (maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product)) : esc_attr($en_updated_product);
     108
     109                    if ($custom_field == '_dropship_location') {
     110                        $en_updated_product = maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product);
     111                    } elseif ($custom_field == '_eniture_product_level_fulfillment_offset_days') {
     112                        $en_updated_product = $this->sanitize_fulfillment_offset_days($en_updated_product);
     113                    } else {
     114                        $en_updated_product = esc_attr($en_updated_product);
     115                    }
     116
    110117                    update_post_meta($postId, $custom_field, $en_updated_product);
    111118                }
    112119            }
     120        }
     121
     122        private function sanitize_fulfillment_offset_days($value)
     123        {
     124            $value = trim($value);
     125            if ($value === '' || $value === null) return '';
     126
     127            $value = preg_replace('/[^0-9.-]/', '', $value);
     128            if (!is_numeric($value)) return '';
     129
     130            $value = intval(floor(floatval($value)));
     131            return $value < 0 ? '' : strval($value);
    113132        }
    114133
     
    166185                    'description' => "Increases the amount of the returned quote by a specified amount prior to displaying it in the shopping cart. The number entered will be interpreted as dollars and cents unless it is followed by a % sign. For example, entering 5.00 will cause $5.00 to be added to the quotes. Entering 5% will cause 5 percent of the item's price to be added to the shipping quotes."
    167186                ],
     187                  [
     188                    'type' => 'input_field',
     189                    'input_type' => 'number',
     190                    'id' => '_eniture_product_level_fulfillment_offset_days',
     191                    'class' => '_eniture_product_level_fulfillment_offset_days short',
     192                    'label' => __( 'Fulfillment Offset Days', 'woocommerce' ),
     193                    'placeholder' => 'Fulfillment offset days, e.g. 2',
     194                    'description' => "The number of days the ship date needs to be moved to allow for the processing of the order. If set, this will override the global Fulfillment Offset Days setting for this product.",
     195                    'custom_attributes' => [
     196                        'min' => '1',
     197                        'max' => '20',
     198                        'step' => '1',
     199                        'title' => 'Value must be greater than or equal to 1 and less than or equal to 20',
     200                        'oninvalid' => "this.setCustomValidity(this.validity.rangeUnderflow ? 'Value must be greater than or equal to 1' : this.validity.rangeOverflow ? 'Value must be less than or equal to 20' : '')",
     201                        'oninput' => "this.setCustomValidity('')"
     202                    ]
     203                ],
    168204                [
    169205                    'type' => 'checkbox',
     
    289325                'label' => $custom_field['label'],
    290326                'class' => $custom_field['class'],
    291                 'placeholder' => $custom_field['label'],
     327                'placeholder' => isset($custom_field['placeholder']) ? $custom_field['placeholder'] : $custom_field['label'],
    292328                'value' => get_post_meta($postId, $custom_field['id'], true)
    293329            ];
     330
     331            // Add input type if specified (e.g., 'number')
     332            if (isset($custom_field['input_type'])) {
     333                $custom_input_field['type'] = $custom_field['input_type'];
     334            }
     335
     336            // Add custom attributes if specified (e.g., min, max, step)
     337            if (isset($custom_field['custom_attributes'])) {
     338                $custom_input_field['custom_attributes'] = $custom_field['custom_attributes'];
     339            }
    294340
    295341            if (isset($custom_field['description'])) {
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/en-tab.php

    r3439259 r3459659  
    199199
    200200    $en_tab = new EnUvsTab();
    201     return $en_tab->en_load();
     201    $en_tab->en_load();
     202
     203    return $en_tab;
    202204}
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/location/assets/css/en-uvs-location.css

    r3439259 r3459659  
    3333}
    3434
     35.en_uvs_location_btn_container {
     36    clear: both;
     37    display: flex;
     38    justify-content: flex-end;
     39}
     40
    3541.en_uvs_location_btn {
    36     clear: both;
    37     float: right;
    38     margin: 10px !important;
     42    margin-right: 10px !important;
    3943}
    4044
     
    124128    color: #ff0000;
    125129    font-size: 11px;
    126     margin: 0 0 0 40.2%;
    127     width: 100%;
     130    width: 58.7%;
    128131    display: block;
    129 }
    130 
    131 .en_location_error_message {
     132    float: right;
     133}
     134
     135.en_location_error_message,
     136.en_uvs_warehouse_error_message, .en_uvs_dropship_error_message {
    132137    background: #f1f1f1 none repeat scroll 0 0;
    133138    border-left: 4px solid #dc3232;
     
    139144}
    140145
    141 .en_location_success_message {
     146.en_uvs_wh_location_success_message, .en_uvs_ds_location_success_message,
     147.en_uvs_warehouse_success_message, .en_uvs_dropship_success_message {
    142148    background: #fff none repeat scroll 0 0;
    143149    border-left: 4px solid #46b450;
     
    149155}
    150156
     157.en_uvs_warehouse_success_message, .en_uvs_dropship_success_message {
     158    margin: auto;
     159}
     160
    151161.spinner_disable {
    152162    color: #000000;
     
    175185.en_location_table td,
    176186.en_location_table th {
    177     color: #555;
    178187    padding: 12px 35px;
    179188    border-collapse: collapse;
    180189    text-align: center;
     190}
     191
     192.en_location_table td {
     193    color: #555;
    181194}
    182195
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/location/assets/js/en-uvs-location.js

    r3439259 r3459659  
    154154        } else if (en_is_var_exist('severity', data) && data['severity'] == 'error') {
    155155            jQuery('.en_popup_location_form').delay(200).animate({scrollTop: 0}, 300);
    156             jQuery('.en_location_error_message span').text(data['message']);
    157             en_show_errors('.en_location_error_message');
     156            const location_type = data['location'] == 'warehouse' ? 'wh' : 'ds';
     157            jQuery(`.en_uvs_${location_type}_location_error_message span`).text(data['message']);
     158            en_show_errors(`.en_uvs_${location_type}_location_error_message`);
    158159        }
    159160
     
    283284        const id = en_location_type ? 'wh' : 'ds';
    284285        jQuery('.en_location_residential_pickup_parcel input[type="checkbox"]').attr('id', `en_${id}_residential_pickup_parcel`);
     286        jQuery('.en_location_residential_pickup input[type="checkbox"]').attr('id', `en_${id}_residential_pickup`);
     287        jQuery('.en_location_liftgate_pickup input[type="checkbox"]').attr('id', `en_${id}_liftgate_pickup`);
    285288
    286289        if (en_location_type) {
     
    359362    function en_uvs_location_delete(e, data, en_location_type, en_location_id) {
    360363        e.preventDefault();
     364       
     365        // If warehouse location then directly delete
     366        if (en_location_type) {
     367            en_uvs_location_confirm_delete(data, en_location_type, en_location_id);
     368            return;
     369        }
     370
    361371        en_popup_confirmation_location_delete_show();
    362372
     
    399409        if (data['message'].length > 0) {
    400410            jQuery('.en_popup_location_form').delay(200).animate({scrollTop: 0}, 300);
    401             jQuery('.en_location_success_message span').text(data['message']);
    402             en_show_errors('.en_location_success_message');
     411            const location_type = data['location'] == 'warehouse' ? 'wh' : 'ds';
     412            jQuery(`.en_uvs_${location_type}_location_success_message span`).text(data['message']);
     413            en_show_errors(`.en_uvs_${location_type}_location_success_message`);
    403414        }
    404415    }
     
    440451    }
    441452}
     453
     454if (typeof en_uvs_select_bulk_locations != 'function') {
     455    function en_uvs_select_bulk_locations(location_class, e) {
     456        const checked = jQuery(e).is(':checked');
     457        jQuery(`.${location_class}`).prop('checked', checked);
     458    }
     459}
     460
     461if (typeof en_uvs_toggle_select_all_locations != 'function') {
     462    function en_uvs_toggle_select_all_locations(location_class, select_all_locations) {
     463        const allChecked = jQuery(`.${location_class}:checked`).length === jQuery(`.${location_class}`).length;
     464        jQuery(`#${select_all_locations}`).prop('checked', allChecked);
     465    }
     466}
     467
     468if (typeof en_uvs_delete_bulk_locations != 'function') {
     469    function en_uvs_delete_bulk_locations(e, location_class, location_type) {
     470        e.preventDefault();
     471        const location_ids = jQuery(`.${location_class}:checked`).map(function () {
     472            return this.value;
     473        }).get();
     474
     475        // Show error message if no locations are selected
     476        if (location_ids.length === 0) {
     477            const loc_error_class = `en_uvs_${location_type}_error_message`;
     478            jQuery(`.${loc_error_class}`).show('slow').delay(3000).hide('slow');
     479            return;
     480        }
     481
     482        const postForm = {
     483            'action': 'en_uvs_wd_bulk_delete_locations',
     484            'location_ids': location_ids,
     485            'location_type': location_type,
     486            'wp_nonce': en_uvs_location_script.nonce
     487        }
     488        const delete_locs_btn = `.en_uvs_bulk_delete_${location_type}`,
     489        loc_success_class = `.en_uvs_${location_type}_success_message`,
     490        loc_error_class = `.en_uvs_${location_type}_error_message`;
     491       
     492        jQuery.ajax({
     493            type: 'POST',
     494            url: ajaxurl,
     495            data: postForm,
     496            dataType: 'json',
     497            beforeSend: function () {
     498                jQuery(delete_locs_btn).addClass('spinner_disable');
     499            },
     500            success: function (data) {
     501                if (data.error && data.message) {
     502                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     503                    jQuery(loc_error_class).show('slow').delay(3000).hide('slow');
     504                } else {
     505                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     506                    jQuery(data.target_location).replaceWith(data.html);
     507                    jQuery(loc_success_class).show('slow').delay(3000).hide('slow');
     508                    jQuery('.warehouse_updated, .warehouse_created, .warehouse_deleted, .dropship_deleted, .dropship_updated, .dropship_created').css('display', 'none');
     509                }
     510            },
     511            error: function (error) {
     512                jQuery(delete_locs_btn).removeClass('spinner_disable');
     513                console.log(error);
     514            }
     515        });
     516    }
     517}
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/location/dropship/en-dropship.php

    r3241894 r3459659  
    4040            <div class="en_location_dropship_main_div">
    4141
    42             <h1><?php __('Drop ships', 'shipengine-eniture'); ?></h1>
    43             <button type="button" onclick="en_show_popup_location(false)"
    44                     class="button-primary">Add</button>
    45             <p><?php __("Locations that inventory specific items that are drop shipped to the destination. Use the product's settings
     42            <h1>Drop ships</h1>
     43            <div style="display: flex; gap: 3px; margin-top: 10px;">
     44                <button type="button" onclick="en_show_popup_location(false)"
     45                        class="button-primary">Add</button>
     46                <button type="button" class="en_uvs_bulk_delete_dropship en_wd_add_dropship_btn button-primary" title="Delete Drop Ships" onclick="return en_uvs_delete_bulk_locations(event, 'en_uvs_delete_dropship_item', 'dropship');"><?php _e('Delete', 'eniture-technology'); ?></button>
     47            </div>
     48            <p>Locations that inventory specific items that are drop shipped to the destination. Use the product's settings
    4649                    page to identify it as a drop shipped item and its associated drop ship location. Orders that include drop
    4750                    shipped items will display a single figure for the shipping rate estimate that is equal to the sum of the
    48                     cheapest option of each shipment required to fulfill the order.", 'shipengine-eniture'); ?></p>
     51                    cheapest option of each shipment required to fulfill the order.</p>
     52           
     53            <!-- Success/Error messages -->
     54            <div class="en_uvs_ds_location_success_message"><strong>Success! </strong><span></span></div>
     55            <div class="en_uvs_dropship_success_message"><strong>Success!</strong> <span>Selected drop ships deleted successfully.</span></div>
     56            <div class="en_uvs_dropship_error_message"><strong>Error!</strong> Please select at least one drop ship to delete.</div>
    4957
    5058            <table class="en_location_table en_location_dropship_table">
    5159            <thead>
    5260            <tr>
     61                <th class="en_wd_dropship_list_heading en_uvs_bulk_delete_col">
     62                    <!-- Bulk delete -->
     63                    <input type="checkbox" name="en_uvs_bulk_delete_dropships" id="en_uvs_bulk_delete_dropships" onclick="return en_uvs_select_bulk_locations('en_uvs_delete_dropship_item', this);" />
     64                </th>
    5365                <?php echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    54                 <th><?php __('Action', 'shipengine-eniture'); ?></th>
     66                <th>Action</th>
    5567            </tr>
    5668            </thead>
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/location/en-location.php

    r3439259 r3459659  
    123123                    'append' => ' data-optional="1" ',
    124124                ],
     125                "en_{$loc_type}_residential_pickup" => [
     126                    'type' => 'en_checkbox',
     127                    'name' => "en_{$loc_type}_residential_pickup",
     128                    'id' => "en_{$loc_type}_residential_pickup",
     129                    'label' => 'Residential pickup required for LTL freight',
     130                    'title' => 'Residential pickup required for LTL freight',
     131                    'class' => 'en_location_checkout_field en_location_residential_pickup',
     132                    'append' => ' data-optional="1" ',
     133                ],
     134                "en_{$loc_type}_liftgate_pickup" => [
     135                    'type' => 'en_checkbox',
     136                    'name' => "en_{$loc_type}_liftgate_pickup",
     137                    'id' => "en_{$loc_type}_liftgate_pickup",
     138                    'label' => 'Liftgate pickup required for LTL freight',
     139                    'title' => 'Liftgate pickup required for LTL freight',
     140                    'class' => 'en_location_checkout_field en_location_liftgate_pickup',
     141                    'append' => ' data-optional="1" ',
     142                ],
    125143                'en_in_store_pickup' => [
    126144                    'type' => 'en_heading',
     
    256274                $loc_type = isset($location['location']) && $location['location'] == 'warehouse' ? 'wh' : 'ds';
    257275                $location["en_{$loc_type}_residential_pickup_parcel"] = isset($location['enable_residential_pickup_parcel']) && $location['enable_residential_pickup_parcel'] ? 'on' : 'no';
     276                $location["en_{$loc_type}_residential_pickup"] = isset($location['enable_residential_pickup']) && $location['enable_residential_pickup'] ? 'on' : 'no';
     277                $location["en_{$loc_type}_liftgate_pickup"] = isset($location['enable_liftgate_pickup']) && $location['enable_liftgate_pickup'] ? 'on' : 'no';
    258278
    259279                echo '<tr class="' . esc_attr($append_class) . '" id="en_location_row_id_' . esc_attr($en_location_id). '">';
     280
     281                 $_type = ($loc_type === 'wh') ? 'warehouse' : 'dropship';
     282                echo '<td class="en_wd_' . esc_attr($_type) . '_list_data en_uvs_bulk_delete_col">
     283                    <input
     284                        type="checkbox"
     285                        class="en_uvs_delete_' . esc_attr($_type) . '_item"
     286                        value="' . esc_attr($en_location_id) . '"
     287                        onclick="return en_uvs_toggle_select_all_locations(
     288                            \'en_uvs_delete_' . esc_attr($_type) . '_item\',
     289                            \'en_uvs_bulk_delete_' . esc_attr($_type) . 's\'
     290                        );"
     291                    >
     292                </td>';
    260293                echo "<td> " . implode(" <td> ", $en_sorted_location) . " </td>";
    261294
     
    289322                    <h2 class="en_confirmation_warning">Warning!
    290323                    </h2>
    291                     <p class="en_confirmation_message">If you delete this location, then location settings will be disabled against products (if any).
     324                    <hr>
     325                    <p class="en_confirmation_message">Warning! If you delete this location, Drop ship location settings will be disabled against products if any.
    292326                    </p>
     327                    <hr>
    293328                    <div class="en_confirmation_buttons">
    294329                        <a href="#"
     
    345380            }
    346381
    347             echo '<input type="submit" value="Save" class="en_uvs_location_btn button-primary">';
     382            echo '<div class="en_uvs_location_btn_container"><input type="submit" value="Save" class="en_uvs_location_btn button-primary"></div>';
    348383            echo '</form>';
    349384            echo '</div>';
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/location/includes/en-location-ajax.php

    r3439259 r3459659  
    2121            add_action('wp_ajax_nopriv_en_uvs_location_delete_row', [$this, 'en_uvs_location_delete_row']);
    2222            add_action('wp_ajax_en_uvs_location_delete_row', [$this, 'en_uvs_location_delete_row']);
     23
     24            add_action('wp_ajax_nopriv_en_uvs_wd_bulk_delete_locations', array($this, 'en_uvs_bulk_delete_locations_ajax'));
     25            add_action('wp_ajax_en_uvs_wd_bulk_delete_locations', array($this, 'en_uvs_bulk_delete_locations_ajax'));
    2326        }
    2427
     
    182185           
    183186            unset($post_data['en_wd_origin_markup']);
    184             $resi_lfg_pickup = ['residential_pickup_parcel'];
     187            $resi_lfg_pickup = ['residential_pickup_parcel', 'residential_pickup', 'liftgate_pickup'];
    185188            foreach ($resi_lfg_pickup as $value) {
    186189                $post_data["enable_{$value}"] = isset($post_data["en_wh_{$value}"]) && $post_data["en_wh_{$value}"] == 'on' || isset($post_data["en_ds_{$value}"]) && $post_data["en_ds_{$value}"] == 'on';
     
    196199
    197200                if ($location === 'warehouse') {
    198                     $location_step = 'Warehouse';
     201                    $location_step = 'New warehouse';
    199202                    $en_location_template_obj = new EnUvsWarehouseTemplate();
    200203                    $en_target_location = '.en_location_warehouse_main_div';
    201204                    $validate = ['zip', 'city', 'state', 'country', 'location'];
    202205                } else {
    203                     $location_step = 'Dropship';
     206                    $location_step = 'New drop ship';
    204207                    $en_location_template_obj = new EnUvsDropshipTemplate();
    205208                    $en_target_location = '.en_location_dropship_main_div';
     
    219222                        (!empty($en_location_data) &&
    220223                        reset($en_location_data)['id'] === $location_id))) {
     224                    $location_step = $location === 'warehouse' ? 'Warehouse' : 'Drop ship';
    221225                    $message = $location_step . ' updated successfully.';
    222226                    $action = 'update';
     
    247251        }
    248252
     253        function en_uvs_bulk_delete_locations_ajax()
     254        {
     255            if (!(current_user_can('manage_options') || current_user_can('manage_woocommerce')) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['wp_nonce'])), 'en_uvs_location_nonce')) {
     256                echo wp_json_encode(array('error' => true, 'message' => 'Unauthorized Access.'));
     257                exit;
     258            }
     259
     260            $location_ids = isset($_POST['location_ids']) ? $_POST['location_ids'] : array();
     261            $loc_type = isset($_POST['location_type']) ? $_POST['location_type'] : '';
     262            if (empty($location_ids)) {
     263                echo wp_json_encode(['error' => true, 'message' => "Please select at least one {$loc_type} to delete."]);
     264                exit;
     265            }
     266
     267            global $wpdb;
     268            foreach ($location_ids as $location_id) {
     269                if ($loc_type == 'dropship') {
     270                    $get_dropship_id = '';
     271                    $dropship_id = intval($location_id);
     272                    $get_dropship_array = array($dropship_id);
     273                    $ser = maybe_serialize($get_dropship_id);
     274                    $get_dropship_val = array_map('intval', $get_dropship_array);
     275                    $get_post_id = $wpdb->get_results("SELECT group_concat(post_id) as post_ids_list FROM `" . $wpdb->prefix . "postmeta` WHERE `meta_key` = '_dropship_location' AND (`meta_value` LIKE '%" . $ser . "%' OR `meta_value` = '" . $dropship_id . "')");
     276                    $post_id = reset($get_post_id)->post_ids_list;
     277
     278                    if (isset($post_id)) {
     279                        $wpdb->query("UPDATE `" . $wpdb->prefix . "postmeta` SET `meta_value` = '' WHERE `meta_key` IN('_enable_dropship','_dropship_location')  AND `post_id` IN ($post_id)");
     280                    }
     281                }
     282
     283                $wpdb->delete($wpdb->prefix . "warehouse", array('id' => intval($location_id), 'location' => $loc_type));
     284            }
     285
     286            if ($loc_type === 'warehouse') {
     287                $en_location_template_obj = new EnUvsWarehouseTemplate();
     288                $en_target_location = '.en_location_warehouse_main_div';
     289            } else {
     290                $en_location_template_obj = new EnUvsDropshipTemplate();
     291                $en_target_location = '.en_location_dropship_main_div';
     292            }
     293
     294            $html = $en_location_template_obj::en_load();
     295
     296            echo wp_json_encode(['error' => false, 'message' => 'Locations deleted successfully.', 'target_location' => $en_target_location, 'html' => $html]);
     297            exit;
     298        }
    249299    }
    250300
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/location/warehouse/en-warehouse.php

    r3241894 r3459659  
    3434            <div class="en_location_warehouse_main_div">
    3535
    36                 <div class="en_location_success_message">
    37                     <strong><?php __('Success!', 'shipengine-eniture'); ?> </strong><span></span></div>
     36                <h1>Warehouses</h1>
     37                <div style="display: flex; gap: 3px; margin-top: 10px;">
     38                    <button type="button" onclick="en_show_popup_location(true)"
     39                            class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>">Add</button>
     40                    <button type="button" class="en_uvs_bulk_delete_warehouse en_wd_add_warehouse_btn button-primary" title="Delete Warehouses" onclick="return en_uvs_delete_bulk_locations(event, 'en_uvs_delete_warehouse_item', 'warehouse');"><?php _e('Delete', 'eniture-technology'); ?>
     41                    </button>
    3842
    39                 <h1><?php __('Warehouses', 'shipengine-eniture'); ?></h1>
    40                 <button type="button" onclick="en_show_popup_location(true)"
    41                         class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>">Add</button>
    42                 <?php echo force_balance_tags(self::$plan_required); ?>
     43                    <?php echo force_balance_tags(self::$plan_required); ?>
     44                </div>
    4345
    44                 <p><?php __('Warehouses that inventory all products not otherwise identified as drop shipped items. The warehouse with the
    45             lowest shipping cost to the destination is used for quoting purposes.', 'shipengine-eniture'); ?></p>
     46                <p>Warehouses that inventory all products not otherwise identified as drop shipped items. The warehouse with the lowest shipping cost to the destination is used for quoting purposes.</p>
     47                <div class="en_uvs_wh_location_success_message">
     48                    <strong>Success! </strong><span></span></div>
     49                <div class="en_uvs_warehouse_success_message"><strong>Success! </strong> <span>Selected warehouses deleted successfully.</span></div>
     50                <div class="en_uvs_warehouse_error_message"><strong>Error!</strong> <span>Please select at least one warehouse to delete.</span></div>
    4651
    4752                <table class="en_location_table en_location_warehouse_table">
    4853                    <thead>
    4954                        <tr>
     55                             <th class="en_wd_warehouse_list_heading en_uvs_bulk_delete_col">
     56                                <!-- Bulk delete -->
     57                                <input type="checkbox" name="en_uvs_bulk_delete_warehouses" id="en_uvs_bulk_delete_warehouses" onclick="return en_uvs_select_bulk_locations('en_uvs_delete_warehouse_item', this);" />
     58                            </th>
    5059                            <?php echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    51                             <th><?php __('Action', 'shipengine-eniture'); ?></th>
     60                            <th>Action</th>
    5261                        </tr>
    5362                    </thead>
  • shipengine-shipping-quotes/tags/1.1.1/admin/tab/quote-settings/en-quote-settings.php

    r3439259 r3459659  
    354354                ],
    355355                'en_uvs_fulfilment_offset_days' => [
    356                     'name' => __('Fulfilment Offset Days', 'shipengine-eniture'),
    357                     'type' => 'text',
    358                     'class' => $option,
    359                     'desc' => 'The number of days the ship date needs to be moved to allow the processing of the order.',
     356                    'name' => __('Fulfillment Offset Days', 'shipengine-eniture'),
     357                    'type' => 'text',
     358                    'class' => $option,
     359                    'placeholder' => 'Fulfillment offset days, e.g. 2',
     360                    'desc' => 'The number of days the ship date needs to be moved to allow for the processing of the order.',
    360361                    'id' => 'en_uvs_fulfilment_offset_days'
    361362                ],
  • shipengine-shipping-quotes/tags/1.1.1/en-install.php

    r3439259 r3459659  
    2626        wp_enqueue_script('EnUvsTagging', EN_UVS_DIR_FILE . '/admin/tab/location/assets/js/en-uvs-tagging.js', [], '1.0.1');
    2727       
    28         wp_enqueue_script('EnUvsAdminJs', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.js', [], '1.0.5');
     28        wp_enqueue_script('EnUvsAdminJs', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.js', [], '1.0.6');
    2929        wp_localize_script('EnUvsAdminJs', 'en_uvs_admin_script', [
    3030            'pluginsUrl' => EN_UVS_PLUGIN_URL,
     
    3838        ]);
    3939
    40         wp_enqueue_script('EnUvsLocationScript', EN_UVS_DIR_FILE . '/admin/tab/location/assets/js/en-uvs-location.js', [], '1.0.2');
     40        wp_enqueue_script('EnUvsLocationScript', EN_UVS_DIR_FILE . '/admin/tab/location/assets/js/en-uvs-location.js', [], '1.0.3');
    4141        wp_localize_script('EnUvsLocationScript', 'en_uvs_location_script', array(
    4242            'pluginsUrl' => EN_UVS_PLUGIN_URL,
     
    4747        wp_enqueue_style('EnWickedPickerCss');
    4848
    49         wp_register_style('EnUvsLocationStyle', EN_UVS_DIR_FILE . '/admin/tab/location/assets/css/en-uvs-location.css', false, '1.0.2');
     49        wp_register_style('EnUvsLocationStyle', EN_UVS_DIR_FILE . '/admin/tab/location/assets/css/en-uvs-location.css', false, '1.0.3');
    5050        wp_enqueue_style('EnUvsLocationStyle');
    5151
    52         wp_register_style('EnUvsAdminCss', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.css', false, '1.0.5');
     52        wp_register_style('EnUvsAdminCss', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.css', false, '1.0.6');
    5353        wp_enqueue_style('EnUvsAdminCss');
    5454
     
    107107
    108108    function en_uvs_shipping_sections($settings) {
    109         $settings[] = include('admin/tab/en-tab.php');
     109        $en_tab = include('admin/tab/en-tab.php');
     110        if (is_object($en_tab)) {
     111            $settings[] = $en_tab;
     112        }
     113
    110114        return $settings;
    111115    }
  • shipengine-shipping-quotes/tags/1.1.1/http/en-curl.php

    r3439259 r3459659  
    133133                $cachable_data['addressLine2'] = (isset($request_data['addressLine2'])) ? $request_data['addressLine2'] : '';
    134134                $cachable_data['defaultRADAddressType'] = $request_data['defaultRADAddressType'];
     135                $cachable_data['defaultRADWithoutStreetAddress'] = $request_data['defaultRADWithoutStreetAddress'];
    135136                $cachable_data['poboxAddressValidation'] = $request_data['poboxAddressValidation'];
    136137            }
  • shipengine-shipping-quotes/tags/1.1.1/readme.txt

    r3439259 r3459659  
    44Requires at least: 6.4
    55Tested up to: 6.9
    6 Stable tag: 1.1.0
     6Stable tag: 1.1.1
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    7777== Changelog ==
    7878
     79= 1.1.1 - 2026-02-12 =
     80* Update: Introduced **bulk delete functionality** for warehouse and dropship locations to simplify location management.
     81* Update: Added **product-level offset days** functionality to allow more granular shipment timing control.
     82
    7983= 1.1.0 - 2026-01-14 =
    8084* Update: Added data sanitization in the quotes request to ensure cleaner and more reliable API communication.
  • shipengine-shipping-quotes/tags/1.1.1/server/api/en-response.php

    r3439259 r3459659  
    305305
    306306                    if ($delivery_estimate_option == "delivery_date" && strlen($calender_date) > 0) {
    307                         $label .= ' ( Expected delivery by ' . gmdate('Y-m-d', strtotime($calender_date)) . ')';
     307                        $label .= ' ( Expected delivery by ' . gmdate('m-d-Y', strtotime($calender_date)) . ')';
    308308                    } elseif ($delivery_estimate_option == "delivery_days" && strlen($calender_days_in_transit) > 0) {
    309309                        $label .= ' ( Intransit Days: ' . $calender_days_in_transit . ' )';
  • shipengine-shipping-quotes/tags/1.1.1/server/package/en-package.php

    r3439259 r3459659  
    121121                    // Get product level markup value
    122122                    $product_level_markup = self::en_get_product_level_markup($product_data, $product['variation_id'], $product['product_id'], $product['quantity']);
     123                    $product_level_fulfillment_offset = self::en_get_product_level_fulfillment_offset_days($product_data, $product['variation_id'], $product['product_id']);
    123124
    124125                    $product_item = [
     
    141142                        'productPrice' => $isInsuranceIctive ? $product_data->get_price() : 0,
    142143                        'actualProductPrice' => $product_data->get_price(),
     144                        'fulfillment_offset_days' => $product_level_fulfillment_offset,
    143145                    ];
    144146
     
    266268
    267269                    if ($shipment_type && strlen($origin_zip_code) > 0) {
    268                         $product_title = str_replace(array("'", '"', '’'), '', $product_data->get_title());
     270                        $product_title = str_replace(array("'", '"', '’'), '', $product_data->get_name());
    269271                        self::$en_request['product_name'][$origin_zip_code][] = $product_quantity . " x " . $product_title;
    270272
     
    279281                        self::$en_request['originAddress'][$origin_zip_code] = $origin_address;
    280282                       
     283                         // Track maximum fulfillment offset days
     284                        if (is_numeric($product_level_fulfillment_offset) && $product_level_fulfillment_offset > 0) {
     285                            if (!isset(self::$en_request['max_fulfillment_offset_days']) || $product_level_fulfillment_offset > self::$en_request['max_fulfillment_offset_days']) {
     286                                self::$en_request['max_fulfillment_offset_days'] = $product_level_fulfillment_offset;
     287                            }
     288                        }
     289
    281290                        // Product tags
    282291                        $product_tags = get_the_terms($product['product_id'], 'product_tag');
     
    507516            $en_settings = json_decode(EN_UVS_SET_QUOTE_SETTINGS, true);
    508517            extract($en_settings);
     518
     519            // Use product-level fulfillment offset days if available, otherwise use global setting
     520            if (isset(self::$en_request['max_fulfillment_offset_days']) && is_numeric(self::$en_request['max_fulfillment_offset_days']) && self::$en_request['max_fulfillment_offset_days'] > 0) {
     521                $fulfilment_offset_days = self::$en_request['max_fulfillment_offset_days'];
     522            }
     523
    509524            if (!is_array($feature_option) && ($delivery_estimate_option == 'delivery_days' || $delivery_estimate_option == 'delivery_date')) {
    510525                $settings = [
     
    813828        }
    814829
     830        static public function en_get_product_level_fulfillment_offset_days($_product, $variation_id, $product_id)
     831        {
     832            $fulfillment_offset_days = '';
     833            $field_name = '_eniture_product_level_fulfillment_offset_days';
     834
     835            if ($_product->get_type() == 'variation' && $variation_id > 0) {
     836                $fulfillment_offset_days = get_post_meta($variation_id, $field_name, true);
     837
     838                if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     839                    $parent_id = $_product->get_parent_id();
     840                    if ($parent_id > 0) {
     841                        $fulfillment_offset_days = get_post_meta($parent_id, $field_name, true);
     842                    }
     843                }
     844            } else {
     845                $fulfillment_offset_days = get_post_meta($_product->get_id(), $field_name, true);
     846            }
     847
     848            if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     849                $fulfillment_offset_days = get_post_meta($product_id, $field_name, true);
     850            }
     851
     852            // Validate and sanitize the value before returning
     853            if (is_numeric($fulfillment_offset_days)) {
     854                $value = intval($fulfillment_offset_days);
     855                return $value;
     856            }
     857
     858            // Return empty string if invalid
     859            return '';
     860        }
     861
    815862    }
    816863
  • shipengine-shipping-quotes/tags/1.1.1/shipengine-eniture.php

    r3439259 r3459659  
    44 * Plugin URI: https://eniture.com/products/
    55 * Description: Dynamically retrieves your discounted shipping rates and displays the results in the WooCommerce shopping cart.
    6  * Version: 1.1.0
     6 * Version: 1.1.1
    77 * Requires at least: 6.4
    88 * Author: Eniture Technology
    99 * Author URI: http://eniture.com/
    1010 * Text Domain: shipengine-eniture
    11  * License: GPL-2.0-or-later
     11 * License: GPLv2 or later
    1212 */
    1313
     
    2727});
    2828
    29 if (empty(\EnUvs\EnUvsGuard::en_check_prerequisites('ShipEngine Shipping Rates', '5.6', '4.0', '5.7'))) {
    30     require_once 'en-install.php';
    31 }
     29add_action( 'init', function () {
     30    if (!is_textdomain_loaded('shipengine-eniture')) {
     31        load_plugin_textdomain('shipengine-eniture', false, dirname(plugin_basename(__FILE__)) . '/languages/');
     32    }
     33   
     34    if (empty(\EnUvs\EnUvsGuard::en_check_prerequisites('ShipEngine Shipping Rates', '5.6', '4.0', '5.7'))) {
     35        require_once 'en-install.php';
     36    }
     37});
  • shipengine-shipping-quotes/trunk/admin/assets/en-uvs-admin.css

    r3439259 r3459659  
    6363}
    6464
    65 #en_uvs_quote_settings .en_uvs_shipment_day,
    66 #en_uvs_quote_settings .en_uvs_all_shipment {
    67     display: inline-block !important;
     65#en_uvs_quote_settings tr.en_uvs_all_shipment {
     66    display: inline-flex !important;
     67    align-items: baseline;
     68}
     69
     70#en_uvs_quote_settings .en_uvs_shipment_day {
     71    display: inline-block;
    6872}
    6973
     
    145149.en_quote_settings_sub_options th {
    146150    padding: 0 0 10px 15px;
    147     margin-left: 10px !important;
    148151    font-weight: 400;
    149152}
     
    358361    background: none !important;
    359362}
     363
     364.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"] {
     365    width: 100% !important;
     366    max-width: 100% !important;
     367}
     368.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"] {
     369    margin: 2px 0 0;
     370    padding: 5px;
     371}
     372.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_eniture_product_level_fulfillment_offset_days"] span.woocommerce-help-tip {
     373    float: right;
     374}
  • shipengine-shipping-quotes/trunk/admin/assets/en-uvs-admin.js

    r3439259 r3459659  
    55    jQuery('#en_uvs_hazardous_material_settings').closest('table').attr('id', 'en_uvs_quote_settings');
    66    jQuery("#order_shipping_line_items .shipping .display_meta").css('display', 'none');
     7    jQuery("#en_uvs_fulfilment_offset_days").attr('maxlength', '2');
    78
    89    // Quote Settings Tab
     
    144145                'en_max_length': false,
    145146                'en_error_msg': 'Air hazardous material fee format should be 100.20 or 10%.',
    146             };
    147 
    148             en_validate_settings['#en_uvs_fulfilment_offset_days'] = {
    149                 'en_data_type': 'isNumeric',
    150                 'en_decimal': false,
    151                 'en_after_decimal': 0,
    152                 'en_add_percentage': false,
    153                 'en_minus_sign': false,
    154                 'en_max_length': 1,
    155                 'en_error_msg': 'Entered Days are not valid.',
    156147            };
    157148
     
    237228            }
    238229
     230            // fulfillment offset days validation
     231            if (!en_uvs_validate_fullfillment_offset_days()) return false;
    239232            // backup rates validation
    240233            if (!en_uvs_backup_rates_validations()) return false;
     
    687680    }
    688681}
     682
     683if (typeof en_uvs_validate_fullfillment_offset_days !== 'function') {
     684    function en_uvs_validate_fullfillment_offset_days() {
     685        const offset_days = jQuery("#en_uvs_fulfilment_offset_days").val();
     686        const number_regex = /^[0-9]+$/;
     687        let error_msg = '';
     688       
     689        if (offset_days != "" && offset_days < 1) error_msg = 'Fulfillment Offset Days must be greater than or equal to 1.';
     690        if (!error_msg && offset_days != "" && offset_days > 20) error_msg = 'Fulfillment Offset Days must be less than or equal to 20.';
     691        if (!error_msg && offset_days != "" && !number_regex.test(offset_days)) error_msg = 'Fulfillment Offset Days must be a number.';
     692
     693        if (error_msg) {
     694            jQuery('.en_uvs_plan_notice').after('<div class="notice notice-error en_settings_message"><p><strong>Error! </strong>' + error_msg + '</p></div>');
     695            jQuery('#en_settings_message').delay(200).animate({scrollTop: 0}, 1000);
     696            jQuery('html, body').animate({scrollTop: 0}, 'slow');
     697            return false;
     698        }
     699
     700        return true;
     701    }
     702}
  • shipengine-shipping-quotes/trunk/admin/product/en-product-detail.php

    r3288959 r3459659  
    106106                    $custom_field = (isset($custom_field['id'])) ? $custom_field['id'] : '';
    107107                    $en_updated_product = (isset($_POST[$custom_field][$postId])) ? sanitize_text_field($_POST[$custom_field][$postId]) : '';
    108                     $en_updated_product = $custom_field == '_dropship_location' ?
    109                         (maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product)) : esc_attr($en_updated_product);
     108
     109                    if ($custom_field == '_dropship_location') {
     110                        $en_updated_product = maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product);
     111                    } elseif ($custom_field == '_eniture_product_level_fulfillment_offset_days') {
     112                        $en_updated_product = $this->sanitize_fulfillment_offset_days($en_updated_product);
     113                    } else {
     114                        $en_updated_product = esc_attr($en_updated_product);
     115                    }
     116
    110117                    update_post_meta($postId, $custom_field, $en_updated_product);
    111118                }
    112119            }
     120        }
     121
     122        private function sanitize_fulfillment_offset_days($value)
     123        {
     124            $value = trim($value);
     125            if ($value === '' || $value === null) return '';
     126
     127            $value = preg_replace('/[^0-9.-]/', '', $value);
     128            if (!is_numeric($value)) return '';
     129
     130            $value = intval(floor(floatval($value)));
     131            return $value < 0 ? '' : strval($value);
    113132        }
    114133
     
    166185                    'description' => "Increases the amount of the returned quote by a specified amount prior to displaying it in the shopping cart. The number entered will be interpreted as dollars and cents unless it is followed by a % sign. For example, entering 5.00 will cause $5.00 to be added to the quotes. Entering 5% will cause 5 percent of the item's price to be added to the shipping quotes."
    167186                ],
     187                  [
     188                    'type' => 'input_field',
     189                    'input_type' => 'number',
     190                    'id' => '_eniture_product_level_fulfillment_offset_days',
     191                    'class' => '_eniture_product_level_fulfillment_offset_days short',
     192                    'label' => __( 'Fulfillment Offset Days', 'woocommerce' ),
     193                    'placeholder' => 'Fulfillment offset days, e.g. 2',
     194                    'description' => "The number of days the ship date needs to be moved to allow for the processing of the order. If set, this will override the global Fulfillment Offset Days setting for this product.",
     195                    'custom_attributes' => [
     196                        'min' => '1',
     197                        'max' => '20',
     198                        'step' => '1',
     199                        'title' => 'Value must be greater than or equal to 1 and less than or equal to 20',
     200                        'oninvalid' => "this.setCustomValidity(this.validity.rangeUnderflow ? 'Value must be greater than or equal to 1' : this.validity.rangeOverflow ? 'Value must be less than or equal to 20' : '')",
     201                        'oninput' => "this.setCustomValidity('')"
     202                    ]
     203                ],
    168204                [
    169205                    'type' => 'checkbox',
     
    289325                'label' => $custom_field['label'],
    290326                'class' => $custom_field['class'],
    291                 'placeholder' => $custom_field['label'],
     327                'placeholder' => isset($custom_field['placeholder']) ? $custom_field['placeholder'] : $custom_field['label'],
    292328                'value' => get_post_meta($postId, $custom_field['id'], true)
    293329            ];
     330
     331            // Add input type if specified (e.g., 'number')
     332            if (isset($custom_field['input_type'])) {
     333                $custom_input_field['type'] = $custom_field['input_type'];
     334            }
     335
     336            // Add custom attributes if specified (e.g., min, max, step)
     337            if (isset($custom_field['custom_attributes'])) {
     338                $custom_input_field['custom_attributes'] = $custom_field['custom_attributes'];
     339            }
    294340
    295341            if (isset($custom_field['description'])) {
  • shipengine-shipping-quotes/trunk/admin/tab/en-tab.php

    r3439259 r3459659  
    199199
    200200    $en_tab = new EnUvsTab();
    201     return $en_tab->en_load();
     201    $en_tab->en_load();
     202
     203    return $en_tab;
    202204}
  • shipengine-shipping-quotes/trunk/admin/tab/location/assets/css/en-uvs-location.css

    r3439259 r3459659  
    3333}
    3434
     35.en_uvs_location_btn_container {
     36    clear: both;
     37    display: flex;
     38    justify-content: flex-end;
     39}
     40
    3541.en_uvs_location_btn {
    36     clear: both;
    37     float: right;
    38     margin: 10px !important;
     42    margin-right: 10px !important;
    3943}
    4044
     
    124128    color: #ff0000;
    125129    font-size: 11px;
    126     margin: 0 0 0 40.2%;
    127     width: 100%;
     130    width: 58.7%;
    128131    display: block;
    129 }
    130 
    131 .en_location_error_message {
     132    float: right;
     133}
     134
     135.en_location_error_message,
     136.en_uvs_warehouse_error_message, .en_uvs_dropship_error_message {
    132137    background: #f1f1f1 none repeat scroll 0 0;
    133138    border-left: 4px solid #dc3232;
     
    139144}
    140145
    141 .en_location_success_message {
     146.en_uvs_wh_location_success_message, .en_uvs_ds_location_success_message,
     147.en_uvs_warehouse_success_message, .en_uvs_dropship_success_message {
    142148    background: #fff none repeat scroll 0 0;
    143149    border-left: 4px solid #46b450;
     
    149155}
    150156
     157.en_uvs_warehouse_success_message, .en_uvs_dropship_success_message {
     158    margin: auto;
     159}
     160
    151161.spinner_disable {
    152162    color: #000000;
     
    175185.en_location_table td,
    176186.en_location_table th {
    177     color: #555;
    178187    padding: 12px 35px;
    179188    border-collapse: collapse;
    180189    text-align: center;
     190}
     191
     192.en_location_table td {
     193    color: #555;
    181194}
    182195
  • shipengine-shipping-quotes/trunk/admin/tab/location/assets/js/en-uvs-location.js

    r3439259 r3459659  
    154154        } else if (en_is_var_exist('severity', data) && data['severity'] == 'error') {
    155155            jQuery('.en_popup_location_form').delay(200).animate({scrollTop: 0}, 300);
    156             jQuery('.en_location_error_message span').text(data['message']);
    157             en_show_errors('.en_location_error_message');
     156            const location_type = data['location'] == 'warehouse' ? 'wh' : 'ds';
     157            jQuery(`.en_uvs_${location_type}_location_error_message span`).text(data['message']);
     158            en_show_errors(`.en_uvs_${location_type}_location_error_message`);
    158159        }
    159160
     
    283284        const id = en_location_type ? 'wh' : 'ds';
    284285        jQuery('.en_location_residential_pickup_parcel input[type="checkbox"]').attr('id', `en_${id}_residential_pickup_parcel`);
     286        jQuery('.en_location_residential_pickup input[type="checkbox"]').attr('id', `en_${id}_residential_pickup`);
     287        jQuery('.en_location_liftgate_pickup input[type="checkbox"]').attr('id', `en_${id}_liftgate_pickup`);
    285288
    286289        if (en_location_type) {
     
    359362    function en_uvs_location_delete(e, data, en_location_type, en_location_id) {
    360363        e.preventDefault();
     364       
     365        // If warehouse location then directly delete
     366        if (en_location_type) {
     367            en_uvs_location_confirm_delete(data, en_location_type, en_location_id);
     368            return;
     369        }
     370
    361371        en_popup_confirmation_location_delete_show();
    362372
     
    399409        if (data['message'].length > 0) {
    400410            jQuery('.en_popup_location_form').delay(200).animate({scrollTop: 0}, 300);
    401             jQuery('.en_location_success_message span').text(data['message']);
    402             en_show_errors('.en_location_success_message');
     411            const location_type = data['location'] == 'warehouse' ? 'wh' : 'ds';
     412            jQuery(`.en_uvs_${location_type}_location_success_message span`).text(data['message']);
     413            en_show_errors(`.en_uvs_${location_type}_location_success_message`);
    403414        }
    404415    }
     
    440451    }
    441452}
     453
     454if (typeof en_uvs_select_bulk_locations != 'function') {
     455    function en_uvs_select_bulk_locations(location_class, e) {
     456        const checked = jQuery(e).is(':checked');
     457        jQuery(`.${location_class}`).prop('checked', checked);
     458    }
     459}
     460
     461if (typeof en_uvs_toggle_select_all_locations != 'function') {
     462    function en_uvs_toggle_select_all_locations(location_class, select_all_locations) {
     463        const allChecked = jQuery(`.${location_class}:checked`).length === jQuery(`.${location_class}`).length;
     464        jQuery(`#${select_all_locations}`).prop('checked', allChecked);
     465    }
     466}
     467
     468if (typeof en_uvs_delete_bulk_locations != 'function') {
     469    function en_uvs_delete_bulk_locations(e, location_class, location_type) {
     470        e.preventDefault();
     471        const location_ids = jQuery(`.${location_class}:checked`).map(function () {
     472            return this.value;
     473        }).get();
     474
     475        // Show error message if no locations are selected
     476        if (location_ids.length === 0) {
     477            const loc_error_class = `en_uvs_${location_type}_error_message`;
     478            jQuery(`.${loc_error_class}`).show('slow').delay(3000).hide('slow');
     479            return;
     480        }
     481
     482        const postForm = {
     483            'action': 'en_uvs_wd_bulk_delete_locations',
     484            'location_ids': location_ids,
     485            'location_type': location_type,
     486            'wp_nonce': en_uvs_location_script.nonce
     487        }
     488        const delete_locs_btn = `.en_uvs_bulk_delete_${location_type}`,
     489        loc_success_class = `.en_uvs_${location_type}_success_message`,
     490        loc_error_class = `.en_uvs_${location_type}_error_message`;
     491       
     492        jQuery.ajax({
     493            type: 'POST',
     494            url: ajaxurl,
     495            data: postForm,
     496            dataType: 'json',
     497            beforeSend: function () {
     498                jQuery(delete_locs_btn).addClass('spinner_disable');
     499            },
     500            success: function (data) {
     501                if (data.error && data.message) {
     502                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     503                    jQuery(loc_error_class).show('slow').delay(3000).hide('slow');
     504                } else {
     505                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     506                    jQuery(data.target_location).replaceWith(data.html);
     507                    jQuery(loc_success_class).show('slow').delay(3000).hide('slow');
     508                    jQuery('.warehouse_updated, .warehouse_created, .warehouse_deleted, .dropship_deleted, .dropship_updated, .dropship_created').css('display', 'none');
     509                }
     510            },
     511            error: function (error) {
     512                jQuery(delete_locs_btn).removeClass('spinner_disable');
     513                console.log(error);
     514            }
     515        });
     516    }
     517}
  • shipengine-shipping-quotes/trunk/admin/tab/location/dropship/en-dropship.php

    r3241894 r3459659  
    4040            <div class="en_location_dropship_main_div">
    4141
    42             <h1><?php __('Drop ships', 'shipengine-eniture'); ?></h1>
    43             <button type="button" onclick="en_show_popup_location(false)"
    44                     class="button-primary">Add</button>
    45             <p><?php __("Locations that inventory specific items that are drop shipped to the destination. Use the product's settings
     42            <h1>Drop ships</h1>
     43            <div style="display: flex; gap: 3px; margin-top: 10px;">
     44                <button type="button" onclick="en_show_popup_location(false)"
     45                        class="button-primary">Add</button>
     46                <button type="button" class="en_uvs_bulk_delete_dropship en_wd_add_dropship_btn button-primary" title="Delete Drop Ships" onclick="return en_uvs_delete_bulk_locations(event, 'en_uvs_delete_dropship_item', 'dropship');"><?php _e('Delete', 'eniture-technology'); ?></button>
     47            </div>
     48            <p>Locations that inventory specific items that are drop shipped to the destination. Use the product's settings
    4649                    page to identify it as a drop shipped item and its associated drop ship location. Orders that include drop
    4750                    shipped items will display a single figure for the shipping rate estimate that is equal to the sum of the
    48                     cheapest option of each shipment required to fulfill the order.", 'shipengine-eniture'); ?></p>
     51                    cheapest option of each shipment required to fulfill the order.</p>
     52           
     53            <!-- Success/Error messages -->
     54            <div class="en_uvs_ds_location_success_message"><strong>Success! </strong><span></span></div>
     55            <div class="en_uvs_dropship_success_message"><strong>Success!</strong> <span>Selected drop ships deleted successfully.</span></div>
     56            <div class="en_uvs_dropship_error_message"><strong>Error!</strong> Please select at least one drop ship to delete.</div>
    4957
    5058            <table class="en_location_table en_location_dropship_table">
    5159            <thead>
    5260            <tr>
     61                <th class="en_wd_dropship_list_heading en_uvs_bulk_delete_col">
     62                    <!-- Bulk delete -->
     63                    <input type="checkbox" name="en_uvs_bulk_delete_dropships" id="en_uvs_bulk_delete_dropships" onclick="return en_uvs_select_bulk_locations('en_uvs_delete_dropship_item', this);" />
     64                </th>
    5365                <?php echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    54                 <th><?php __('Action', 'shipengine-eniture'); ?></th>
     66                <th>Action</th>
    5567            </tr>
    5668            </thead>
  • shipengine-shipping-quotes/trunk/admin/tab/location/en-location.php

    r3439259 r3459659  
    123123                    'append' => ' data-optional="1" ',
    124124                ],
     125                "en_{$loc_type}_residential_pickup" => [
     126                    'type' => 'en_checkbox',
     127                    'name' => "en_{$loc_type}_residential_pickup",
     128                    'id' => "en_{$loc_type}_residential_pickup",
     129                    'label' => 'Residential pickup required for LTL freight',
     130                    'title' => 'Residential pickup required for LTL freight',
     131                    'class' => 'en_location_checkout_field en_location_residential_pickup',
     132                    'append' => ' data-optional="1" ',
     133                ],
     134                "en_{$loc_type}_liftgate_pickup" => [
     135                    'type' => 'en_checkbox',
     136                    'name' => "en_{$loc_type}_liftgate_pickup",
     137                    'id' => "en_{$loc_type}_liftgate_pickup",
     138                    'label' => 'Liftgate pickup required for LTL freight',
     139                    'title' => 'Liftgate pickup required for LTL freight',
     140                    'class' => 'en_location_checkout_field en_location_liftgate_pickup',
     141                    'append' => ' data-optional="1" ',
     142                ],
    125143                'en_in_store_pickup' => [
    126144                    'type' => 'en_heading',
     
    256274                $loc_type = isset($location['location']) && $location['location'] == 'warehouse' ? 'wh' : 'ds';
    257275                $location["en_{$loc_type}_residential_pickup_parcel"] = isset($location['enable_residential_pickup_parcel']) && $location['enable_residential_pickup_parcel'] ? 'on' : 'no';
     276                $location["en_{$loc_type}_residential_pickup"] = isset($location['enable_residential_pickup']) && $location['enable_residential_pickup'] ? 'on' : 'no';
     277                $location["en_{$loc_type}_liftgate_pickup"] = isset($location['enable_liftgate_pickup']) && $location['enable_liftgate_pickup'] ? 'on' : 'no';
    258278
    259279                echo '<tr class="' . esc_attr($append_class) . '" id="en_location_row_id_' . esc_attr($en_location_id). '">';
     280
     281                 $_type = ($loc_type === 'wh') ? 'warehouse' : 'dropship';
     282                echo '<td class="en_wd_' . esc_attr($_type) . '_list_data en_uvs_bulk_delete_col">
     283                    <input
     284                        type="checkbox"
     285                        class="en_uvs_delete_' . esc_attr($_type) . '_item"
     286                        value="' . esc_attr($en_location_id) . '"
     287                        onclick="return en_uvs_toggle_select_all_locations(
     288                            \'en_uvs_delete_' . esc_attr($_type) . '_item\',
     289                            \'en_uvs_bulk_delete_' . esc_attr($_type) . 's\'
     290                        );"
     291                    >
     292                </td>';
    260293                echo "<td> " . implode(" <td> ", $en_sorted_location) . " </td>";
    261294
     
    289322                    <h2 class="en_confirmation_warning">Warning!
    290323                    </h2>
    291                     <p class="en_confirmation_message">If you delete this location, then location settings will be disabled against products (if any).
     324                    <hr>
     325                    <p class="en_confirmation_message">Warning! If you delete this location, Drop ship location settings will be disabled against products if any.
    292326                    </p>
     327                    <hr>
    293328                    <div class="en_confirmation_buttons">
    294329                        <a href="#"
     
    345380            }
    346381
    347             echo '<input type="submit" value="Save" class="en_uvs_location_btn button-primary">';
     382            echo '<div class="en_uvs_location_btn_container"><input type="submit" value="Save" class="en_uvs_location_btn button-primary"></div>';
    348383            echo '</form>';
    349384            echo '</div>';
  • shipengine-shipping-quotes/trunk/admin/tab/location/includes/en-location-ajax.php

    r3439259 r3459659  
    2121            add_action('wp_ajax_nopriv_en_uvs_location_delete_row', [$this, 'en_uvs_location_delete_row']);
    2222            add_action('wp_ajax_en_uvs_location_delete_row', [$this, 'en_uvs_location_delete_row']);
     23
     24            add_action('wp_ajax_nopriv_en_uvs_wd_bulk_delete_locations', array($this, 'en_uvs_bulk_delete_locations_ajax'));
     25            add_action('wp_ajax_en_uvs_wd_bulk_delete_locations', array($this, 'en_uvs_bulk_delete_locations_ajax'));
    2326        }
    2427
     
    182185           
    183186            unset($post_data['en_wd_origin_markup']);
    184             $resi_lfg_pickup = ['residential_pickup_parcel'];
     187            $resi_lfg_pickup = ['residential_pickup_parcel', 'residential_pickup', 'liftgate_pickup'];
    185188            foreach ($resi_lfg_pickup as $value) {
    186189                $post_data["enable_{$value}"] = isset($post_data["en_wh_{$value}"]) && $post_data["en_wh_{$value}"] == 'on' || isset($post_data["en_ds_{$value}"]) && $post_data["en_ds_{$value}"] == 'on';
     
    196199
    197200                if ($location === 'warehouse') {
    198                     $location_step = 'Warehouse';
     201                    $location_step = 'New warehouse';
    199202                    $en_location_template_obj = new EnUvsWarehouseTemplate();
    200203                    $en_target_location = '.en_location_warehouse_main_div';
    201204                    $validate = ['zip', 'city', 'state', 'country', 'location'];
    202205                } else {
    203                     $location_step = 'Dropship';
     206                    $location_step = 'New drop ship';
    204207                    $en_location_template_obj = new EnUvsDropshipTemplate();
    205208                    $en_target_location = '.en_location_dropship_main_div';
     
    219222                        (!empty($en_location_data) &&
    220223                        reset($en_location_data)['id'] === $location_id))) {
     224                    $location_step = $location === 'warehouse' ? 'Warehouse' : 'Drop ship';
    221225                    $message = $location_step . ' updated successfully.';
    222226                    $action = 'update';
     
    247251        }
    248252
     253        function en_uvs_bulk_delete_locations_ajax()
     254        {
     255            if (!(current_user_can('manage_options') || current_user_can('manage_woocommerce')) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['wp_nonce'])), 'en_uvs_location_nonce')) {
     256                echo wp_json_encode(array('error' => true, 'message' => 'Unauthorized Access.'));
     257                exit;
     258            }
     259
     260            $location_ids = isset($_POST['location_ids']) ? $_POST['location_ids'] : array();
     261            $loc_type = isset($_POST['location_type']) ? $_POST['location_type'] : '';
     262            if (empty($location_ids)) {
     263                echo wp_json_encode(['error' => true, 'message' => "Please select at least one {$loc_type} to delete."]);
     264                exit;
     265            }
     266
     267            global $wpdb;
     268            foreach ($location_ids as $location_id) {
     269                if ($loc_type == 'dropship') {
     270                    $get_dropship_id = '';
     271                    $dropship_id = intval($location_id);
     272                    $get_dropship_array = array($dropship_id);
     273                    $ser = maybe_serialize($get_dropship_id);
     274                    $get_dropship_val = array_map('intval', $get_dropship_array);
     275                    $get_post_id = $wpdb->get_results("SELECT group_concat(post_id) as post_ids_list FROM `" . $wpdb->prefix . "postmeta` WHERE `meta_key` = '_dropship_location' AND (`meta_value` LIKE '%" . $ser . "%' OR `meta_value` = '" . $dropship_id . "')");
     276                    $post_id = reset($get_post_id)->post_ids_list;
     277
     278                    if (isset($post_id)) {
     279                        $wpdb->query("UPDATE `" . $wpdb->prefix . "postmeta` SET `meta_value` = '' WHERE `meta_key` IN('_enable_dropship','_dropship_location')  AND `post_id` IN ($post_id)");
     280                    }
     281                }
     282
     283                $wpdb->delete($wpdb->prefix . "warehouse", array('id' => intval($location_id), 'location' => $loc_type));
     284            }
     285
     286            if ($loc_type === 'warehouse') {
     287                $en_location_template_obj = new EnUvsWarehouseTemplate();
     288                $en_target_location = '.en_location_warehouse_main_div';
     289            } else {
     290                $en_location_template_obj = new EnUvsDropshipTemplate();
     291                $en_target_location = '.en_location_dropship_main_div';
     292            }
     293
     294            $html = $en_location_template_obj::en_load();
     295
     296            echo wp_json_encode(['error' => false, 'message' => 'Locations deleted successfully.', 'target_location' => $en_target_location, 'html' => $html]);
     297            exit;
     298        }
    249299    }
    250300
  • shipengine-shipping-quotes/trunk/admin/tab/location/warehouse/en-warehouse.php

    r3241894 r3459659  
    3434            <div class="en_location_warehouse_main_div">
    3535
    36                 <div class="en_location_success_message">
    37                     <strong><?php __('Success!', 'shipengine-eniture'); ?> </strong><span></span></div>
     36                <h1>Warehouses</h1>
     37                <div style="display: flex; gap: 3px; margin-top: 10px;">
     38                    <button type="button" onclick="en_show_popup_location(true)"
     39                            class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>">Add</button>
     40                    <button type="button" class="en_uvs_bulk_delete_warehouse en_wd_add_warehouse_btn button-primary" title="Delete Warehouses" onclick="return en_uvs_delete_bulk_locations(event, 'en_uvs_delete_warehouse_item', 'warehouse');"><?php _e('Delete', 'eniture-technology'); ?>
     41                    </button>
    3842
    39                 <h1><?php __('Warehouses', 'shipengine-eniture'); ?></h1>
    40                 <button type="button" onclick="en_show_popup_location(true)"
    41                         class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>">Add</button>
    42                 <?php echo force_balance_tags(self::$plan_required); ?>
     43                    <?php echo force_balance_tags(self::$plan_required); ?>
     44                </div>
    4345
    44                 <p><?php __('Warehouses that inventory all products not otherwise identified as drop shipped items. The warehouse with the
    45             lowest shipping cost to the destination is used for quoting purposes.', 'shipengine-eniture'); ?></p>
     46                <p>Warehouses that inventory all products not otherwise identified as drop shipped items. The warehouse with the lowest shipping cost to the destination is used for quoting purposes.</p>
     47                <div class="en_uvs_wh_location_success_message">
     48                    <strong>Success! </strong><span></span></div>
     49                <div class="en_uvs_warehouse_success_message"><strong>Success! </strong> <span>Selected warehouses deleted successfully.</span></div>
     50                <div class="en_uvs_warehouse_error_message"><strong>Error!</strong> <span>Please select at least one warehouse to delete.</span></div>
    4651
    4752                <table class="en_location_table en_location_warehouse_table">
    4853                    <thead>
    4954                        <tr>
     55                             <th class="en_wd_warehouse_list_heading en_uvs_bulk_delete_col">
     56                                <!-- Bulk delete -->
     57                                <input type="checkbox" name="en_uvs_bulk_delete_warehouses" id="en_uvs_bulk_delete_warehouses" onclick="return en_uvs_select_bulk_locations('en_uvs_delete_warehouse_item', this);" />
     58                            </th>
    5059                            <?php echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    51                             <th><?php __('Action', 'shipengine-eniture'); ?></th>
     60                            <th>Action</th>
    5261                        </tr>
    5362                    </thead>
  • shipengine-shipping-quotes/trunk/admin/tab/quote-settings/en-quote-settings.php

    r3439259 r3459659  
    354354                ],
    355355                'en_uvs_fulfilment_offset_days' => [
    356                     'name' => __('Fulfilment Offset Days', 'shipengine-eniture'),
    357                     'type' => 'text',
    358                     'class' => $option,
    359                     'desc' => 'The number of days the ship date needs to be moved to allow the processing of the order.',
     356                    'name' => __('Fulfillment Offset Days', 'shipengine-eniture'),
     357                    'type' => 'text',
     358                    'class' => $option,
     359                    'placeholder' => 'Fulfillment offset days, e.g. 2',
     360                    'desc' => 'The number of days the ship date needs to be moved to allow for the processing of the order.',
    360361                    'id' => 'en_uvs_fulfilment_offset_days'
    361362                ],
  • shipengine-shipping-quotes/trunk/en-install.php

    r3439259 r3459659  
    2626        wp_enqueue_script('EnUvsTagging', EN_UVS_DIR_FILE . '/admin/tab/location/assets/js/en-uvs-tagging.js', [], '1.0.1');
    2727       
    28         wp_enqueue_script('EnUvsAdminJs', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.js', [], '1.0.5');
     28        wp_enqueue_script('EnUvsAdminJs', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.js', [], '1.0.6');
    2929        wp_localize_script('EnUvsAdminJs', 'en_uvs_admin_script', [
    3030            'pluginsUrl' => EN_UVS_PLUGIN_URL,
     
    3838        ]);
    3939
    40         wp_enqueue_script('EnUvsLocationScript', EN_UVS_DIR_FILE . '/admin/tab/location/assets/js/en-uvs-location.js', [], '1.0.2');
     40        wp_enqueue_script('EnUvsLocationScript', EN_UVS_DIR_FILE . '/admin/tab/location/assets/js/en-uvs-location.js', [], '1.0.3');
    4141        wp_localize_script('EnUvsLocationScript', 'en_uvs_location_script', array(
    4242            'pluginsUrl' => EN_UVS_PLUGIN_URL,
     
    4747        wp_enqueue_style('EnWickedPickerCss');
    4848
    49         wp_register_style('EnUvsLocationStyle', EN_UVS_DIR_FILE . '/admin/tab/location/assets/css/en-uvs-location.css', false, '1.0.2');
     49        wp_register_style('EnUvsLocationStyle', EN_UVS_DIR_FILE . '/admin/tab/location/assets/css/en-uvs-location.css', false, '1.0.3');
    5050        wp_enqueue_style('EnUvsLocationStyle');
    5151
    52         wp_register_style('EnUvsAdminCss', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.css', false, '1.0.5');
     52        wp_register_style('EnUvsAdminCss', EN_UVS_DIR_FILE . '/admin/assets/en-uvs-admin.css', false, '1.0.6');
    5353        wp_enqueue_style('EnUvsAdminCss');
    5454
     
    107107
    108108    function en_uvs_shipping_sections($settings) {
    109         $settings[] = include('admin/tab/en-tab.php');
     109        $en_tab = include('admin/tab/en-tab.php');
     110        if (is_object($en_tab)) {
     111            $settings[] = $en_tab;
     112        }
     113
    110114        return $settings;
    111115    }
  • shipengine-shipping-quotes/trunk/http/en-curl.php

    r3439259 r3459659  
    133133                $cachable_data['addressLine2'] = (isset($request_data['addressLine2'])) ? $request_data['addressLine2'] : '';
    134134                $cachable_data['defaultRADAddressType'] = $request_data['defaultRADAddressType'];
     135                $cachable_data['defaultRADWithoutStreetAddress'] = $request_data['defaultRADWithoutStreetAddress'];
    135136                $cachable_data['poboxAddressValidation'] = $request_data['poboxAddressValidation'];
    136137            }
  • shipengine-shipping-quotes/trunk/readme.txt

    r3439259 r3459659  
    44Requires at least: 6.4
    55Tested up to: 6.9
    6 Stable tag: 1.1.0
     6Stable tag: 1.1.1
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    7777== Changelog ==
    7878
     79= 1.1.1 - 2026-02-12 =
     80* Update: Introduced **bulk delete functionality** for warehouse and dropship locations to simplify location management.
     81* Update: Added **product-level offset days** functionality to allow more granular shipment timing control.
     82
    7983= 1.1.0 - 2026-01-14 =
    8084* Update: Added data sanitization in the quotes request to ensure cleaner and more reliable API communication.
  • shipengine-shipping-quotes/trunk/server/api/en-response.php

    r3439259 r3459659  
    305305
    306306                    if ($delivery_estimate_option == "delivery_date" && strlen($calender_date) > 0) {
    307                         $label .= ' ( Expected delivery by ' . gmdate('Y-m-d', strtotime($calender_date)) . ')';
     307                        $label .= ' ( Expected delivery by ' . gmdate('m-d-Y', strtotime($calender_date)) . ')';
    308308                    } elseif ($delivery_estimate_option == "delivery_days" && strlen($calender_days_in_transit) > 0) {
    309309                        $label .= ' ( Intransit Days: ' . $calender_days_in_transit . ' )';
  • shipengine-shipping-quotes/trunk/server/package/en-package.php

    r3439259 r3459659  
    121121                    // Get product level markup value
    122122                    $product_level_markup = self::en_get_product_level_markup($product_data, $product['variation_id'], $product['product_id'], $product['quantity']);
     123                    $product_level_fulfillment_offset = self::en_get_product_level_fulfillment_offset_days($product_data, $product['variation_id'], $product['product_id']);
    123124
    124125                    $product_item = [
     
    141142                        'productPrice' => $isInsuranceIctive ? $product_data->get_price() : 0,
    142143                        'actualProductPrice' => $product_data->get_price(),
     144                        'fulfillment_offset_days' => $product_level_fulfillment_offset,
    143145                    ];
    144146
     
    266268
    267269                    if ($shipment_type && strlen($origin_zip_code) > 0) {
    268                         $product_title = str_replace(array("'", '"', '’'), '', $product_data->get_title());
     270                        $product_title = str_replace(array("'", '"', '’'), '', $product_data->get_name());
    269271                        self::$en_request['product_name'][$origin_zip_code][] = $product_quantity . " x " . $product_title;
    270272
     
    279281                        self::$en_request['originAddress'][$origin_zip_code] = $origin_address;
    280282                       
     283                         // Track maximum fulfillment offset days
     284                        if (is_numeric($product_level_fulfillment_offset) && $product_level_fulfillment_offset > 0) {
     285                            if (!isset(self::$en_request['max_fulfillment_offset_days']) || $product_level_fulfillment_offset > self::$en_request['max_fulfillment_offset_days']) {
     286                                self::$en_request['max_fulfillment_offset_days'] = $product_level_fulfillment_offset;
     287                            }
     288                        }
     289
    281290                        // Product tags
    282291                        $product_tags = get_the_terms($product['product_id'], 'product_tag');
     
    507516            $en_settings = json_decode(EN_UVS_SET_QUOTE_SETTINGS, true);
    508517            extract($en_settings);
     518
     519            // Use product-level fulfillment offset days if available, otherwise use global setting
     520            if (isset(self::$en_request['max_fulfillment_offset_days']) && is_numeric(self::$en_request['max_fulfillment_offset_days']) && self::$en_request['max_fulfillment_offset_days'] > 0) {
     521                $fulfilment_offset_days = self::$en_request['max_fulfillment_offset_days'];
     522            }
     523
    509524            if (!is_array($feature_option) && ($delivery_estimate_option == 'delivery_days' || $delivery_estimate_option == 'delivery_date')) {
    510525                $settings = [
     
    813828        }
    814829
     830        static public function en_get_product_level_fulfillment_offset_days($_product, $variation_id, $product_id)
     831        {
     832            $fulfillment_offset_days = '';
     833            $field_name = '_eniture_product_level_fulfillment_offset_days';
     834
     835            if ($_product->get_type() == 'variation' && $variation_id > 0) {
     836                $fulfillment_offset_days = get_post_meta($variation_id, $field_name, true);
     837
     838                if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     839                    $parent_id = $_product->get_parent_id();
     840                    if ($parent_id > 0) {
     841                        $fulfillment_offset_days = get_post_meta($parent_id, $field_name, true);
     842                    }
     843                }
     844            } else {
     845                $fulfillment_offset_days = get_post_meta($_product->get_id(), $field_name, true);
     846            }
     847
     848            if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     849                $fulfillment_offset_days = get_post_meta($product_id, $field_name, true);
     850            }
     851
     852            // Validate and sanitize the value before returning
     853            if (is_numeric($fulfillment_offset_days)) {
     854                $value = intval($fulfillment_offset_days);
     855                return $value;
     856            }
     857
     858            // Return empty string if invalid
     859            return '';
     860        }
     861
    815862    }
    816863
  • shipengine-shipping-quotes/trunk/shipengine-eniture.php

    r3439259 r3459659  
    44 * Plugin URI: https://eniture.com/products/
    55 * Description: Dynamically retrieves your discounted shipping rates and displays the results in the WooCommerce shopping cart.
    6  * Version: 1.1.0
     6 * Version: 1.1.1
    77 * Requires at least: 6.4
    88 * Author: Eniture Technology
    99 * Author URI: http://eniture.com/
    1010 * Text Domain: shipengine-eniture
    11  * License: GPL-2.0-or-later
     11 * License: GPLv2 or later
    1212 */
    1313
     
    2727});
    2828
    29 if (empty(\EnUvs\EnUvsGuard::en_check_prerequisites('ShipEngine Shipping Rates', '5.6', '4.0', '5.7'))) {
    30     require_once 'en-install.php';
    31 }
     29add_action( 'init', function () {
     30    if (!is_textdomain_loaded('shipengine-eniture')) {
     31        load_plugin_textdomain('shipengine-eniture', false, dirname(plugin_basename(__FILE__)) . '/languages/');
     32    }
     33   
     34    if (empty(\EnUvs\EnUvsGuard::en_check_prerequisites('ShipEngine Shipping Rates', '5.6', '4.0', '5.7'))) {
     35        require_once 'en-install.php';
     36    }
     37});
Note: See TracChangeset for help on using the changeset viewer.