Plugin Directory

Changeset 3453531


Ignore:
Timestamp:
02/04/2026 06:47:07 AM (8 weeks ago)
Author:
enituretechnology
Message:

1.1.16 - 2026-02-04

  • Update: Added a caching mechanism to efficiently handle multiple identical quote requests and reduce redundant API calls.
  • Update: Introduced bulk delete functionality for warehouse and dropship locations to simplify location management.
  • Update: Added delivery estimate options and order cutoff time functionality in the quote settings for improved delivery accuracy.
  • Update: Added product-level offset days functionality to allow more granular shipment timing control.
  • Update: Added parent product checks to properly handle empty variant scenarios and prevent invalid processing.

Ticket 23708293437

Location:
ltl-freight-quotes-echo-edition
Files:
12 added
46 edited
1 copied

Legend:

Unmodified
Added
Removed
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/assets/en-echo-admin.css

    r3381167 r3453531  
    120120
    121121.en_quote_settings_sub_options th {
    122     padding: 0 0 10px 15px;
     122    padding: 10px 0 10px 15px;
    123123    margin-left: 10px !important;
    124124    font-weight: 400;
     
    360360.woocommerce_variable_attributes.wc-metabox-content [class^="_nmfc_number"],
    361361.woocommerce_variable_attributes.wc-metabox-content [class^="_en_product_markup"],
     362.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"],
    362363.woocommerce_variable_attributes.wc-metabox-content [class^="_nestedPercentage"],
    363364.woocommerce_variable_attributes.wc-metabox-content [class^="en_flat_rate_price"],
     
    383384.woocommerce_variable_attributes.wc-metabox-content [class^="en_flat_rate_price"],
    384385.woocommerce_variable_attributes.wc-metabox-content [class^="_en_product_markup"],
     386.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"],
    385387.woocommerce_variable_attributes.wc-metabox-content [class^="_nestedPercentage"],
    386388.woocommerce_variable_attributes.wc-metabox-content [class^="_nestedDimension"],
     
    392394.woocommerce_variable_attributes.wc-metabox-content .data p[class*="en_flat_rate_price"] span.woocommerce-help-tip,
    393395.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_en_product_markup"] span.woocommerce-help-tip,
     396.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_eniture_product_level_fulfillment_offset_days"] span.woocommerce-help-tip,
    394397.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_nestedPercentage"] span.woocommerce-help-tip,
    395398.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_nestedDimension"] span.woocommerce-help-tip,
     
    469472    aspect-ratio: 16 / 9;
    470473}
     474
     475.en_quote_settings_all_shipment_days_echo_tr td {
     476  display: flex !important;
     477  align-items: center;
     478  gap: 16px;
     479  flex-wrap: nowrap;
     480}
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/assets/en-echo-admin.js

    r3381167 r3453531  
    9696            "#en_quote_settings_handling_unit_weight_echo," +
    9797            "#en_quote_settings_max_weight_handling_unit_echo," +
    98             "#en_woo_addons_liftgate_with_auto_residential").closest('tr').addClass("en_quote_settings_sub_options");
     98            "#en_woo_addons_liftgate_with_auto_residential," +
     99            "#en_quote_settings_order_cut_off_time_echo, #en_quote_settings_shipment_offset_days_echo, #en_quote_settings_all_shipment_days_echo").closest('tr').addClass("en_quote_settings_sub_options");
    99100
    100101        jQuery('#en_quote_settings_hold_at_terminal_fee_echo').on('keydown', function (e) {
     
    351352    // Backup rates settings
    352353    echoBackupRatesSettings();
     354    // Cut-off time and shipment offset days settings
     355    echoCutOffTimeAndShipDateSettings();
    353356});
    354357
     
    773776    }
    774777}
     778
     779if (typeof echoCutOffTimeAndShipDateSettings != 'function') {
     780    function echoCutOffTimeAndShipDateSettings() {
     781        jQuery("#en_quote_settings_shipment_offset_days_echo").closest('tr').addClass("en_quote_settings_shipment_offset_days_echo_tr");
     782        jQuery("#en_quote_settings_all_shipment_days_echo").closest('tr').addClass("en_quote_settings_all_shipment_days_echo_tr");
     783        jQuery(".echo_shipment_day ").closest('tr').addClass("echo_shipment_day_tr");
     784        jQuery("#en_quote_settings_order_cut_off_time_echo").closest('tr').addClass("echo_cutt_off_time_ship_date_offset");
     785
     786        const echo_current_time = en_echo_admin_script.echo_order_cut_off_time;
     787        if (echo_current_time == '') {
     788            jQuery('#en_quote_settings_order_cut_off_time_echo').wickedpicker({
     789                now: '',
     790                title: 'Cut Off Time',
     791            });
     792        } else {
     793            jQuery('#en_quote_settings_order_cut_off_time_echo').wickedpicker({
     794                now: echo_current_time,
     795                title: 'Cut Off Time'
     796            });
     797        }
     798
     799        const show_delivery_estimate = jQuery('input[name=en_quote_settings_delivery_estimate_options_echo]:checked').val();
     800        if (show_delivery_estimate === 'dont_show_estimates') {
     801            jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', true);
     802            jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', true);
     803            jQuery("#en_quote_settings_shipment_offset_days_echo").css("cursor", "not-allowed");
     804            jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "not-allowed");
     805            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', true);
     806            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "not-allowed");
     807        } else {
     808            jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', false);
     809            jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', false);
     810            jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "");
     811            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', false);
     812            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "");
     813        }
     814
     815        jQuery("input[name=en_quote_settings_delivery_estimate_options_echo]").change(function () {
     816            const show_delivery_estimate = jQuery('input[name=en_quote_settings_delivery_estimate_options_echo]:checked').val();
     817           
     818            if (show_delivery_estimate === 'dont_show_estimates') {
     819                jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', true);
     820                jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', true);
     821                jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "not-allowed");
     822                jQuery("#en_quote_settings_shipment_offset_days_echo").css("cursor", "not-allowed");
     823                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', true);
     824                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "not-allowed");
     825            } else {
     826                jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', false);
     827                jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', false);
     828                jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "auto");
     829                jQuery("#en_quote_settings_shipment_offset_days_echo").css("cursor", "auto");
     830                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', false);
     831                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "auto");
     832            }
     833        });
     834
     835        /*
     836        * Uncheck Week days Select All Checkbox
     837        */
     838        jQuery(".echo_shipment_day").on('change load', function () {
     839
     840            const checkboxes = jQuery('.echo_shipment_day:checked').length;
     841            const un_checkboxes = jQuery('.echo_shipment_day').length;
     842
     843            if (checkboxes === un_checkboxes) {
     844                jQuery('.all_shipment_days_echo_lfq').prop('checked', true);
     845            } else {
     846                jQuery('.all_shipment_days_echo_lfq').prop('checked', false);
     847            }
     848        });
     849
     850        /*
     851        * Select All Shipment Week days
     852        */
     853
     854        const all_int_checkboxes = jQuery('.all_shipment_days_echo_lfq');
     855        if (all_int_checkboxes.length === all_int_checkboxes.filter(":checked").length) {
     856            jQuery('.all_shipment_days_echo_lfq').prop('checked', true);
     857        }
     858
     859        jQuery(".all_shipment_days_echo_lfq").change(function () {
     860            if (this.checked) {
     861                jQuery(".echo_shipment_day").each(function () {
     862                    this.checked = true;
     863                });
     864            } else {
     865                jQuery(".echo_shipment_day").each(function () {
     866                    this.checked = false;
     867                });
     868            }
     869        });
     870
     871        const main_td = jQuery('.en_quote_settings_all_shipment_days_echo_tr td');
     872        const rows   = jQuery('tr.echo_shipment_day_tr');
     873
     874        if (!main_td.length || !rows.length) return;
     875
     876        // Move all day checkboxes into main row
     877        rows.each(function () {
     878            const input_wrap = jQuery(this).find('td > *').first();
     879            if (input_wrap.length) main_td.append(input_wrap);
     880        });
     881
     882        // Hide original rows
     883        rows.hide();
     884
     885        // ---- Select All sync ----
     886        const select_all = main_td.find('input[type="checkbox"]').first();
     887        const days = main_td.find('input[type="checkbox"]').not(select_all);
     888
     889        // Select all → days
     890        select_all.on('change', function () {
     891            days.prop('checked', this.checked);
     892        });
     893        // Days → select all
     894        days.on('change', function () {
     895            select_all.prop('checked', days.length === days.filter(':checked').length);
     896        });
     897    }
     898}
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/product/assets/en-custom-fields.js

    r2785919 r3453531  
    7171    }
    7272
     73    jQuery(document).on('input', '._eniture_product_level_fulfillment_offset_days.short', function () {
     74        if (this.value !== '' && !/^\d+$/.test(this.value)) {
     75            this.setCustomValidity('Please enter a number');
     76        } else if (this.value !== '' && Number(this.value) < 1) {
     77            this.setCustomValidity('Value must be greater than or equal to 1');
     78        } else if (this.value !== '' && Number(this.value) > 20) {
     79            this.setCustomValidity('Value must be less than or equal to 20');
     80        } else {
     81            this.setCustomValidity('');
     82        }
     83    });
    7384})
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/product/en-product-detail.php

    r3261316 r3453531  
    8686                    $custom_field = (isset($custom_field['id'])) ? $custom_field['id'] : '';
    8787                    $en_updated_product = (isset($_POST[$custom_field][$postId])) ? sanitize_text_field($_POST[$custom_field][$postId]) : '';
    88                     $en_updated_product = $custom_field == '_dropship_location' ?
    89                         (maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product)) : esc_attr($en_updated_product);
     88
     89                    if ($custom_field == '_dropship_location') {
     90                        $en_updated_product = maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product);
     91                    } elseif ($custom_field == '_eniture_product_level_fulfillment_offset_days') {
     92                        $en_updated_product = $this->sanitize_fulfillment_offset_days($en_updated_product);
     93                    } else {
     94                        $en_updated_product = esc_attr($en_updated_product);
     95                    }
     96
    9097                    update_post_meta($postId, $custom_field, $en_updated_product);
    9198                }
    9299            }
     100        }
     101
     102        private function sanitize_fulfillment_offset_days($value)
     103        {
     104            $value = trim($value);
     105            if ($value === '' || $value === null) return '';
     106
     107            $value = preg_replace('/[^0-9.-]/', '', $value);
     108            if (!is_numeric($value)) return '';
     109
     110            $value = intval(floor(floatval($value)));
     111            return $value < 0 ? '' : strval($value);
    93112        }
    94113
     
    186205                    '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."
    187206                ],
     207                 [
     208                    'type' => 'input_field',
     209                    'input_type' => 'number',
     210                    'id' => '_eniture_product_level_fulfillment_offset_days',
     211                    'class' => '_eniture_product_level_fulfillment_offset_days short',
     212                    'label' => __( 'Fulfillment Offset Days', 'woocommerce' ),
     213                    'placeholder' => 'Fulfillment offset days, e.g. 2',
     214                    '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.",
     215                    'custom_attributes' => [
     216                        'min' => '1',
     217                        'max' => '20',
     218                        'step' => '1',
     219                        'title' => 'Value must be greater than or equal to 1 and less than or equal to 20',
     220                        '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' : '')",
     221                        'oninput' => "this.setCustomValidity('')"
     222                    ]
     223                ],
    188224                [
    189225                    'type' => 'checkbox',
     
    305341                'label' => $custom_field['label'],
    306342                'class' => $custom_field['class'],
    307                 'placeholder' => $custom_field['label'],
     343                'placeholder' => isset($custom_field['placeholder']) ? $custom_field['placeholder'] : $custom_field['label'],
    308344                'value' => get_post_meta($postId, $custom_field['id'], true)
    309345            ];
     346
     347            // Add input type if specified (e.g., 'number')
     348            if (isset($custom_field['input_type'])) {
     349                $custom_input_field['type'] = $custom_field['input_type'];
     350            }
     351
     352            // Add custom attributes if specified (e.g., min, max, step)
     353            if (isset($custom_field['custom_attributes'])) {
     354                $custom_input_field['custom_attributes'] = $custom_field['custom_attributes'];
     355            }
    310356
    311357            if (isset($custom_field['description'])) {
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/en-tab.php

    r3381167 r3453531  
    159159            $settings = $this->get_settings($current_section);
    160160
     161            // Cuttoff Time
     162            if (isset($_POST['en_quote_settings_order_cut_off_time_echo']) && $_POST['en_quote_settings_order_cut_off_time_echo'] != '') {
     163                $time_24_format = $this->get_time_in_24_hours($_POST['en_quote_settings_order_cut_off_time_echo']);
     164                $_POST['en_quote_settings_order_cut_off_time_echo'] = $time_24_format;
     165            }
     166
    161167            // backup rates
    162168            $backup_rates_fields = ['echo_backup_rates_fixed_rate', 'echo_backup_rates_cart_price_percentage', 'echo_backup_rates_weight_function'];
     
    167173            WC_Admin_Settings::save_fields($settings);
    168174        }
     175
     176        /**
     177         * Cuttoff Time
     178         * @param $timeStr
     179         * @return false|string
     180        */
     181        public function get_time_in_24_hours($timeStr)
     182        {
     183            $cutOffTime = explode(' ', $timeStr);
     184            $hours = $cutOffTime[0];
     185            $separator = $cutOffTime[1];
     186            $minutes = $cutOffTime[2];
     187            $meridiem = $cutOffTime[3];
     188            $cutOffTime = "{$hours}{$separator}{$minutes} $meridiem";
     189           
     190            return date("H:i", strtotime($cutOffTime));
     191        }
    169192    }
    170193
    171194    $en_tab = new EnEchoTab();
    172     return $en_tab->en_load();
     195    $en_tab->en_load();
     196
     197    return $en_tab;
    173198}
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/location/assets/css/en-echo-location.css

    r3381167 r3453531  
    125125}
    126126
    127 .en_location_error_message {
     127.en_location_error_message,
     128.en_echo_warehouse_error_message, .en_echo_dropship_error_message {
    128129    background: #f1f1f1 none repeat scroll 0 0;
    129130    border-left: 4px solid #dc3232;
     
    135136}
    136137
    137 .en_location_success_message {
     138.en_echo_warehouse_error_message, .en_echo_dropship_error_message {
     139    background-color: #fff !important;
     140    margin: auto;
     141}
     142
     143.en_location_success_message,
     144.en_echo_warehouse_success_message, .en_echo_dropship_success_message {
    138145    background: #fff none repeat scroll 0 0;
    139146    border-left: 4px solid #46b450;
     
    143150    clear: both;
    144151    display: none;
     152}
     153
     154.en_echo_warehouse_success_message, .en_echo_dropship_success_message {
     155    margin: auto;
    145156}
    146157
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/location/assets/js/en-echo-location.js

    r3381167 r3453531  
    446446    }
    447447}
     448
     449if (typeof en_echo_select_bulk_locations != 'function') {
     450    function en_echo_select_bulk_locations(location_class, e) {
     451        const checked = jQuery(e).is(':checked');
     452        jQuery(`.${location_class}`).prop('checked', checked);
     453    }
     454}
     455
     456if (typeof en_echo_toggle_select_all_locations != 'function') {
     457    function en_echo_toggle_select_all_locations(location_class, select_all_locations) {
     458        const allChecked = jQuery(`.${location_class}:checked`).length === jQuery(`.${location_class}`).length;
     459        jQuery(`#${select_all_locations}`).prop('checked', allChecked);
     460    }
     461}
     462
     463if (typeof en_echo_delete_bulk_locations != 'function') {
     464    function en_echo_delete_bulk_locations(e, location_class, location_type) {
     465        e.preventDefault();
     466        const location_ids = jQuery(`.${location_class}:checked`).map(function () {
     467            return this.value;
     468        }).get();
     469
     470        // Show error message if no locations are selected
     471        if (location_ids.length === 0) {
     472            const loc_error_class = `en_echo_${location_type}_error_message`;
     473            jQuery(`.${loc_error_class}`).show('slow').delay(3000).hide('slow');
     474            return;
     475        }
     476
     477        const postForm = {
     478            'action': 'en_echo_wd_bulk_delete_locations',
     479            'location_ids': location_ids,
     480            'location_type': location_type,
     481            'wp_nonce': en_echo_location_script.nonce
     482        }
     483        const delete_locs_btn = `.en_echo_bulk_delete_${location_type}`,
     484        loc_success_class = `.en_echo_${location_type}_success_message`,
     485        loc_error_class = `.en_echo_${location_type}_error_message`;
     486       
     487        jQuery.ajax({
     488            type: 'POST',
     489            url: ajaxurl,
     490            data: postForm,
     491            dataType: 'json',
     492            beforeSend: function () {
     493                jQuery(delete_locs_btn).addClass('spinner_disable');
     494            },
     495            success: function (data) {
     496                if (data.error && data.message) {
     497                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     498                    jQuery(loc_error_class).show('slow').delay(3000).hide('slow');
     499                } else {
     500                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     501                    jQuery(data.target_location).replaceWith(data.html);
     502                    jQuery(loc_success_class).show('slow').delay(3000).hide('slow');
     503                    jQuery('.warehouse_updated, .warehouse_created, .warehouse_deleted, .dropship_deleted, .dropship_updated, .dropship_created').css('display', 'none');
     504                }
     505            },
     506            error: function (error) {
     507                jQuery(delete_locs_btn).removeClass('spinner_disable');
     508                console.log(error);
     509            }
     510        });
     511    }
     512}
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/location/dropship/en-dropship.php

    r3289951 r3453531  
    4242            <h1><?php _e('Drop ships', 'eniture-technology'); ?></h1>
    4343            <button type="button" onclick="en_show_popup_location(false)"
    44                     class="button-primary"><?php _e('Add', 'eniture-technology'); ?></button>
     44                    class="button-primary" title="Add Drop Ship"><?php _e('Add', 'eniture-technology'); ?></button>
     45            <button type="button" class="en_echo_bulk_delete_dropship en_wd_add_dropship_btn button-primary" title="Delete Drop Ships" onclick="return en_echo_delete_bulk_locations(event, 'en_echo_delete_dropship_item', 'dropship');"><?php _e('Delete', 'eniture-technology'); ?></button>
    4546            <p><?php _e('Locations that inventory specific items that are drop shipped to the destination. Use the products settings
    4647                    page to identify it as a drop shipped item and its associated drop ship location. Orders that include drop
    4748                    shipped items will display a single figure for the shipping rate estimate that is equal to the sum of the
    4849                    cheapest option of each shipment required to fulfill the order.', 'eniture-technology'); ?></p>
     50            <div class="en_echo_dropship_success_message"><strong>Success!</strong> <span>Selected drop ships deleted successfully.</span></div>
     51            <div class="en_echo_dropship_error_message"><strong>Error!</strong> Please select at least one drop ship to delete.</div>
    4952
    5053            <table class="en_location_table en_location_dropship_table">
    5154            <thead>
    5255            <tr>
     56                <th class="en_wd_dropship_list_heading en_echo_bulk_delete_col">
     57                    <!-- Bulk delete -->
     58                    <input type="checkbox" name="en_echo_bulk_delete_dropships" id="en_echo_bulk_delete_dropships" onclick="return en_echo_select_bulk_locations('en_echo_delete_dropship_item', this);" />
     59                </th>
    5360                <?php echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    5461                <th><?php _e('Action', 'eniture-technology'); ?></th>
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/location/en-location.php

    r3381167 r3453531  
    257257
    258258                echo '<tr class="' . esc_attr($append_class) . '" id="en_location_row_id_' . esc_attr($en_location_id) . '">';
     259
     260                $_type = ($loc_type === 'wh') ? 'warehouse' : 'dropship';
     261                echo '<td class="en_wd_' . esc_attr($_type) . '_list_data en_echo_bulk_delete_col">
     262                    <input
     263                        type="checkbox"
     264                        class="en_echo_delete_' . esc_attr($_type) . '_item"
     265                        value="' . esc_attr($en_location_id) . '"
     266                        onclick="return en_echo_toggle_select_all_locations(
     267                            \'en_echo_delete_' . esc_attr($_type) . '_item\',
     268                            \'en_echo_bulk_delete_' . esc_attr($_type) . 's\'
     269                        );"
     270                    >
     271                </td>';
    259272                echo self::en_arrange_table_data('td', $en_sorted_location);
    260273
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/location/includes/en-location-ajax.php

    r3381167 r3453531  
    2323            add_action('wp_ajax_nopriv_en_echo_location_delete_row', [$this, 'en_echo_location_delete_row']);
    2424            add_action('wp_ajax_en_echo_location_delete_row', [$this, 'en_echo_location_delete_row']);
     25
     26            add_action('wp_ajax_nopriv_en_echo_bulk_delete_locations', array($this, 'en_echo_bulk_delete_locations_ajax'));
     27            add_action('wp_ajax_en_echo_wd_bulk_delete_locations', array($this, 'en_echo_bulk_delete_locations_ajax'));
    2528        }
    2629
     
    252255        }
    253256
     257        function en_echo_bulk_delete_locations_ajax()
     258        {
     259            if (!(current_user_can('manage_options') || current_user_can('manage_woocommerce')) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['wp_nonce'])), 'en_echo_location_nonce')) {
     260                echo wp_json_encode(array('error' => true, 'message' => 'Unauthorized Access.'));
     261                exit;
     262            }
     263
     264            $location_ids = isset($_POST['location_ids']) ? $_POST['location_ids'] : array();
     265            $loc_type = isset($_POST['location_type']) ? $_POST['location_type'] : '';
     266            if (empty($location_ids)) {
     267                echo wp_json_encode(['error' => true, 'message' => "Please select at least one {$loc_type} to delete."]);
     268                exit;
     269            }
     270
     271            global $wpdb;
     272            foreach ($location_ids as $location_id) {
     273                if ($loc_type == 'dropship') {
     274                    $get_dropship_id = '';
     275                    $dropship_id = intval($location_id);
     276                    $get_dropship_array = array($dropship_id);
     277                    $ser = maybe_serialize($get_dropship_id);
     278                    $get_dropship_val = array_map('intval', $get_dropship_array);
     279                    $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 . "')");
     280                    $post_id = reset($get_post_id)->post_ids_list;
     281
     282                    if (isset($post_id)) {
     283                        $wpdb->query("UPDATE `" . $wpdb->prefix . "postmeta` SET `meta_value` = '' WHERE `meta_key` IN('_enable_dropship','_dropship_location')  AND `post_id` IN ($post_id)");
     284                    }
     285                }
     286
     287                $wpdb->delete($wpdb->prefix . "warehouse", array('id' => intval($location_id), 'location' => $loc_type));
     288            }
     289
     290            if ($loc_type === 'warehouse') {
     291                $en_location_template_obj = new EnEchoWarehouseTemplate();
     292                $en_target_location = '.en_location_warehouse_main_div';
     293            } else {
     294                $en_location_template_obj = new EnEchoDropshipTemplate();
     295                $en_target_location = '.en_location_dropship_main_div';
     296            }
     297
     298            $html = $en_location_template_obj::en_load();
     299
     300            echo wp_json_encode(['error' => false, 'message' => 'Locations deleted successfully.', 'target_location' => $en_target_location, 'html' => $html]);
     301            exit;
     302        }
     303
    254304    }
    255305
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/location/warehouse/en-warehouse.php

    r3289951 r3453531  
    4040
    4141            <h1><?php _e('Warehouses', 'eniture-technology'); ?></h1>
    42             <button type="button" onclick="en_show_popup_location(true)"
    43                     class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>"><?php _e('Add', 'eniture-technology'); ?>
    44             </button>
    45             <?php echo force_balance_tags(self::$plan_required); ?>
     42            <div style="display: flex; gap: 3px;">
     43                <button type="button" onclick="en_show_popup_location(true)"
     44                        class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>" title="Add Warehouse"><?php _e('Add', 'eniture-technology'); ?>
     45                </button>
     46                <button type="button" class="en_echo_bulk_delete_warehouse en_wd_add_warehouse_btn button-primary" title="Delete Warehouses" onclick="return en_echo_delete_bulk_locations(event, 'en_echo_delete_warehouse_item', 'warehouse');"><?php _e('Delete', 'eniture-technology'); ?></button>
     47                </button>
     48                <?php echo force_balance_tags(self::$plan_required); ?>
     49            </div>
    4650
    4751            <p><?php _e('Warehouses that inventory all products not otherwise identified as drop shipped items. The warehouse closest to the shopper will be used to retrieve shipping rate estimates.', 'eniture-technology'); ?></p>
     52            <div class="en_echo_warehouse_success_message"><strong>Success! </strong> <span>Selected warehouses deleted successfully.</span></div>
     53            <div class="en_echo_warehouse_error_message"><strong>Error!</strong> <span>Please select at least one warehouse to delete.</span></div>
    4854
    4955            <table class="en_location_table en_location_warehouse_table">
    5056            <thead>
    5157            <tr>
     58                <th class="en_wd_warehouse_list_heading en_echo_bulk_delete_col">
     59                    <!-- Bulk delete -->
     60                    <input type="checkbox" name="en_echo_bulk_delete_warehouses" id="en_echo_bulk_delete_warehouses" onclick="return en_echo_select_bulk_locations('en_echo_delete_warehouse_item', this);" />
     61                </th>
    5262                <?php  echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    5363                <th><?php _e('Action', 'eniture-technology'); ?></th>
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/logs/en-json-tree-view/en-jtv-style.css

    r3029336 r3453531  
    9797.en_jtv li.en_folder > div.en_key > span::before {
    9898  content: "\2212";
    99   opacity: 0.33;
    100   color: #111;
    101   background-color: #e0eaef;
     99  opacity: 1;
     100  color: #fff;
     101  background-color: black !important;
    102102  text-align: center;
    103103  display: inline-block;
     
    112112.en_jtv li.en_folder.en_folded > div.en_key > span::before {
    113113  content: "+";
     114  font-weight: bolder;
     115    background-color: black !important;
     116    color: #fff !important;
    114117}
    115118
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/logs/en-logs.php

    r3134911 r3453531  
    4848
    4949            $url = EN_ECHO_ROOT_URL_QUOTES . '/request-log/index.php';
    50             $logs = $obj_classs->en_echo_sent_http_request($url, $data, 'POST');
     50            $logs = $obj_classs->en_echo_sent_http_request($url, $data, 'POST', 'Logs');
    5151            $logs = (isset($logs) && is_string($logs) && strlen($logs) > 0) ? json_decode($logs, true) : [];
    5252
  • ltl-freight-quotes-echo-edition/tags/1.1.16/admin/tab/quote-settings/en-quote-settings.php

    r3381167 r3453531  
    2525                    $number_of_options[$c] = __($c, $c);
    2626                }
     27            }
     28
     29            if (get_option('en_quote_settings_show_delivery_estimate_echo') == 'yes') {
     30                update_option('en_quote_settings_delivery_estimate_options_echo', 'delivery_days');
     31                delete_option('en_quote_settings_show_delivery_estimate_echo');
     32            }
     33
     34            // Cuttoff Time
     35            $echo_disable_cutt_off_time_ship_date_offset = "";
     36            $echo_cutt_off_time_package_required = "";
     37
     38            //  Check the cutt of time & offset days plans for disable input fields
     39            $echo_action_cutOffTime_shipDateOffset = apply_filters('echo_plans_suscription_and_features', 'order_cutt_off_time');
     40            if (is_array($echo_action_cutOffTime_shipDateOffset)) {
     41                $echo_disable_cutt_off_time_ship_date_offset = "disabled_me";
     42                $echo_cutt_off_time_package_required = apply_filters('echo_plans_notification_link', $echo_action_cutOffTime_shipDateOffset);
    2743            }
    2844
     
    94110                 * ==================================================================
    95111                 */
    96                 'en_quote_settings_show_delivery_estimate_echo' => [
    97                     'name' => __('Show Delivery Estimate ', 'woocommerce-settings-echo'),
    98                     'type' => 'checkbox',
    99                     'id' => 'en_quote_settings_show_delivery_estimate_echo'
    100                 ],
     112
     113                //** Start Delivery Estimate Options - Cuttoff Time
     114                 'delivery_estimate_options_echo' => array(
     115                    'name' => __('Delivery Estimate Options ', 'woocommerce-settings-en_woo_addons_packages_quotes'),
     116                    'type' => 'text',
     117                    'class' => 'hidden',
     118                    'id' => 'delivery_estimate_options_echo'
     119                ),
     120                'en_quote_settings_delivery_estimate_options_echo' => array(
     121                    'name' => '',
     122                    'type' => 'radio',
     123                    'default' => 'dont_show_estimates',
     124                    'options' => array(
     125                        'dont_show_estimates' => __("Don't display delivery estimates.", 'woocommerce'),
     126                        'delivery_days' => __("Display estimated number of days until delivery.", 'woocommerce'),
     127                        'delivery_date' => __("Display estimated delivery date.", 'woocommerce'),
     128                    ),
     129                    'id' => 'en_quote_settings_delivery_estimate_options_echo',
     130                    'class' => 'freightview_lfq_dont_show_estimate_option',
     131                ),
     132                //** End Delivery Estimate Options
     133
     134                //**Start: Cut Off Time & Ship Date Offset
     135                'en_quote_settings_cutt_off_time_ship_date_offset_echo' => array(
     136                    'name' => __('Cut Off Time & Ship Date Offset ', 'woocommerce-settings-en_woo_addons_packages_quotes'),
     137                    'type' => 'text',
     138                    'class' => 'hidden',
     139                    'desc' => $echo_cutt_off_time_package_required,
     140                    'id' => 'en_quote_settings_cutt_off_time_ship_date_offset_echo'
     141                ),
     142                'en_quote_settings_order_cut_off_time_echo' => array(
     143                    'name' => __('Order Cut Off Time ', 'woocommerce-settings-priority-one_lfq_freight_freight_orderCutoffTime'),
     144                    'type' => 'text',
     145                    'placeholder' => '-- : -- --',
     146                    'desc' => 'Enter the cut off time (e.g. 2:00) for orders. Orders placed after this time will be quoted as shipping the next business day.',
     147                    'id' => 'en_quote_settings_order_cut_off_time_echo',
     148                    'class' => $echo_disable_cutt_off_time_ship_date_offset,
     149                ),
     150                'en_quote_settings_shipment_offset_days_echo' => array(
     151                    'name' => __('Fulfillment Offset Days ', 'woocommerce-settings-priority-one_lfq_freight_shipment_offset_days'),
     152                    'type' => 'text',
     153                    'desc' => 'The number of days the ship date needs to be moved to allow for the processing of the order.',
     154                    'placeholder' => 'Fulfillment Offset Days, e.g. 2',
     155                    'id' => 'en_quote_settings_shipment_offset_days_echo',
     156                    'class' => $echo_disable_cutt_off_time_ship_date_offset,
     157                ),
     158                'en_quote_settings_all_shipment_days_echo' => array(
     159                    'name' => __("What days do you ship orders?", 'woocommerce-settings-priority-one_lfq_quotes'),
     160                    'type' => 'checkbox',
     161                    'desc' => 'Select All',
     162                    'class' => "all_shipment_days_echo_lfq $echo_disable_cutt_off_time_ship_date_offset",
     163                    'id' => 'en_quote_settings_all_shipment_days_echo'
     164                ),
     165                'en_quote_settings_monday_shipment_day_echo' => array(
     166                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     167                    'type' => 'checkbox',
     168                    'desc' => 'Monday',
     169                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     170                    'id' => 'en_quote_settings_monday_shipment_day_echo'
     171                ),
     172                'en_quote_settings_tuesday_shipment_day_echo' => array(
     173                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     174                    'type' => 'checkbox',
     175                    'desc' => 'Tuesday',
     176                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     177                    'id' => 'en_quote_settings_tuesday_shipment_day_echo'
     178                ),
     179                'en_quote_settings_wednesday_shipment_day_echo' => array(
     180                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     181                    'type' => 'checkbox',
     182                    'desc' => 'Wednesday',
     183                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     184                    'id' => 'en_quote_settings_wednesday_shipment_day_echo'
     185                ),
     186                'en_quote_settings_thursday_shipment_day_echo' => array(
     187                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     188                    'type' => 'checkbox',
     189                    'desc' => 'Thursday',
     190                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     191                    'id' => 'en_quote_settings_thursday_shipment_day_echo'
     192                ),
     193                'en_quote_settings_friday_shipment_day_echo' => array(
     194                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     195                    'type' => 'checkbox',
     196                    'desc' => 'Friday',
     197                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     198                    'id' => 'en_quote_settings_friday_shipment_day_echo'
     199                ),
     200                'show_delivery_estimate_echo' => array(
     201                    'title' => __('', 'woocommerce'),
     202                    'name' => __('', 'woocommerce-settings-priority-one_lfq_quotes'),
     203                    'desc' => '',
     204                    'id' => 'echo_lfq_show_delivery_estimates',
     205                    'css' => '',
     206                    'default' => '',
     207                    'type' => 'title',
     208                ),
     209                //**End: Cut Off Time & Ship Date Offset
     210
    101211                'residential_delivery_options_label' => [
    102212                    'name' => __('Residential Delivery', 'woocommerce-settings-echo'),
  • ltl-freight-quotes-echo-edition/tags/1.1.16/common/en-plans.php

    r3381167 r3453531  
    145145                'multi_dropships' => ['0', '1', '2', '3'],
    146146                'hold_at_terminal' => ['3'],
     147                'order_cutt_off_time' => ['2', '3']
    147148            ];
    148149
  • ltl-freight-quotes-echo-edition/tags/1.1.16/en-install.php

    r3381167 r3453531  
    6666        ]);
    6767
    68         wp_enqueue_script('EnEchoAdminJs', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.js', [], '1.0.5');
     68        wp_enqueue_script('EnEchoAdminJs', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.js', [], '1.0.6');
    6969        wp_localize_script('EnEchoAdminJs', 'en_echo_admin_script', [
    7070            'pluginsUrl' => EN_ECHO_PLUGIN_URL,
     
    7676            'echo_backup_rates_cart_price_percentage' => get_option("echo_backup_rates_cart_price_percentage"),
    7777            'echo_backup_rates_weight_function' => get_option("echo_backup_rates_weight_function"),
     78            // Cuttoff Time
     79            'echo_order_cut_off_time' => get_option("en_quote_settings_order_cut_off_time_echo")
    7880        ]);
    7981
    80         wp_enqueue_script('EnEchoLocationScript', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/js/en-echo-location.js', [], '1.0.3');
     82        wp_enqueue_script('EnEchoLocationScript', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/js/en-echo-location.js', [], '1.0.4');
    8183        wp_localize_script('EnEchoLocationScript', 'en_echo_location_script', array(
    8284            'pluginsUrl' => EN_ECHO_PLUGIN_URL,
     
    8486        ));
    8587
    86         wp_enqueue_script('EnEchoProdcutScript', EN_ECHO_DIR_FILE . '/admin/product/assets/en-custom-fields.js', [], '1.0.0');
     88        wp_enqueue_script('EnEchoProdcutScript', EN_ECHO_DIR_FILE . '/admin/product/assets/en-custom-fields.js', [], '1.0.1');
    8789        wp_localize_script('EnEchoProductScript', 'script', array(
    8890            'pluginsUrl' => EN_ECHO_PLUGIN_URL,
    8991        ));
    9092
    91         wp_register_style('EnEchoLocationStyle', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/css/en-echo-location.css', false, '1.0.1');
     93        wp_register_style('EnEchoLocationStyle', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/css/en-echo-location.css', false, '1.0.2');
    9294        wp_enqueue_style('EnEchoLocationStyle');
    9395
    94         wp_register_style('EnEchoAdminCss', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.css', false, '1.0.2');
     96        wp_register_style('EnEchoAdminCss', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.css', false, '1.0.3');
    9597        wp_enqueue_style('EnEchoAdminCss');
    9698
    9799        wp_register_style('EnEchoProductCss', EN_ECHO_DIR_FILE . '/admin/product/assets/en-custom-fields-style.css', false, '1.0.0');
    98100        wp_enqueue_style('EnEchoProductCss');
     101
     102        // Cuttoff Time
     103        wp_register_style('EnEchoWickedPickerStyle', EN_ECHO_DIR_FILE . '/admin/assets/wickedpicker.min.css', false, '1.0.0');
     104        wp_register_script('EnEchoWickedPickerScript', EN_ECHO_DIR_FILE . '/admin/assets/wickedpicker.js', false, '1.0.0');
     105       
     106        wp_enqueue_style('EnEchoWickedPickerStyle');
     107        wp_enqueue_script('EnEchoWickedPickerScript');
    99108    }
    100109
     
    127136    function en_echo_shipping_sections($settings)
    128137    {
    129         $settings[] = include('admin/tab/en-tab.php');
     138        $en_tab = include('admin/tab/en-tab.php');
     139        if (is_object($en_tab)) {
     140            $settings[] = $en_tab;
     141        }
     142
    130143        return $settings;
    131144    }
  • ltl-freight-quotes-echo-edition/tags/1.1.16/http/en-curl.php

    r3381167 r3453531  
    2626        {
    2727            $curl_response = '';
     28            $en_time_start = 0;
     29
    2830            if (strlen($url) > 0 && is_array($post_data) && !empty($post_data)) {
    2931
    3032                $post_data = self::remove_extra_request_data($post_data);
     33                $cachable_data = self::create_cachable_data($post_data);
     34                $cache_key = self::create_cache_key($cachable_data);
     35                $cache_response = get_transient($cache_key);
     36                $skip_cache = in_array($step_for, ['Plan', 'Logs', 'Address', 'Connection']);
    3137
    32                 // Eniture Execution Time
    33                 $en_time_start = microtime(true);
     38                if (false === $cache_response || $skip_cache) {
     39                    // Eniture Execution Time
     40                    $en_time_start = microtime(true);
    3441
    35                 // Eniture Debug Mood
    36                 do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Request ", $post_data);
     42                    // Eniture Debug Mood
     43                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Request ", $post_data);
    3744
    38                 $method == 'POST' ? $post_data = http_build_query($post_data) : '';
     45                    $method == 'POST' ? $post_data = http_build_query($post_data) : '';
    3946
    40                 // Eniture Debug Mood
    41                 do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Build Query ", $post_data);
     47                    // Eniture Debug Mood
     48                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Build Query ", $post_data);
    4249
    43                 $response = wp_remote_post($url, [
    44                         'method' => $method,
    45                         'timeout' => 60,
    46                         'redirection' => 5,
    47                         'blocking' => true,
    48                         'body' => $post_data,
    49                     ]
    50                 );
    51                 $curl_response = wp_remote_retrieve_body($response);
    52                 $headers = wp_remote_retrieve_headers($response);
    53                 self::eniture_check_rate_limit_headers($headers);
     50                    $response = wp_remote_post($url, [
     51                            'method' => $method,
     52                            'timeout' => 60,
     53                            'redirection' => 5,
     54                            'blocking' => true,
     55                            'body' => $post_data,
     56                        ]
     57                    );
     58                    $headers = wp_remote_retrieve_headers($response);
     59                    self::eniture_check_rate_limit_headers($headers);
     60                    $curl_response = wp_remote_retrieve_body($response);
     61                    $resp = !empty($curl_response) && is_string($curl_response) ? json_decode($curl_response, true) : $curl_response;
     62
     63                    if (!empty($resp) && (!isset($resp['severity']) || (isset($resp['severity']) && $resp['severity'] != 'ERROR'))) {
     64                        set_transient($cache_key, $curl_response, 12 * HOUR_IN_SECONDS);
     65                    }
     66
     67                    // Eniture Debug Mood
     68                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Response ", json_decode($curl_response));
     69                } else {
     70                    $curl_response = $cache_response;
     71                    // Eniture Debug Mood
     72                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Cache Response ", $curl_response);
     73                }
    5474            }
    55 
    56             // Eniture Debug Mood
    57             do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Response ", json_decode($curl_response));
    5875
    5976            // Eniture Execution Time
     
    98115            return $request_data;
    99116        }
     117
     118        static public function create_cachable_data($request_data)
     119        {
     120            $cache_keys = ['serverName', 'platform', 'carrierType', 'carrierName', 'carrierMode', 'requestVersion', 'apiKey', 'accountNumber', 'licenseKey', 'shipment_type', 'LTL_FREIGHT', 'product_name', 'shipment_weight', 'commdityDetails', 'originAddress', 'receiverZip', 'receiverState', 'receiverCountryCode', 'receiverCity', 'instorPickupLocalDelEnable', 'doNesting', 'accessorials', 'handlingUnitWeight', 'maxWeightPerHandlingUnit', 'holdAtTerminal', 'shipmentOffsetDays', 'OrderCutoffTime', 'shipmentWeekDays', 'modifyShipmentDateTime'];
     121            $cachable_data = [];
     122           
     123            // get the data from the request
     124            foreach ($cache_keys as $key) {
     125                if (isset($request_data[$key])) $cachable_data[$key] = $request_data[$key];
     126            }
     127
     128            if (isset($request_data['autoResidentials']) && $request_data['autoResidentials'] == 1) {
     129                $cachable_data['autoResidentials'] = $request_data['autoResidentials'];
     130                $cachable_data['addressLine'] = $request_data['addressLine'];
     131                $cachable_data['addressLine2'] = (isset($request_data['addressLine2'])) ? $request_data['addressLine2'] : '';
     132                $cachable_data['defaultRADAddressType'] = $request_data['defaultRADAddressType'];
     133                $cachable_data['defaultRADWithoutStreetAddress'] = $request_data['defaultRADWithoutStreetAddress'];
     134                $cachable_data['poboxAddressValidation'] = $request_data['poboxAddressValidation'];
     135            }
     136
     137            if (isset($request_data['standardPackaging']) && $request_data['standardPackaging'] == 1) {
     138                $cachable_data['standardPackaging'] = $request_data['standardPackaging'];
     139                $cachable_data['pallet'] = $request_data['pallet'];
     140            }
     141
     142            return $cachable_data;
     143        }
     144
     145        static public function create_cache_key($request_data)
     146        {
     147            $request_build_query = http_build_query($request_data);
     148            return md5('eniture_echo_' . $request_build_query);
     149        }
    100150    }
    101151
  • ltl-freight-quotes-echo-edition/tags/1.1.16/ltl-freight-quotes-echo-edition.php

    r3388669 r3453531  
    44 * Plugin URI: https://eniture.com/products/
    55 * Description: Dynamically retrieves your negotiated shipping rates from Echo and displays the results in the WooCommerce shopping cart.
    6  * Version: 1.1.15
     6 * Version: 1.1.16
    77 * Author: Eniture Technology
    88 * Author URI: http://eniture.com/
    99 * Text Domain: eniture-technology
    1010 * License: GPLv2 or later
    11  * WC requires at least: 6.4
    12  * WC tested up to: 10.2.2
     11 * Requires Plugins: woocommerce
    1312 */
    1413
  • ltl-freight-quotes-echo-edition/tags/1.1.16/readme.txt

    r3388669 r3453531  
    33Tags: eniture,Echo,,LTL freight rates,LTL freight quotes, shipping estimates
    44Requires at least: 6.4
    5 Tested up to: 6.8
    6 Stable tag: 1.1.15
     5Tested up to: 6.9
     6Stable tag: 1.1.16
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    148148== Changelog ==
    149149
     150= 1.1.16 - 2026-02-04 =
     151* Update: Added a **caching mechanism** to efficiently handle multiple identical quote requests and reduce redundant API calls.
     152* Update: Introduced **bulk delete functionality** for warehouse and dropship locations to simplify location management.
     153* Update: Added **delivery estimate options** and **order cutoff time** functionality in the quote settings for improved delivery accuracy.
     154* Update: Added **product-level offset days** functionality to allow more granular shipment timing control.
     155* Update: Added parent product checks to properly handle empty variant scenarios and prevent invalid processing.
     156
    150157= 1.1.15 - 2025-11-03 =
    151158* Update: Updated the **Stacking** property default value from **“Maximized”** to **“Evenly”** for better consistency in nested layouts. (Existing configurations will remain unchanged unless manually updated.)
  • ltl-freight-quotes-echo-edition/tags/1.1.16/server/api/en-response.php

    r3381167 r3453531  
    196196         * @param string $index
    197197         * @param dynamic $is_not_matched
    198          * @return dynamic mixed
     198         * @return dynamic|string|array mixed
    199199         */
    200200        static public function en_sanitize_rate($index, $is_not_matched)
     
    256256            $en_count_rates = 0;
    257257
    258             $handling_fee = $en_settings_label = $rating_method = $transit_days = $enable_carriers = $liftgate_resid_delivery = $liftgate_delivery_option = '';
     258            $handling_fee = $en_settings_label = $rating_method = $transit_days = $liftgate_resid_delivery = $liftgate_delivery_option = '';
     259            $enable_carriers = [];
    259260            self::$en_settings = json_decode(EN_ECHO_SET_QUOTE_SETTINGS, true);
    260261            self::$en_accessorial = json_decode(EN_ECHO_ACCESSORIAL, true);
     
    294295                && $guarenteed_value == 'GN') {
    295296                    $label = isset($is_valid_label) && $is_valid_label ? $en_settings_label : self::en_sanitize_rate('CarrierName', '');
    296                     $calculated_transit_days = self::en_sanitize_rate('CarrierTransitDays', '');
    297                     $transit_time = '';
    298                     if (is_numeric($calculated_transit_days) &&
    299                         self::$en_is_shipment == 'en_single_shipment' &&
    300                         $transit_days == "yes") {
    301                         $transit_time = ' (Intransit days: ' . $calculated_transit_days . ')';
     297                    $calculated_transit_days = self::en_sanitize_rate('totalTransitTimeInDays', '');
     298                    $calculated_transit_date = self::en_sanitize_rate('deliveryDate', '');
     299                    $transit_time = $append_transit = '';
     300
     301                    if ($rating_method != "average_rate" && isset($delivery_estimate_option) && !empty($delivery_estimate_option) && $delivery_estimate_option != 'dont_show_estimates' && self::$en_is_shipment != 'multi_shipment') {
     302
     303                        if ($delivery_estimate_option == 'delivery_date' && is_string($calculated_transit_date) && strlen($calculated_transit_date) > 0) {
     304                            $append_transit = ' (Expected delivery by ' . date('m-d-Y', strtotime($calculated_transit_date)) . ')';
     305                        } else if ($delivery_estimate_option == 'delivery_days' && is_numeric($calculated_transit_days)) {
     306                            $append_transit = ' (Intransit days: ' . $calculated_transit_days . ')';
     307                        }
    302308                    }
     309
    303310                    // make data for order widget detail
    304311                    $meta_data['service_type'] = $label;
     
    328335                        'transit_days' => $transit_days,
    329336                        'transit_time' => $transit_time,
     337                        'append_transit' => $append_transit,
    330338                        'plugin_name' => 'echo',
    331339                        'plugin_type' => 'ltl',
     
    333341                    ];
    334342
     343                    // Add pallet fee
     344                    $rate = has_filter('en_pallet_price') ? apply_filters('en_pallet_price', $rate) : $rate;
    335345
    336346                    foreach (self::$en_accessorial as $key => $accessorial) {
     
    472482            $label = self::en_sanitize_rate('CarrierName', '');
    473483            $hat_terminal_address = self::get_address_terminal();
    474             $calculated_transit_days = self::en_sanitize_rate('CarrierTransitDays', '');
    475             $transit_time = '';
    476             if (is_numeric($calculated_transit_days) && self::$en_is_shipment == 'en_single_shipment' && $transit_days == "yes") {
    477                 $transit_time = ' (Intransit days: ' . $calculated_transit_days . ')';
     484            $calculated_transit_days = self::en_sanitize_rate('totalTransitTimeInDays', '');
     485            $calculated_transit_date = self::en_sanitize_rate('deliveryDate', '');
     486            $rating_method = get_option('en_quote_settings_rating_method_echo');
     487            $delivery_estimate_option = get_option('en_quote_settings_delivery_estimate_options_echo');
     488            $transit_time = $append_transit = '';
     489
     490            if ($rating_method != "average_rate" && isset($delivery_estimate_option) && !empty($delivery_estimate_option) && $delivery_estimate_option != 'dont_show_estimates' && self::$en_is_shipment != 'multi_shipment') {
     491
     492                if ($delivery_estimate_option == 'delivery_date' && is_string($calculated_transit_date) && strlen($calculated_transit_date) > 0) {
     493                    $append_transit = ' (Expected delivery by ' . date('m-d-Y', strtotime($calculated_transit_date)) . ')';
     494                } else if ($delivery_estimate_option == 'delivery_days' && is_numeric($calculated_transit_days)) {
     495                    $append_transit = ' (Intransit days: ' . $calculated_transit_days . ')';
     496                }
    478497            }
    479498           
     
    522541                'transit_days' => $transit_days,
    523542                'transit_time' => $transit_time,
     543                'append_transit' => $append_transit,
    524544                'label_sufex' => ['HAT'],
    525545                'hat_append_label' => $hat_terminal_address,
  • ltl-freight-quotes-echo-edition/tags/1.1.16/server/common/en-quote-settings.php

    r3383387 r3453531  
    3232            $accessorials['holdAtTerminal'] = isset($en_settings['hold_at_terminal']) && $en_settings['hold_at_terminal'] == 'yes' ? '1' : '0';
    3333            $accessorials['enabledCarrier'] = !empty($en_settings['enable_carriers']) ? $en_settings['enable_carriers'] : [];
     34            $accessorials['handling_fee'] = isset($en_settings['handling_fee']) ? $en_settings['handling_fee'] : '';
    3435
    3536            return $accessorials;
     
    6263            $quote_settings = [
    6364                'transit_days' => get_option('en_quote_settings_show_delivery_estimate_echo'),
     65                'delivery_estimate_option' => get_option('en_quote_settings_delivery_estimate_options_echo'),
    6466                'own_freight' => get_option('en_quote_settings_own_arrangment_echo'),
    6567                'own_freight_label' => get_option('en_quote_settings_text_for_own_arrangment_echo'),
     
    7981                'hold_at_terminal' => get_option('en_quote_settings_hold_at_terminal_echo'),
    8082                'hold_at_terminal_fee' => get_option('en_quote_settings_hold_at_terminal_fee_echo'),
     83                'order_cutoff_time' => get_option('en_quote_settings_order_cut_off_time_echo'),
     84                'fullfillment_offset_days' => get_option('en_quote_settings_shipment_offset_days_echo'),
    8185            ];
    8286
  • ltl-freight-quotes-echo-edition/tags/1.1.16/server/en-shipping-rates.php

    r3383387 r3453531  
    4646            public $small_package = [];
    4747            public $ltl_package = [];
     48            public $en_fdo_meta_data_third_party = [];
    4849
    4950            /**
     
    148149
    149150                    $en_package = array_merge(json_decode(EN_ECHO_GET_CONNECTION_SETTINGS, true), $en_package, json_decode(EN_ECHO_GET_QUOTE_SETTINGS, true));
     151                    $en_package = $this->addOrderCutOffTime($en_package);
    150152
    151153                    $response = \EnEchoCurl\EnEchoCurl::en_echo_sent_http_request(EN_ECHO_HITTING_API_URL, $en_package, 'POST', 'Quotes');
     
    222224                                }
    223225                            }
    224                             (isset($rate['transit_time']) && strlen($rate['transit_time']) > 0) ? $rate['label'] .= $rate['transit_time'] : '';
     226
     227                            $rate['label'] .= !empty($rate['append_transit']) ? $rate['append_transit'] : '';
     228
    225229                            isset($rate['cost']) && $en_small_package_charges > 0 ?
    226230                                $rate['cost'] = $rate['cost'] + $en_small_package_charges : 0;
     
    362366                }
    363367            }
     368
     369            function addOrderCutOffTime($en_package)
     370            {
     371                $shipment_week_days = [];
     372                $order_cut_off_time = $shipment_off_set_days = $modify_shipment_date_time = $store_date_time = "";
     373                $delivery_estimate_option = get_option('en_quote_settings_delivery_estimate_options_echo');
     374
     375                if ($delivery_estimate_option == 'delivery_days' || $delivery_estimate_option == 'delivery_date') {
     376                    $order_cut_off_time = get_option('en_quote_settings_order_cut_off_time_echo');
     377                    $shipment_off_set_days = get_option('en_quote_settings_shipment_offset_days_echo');
     378                   
     379                    // Use product-level fulfillment offset days if available, otherwise use global setting
     380                    if (isset($en_package['max_fulfillment_offset_days']) && is_numeric($en_package['max_fulfillment_offset_days']) && $en_package['max_fulfillment_offset_days'] > 0) {
     381                        $shipment_off_set_days = $en_package['max_fulfillment_offset_days'];
     382                    }
     383
     384                    $shipment_week_days = $this->get_shipment_week_days();
     385                    $modify_shipment_date_time = ($order_cut_off_time != '' || $shipment_off_set_days != '' || (is_array($shipment_week_days) && count($shipment_week_days) > 0)) ? 1 : 0;
     386                    $store_date_time = date('Y-m-d H:i:s', current_time('timestamp'));
     387                }
     388
     389                $en_package['OrderCutoffTime'] = $order_cut_off_time;
     390                $en_package['shipmentOffsetDays'] = $shipment_off_set_days;
     391                $en_package['shipmentWeekDays'] = $shipment_week_days;
     392                $en_package['modifyShipmentDateTime'] = $modify_shipment_date_time;
     393                $en_package['storeDateTime'] = $store_date_time;
     394
     395                return $en_package;
     396            }
     397
     398            function get_shipment_week_days()
     399            {
     400                $shipment_days_of_week = ['1', '2', '3', '4', '5'];
     401                if (get_option('en_quote_settings_all_shipment_days_echo') == 'yes') return $shipment_days_of_week;
     402
     403                $shipment_days_of_week = [];
     404                if (get_option('en_quote_settings_monday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 1;
     405                if (get_option('en_quote_settings_tuesday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 2;
     406                if (get_option('en_quote_settings_wednesday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 3;
     407                if (get_option('en_quote_settings_thursday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 4;
     408                if (get_option('en_quote_settings_friday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 5;
     409
     410                return $shipment_days_of_week;
     411            }
    364412        }
    365413
  • ltl-freight-quotes-echo-edition/tags/1.1.16/server/package/en-package.php

    r3388669 r3453531  
    2323    {
    2424        static public $post_id;
     25        static public $parent_id;
    2526        static public $locations;
    2627        static public $product_key_name;
     
    108109                    self::$post_id = (isset($product['variation_id']) && $product['variation_id'] > 0) ?
    109110                        $product['variation_id'] : $product_data->get_id();
    110                     $parent_id = self::$post_id;
     111                    self::$parent_id = $parent_id = self::$post_id;
    111112                    if(isset($product['variation_id']) && $product['variation_id'] > 0){
    112113                        $variation = wc_get_product($product['variation_id']);
    113                         $parent_id = $variation->get_parent_id();
     114                        self::$parent_id = $parent_id = $variation->get_parent_id();
    114115                    }
    115116                    $haz_array = get_post_meta(self::$post_id, '_hazardousmaterials');
     117                    if (isset($haz_array[0]) && empty($haz_array[0]) && self::$parent_id != self::$post_id) {
     118                        $haz_array = get_post_meta(self::$parent_id, '_hazardousmaterials');
     119                    }
    116120
    117121                    isset($haz_array[0]) && $haz_array[0] == 'yes' ? $haz_status = 'Y' : $haz_status = 'no';
    118122
    119123                    $nested_mat = get_post_meta(self::$post_id, '_nestedMaterials');
     124                    if (isset($nested_mat[0]) && empty($nested_mat[0]) && self::$parent_id != self::$post_id) {
     125                        $nested_mat = get_post_meta(self::$parent_id, '_nestedMaterials');
     126                    }
     127
    120128                    isset($nested_mat[0]) && $nested_mat[0] == 'yes' ? $doNesting = 1 : $doNesting = '';
    121129
     
    125133
    126134                    $nested_percen = get_post_meta(self::$post_id, '_nestedPercentage');
     135                    if (isset($nested_percen[0]) && empty($nested_percen[0]) && self::$parent_id != self::$post_id) {
     136                        $nested_percen = get_post_meta(self::$parent_id, '_nestedPercentage');
     137                    }
     138
    127139                    !empty($nested_percen) ? $nested_percentage = $nested_percen[0] : $nested_percentage = 0;
    128140
     
    131143
    132144                    $max_nested_item = get_post_meta(self::$post_id, '_maxNestedItems');
     145                    if (isset($max_nested_item[0]) && empty($max_nested_item[0]) && self::$parent_id != self::$post_id) {
     146                        $max_nested_item = get_post_meta(self::$parent_id, '_maxNestedItems');
     147                    }
     148
    133149                    !empty($max_nested_item) ? $max_nested_items = $max_nested_item[0] : $max_nested_items = 0;
    134150
     
    136152                    $nested_staking_property = isset($nested_staking[0]) && !empty($nested_staking[0]) ? $nested_staking[0] : 'Evenly';
    137153
    138                     $product_title = str_replace(array("'", '"'), '', esc_attr($product_data->get_title()));
     154                    $product_title = str_replace(array("'", '"'), '', esc_attr($product_data->get_name()));
    139155                    // Get product level markup value
    140156                    $product_level_markup = self::en_get_product_level_markup($product_data, $product['variation_id'], $product['product_id'], $product['quantity']);
     157                    $product_level_fulfillment_offset = self::en_get_product_level_fulfillment_offset_days($product_data, $product['variation_id'], $product['product_id']);
    141158
    142159                    $product_item = [
     
    164181                        'lineItemPackageCode' => $lineItemPackageCode,
    165182                        'lineItemPackageType' => $lineItemPackageType,
    166                         'markup' => $product_level_markup
     183                        'markup' => $product_level_markup,
     184                        'fulfillment_offset_days' => $product_level_fulfillment_offset
    167185                    ];
    168186
     
    286304                        self::$en_request['commdityDetails'][$origin_zip_code][] = $product_item;
    287305                        self::$en_request['originAddress'][$origin_zip_code] = $origin_address;
     306
     307                         // Track maximum fulfillment offset days
     308                        if (is_numeric($product_level_fulfillment_offset) && $product_level_fulfillment_offset > 0) {
     309                            if (!isset(self::$en_request['max_fulfillment_offset_days']) || $product_level_fulfillment_offset > self::$en_request['max_fulfillment_offset_days']) {
     310                                self::$en_request['max_fulfillment_offset_days'] = $product_level_fulfillment_offset;
     311                            }
     312                        }
    288313                    }
    289314                }
     
    549574                case '_dropship_location':
    550575                    $enable_dropship = get_post_meta(self::$post_id, '_enable_dropship', true);
     576
     577                    if (empty($enable_dropship) && self::$parent_id > 0 && self::$parent_id != self::$post_id) {
     578                        self::$post_id = self::$parent_id;
     579                        $enable_dropship = get_post_meta(self::$post_id, '_enable_dropship', true);
     580                    }
     581
    551582                    switch ($enable_dropship) {
    552583                        case 'yes':
     
    681712        }
    682713
     714         static public function en_get_product_level_fulfillment_offset_days($_product, $variation_id, $product_id)
     715        {
     716            $fulfillment_offset_days = '';
     717            $field_name = '_eniture_product_level_fulfillment_offset_days';
     718
     719            if ($_product->get_type() == 'variation' && $variation_id > 0) {
     720                $fulfillment_offset_days = get_post_meta($variation_id, $field_name, true);
     721
     722                if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     723                    $parent_id = $_product->get_parent_id();
     724                    if ($parent_id > 0) {
     725                        $fulfillment_offset_days = get_post_meta($parent_id, $field_name, true);
     726                    }
     727                }
     728            } else {
     729                $fulfillment_offset_days = get_post_meta($_product->get_id(), $field_name, true);
     730            }
     731
     732            if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     733                $fulfillment_offset_days = get_post_meta($product_id, $field_name, true);
     734            }
     735
     736            // Validate and sanitize the value before returning
     737            if (is_numeric($fulfillment_offset_days)) {
     738                $value = intval($fulfillment_offset_days);
     739                return $value;
     740            }
     741
     742            // Return empty string if invalid
     743            return '';
     744        }
    683745    }
    684746
  • ltl-freight-quotes-echo-edition/trunk/admin/assets/en-echo-admin.css

    r3381167 r3453531  
    120120
    121121.en_quote_settings_sub_options th {
    122     padding: 0 0 10px 15px;
     122    padding: 10px 0 10px 15px;
    123123    margin-left: 10px !important;
    124124    font-weight: 400;
     
    360360.woocommerce_variable_attributes.wc-metabox-content [class^="_nmfc_number"],
    361361.woocommerce_variable_attributes.wc-metabox-content [class^="_en_product_markup"],
     362.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"],
    362363.woocommerce_variable_attributes.wc-metabox-content [class^="_nestedPercentage"],
    363364.woocommerce_variable_attributes.wc-metabox-content [class^="en_flat_rate_price"],
     
    383384.woocommerce_variable_attributes.wc-metabox-content [class^="en_flat_rate_price"],
    384385.woocommerce_variable_attributes.wc-metabox-content [class^="_en_product_markup"],
     386.woocommerce_variable_attributes.wc-metabox-content [class^="_eniture_product_level_fulfillment_offset_days"],
    385387.woocommerce_variable_attributes.wc-metabox-content [class^="_nestedPercentage"],
    386388.woocommerce_variable_attributes.wc-metabox-content [class^="_nestedDimension"],
     
    392394.woocommerce_variable_attributes.wc-metabox-content .data p[class*="en_flat_rate_price"] span.woocommerce-help-tip,
    393395.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_en_product_markup"] span.woocommerce-help-tip,
     396.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_eniture_product_level_fulfillment_offset_days"] span.woocommerce-help-tip,
    394397.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_nestedPercentage"] span.woocommerce-help-tip,
    395398.woocommerce_variable_attributes.wc-metabox-content .data p[class*="_nestedDimension"] span.woocommerce-help-tip,
     
    469472    aspect-ratio: 16 / 9;
    470473}
     474
     475.en_quote_settings_all_shipment_days_echo_tr td {
     476  display: flex !important;
     477  align-items: center;
     478  gap: 16px;
     479  flex-wrap: nowrap;
     480}
  • ltl-freight-quotes-echo-edition/trunk/admin/assets/en-echo-admin.js

    r3381167 r3453531  
    9696            "#en_quote_settings_handling_unit_weight_echo," +
    9797            "#en_quote_settings_max_weight_handling_unit_echo," +
    98             "#en_woo_addons_liftgate_with_auto_residential").closest('tr').addClass("en_quote_settings_sub_options");
     98            "#en_woo_addons_liftgate_with_auto_residential," +
     99            "#en_quote_settings_order_cut_off_time_echo, #en_quote_settings_shipment_offset_days_echo, #en_quote_settings_all_shipment_days_echo").closest('tr').addClass("en_quote_settings_sub_options");
    99100
    100101        jQuery('#en_quote_settings_hold_at_terminal_fee_echo').on('keydown', function (e) {
     
    351352    // Backup rates settings
    352353    echoBackupRatesSettings();
     354    // Cut-off time and shipment offset days settings
     355    echoCutOffTimeAndShipDateSettings();
    353356});
    354357
     
    773776    }
    774777}
     778
     779if (typeof echoCutOffTimeAndShipDateSettings != 'function') {
     780    function echoCutOffTimeAndShipDateSettings() {
     781        jQuery("#en_quote_settings_shipment_offset_days_echo").closest('tr').addClass("en_quote_settings_shipment_offset_days_echo_tr");
     782        jQuery("#en_quote_settings_all_shipment_days_echo").closest('tr').addClass("en_quote_settings_all_shipment_days_echo_tr");
     783        jQuery(".echo_shipment_day ").closest('tr').addClass("echo_shipment_day_tr");
     784        jQuery("#en_quote_settings_order_cut_off_time_echo").closest('tr').addClass("echo_cutt_off_time_ship_date_offset");
     785
     786        const echo_current_time = en_echo_admin_script.echo_order_cut_off_time;
     787        if (echo_current_time == '') {
     788            jQuery('#en_quote_settings_order_cut_off_time_echo').wickedpicker({
     789                now: '',
     790                title: 'Cut Off Time',
     791            });
     792        } else {
     793            jQuery('#en_quote_settings_order_cut_off_time_echo').wickedpicker({
     794                now: echo_current_time,
     795                title: 'Cut Off Time'
     796            });
     797        }
     798
     799        const show_delivery_estimate = jQuery('input[name=en_quote_settings_delivery_estimate_options_echo]:checked').val();
     800        if (show_delivery_estimate === 'dont_show_estimates') {
     801            jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', true);
     802            jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', true);
     803            jQuery("#en_quote_settings_shipment_offset_days_echo").css("cursor", "not-allowed");
     804            jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "not-allowed");
     805            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', true);
     806            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "not-allowed");
     807        } else {
     808            jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', false);
     809            jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', false);
     810            jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "");
     811            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', false);
     812            jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "");
     813        }
     814
     815        jQuery("input[name=en_quote_settings_delivery_estimate_options_echo]").change(function () {
     816            const show_delivery_estimate = jQuery('input[name=en_quote_settings_delivery_estimate_options_echo]:checked').val();
     817           
     818            if (show_delivery_estimate === 'dont_show_estimates') {
     819                jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', true);
     820                jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', true);
     821                jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "not-allowed");
     822                jQuery("#en_quote_settings_shipment_offset_days_echo").css("cursor", "not-allowed");
     823                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', true);
     824                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "not-allowed");
     825            } else {
     826                jQuery("#en_quote_settings_order_cut_off_time_echo").prop('disabled', false);
     827                jQuery("#en_quote_settings_shipment_offset_days_echo").prop('disabled', false);
     828                jQuery("#en_quote_settings_order_cut_off_time_echo").css("cursor", "auto");
     829                jQuery("#en_quote_settings_shipment_offset_days_echo").css("cursor", "auto");
     830                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').prop('disabled', false);
     831                jQuery('.all_shipment_days_echo_lfq, .echo_shipment_day').css("cursor", "auto");
     832            }
     833        });
     834
     835        /*
     836        * Uncheck Week days Select All Checkbox
     837        */
     838        jQuery(".echo_shipment_day").on('change load', function () {
     839
     840            const checkboxes = jQuery('.echo_shipment_day:checked').length;
     841            const un_checkboxes = jQuery('.echo_shipment_day').length;
     842
     843            if (checkboxes === un_checkboxes) {
     844                jQuery('.all_shipment_days_echo_lfq').prop('checked', true);
     845            } else {
     846                jQuery('.all_shipment_days_echo_lfq').prop('checked', false);
     847            }
     848        });
     849
     850        /*
     851        * Select All Shipment Week days
     852        */
     853
     854        const all_int_checkboxes = jQuery('.all_shipment_days_echo_lfq');
     855        if (all_int_checkboxes.length === all_int_checkboxes.filter(":checked").length) {
     856            jQuery('.all_shipment_days_echo_lfq').prop('checked', true);
     857        }
     858
     859        jQuery(".all_shipment_days_echo_lfq").change(function () {
     860            if (this.checked) {
     861                jQuery(".echo_shipment_day").each(function () {
     862                    this.checked = true;
     863                });
     864            } else {
     865                jQuery(".echo_shipment_day").each(function () {
     866                    this.checked = false;
     867                });
     868            }
     869        });
     870
     871        const main_td = jQuery('.en_quote_settings_all_shipment_days_echo_tr td');
     872        const rows   = jQuery('tr.echo_shipment_day_tr');
     873
     874        if (!main_td.length || !rows.length) return;
     875
     876        // Move all day checkboxes into main row
     877        rows.each(function () {
     878            const input_wrap = jQuery(this).find('td > *').first();
     879            if (input_wrap.length) main_td.append(input_wrap);
     880        });
     881
     882        // Hide original rows
     883        rows.hide();
     884
     885        // ---- Select All sync ----
     886        const select_all = main_td.find('input[type="checkbox"]').first();
     887        const days = main_td.find('input[type="checkbox"]').not(select_all);
     888
     889        // Select all → days
     890        select_all.on('change', function () {
     891            days.prop('checked', this.checked);
     892        });
     893        // Days → select all
     894        days.on('change', function () {
     895            select_all.prop('checked', days.length === days.filter(':checked').length);
     896        });
     897    }
     898}
  • ltl-freight-quotes-echo-edition/trunk/admin/product/assets/en-custom-fields.js

    r2785919 r3453531  
    7171    }
    7272
     73    jQuery(document).on('input', '._eniture_product_level_fulfillment_offset_days.short', function () {
     74        if (this.value !== '' && !/^\d+$/.test(this.value)) {
     75            this.setCustomValidity('Please enter a number');
     76        } else if (this.value !== '' && Number(this.value) < 1) {
     77            this.setCustomValidity('Value must be greater than or equal to 1');
     78        } else if (this.value !== '' && Number(this.value) > 20) {
     79            this.setCustomValidity('Value must be less than or equal to 20');
     80        } else {
     81            this.setCustomValidity('');
     82        }
     83    });
    7384})
  • ltl-freight-quotes-echo-edition/trunk/admin/product/en-product-detail.php

    r3261316 r3453531  
    8686                    $custom_field = (isset($custom_field['id'])) ? $custom_field['id'] : '';
    8787                    $en_updated_product = (isset($_POST[$custom_field][$postId])) ? sanitize_text_field($_POST[$custom_field][$postId]) : '';
    88                     $en_updated_product = $custom_field == '_dropship_location' ?
    89                         (maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product)) : esc_attr($en_updated_product);
     88
     89                    if ($custom_field == '_dropship_location') {
     90                        $en_updated_product = maybe_serialize(is_array($en_updated_product) ? array_map('intval', $en_updated_product) : $en_updated_product);
     91                    } elseif ($custom_field == '_eniture_product_level_fulfillment_offset_days') {
     92                        $en_updated_product = $this->sanitize_fulfillment_offset_days($en_updated_product);
     93                    } else {
     94                        $en_updated_product = esc_attr($en_updated_product);
     95                    }
     96
    9097                    update_post_meta($postId, $custom_field, $en_updated_product);
    9198                }
    9299            }
     100        }
     101
     102        private function sanitize_fulfillment_offset_days($value)
     103        {
     104            $value = trim($value);
     105            if ($value === '' || $value === null) return '';
     106
     107            $value = preg_replace('/[^0-9.-]/', '', $value);
     108            if (!is_numeric($value)) return '';
     109
     110            $value = intval(floor(floatval($value)));
     111            return $value < 0 ? '' : strval($value);
    93112        }
    94113
     
    186205                    '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."
    187206                ],
     207                 [
     208                    'type' => 'input_field',
     209                    'input_type' => 'number',
     210                    'id' => '_eniture_product_level_fulfillment_offset_days',
     211                    'class' => '_eniture_product_level_fulfillment_offset_days short',
     212                    'label' => __( 'Fulfillment Offset Days', 'woocommerce' ),
     213                    'placeholder' => 'Fulfillment offset days, e.g. 2',
     214                    '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.",
     215                    'custom_attributes' => [
     216                        'min' => '1',
     217                        'max' => '20',
     218                        'step' => '1',
     219                        'title' => 'Value must be greater than or equal to 1 and less than or equal to 20',
     220                        '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' : '')",
     221                        'oninput' => "this.setCustomValidity('')"
     222                    ]
     223                ],
    188224                [
    189225                    'type' => 'checkbox',
     
    305341                'label' => $custom_field['label'],
    306342                'class' => $custom_field['class'],
    307                 'placeholder' => $custom_field['label'],
     343                'placeholder' => isset($custom_field['placeholder']) ? $custom_field['placeholder'] : $custom_field['label'],
    308344                'value' => get_post_meta($postId, $custom_field['id'], true)
    309345            ];
     346
     347            // Add input type if specified (e.g., 'number')
     348            if (isset($custom_field['input_type'])) {
     349                $custom_input_field['type'] = $custom_field['input_type'];
     350            }
     351
     352            // Add custom attributes if specified (e.g., min, max, step)
     353            if (isset($custom_field['custom_attributes'])) {
     354                $custom_input_field['custom_attributes'] = $custom_field['custom_attributes'];
     355            }
    310356
    311357            if (isset($custom_field['description'])) {
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/en-tab.php

    r3381167 r3453531  
    159159            $settings = $this->get_settings($current_section);
    160160
     161            // Cuttoff Time
     162            if (isset($_POST['en_quote_settings_order_cut_off_time_echo']) && $_POST['en_quote_settings_order_cut_off_time_echo'] != '') {
     163                $time_24_format = $this->get_time_in_24_hours($_POST['en_quote_settings_order_cut_off_time_echo']);
     164                $_POST['en_quote_settings_order_cut_off_time_echo'] = $time_24_format;
     165            }
     166
    161167            // backup rates
    162168            $backup_rates_fields = ['echo_backup_rates_fixed_rate', 'echo_backup_rates_cart_price_percentage', 'echo_backup_rates_weight_function'];
     
    167173            WC_Admin_Settings::save_fields($settings);
    168174        }
     175
     176        /**
     177         * Cuttoff Time
     178         * @param $timeStr
     179         * @return false|string
     180        */
     181        public function get_time_in_24_hours($timeStr)
     182        {
     183            $cutOffTime = explode(' ', $timeStr);
     184            $hours = $cutOffTime[0];
     185            $separator = $cutOffTime[1];
     186            $minutes = $cutOffTime[2];
     187            $meridiem = $cutOffTime[3];
     188            $cutOffTime = "{$hours}{$separator}{$minutes} $meridiem";
     189           
     190            return date("H:i", strtotime($cutOffTime));
     191        }
    169192    }
    170193
    171194    $en_tab = new EnEchoTab();
    172     return $en_tab->en_load();
     195    $en_tab->en_load();
     196
     197    return $en_tab;
    173198}
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/location/assets/css/en-echo-location.css

    r3381167 r3453531  
    125125}
    126126
    127 .en_location_error_message {
     127.en_location_error_message,
     128.en_echo_warehouse_error_message, .en_echo_dropship_error_message {
    128129    background: #f1f1f1 none repeat scroll 0 0;
    129130    border-left: 4px solid #dc3232;
     
    135136}
    136137
    137 .en_location_success_message {
     138.en_echo_warehouse_error_message, .en_echo_dropship_error_message {
     139    background-color: #fff !important;
     140    margin: auto;
     141}
     142
     143.en_location_success_message,
     144.en_echo_warehouse_success_message, .en_echo_dropship_success_message {
    138145    background: #fff none repeat scroll 0 0;
    139146    border-left: 4px solid #46b450;
     
    143150    clear: both;
    144151    display: none;
     152}
     153
     154.en_echo_warehouse_success_message, .en_echo_dropship_success_message {
     155    margin: auto;
    145156}
    146157
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/location/assets/js/en-echo-location.js

    r3381167 r3453531  
    446446    }
    447447}
     448
     449if (typeof en_echo_select_bulk_locations != 'function') {
     450    function en_echo_select_bulk_locations(location_class, e) {
     451        const checked = jQuery(e).is(':checked');
     452        jQuery(`.${location_class}`).prop('checked', checked);
     453    }
     454}
     455
     456if (typeof en_echo_toggle_select_all_locations != 'function') {
     457    function en_echo_toggle_select_all_locations(location_class, select_all_locations) {
     458        const allChecked = jQuery(`.${location_class}:checked`).length === jQuery(`.${location_class}`).length;
     459        jQuery(`#${select_all_locations}`).prop('checked', allChecked);
     460    }
     461}
     462
     463if (typeof en_echo_delete_bulk_locations != 'function') {
     464    function en_echo_delete_bulk_locations(e, location_class, location_type) {
     465        e.preventDefault();
     466        const location_ids = jQuery(`.${location_class}:checked`).map(function () {
     467            return this.value;
     468        }).get();
     469
     470        // Show error message if no locations are selected
     471        if (location_ids.length === 0) {
     472            const loc_error_class = `en_echo_${location_type}_error_message`;
     473            jQuery(`.${loc_error_class}`).show('slow').delay(3000).hide('slow');
     474            return;
     475        }
     476
     477        const postForm = {
     478            'action': 'en_echo_wd_bulk_delete_locations',
     479            'location_ids': location_ids,
     480            'location_type': location_type,
     481            'wp_nonce': en_echo_location_script.nonce
     482        }
     483        const delete_locs_btn = `.en_echo_bulk_delete_${location_type}`,
     484        loc_success_class = `.en_echo_${location_type}_success_message`,
     485        loc_error_class = `.en_echo_${location_type}_error_message`;
     486       
     487        jQuery.ajax({
     488            type: 'POST',
     489            url: ajaxurl,
     490            data: postForm,
     491            dataType: 'json',
     492            beforeSend: function () {
     493                jQuery(delete_locs_btn).addClass('spinner_disable');
     494            },
     495            success: function (data) {
     496                if (data.error && data.message) {
     497                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     498                    jQuery(loc_error_class).show('slow').delay(3000).hide('slow');
     499                } else {
     500                    jQuery(delete_locs_btn).removeClass('spinner_disable');
     501                    jQuery(data.target_location).replaceWith(data.html);
     502                    jQuery(loc_success_class).show('slow').delay(3000).hide('slow');
     503                    jQuery('.warehouse_updated, .warehouse_created, .warehouse_deleted, .dropship_deleted, .dropship_updated, .dropship_created').css('display', 'none');
     504                }
     505            },
     506            error: function (error) {
     507                jQuery(delete_locs_btn).removeClass('spinner_disable');
     508                console.log(error);
     509            }
     510        });
     511    }
     512}
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/location/dropship/en-dropship.php

    r3289951 r3453531  
    4242            <h1><?php _e('Drop ships', 'eniture-technology'); ?></h1>
    4343            <button type="button" onclick="en_show_popup_location(false)"
    44                     class="button-primary"><?php _e('Add', 'eniture-technology'); ?></button>
     44                    class="button-primary" title="Add Drop Ship"><?php _e('Add', 'eniture-technology'); ?></button>
     45            <button type="button" class="en_echo_bulk_delete_dropship en_wd_add_dropship_btn button-primary" title="Delete Drop Ships" onclick="return en_echo_delete_bulk_locations(event, 'en_echo_delete_dropship_item', 'dropship');"><?php _e('Delete', 'eniture-technology'); ?></button>
    4546            <p><?php _e('Locations that inventory specific items that are drop shipped to the destination. Use the products settings
    4647                    page to identify it as a drop shipped item and its associated drop ship location. Orders that include drop
    4748                    shipped items will display a single figure for the shipping rate estimate that is equal to the sum of the
    4849                    cheapest option of each shipment required to fulfill the order.', 'eniture-technology'); ?></p>
     50            <div class="en_echo_dropship_success_message"><strong>Success!</strong> <span>Selected drop ships deleted successfully.</span></div>
     51            <div class="en_echo_dropship_error_message"><strong>Error!</strong> Please select at least one drop ship to delete.</div>
    4952
    5053            <table class="en_location_table en_location_dropship_table">
    5154            <thead>
    5255            <tr>
     56                <th class="en_wd_dropship_list_heading en_echo_bulk_delete_col">
     57                    <!-- Bulk delete -->
     58                    <input type="checkbox" name="en_echo_bulk_delete_dropships" id="en_echo_bulk_delete_dropships" onclick="return en_echo_select_bulk_locations('en_echo_delete_dropship_item', this);" />
     59                </th>
    5360                <?php echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    5461                <th><?php _e('Action', 'eniture-technology'); ?></th>
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/location/en-location.php

    r3381167 r3453531  
    257257
    258258                echo '<tr class="' . esc_attr($append_class) . '" id="en_location_row_id_' . esc_attr($en_location_id) . '">';
     259
     260                $_type = ($loc_type === 'wh') ? 'warehouse' : 'dropship';
     261                echo '<td class="en_wd_' . esc_attr($_type) . '_list_data en_echo_bulk_delete_col">
     262                    <input
     263                        type="checkbox"
     264                        class="en_echo_delete_' . esc_attr($_type) . '_item"
     265                        value="' . esc_attr($en_location_id) . '"
     266                        onclick="return en_echo_toggle_select_all_locations(
     267                            \'en_echo_delete_' . esc_attr($_type) . '_item\',
     268                            \'en_echo_bulk_delete_' . esc_attr($_type) . 's\'
     269                        );"
     270                    >
     271                </td>';
    259272                echo self::en_arrange_table_data('td', $en_sorted_location);
    260273
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/location/includes/en-location-ajax.php

    r3381167 r3453531  
    2323            add_action('wp_ajax_nopriv_en_echo_location_delete_row', [$this, 'en_echo_location_delete_row']);
    2424            add_action('wp_ajax_en_echo_location_delete_row', [$this, 'en_echo_location_delete_row']);
     25
     26            add_action('wp_ajax_nopriv_en_echo_bulk_delete_locations', array($this, 'en_echo_bulk_delete_locations_ajax'));
     27            add_action('wp_ajax_en_echo_wd_bulk_delete_locations', array($this, 'en_echo_bulk_delete_locations_ajax'));
    2528        }
    2629
     
    252255        }
    253256
     257        function en_echo_bulk_delete_locations_ajax()
     258        {
     259            if (!(current_user_can('manage_options') || current_user_can('manage_woocommerce')) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['wp_nonce'])), 'en_echo_location_nonce')) {
     260                echo wp_json_encode(array('error' => true, 'message' => 'Unauthorized Access.'));
     261                exit;
     262            }
     263
     264            $location_ids = isset($_POST['location_ids']) ? $_POST['location_ids'] : array();
     265            $loc_type = isset($_POST['location_type']) ? $_POST['location_type'] : '';
     266            if (empty($location_ids)) {
     267                echo wp_json_encode(['error' => true, 'message' => "Please select at least one {$loc_type} to delete."]);
     268                exit;
     269            }
     270
     271            global $wpdb;
     272            foreach ($location_ids as $location_id) {
     273                if ($loc_type == 'dropship') {
     274                    $get_dropship_id = '';
     275                    $dropship_id = intval($location_id);
     276                    $get_dropship_array = array($dropship_id);
     277                    $ser = maybe_serialize($get_dropship_id);
     278                    $get_dropship_val = array_map('intval', $get_dropship_array);
     279                    $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 . "')");
     280                    $post_id = reset($get_post_id)->post_ids_list;
     281
     282                    if (isset($post_id)) {
     283                        $wpdb->query("UPDATE `" . $wpdb->prefix . "postmeta` SET `meta_value` = '' WHERE `meta_key` IN('_enable_dropship','_dropship_location')  AND `post_id` IN ($post_id)");
     284                    }
     285                }
     286
     287                $wpdb->delete($wpdb->prefix . "warehouse", array('id' => intval($location_id), 'location' => $loc_type));
     288            }
     289
     290            if ($loc_type === 'warehouse') {
     291                $en_location_template_obj = new EnEchoWarehouseTemplate();
     292                $en_target_location = '.en_location_warehouse_main_div';
     293            } else {
     294                $en_location_template_obj = new EnEchoDropshipTemplate();
     295                $en_target_location = '.en_location_dropship_main_div';
     296            }
     297
     298            $html = $en_location_template_obj::en_load();
     299
     300            echo wp_json_encode(['error' => false, 'message' => 'Locations deleted successfully.', 'target_location' => $en_target_location, 'html' => $html]);
     301            exit;
     302        }
     303
    254304    }
    255305
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/location/warehouse/en-warehouse.php

    r3289951 r3453531  
    4040
    4141            <h1><?php _e('Warehouses', 'eniture-technology'); ?></h1>
    42             <button type="button" onclick="en_show_popup_location(true)"
    43                     class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>"><?php _e('Add', 'eniture-technology'); ?>
    44             </button>
    45             <?php echo force_balance_tags(self::$plan_required); ?>
     42            <div style="display: flex; gap: 3px;">
     43                <button type="button" onclick="en_show_popup_location(true)"
     44                        class="button-primary <?php echo esc_attr(self::$disabled_plan); ?>" title="Add Warehouse"><?php _e('Add', 'eniture-technology'); ?>
     45                </button>
     46                <button type="button" class="en_echo_bulk_delete_warehouse en_wd_add_warehouse_btn button-primary" title="Delete Warehouses" onclick="return en_echo_delete_bulk_locations(event, 'en_echo_delete_warehouse_item', 'warehouse');"><?php _e('Delete', 'eniture-technology'); ?></button>
     47                </button>
     48                <?php echo force_balance_tags(self::$plan_required); ?>
     49            </div>
    4650
    4751            <p><?php _e('Warehouses that inventory all products not otherwise identified as drop shipped items. The warehouse closest to the shopper will be used to retrieve shipping rate estimates.', 'eniture-technology'); ?></p>
     52            <div class="en_echo_warehouse_success_message"><strong>Success! </strong> <span>Selected warehouses deleted successfully.</span></div>
     53            <div class="en_echo_warehouse_error_message"><strong>Error!</strong> <span>Please select at least one warehouse to delete.</span></div>
    4854
    4955            <table class="en_location_table en_location_warehouse_table">
    5056            <thead>
    5157            <tr>
     58                <th class="en_wd_warehouse_list_heading en_echo_bulk_delete_col">
     59                    <!-- Bulk delete -->
     60                    <input type="checkbox" name="en_echo_bulk_delete_warehouses" id="en_echo_bulk_delete_warehouses" onclick="return en_echo_select_bulk_locations('en_echo_delete_warehouse_item', this);" />
     61                </th>
    5262                <?php  echo force_balance_tags(\EnLocation::en_arrange_table_data('th', $en_heading)); ?>
    5363                <th><?php _e('Action', 'eniture-technology'); ?></th>
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/logs/en-json-tree-view/en-jtv-style.css

    r3029336 r3453531  
    9797.en_jtv li.en_folder > div.en_key > span::before {
    9898  content: "\2212";
    99   opacity: 0.33;
    100   color: #111;
    101   background-color: #e0eaef;
     99  opacity: 1;
     100  color: #fff;
     101  background-color: black !important;
    102102  text-align: center;
    103103  display: inline-block;
     
    112112.en_jtv li.en_folder.en_folded > div.en_key > span::before {
    113113  content: "+";
     114  font-weight: bolder;
     115    background-color: black !important;
     116    color: #fff !important;
    114117}
    115118
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/logs/en-logs.php

    r3134911 r3453531  
    4848
    4949            $url = EN_ECHO_ROOT_URL_QUOTES . '/request-log/index.php';
    50             $logs = $obj_classs->en_echo_sent_http_request($url, $data, 'POST');
     50            $logs = $obj_classs->en_echo_sent_http_request($url, $data, 'POST', 'Logs');
    5151            $logs = (isset($logs) && is_string($logs) && strlen($logs) > 0) ? json_decode($logs, true) : [];
    5252
  • ltl-freight-quotes-echo-edition/trunk/admin/tab/quote-settings/en-quote-settings.php

    r3381167 r3453531  
    2525                    $number_of_options[$c] = __($c, $c);
    2626                }
     27            }
     28
     29            if (get_option('en_quote_settings_show_delivery_estimate_echo') == 'yes') {
     30                update_option('en_quote_settings_delivery_estimate_options_echo', 'delivery_days');
     31                delete_option('en_quote_settings_show_delivery_estimate_echo');
     32            }
     33
     34            // Cuttoff Time
     35            $echo_disable_cutt_off_time_ship_date_offset = "";
     36            $echo_cutt_off_time_package_required = "";
     37
     38            //  Check the cutt of time & offset days plans for disable input fields
     39            $echo_action_cutOffTime_shipDateOffset = apply_filters('echo_plans_suscription_and_features', 'order_cutt_off_time');
     40            if (is_array($echo_action_cutOffTime_shipDateOffset)) {
     41                $echo_disable_cutt_off_time_ship_date_offset = "disabled_me";
     42                $echo_cutt_off_time_package_required = apply_filters('echo_plans_notification_link', $echo_action_cutOffTime_shipDateOffset);
    2743            }
    2844
     
    94110                 * ==================================================================
    95111                 */
    96                 'en_quote_settings_show_delivery_estimate_echo' => [
    97                     'name' => __('Show Delivery Estimate ', 'woocommerce-settings-echo'),
    98                     'type' => 'checkbox',
    99                     'id' => 'en_quote_settings_show_delivery_estimate_echo'
    100                 ],
     112
     113                //** Start Delivery Estimate Options - Cuttoff Time
     114                 'delivery_estimate_options_echo' => array(
     115                    'name' => __('Delivery Estimate Options ', 'woocommerce-settings-en_woo_addons_packages_quotes'),
     116                    'type' => 'text',
     117                    'class' => 'hidden',
     118                    'id' => 'delivery_estimate_options_echo'
     119                ),
     120                'en_quote_settings_delivery_estimate_options_echo' => array(
     121                    'name' => '',
     122                    'type' => 'radio',
     123                    'default' => 'dont_show_estimates',
     124                    'options' => array(
     125                        'dont_show_estimates' => __("Don't display delivery estimates.", 'woocommerce'),
     126                        'delivery_days' => __("Display estimated number of days until delivery.", 'woocommerce'),
     127                        'delivery_date' => __("Display estimated delivery date.", 'woocommerce'),
     128                    ),
     129                    'id' => 'en_quote_settings_delivery_estimate_options_echo',
     130                    'class' => 'freightview_lfq_dont_show_estimate_option',
     131                ),
     132                //** End Delivery Estimate Options
     133
     134                //**Start: Cut Off Time & Ship Date Offset
     135                'en_quote_settings_cutt_off_time_ship_date_offset_echo' => array(
     136                    'name' => __('Cut Off Time & Ship Date Offset ', 'woocommerce-settings-en_woo_addons_packages_quotes'),
     137                    'type' => 'text',
     138                    'class' => 'hidden',
     139                    'desc' => $echo_cutt_off_time_package_required,
     140                    'id' => 'en_quote_settings_cutt_off_time_ship_date_offset_echo'
     141                ),
     142                'en_quote_settings_order_cut_off_time_echo' => array(
     143                    'name' => __('Order Cut Off Time ', 'woocommerce-settings-priority-one_lfq_freight_freight_orderCutoffTime'),
     144                    'type' => 'text',
     145                    'placeholder' => '-- : -- --',
     146                    'desc' => 'Enter the cut off time (e.g. 2:00) for orders. Orders placed after this time will be quoted as shipping the next business day.',
     147                    'id' => 'en_quote_settings_order_cut_off_time_echo',
     148                    'class' => $echo_disable_cutt_off_time_ship_date_offset,
     149                ),
     150                'en_quote_settings_shipment_offset_days_echo' => array(
     151                    'name' => __('Fulfillment Offset Days ', 'woocommerce-settings-priority-one_lfq_freight_shipment_offset_days'),
     152                    'type' => 'text',
     153                    'desc' => 'The number of days the ship date needs to be moved to allow for the processing of the order.',
     154                    'placeholder' => 'Fulfillment Offset Days, e.g. 2',
     155                    'id' => 'en_quote_settings_shipment_offset_days_echo',
     156                    'class' => $echo_disable_cutt_off_time_ship_date_offset,
     157                ),
     158                'en_quote_settings_all_shipment_days_echo' => array(
     159                    'name' => __("What days do you ship orders?", 'woocommerce-settings-priority-one_lfq_quotes'),
     160                    'type' => 'checkbox',
     161                    'desc' => 'Select All',
     162                    'class' => "all_shipment_days_echo_lfq $echo_disable_cutt_off_time_ship_date_offset",
     163                    'id' => 'en_quote_settings_all_shipment_days_echo'
     164                ),
     165                'en_quote_settings_monday_shipment_day_echo' => array(
     166                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     167                    'type' => 'checkbox',
     168                    'desc' => 'Monday',
     169                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     170                    'id' => 'en_quote_settings_monday_shipment_day_echo'
     171                ),
     172                'en_quote_settings_tuesday_shipment_day_echo' => array(
     173                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     174                    'type' => 'checkbox',
     175                    'desc' => 'Tuesday',
     176                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     177                    'id' => 'en_quote_settings_tuesday_shipment_day_echo'
     178                ),
     179                'en_quote_settings_wednesday_shipment_day_echo' => array(
     180                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     181                    'type' => 'checkbox',
     182                    'desc' => 'Wednesday',
     183                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     184                    'id' => 'en_quote_settings_wednesday_shipment_day_echo'
     185                ),
     186                'en_quote_settings_thursday_shipment_day_echo' => array(
     187                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     188                    'type' => 'checkbox',
     189                    'desc' => 'Thursday',
     190                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     191                    'id' => 'en_quote_settings_thursday_shipment_day_echo'
     192                ),
     193                'en_quote_settings_friday_shipment_day_echo' => array(
     194                    'name' => __("", 'woocommerce-settings-priority-one_lfq_quotes'),
     195                    'type' => 'checkbox',
     196                    'desc' => 'Friday',
     197                    'class' => "echo_shipment_day $echo_disable_cutt_off_time_ship_date_offset",
     198                    'id' => 'en_quote_settings_friday_shipment_day_echo'
     199                ),
     200                'show_delivery_estimate_echo' => array(
     201                    'title' => __('', 'woocommerce'),
     202                    'name' => __('', 'woocommerce-settings-priority-one_lfq_quotes'),
     203                    'desc' => '',
     204                    'id' => 'echo_lfq_show_delivery_estimates',
     205                    'css' => '',
     206                    'default' => '',
     207                    'type' => 'title',
     208                ),
     209                //**End: Cut Off Time & Ship Date Offset
     210
    101211                'residential_delivery_options_label' => [
    102212                    'name' => __('Residential Delivery', 'woocommerce-settings-echo'),
  • ltl-freight-quotes-echo-edition/trunk/common/en-plans.php

    r3381167 r3453531  
    145145                'multi_dropships' => ['0', '1', '2', '3'],
    146146                'hold_at_terminal' => ['3'],
     147                'order_cutt_off_time' => ['2', '3']
    147148            ];
    148149
  • ltl-freight-quotes-echo-edition/trunk/en-install.php

    r3381167 r3453531  
    6666        ]);
    6767
    68         wp_enqueue_script('EnEchoAdminJs', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.js', [], '1.0.5');
     68        wp_enqueue_script('EnEchoAdminJs', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.js', [], '1.0.6');
    6969        wp_localize_script('EnEchoAdminJs', 'en_echo_admin_script', [
    7070            'pluginsUrl' => EN_ECHO_PLUGIN_URL,
     
    7676            'echo_backup_rates_cart_price_percentage' => get_option("echo_backup_rates_cart_price_percentage"),
    7777            'echo_backup_rates_weight_function' => get_option("echo_backup_rates_weight_function"),
     78            // Cuttoff Time
     79            'echo_order_cut_off_time' => get_option("en_quote_settings_order_cut_off_time_echo")
    7880        ]);
    7981
    80         wp_enqueue_script('EnEchoLocationScript', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/js/en-echo-location.js', [], '1.0.3');
     82        wp_enqueue_script('EnEchoLocationScript', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/js/en-echo-location.js', [], '1.0.4');
    8183        wp_localize_script('EnEchoLocationScript', 'en_echo_location_script', array(
    8284            'pluginsUrl' => EN_ECHO_PLUGIN_URL,
     
    8486        ));
    8587
    86         wp_enqueue_script('EnEchoProdcutScript', EN_ECHO_DIR_FILE . '/admin/product/assets/en-custom-fields.js', [], '1.0.0');
     88        wp_enqueue_script('EnEchoProdcutScript', EN_ECHO_DIR_FILE . '/admin/product/assets/en-custom-fields.js', [], '1.0.1');
    8789        wp_localize_script('EnEchoProductScript', 'script', array(
    8890            'pluginsUrl' => EN_ECHO_PLUGIN_URL,
    8991        ));
    9092
    91         wp_register_style('EnEchoLocationStyle', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/css/en-echo-location.css', false, '1.0.1');
     93        wp_register_style('EnEchoLocationStyle', EN_ECHO_DIR_FILE . '/admin/tab/location/assets/css/en-echo-location.css', false, '1.0.2');
    9294        wp_enqueue_style('EnEchoLocationStyle');
    9395
    94         wp_register_style('EnEchoAdminCss', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.css', false, '1.0.2');
     96        wp_register_style('EnEchoAdminCss', EN_ECHO_DIR_FILE . '/admin/assets/en-echo-admin.css', false, '1.0.3');
    9597        wp_enqueue_style('EnEchoAdminCss');
    9698
    9799        wp_register_style('EnEchoProductCss', EN_ECHO_DIR_FILE . '/admin/product/assets/en-custom-fields-style.css', false, '1.0.0');
    98100        wp_enqueue_style('EnEchoProductCss');
     101
     102        // Cuttoff Time
     103        wp_register_style('EnEchoWickedPickerStyle', EN_ECHO_DIR_FILE . '/admin/assets/wickedpicker.min.css', false, '1.0.0');
     104        wp_register_script('EnEchoWickedPickerScript', EN_ECHO_DIR_FILE . '/admin/assets/wickedpicker.js', false, '1.0.0');
     105       
     106        wp_enqueue_style('EnEchoWickedPickerStyle');
     107        wp_enqueue_script('EnEchoWickedPickerScript');
    99108    }
    100109
     
    127136    function en_echo_shipping_sections($settings)
    128137    {
    129         $settings[] = include('admin/tab/en-tab.php');
     138        $en_tab = include('admin/tab/en-tab.php');
     139        if (is_object($en_tab)) {
     140            $settings[] = $en_tab;
     141        }
     142
    130143        return $settings;
    131144    }
  • ltl-freight-quotes-echo-edition/trunk/http/en-curl.php

    r3381167 r3453531  
    2626        {
    2727            $curl_response = '';
     28            $en_time_start = 0;
     29
    2830            if (strlen($url) > 0 && is_array($post_data) && !empty($post_data)) {
    2931
    3032                $post_data = self::remove_extra_request_data($post_data);
     33                $cachable_data = self::create_cachable_data($post_data);
     34                $cache_key = self::create_cache_key($cachable_data);
     35                $cache_response = get_transient($cache_key);
     36                $skip_cache = in_array($step_for, ['Plan', 'Logs', 'Address', 'Connection']);
    3137
    32                 // Eniture Execution Time
    33                 $en_time_start = microtime(true);
     38                if (false === $cache_response || $skip_cache) {
     39                    // Eniture Execution Time
     40                    $en_time_start = microtime(true);
    3441
    35                 // Eniture Debug Mood
    36                 do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Request ", $post_data);
     42                    // Eniture Debug Mood
     43                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Request ", $post_data);
    3744
    38                 $method == 'POST' ? $post_data = http_build_query($post_data) : '';
     45                    $method == 'POST' ? $post_data = http_build_query($post_data) : '';
    3946
    40                 // Eniture Debug Mood
    41                 do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Build Query ", $post_data);
     47                    // Eniture Debug Mood
     48                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Build Query ", $post_data);
    4249
    43                 $response = wp_remote_post($url, [
    44                         'method' => $method,
    45                         'timeout' => 60,
    46                         'redirection' => 5,
    47                         'blocking' => true,
    48                         'body' => $post_data,
    49                     ]
    50                 );
    51                 $curl_response = wp_remote_retrieve_body($response);
    52                 $headers = wp_remote_retrieve_headers($response);
    53                 self::eniture_check_rate_limit_headers($headers);
     50                    $response = wp_remote_post($url, [
     51                            'method' => $method,
     52                            'timeout' => 60,
     53                            'redirection' => 5,
     54                            'blocking' => true,
     55                            'body' => $post_data,
     56                        ]
     57                    );
     58                    $headers = wp_remote_retrieve_headers($response);
     59                    self::eniture_check_rate_limit_headers($headers);
     60                    $curl_response = wp_remote_retrieve_body($response);
     61                    $resp = !empty($curl_response) && is_string($curl_response) ? json_decode($curl_response, true) : $curl_response;
     62
     63                    if (!empty($resp) && (!isset($resp['severity']) || (isset($resp['severity']) && $resp['severity'] != 'ERROR'))) {
     64                        set_transient($cache_key, $curl_response, 12 * HOUR_IN_SECONDS);
     65                    }
     66
     67                    // Eniture Debug Mood
     68                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Response ", json_decode($curl_response));
     69                } else {
     70                    $curl_response = $cache_response;
     71                    // Eniture Debug Mood
     72                    do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Cache Response ", $curl_response);
     73                }
    5474            }
    55 
    56             // Eniture Debug Mood
    57             do_action("eniture_debug_mood", EN_ECHO_NAME . " $step_for Response ", json_decode($curl_response));
    5875
    5976            // Eniture Execution Time
     
    98115            return $request_data;
    99116        }
     117
     118        static public function create_cachable_data($request_data)
     119        {
     120            $cache_keys = ['serverName', 'platform', 'carrierType', 'carrierName', 'carrierMode', 'requestVersion', 'apiKey', 'accountNumber', 'licenseKey', 'shipment_type', 'LTL_FREIGHT', 'product_name', 'shipment_weight', 'commdityDetails', 'originAddress', 'receiverZip', 'receiverState', 'receiverCountryCode', 'receiverCity', 'instorPickupLocalDelEnable', 'doNesting', 'accessorials', 'handlingUnitWeight', 'maxWeightPerHandlingUnit', 'holdAtTerminal', 'shipmentOffsetDays', 'OrderCutoffTime', 'shipmentWeekDays', 'modifyShipmentDateTime'];
     121            $cachable_data = [];
     122           
     123            // get the data from the request
     124            foreach ($cache_keys as $key) {
     125                if (isset($request_data[$key])) $cachable_data[$key] = $request_data[$key];
     126            }
     127
     128            if (isset($request_data['autoResidentials']) && $request_data['autoResidentials'] == 1) {
     129                $cachable_data['autoResidentials'] = $request_data['autoResidentials'];
     130                $cachable_data['addressLine'] = $request_data['addressLine'];
     131                $cachable_data['addressLine2'] = (isset($request_data['addressLine2'])) ? $request_data['addressLine2'] : '';
     132                $cachable_data['defaultRADAddressType'] = $request_data['defaultRADAddressType'];
     133                $cachable_data['defaultRADWithoutStreetAddress'] = $request_data['defaultRADWithoutStreetAddress'];
     134                $cachable_data['poboxAddressValidation'] = $request_data['poboxAddressValidation'];
     135            }
     136
     137            if (isset($request_data['standardPackaging']) && $request_data['standardPackaging'] == 1) {
     138                $cachable_data['standardPackaging'] = $request_data['standardPackaging'];
     139                $cachable_data['pallet'] = $request_data['pallet'];
     140            }
     141
     142            return $cachable_data;
     143        }
     144
     145        static public function create_cache_key($request_data)
     146        {
     147            $request_build_query = http_build_query($request_data);
     148            return md5('eniture_echo_' . $request_build_query);
     149        }
    100150    }
    101151
  • ltl-freight-quotes-echo-edition/trunk/ltl-freight-quotes-echo-edition.php

    r3388669 r3453531  
    44 * Plugin URI: https://eniture.com/products/
    55 * Description: Dynamically retrieves your negotiated shipping rates from Echo and displays the results in the WooCommerce shopping cart.
    6  * Version: 1.1.15
     6 * Version: 1.1.16
    77 * Author: Eniture Technology
    88 * Author URI: http://eniture.com/
    99 * Text Domain: eniture-technology
    1010 * License: GPLv2 or later
    11  * WC requires at least: 6.4
    12  * WC tested up to: 10.2.2
     11 * Requires Plugins: woocommerce
    1312 */
    1413
  • ltl-freight-quotes-echo-edition/trunk/readme.txt

    r3388669 r3453531  
    33Tags: eniture,Echo,,LTL freight rates,LTL freight quotes, shipping estimates
    44Requires at least: 6.4
    5 Tested up to: 6.8
    6 Stable tag: 1.1.15
     5Tested up to: 6.9
     6Stable tag: 1.1.16
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    148148== Changelog ==
    149149
     150= 1.1.16 - 2026-02-04 =
     151* Update: Added a **caching mechanism** to efficiently handle multiple identical quote requests and reduce redundant API calls.
     152* Update: Introduced **bulk delete functionality** for warehouse and dropship locations to simplify location management.
     153* Update: Added **delivery estimate options** and **order cutoff time** functionality in the quote settings for improved delivery accuracy.
     154* Update: Added **product-level offset days** functionality to allow more granular shipment timing control.
     155* Update: Added parent product checks to properly handle empty variant scenarios and prevent invalid processing.
     156
    150157= 1.1.15 - 2025-11-03 =
    151158* Update: Updated the **Stacking** property default value from **“Maximized”** to **“Evenly”** for better consistency in nested layouts. (Existing configurations will remain unchanged unless manually updated.)
  • ltl-freight-quotes-echo-edition/trunk/server/api/en-response.php

    r3381167 r3453531  
    196196         * @param string $index
    197197         * @param dynamic $is_not_matched
    198          * @return dynamic mixed
     198         * @return dynamic|string|array mixed
    199199         */
    200200        static public function en_sanitize_rate($index, $is_not_matched)
     
    256256            $en_count_rates = 0;
    257257
    258             $handling_fee = $en_settings_label = $rating_method = $transit_days = $enable_carriers = $liftgate_resid_delivery = $liftgate_delivery_option = '';
     258            $handling_fee = $en_settings_label = $rating_method = $transit_days = $liftgate_resid_delivery = $liftgate_delivery_option = '';
     259            $enable_carriers = [];
    259260            self::$en_settings = json_decode(EN_ECHO_SET_QUOTE_SETTINGS, true);
    260261            self::$en_accessorial = json_decode(EN_ECHO_ACCESSORIAL, true);
     
    294295                && $guarenteed_value == 'GN') {
    295296                    $label = isset($is_valid_label) && $is_valid_label ? $en_settings_label : self::en_sanitize_rate('CarrierName', '');
    296                     $calculated_transit_days = self::en_sanitize_rate('CarrierTransitDays', '');
    297                     $transit_time = '';
    298                     if (is_numeric($calculated_transit_days) &&
    299                         self::$en_is_shipment == 'en_single_shipment' &&
    300                         $transit_days == "yes") {
    301                         $transit_time = ' (Intransit days: ' . $calculated_transit_days . ')';
     297                    $calculated_transit_days = self::en_sanitize_rate('totalTransitTimeInDays', '');
     298                    $calculated_transit_date = self::en_sanitize_rate('deliveryDate', '');
     299                    $transit_time = $append_transit = '';
     300
     301                    if ($rating_method != "average_rate" && isset($delivery_estimate_option) && !empty($delivery_estimate_option) && $delivery_estimate_option != 'dont_show_estimates' && self::$en_is_shipment != 'multi_shipment') {
     302
     303                        if ($delivery_estimate_option == 'delivery_date' && is_string($calculated_transit_date) && strlen($calculated_transit_date) > 0) {
     304                            $append_transit = ' (Expected delivery by ' . date('m-d-Y', strtotime($calculated_transit_date)) . ')';
     305                        } else if ($delivery_estimate_option == 'delivery_days' && is_numeric($calculated_transit_days)) {
     306                            $append_transit = ' (Intransit days: ' . $calculated_transit_days . ')';
     307                        }
    302308                    }
     309
    303310                    // make data for order widget detail
    304311                    $meta_data['service_type'] = $label;
     
    328335                        'transit_days' => $transit_days,
    329336                        'transit_time' => $transit_time,
     337                        'append_transit' => $append_transit,
    330338                        'plugin_name' => 'echo',
    331339                        'plugin_type' => 'ltl',
     
    333341                    ];
    334342
     343                    // Add pallet fee
     344                    $rate = has_filter('en_pallet_price') ? apply_filters('en_pallet_price', $rate) : $rate;
    335345
    336346                    foreach (self::$en_accessorial as $key => $accessorial) {
     
    472482            $label = self::en_sanitize_rate('CarrierName', '');
    473483            $hat_terminal_address = self::get_address_terminal();
    474             $calculated_transit_days = self::en_sanitize_rate('CarrierTransitDays', '');
    475             $transit_time = '';
    476             if (is_numeric($calculated_transit_days) && self::$en_is_shipment == 'en_single_shipment' && $transit_days == "yes") {
    477                 $transit_time = ' (Intransit days: ' . $calculated_transit_days . ')';
     484            $calculated_transit_days = self::en_sanitize_rate('totalTransitTimeInDays', '');
     485            $calculated_transit_date = self::en_sanitize_rate('deliveryDate', '');
     486            $rating_method = get_option('en_quote_settings_rating_method_echo');
     487            $delivery_estimate_option = get_option('en_quote_settings_delivery_estimate_options_echo');
     488            $transit_time = $append_transit = '';
     489
     490            if ($rating_method != "average_rate" && isset($delivery_estimate_option) && !empty($delivery_estimate_option) && $delivery_estimate_option != 'dont_show_estimates' && self::$en_is_shipment != 'multi_shipment') {
     491
     492                if ($delivery_estimate_option == 'delivery_date' && is_string($calculated_transit_date) && strlen($calculated_transit_date) > 0) {
     493                    $append_transit = ' (Expected delivery by ' . date('m-d-Y', strtotime($calculated_transit_date)) . ')';
     494                } else if ($delivery_estimate_option == 'delivery_days' && is_numeric($calculated_transit_days)) {
     495                    $append_transit = ' (Intransit days: ' . $calculated_transit_days . ')';
     496                }
    478497            }
    479498           
     
    522541                'transit_days' => $transit_days,
    523542                'transit_time' => $transit_time,
     543                'append_transit' => $append_transit,
    524544                'label_sufex' => ['HAT'],
    525545                'hat_append_label' => $hat_terminal_address,
  • ltl-freight-quotes-echo-edition/trunk/server/common/en-quote-settings.php

    r3383387 r3453531  
    3232            $accessorials['holdAtTerminal'] = isset($en_settings['hold_at_terminal']) && $en_settings['hold_at_terminal'] == 'yes' ? '1' : '0';
    3333            $accessorials['enabledCarrier'] = !empty($en_settings['enable_carriers']) ? $en_settings['enable_carriers'] : [];
     34            $accessorials['handling_fee'] = isset($en_settings['handling_fee']) ? $en_settings['handling_fee'] : '';
    3435
    3536            return $accessorials;
     
    6263            $quote_settings = [
    6364                'transit_days' => get_option('en_quote_settings_show_delivery_estimate_echo'),
     65                'delivery_estimate_option' => get_option('en_quote_settings_delivery_estimate_options_echo'),
    6466                'own_freight' => get_option('en_quote_settings_own_arrangment_echo'),
    6567                'own_freight_label' => get_option('en_quote_settings_text_for_own_arrangment_echo'),
     
    7981                'hold_at_terminal' => get_option('en_quote_settings_hold_at_terminal_echo'),
    8082                'hold_at_terminal_fee' => get_option('en_quote_settings_hold_at_terminal_fee_echo'),
     83                'order_cutoff_time' => get_option('en_quote_settings_order_cut_off_time_echo'),
     84                'fullfillment_offset_days' => get_option('en_quote_settings_shipment_offset_days_echo'),
    8185            ];
    8286
  • ltl-freight-quotes-echo-edition/trunk/server/en-shipping-rates.php

    r3383387 r3453531  
    4646            public $small_package = [];
    4747            public $ltl_package = [];
     48            public $en_fdo_meta_data_third_party = [];
    4849
    4950            /**
     
    148149
    149150                    $en_package = array_merge(json_decode(EN_ECHO_GET_CONNECTION_SETTINGS, true), $en_package, json_decode(EN_ECHO_GET_QUOTE_SETTINGS, true));
     151                    $en_package = $this->addOrderCutOffTime($en_package);
    150152
    151153                    $response = \EnEchoCurl\EnEchoCurl::en_echo_sent_http_request(EN_ECHO_HITTING_API_URL, $en_package, 'POST', 'Quotes');
     
    222224                                }
    223225                            }
    224                             (isset($rate['transit_time']) && strlen($rate['transit_time']) > 0) ? $rate['label'] .= $rate['transit_time'] : '';
     226
     227                            $rate['label'] .= !empty($rate['append_transit']) ? $rate['append_transit'] : '';
     228
    225229                            isset($rate['cost']) && $en_small_package_charges > 0 ?
    226230                                $rate['cost'] = $rate['cost'] + $en_small_package_charges : 0;
     
    362366                }
    363367            }
     368
     369            function addOrderCutOffTime($en_package)
     370            {
     371                $shipment_week_days = [];
     372                $order_cut_off_time = $shipment_off_set_days = $modify_shipment_date_time = $store_date_time = "";
     373                $delivery_estimate_option = get_option('en_quote_settings_delivery_estimate_options_echo');
     374
     375                if ($delivery_estimate_option == 'delivery_days' || $delivery_estimate_option == 'delivery_date') {
     376                    $order_cut_off_time = get_option('en_quote_settings_order_cut_off_time_echo');
     377                    $shipment_off_set_days = get_option('en_quote_settings_shipment_offset_days_echo');
     378                   
     379                    // Use product-level fulfillment offset days if available, otherwise use global setting
     380                    if (isset($en_package['max_fulfillment_offset_days']) && is_numeric($en_package['max_fulfillment_offset_days']) && $en_package['max_fulfillment_offset_days'] > 0) {
     381                        $shipment_off_set_days = $en_package['max_fulfillment_offset_days'];
     382                    }
     383
     384                    $shipment_week_days = $this->get_shipment_week_days();
     385                    $modify_shipment_date_time = ($order_cut_off_time != '' || $shipment_off_set_days != '' || (is_array($shipment_week_days) && count($shipment_week_days) > 0)) ? 1 : 0;
     386                    $store_date_time = date('Y-m-d H:i:s', current_time('timestamp'));
     387                }
     388
     389                $en_package['OrderCutoffTime'] = $order_cut_off_time;
     390                $en_package['shipmentOffsetDays'] = $shipment_off_set_days;
     391                $en_package['shipmentWeekDays'] = $shipment_week_days;
     392                $en_package['modifyShipmentDateTime'] = $modify_shipment_date_time;
     393                $en_package['storeDateTime'] = $store_date_time;
     394
     395                return $en_package;
     396            }
     397
     398            function get_shipment_week_days()
     399            {
     400                $shipment_days_of_week = ['1', '2', '3', '4', '5'];
     401                if (get_option('en_quote_settings_all_shipment_days_echo') == 'yes') return $shipment_days_of_week;
     402
     403                $shipment_days_of_week = [];
     404                if (get_option('en_quote_settings_monday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 1;
     405                if (get_option('en_quote_settings_tuesday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 2;
     406                if (get_option('en_quote_settings_wednesday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 3;
     407                if (get_option('en_quote_settings_thursday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 4;
     408                if (get_option('en_quote_settings_friday_shipment_day_echo') == 'yes') $shipment_days_of_week[] = 5;
     409
     410                return $shipment_days_of_week;
     411            }
    364412        }
    365413
  • ltl-freight-quotes-echo-edition/trunk/server/package/en-package.php

    r3388669 r3453531  
    2323    {
    2424        static public $post_id;
     25        static public $parent_id;
    2526        static public $locations;
    2627        static public $product_key_name;
     
    108109                    self::$post_id = (isset($product['variation_id']) && $product['variation_id'] > 0) ?
    109110                        $product['variation_id'] : $product_data->get_id();
    110                     $parent_id = self::$post_id;
     111                    self::$parent_id = $parent_id = self::$post_id;
    111112                    if(isset($product['variation_id']) && $product['variation_id'] > 0){
    112113                        $variation = wc_get_product($product['variation_id']);
    113                         $parent_id = $variation->get_parent_id();
     114                        self::$parent_id = $parent_id = $variation->get_parent_id();
    114115                    }
    115116                    $haz_array = get_post_meta(self::$post_id, '_hazardousmaterials');
     117                    if (isset($haz_array[0]) && empty($haz_array[0]) && self::$parent_id != self::$post_id) {
     118                        $haz_array = get_post_meta(self::$parent_id, '_hazardousmaterials');
     119                    }
    116120
    117121                    isset($haz_array[0]) && $haz_array[0] == 'yes' ? $haz_status = 'Y' : $haz_status = 'no';
    118122
    119123                    $nested_mat = get_post_meta(self::$post_id, '_nestedMaterials');
     124                    if (isset($nested_mat[0]) && empty($nested_mat[0]) && self::$parent_id != self::$post_id) {
     125                        $nested_mat = get_post_meta(self::$parent_id, '_nestedMaterials');
     126                    }
     127
    120128                    isset($nested_mat[0]) && $nested_mat[0] == 'yes' ? $doNesting = 1 : $doNesting = '';
    121129
     
    125133
    126134                    $nested_percen = get_post_meta(self::$post_id, '_nestedPercentage');
     135                    if (isset($nested_percen[0]) && empty($nested_percen[0]) && self::$parent_id != self::$post_id) {
     136                        $nested_percen = get_post_meta(self::$parent_id, '_nestedPercentage');
     137                    }
     138
    127139                    !empty($nested_percen) ? $nested_percentage = $nested_percen[0] : $nested_percentage = 0;
    128140
     
    131143
    132144                    $max_nested_item = get_post_meta(self::$post_id, '_maxNestedItems');
     145                    if (isset($max_nested_item[0]) && empty($max_nested_item[0]) && self::$parent_id != self::$post_id) {
     146                        $max_nested_item = get_post_meta(self::$parent_id, '_maxNestedItems');
     147                    }
     148
    133149                    !empty($max_nested_item) ? $max_nested_items = $max_nested_item[0] : $max_nested_items = 0;
    134150
     
    136152                    $nested_staking_property = isset($nested_staking[0]) && !empty($nested_staking[0]) ? $nested_staking[0] : 'Evenly';
    137153
    138                     $product_title = str_replace(array("'", '"'), '', esc_attr($product_data->get_title()));
     154                    $product_title = str_replace(array("'", '"'), '', esc_attr($product_data->get_name()));
    139155                    // Get product level markup value
    140156                    $product_level_markup = self::en_get_product_level_markup($product_data, $product['variation_id'], $product['product_id'], $product['quantity']);
     157                    $product_level_fulfillment_offset = self::en_get_product_level_fulfillment_offset_days($product_data, $product['variation_id'], $product['product_id']);
    141158
    142159                    $product_item = [
     
    164181                        'lineItemPackageCode' => $lineItemPackageCode,
    165182                        'lineItemPackageType' => $lineItemPackageType,
    166                         'markup' => $product_level_markup
     183                        'markup' => $product_level_markup,
     184                        'fulfillment_offset_days' => $product_level_fulfillment_offset
    167185                    ];
    168186
     
    286304                        self::$en_request['commdityDetails'][$origin_zip_code][] = $product_item;
    287305                        self::$en_request['originAddress'][$origin_zip_code] = $origin_address;
     306
     307                         // Track maximum fulfillment offset days
     308                        if (is_numeric($product_level_fulfillment_offset) && $product_level_fulfillment_offset > 0) {
     309                            if (!isset(self::$en_request['max_fulfillment_offset_days']) || $product_level_fulfillment_offset > self::$en_request['max_fulfillment_offset_days']) {
     310                                self::$en_request['max_fulfillment_offset_days'] = $product_level_fulfillment_offset;
     311                            }
     312                        }
    288313                    }
    289314                }
     
    549574                case '_dropship_location':
    550575                    $enable_dropship = get_post_meta(self::$post_id, '_enable_dropship', true);
     576
     577                    if (empty($enable_dropship) && self::$parent_id > 0 && self::$parent_id != self::$post_id) {
     578                        self::$post_id = self::$parent_id;
     579                        $enable_dropship = get_post_meta(self::$post_id, '_enable_dropship', true);
     580                    }
     581
    551582                    switch ($enable_dropship) {
    552583                        case 'yes':
     
    681712        }
    682713
     714         static public function en_get_product_level_fulfillment_offset_days($_product, $variation_id, $product_id)
     715        {
     716            $fulfillment_offset_days = '';
     717            $field_name = '_eniture_product_level_fulfillment_offset_days';
     718
     719            if ($_product->get_type() == 'variation' && $variation_id > 0) {
     720                $fulfillment_offset_days = get_post_meta($variation_id, $field_name, true);
     721
     722                if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     723                    $parent_id = $_product->get_parent_id();
     724                    if ($parent_id > 0) {
     725                        $fulfillment_offset_days = get_post_meta($parent_id, $field_name, true);
     726                    }
     727                }
     728            } else {
     729                $fulfillment_offset_days = get_post_meta($_product->get_id(), $field_name, true);
     730            }
     731
     732            if (empty($fulfillment_offset_days) || !is_numeric($fulfillment_offset_days)) {
     733                $fulfillment_offset_days = get_post_meta($product_id, $field_name, true);
     734            }
     735
     736            // Validate and sanitize the value before returning
     737            if (is_numeric($fulfillment_offset_days)) {
     738                $value = intval($fulfillment_offset_days);
     739                return $value;
     740            }
     741
     742            // Return empty string if invalid
     743            return '';
     744        }
    683745    }
    684746
Note: See TracChangeset for help on using the changeset viewer.