Created
April 12, 2023 16:40
-
-
Save joshmoto/28adfa359834022cc6e30ff07b8ce494 to your computer and use it in GitHub Desktop.
Revisions
-
joshmoto created this gist
Apr 12, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,813 @@ <?php use JetBrains\PhpStorm\NoReturn; /** * @author Josh Cranwell <[email protected]> * @copyright The Sweet People * @version 1.0 * @link https://www.thesweetpeople.com/ * @since March 2023 */ class PricingV2 { public static string $default_currency = 'gbp'; public static array $pricing = [ 'uk' => 'United Kingdom', 'eu' => 'Europe' ]; public static array $currency = [ 'gbp' => 'GBP - British Pound', 'eur' => 'EUR - Euro' ]; public static array|bool $error_logs = false; public function __construct () { // get default currency $this->default_currency(); // loadding shipping form service add_action('wp_ajax_nopriv_load_pricing_table', [ $this, 'ajax_load_pricing_table' ], 20 ); add_action('wp_ajax_load_pricing_table', [ $this, 'ajax_load_pricing_table' ], 20 ); // loadding load json price breaks add_action('wp_ajax_nopriv_load_json_price_breaks', [ $this, 'ajax_load_json_price_breaks' ], 20 ); add_action('wp_ajax_load_json_price_breaks', [ $this, 'ajax_load_json_price_breaks' ], 20 ); } /** * @param mixed $field * @param string $message * @return void */ public static function error_logger(mixed $field, string $message): void { // if field is empty if(!$field) { // set error log self::$error_logs[] = $message; } } /** * @return void */ #[NoReturn] public function ajax_load_pricing_table(): void { // set data array $data = [ 'product_id' => $_GET['product_id'] ? (int)$_GET['product_id'] : false, 'company_id' => $_GET['company_id'] ? (int)$_GET['company_id'] : false, 'country' => $_GET['country'] ?? false, 'service' => $_GET['service'] ?? false, 'currency' => $_GET['currency'] ?? false, 'markup_unit' => $_GET['markup_unit'] ? (float)$_GET['markup_unit'] : false, 'markup_shipping' => $_GET['markup_shipping'] ? (float)$_GET['markup_shipping'] : false, 'markup_origination' => $_GET['markup_origination'] ? (float)$_GET['markup_origination'] : false, 'custom_qty' => $_GET['custom_qty'] ? (int)$_GET['custom_qty'] : false, 'custom_unit' => $_GET['custom_unit'] ? (float)$_GET['custom_unit'] : false, 'custom_origination' => $_GET['custom_origination'] ? (float)$_GET['custom_origination'] : false ]; // update data with new data $data = self::data($data); // start our object buffer ob_start(); // load our pricing table component CF::component('products/single/pricing-table-v2',[ 'data' => $data ],false); // get our buffer $html = ob_get_contents(); // clean our buffer ob_end_clean(); // if we have errors if(isset($data['error']) && is_array($data['error'])) { // send json error wp_send_json_error($html); } else { // else send json success wp_send_json_success($html); } // death is the beginning die(); } /** * @param $data array * @return array */ public static function data(array $data): array { // get pricing $data = self::get_pricing_data($data); // get unit weight $data = Shipping::get_unit_weight($data); // get packaging $data = Shipping::get_packaging_id($data); // get parcel data $data = Shipping::get_parcel_data($data); // get pallet data $data = Shipping::get_pallet_data($data); // get individual shipping //$data = Shipping::get_individual($data); // if we have errors return the logs if(self::$error_logs) { // return the error logs return [ 'error' => self::$error_logs ]; } // calculate the data if no errors return self::calculate($data); } /** * @param array $data * @return array */ public static function calculate(array $data): array { // if we have a price breaks if(isset($data['price_breaks']) && is_array($data['price_breaks'])) { // loop through price breaks and update price break foreach ($data['price_breaks'] as &$break) { // update the unit price to correct currency //$break['unit_price'] = self::currency($break['unit_price'],$data['currency']); // get shipping data $shipping = self::shipping($break['quantity'],$data); // set shipping data $break['shipping_weight_parcel'] = $shipping['weight_parcel'] ?? false; $break['shipping_weight_consignment'] = $shipping['weight_consignment'] ?? false; $break['shipping_parcels'] = $shipping['parcels'] ?? false; $break['shipping_pallets'] = $shipping['pallets'] ?? false; $break['shipping_method'] = $shipping['method'] ?? false; $break['shipping_price'] = $shipping['price'] ?? false; $break['shipping_price_consignment'] = $shipping['price_consignment'] ?? false; $break['shipping_price_total'] = $shipping['price_total'] ?? false; // set the total price $break['total_price'] = $break['shipping_price_total'] !== 'POA' ? self::round_up_currency(($break['unit_price'] * $break['quantity']) + $data['origination'] + $break['shipping_price_total']) : 'POA'; } } // return data return $data; } /** * @param int|float $quantity * @param array $data * @return array */ public static function shipping(int|float $quantity, array $data): array { // set default unit weight for qty unit measurement $unit_weight = $data['unit_weight']; // if unit measure is set and is equal too kg if(isset($data['unit_measure']) && $data['unit_measure'] === 'kg') { // convert quantity to weight kg units $quantity = ( $quantity * 1000 ) / $unit_weight; } // shipping data array $shipping = []; // get the number of parcels $parcels = ceil($quantity / $data['parcel_max_items']); // get parcel box est weight $parcel_box_est_weight = Shipping::$settings['globals'][$data['pricing']]['parcel_box_est_weight']; // set the shipping parcel weight $shipping['weight_parcel'] = $parcel_box_est_weight + (min($data['parcel_max_items'],$quantity) * $unit_weight); // set the total consignment weight $shipping['weight_consignment'] = ($parcels * $parcel_box_est_weight) + ($quantity * $unit_weight); // if we have a parcel outer id if(isset($data['parcel_outer_id']) && $data['parcel_outer_id']) { // if parcels is greater than 1 if($parcels > 1) { // update the outer parcel est weight by 3 $parcel_box_est_weight = $parcel_box_est_weight * 3; // get the shipping outer parcel weight $shipping['weight_parcel'] = $parcel_box_est_weight + (min(( $data['parcel_max_items'] * 2 ),$quantity) * $unit_weight); // get the number of outer parcels $parcels = ceil($parcels / 2); } } // set the number of parcels $shipping['parcels'] = $parcels; // get pallet min parcels $pallet_min_parcels = Shipping::$settings['globals'][$data['pricing']]['pallet_min_parcels']; // if parcels is greater than or equal to pallet min parcels if($parcels >= $pallet_min_parcels) { // get the number of pallets $pallets = ceil($parcels / $data['pallet_parcels_max']); // set the number of pallets $shipping['pallets'] = $pallets; // set the shipping method to pallet $shipping['method'] = 'pallet'; // get the pallet shipping $pallet_service = Shipping::$settings['countries'][$data['country']]['services'][$data['service']]['method']['pallet'] ?? false; // get the pallet shipping price $pallet_price = $pallet_service['price_customer'] ?? false; // get the pallet consignment shipping price $pallet_consignment = $pallet_service['price_consignment'] ?? false; // if we have a pallet price and pallet consignment as strings if(is_string($pallet_price) && is_string($pallet_consignment)) { // set the pallet shipping price $shipping['price'] = self::currency($pallet_price,$data['currency']); // set the pallet consignment shipping price $shipping['price_consignment'] = self::currency($pallet_consignment,$data['currency']); // set the pallet shipping price $shipping['price_total'] = self::markup(($shipping['price'] * $pallets) + $shipping['price_consignment'],$data['markup_shipping']); // return shipping data return $shipping; } // return price on application $shipping['price_total'] = 'POA'; // return shipping data return $shipping; } // set the number of pallets $shipping['pallets'] = 0; // set the shipping method to pallet $shipping['method'] = 'parcel'; // get the parcel shipping $parcel_service = Shipping::$settings['countries'][$data['country']]['services'][$data['service']]['method']['parcel'] ?? false; // get the parcel shipping price $parcel_price = $parcel_service['price_customer'] ?? false; // get the parcel consignment shipping price $parcel_consignment = $parcel_service['price_consignment'] ?? false; // if we have a parcel price and parcel consignment as strings if(is_string($parcel_price) && is_string($parcel_consignment)) { // set the parcel shipping price $shipping['price'] = self::currency($parcel_price,$data['currency']); // set the parcel consignment shipping price $shipping['price_consignment'] = self::currency($parcel_consignment,$data['currency']); // set the parcel shipping price total $shipping['price_total'] = self::markup(($shipping['price'] * $parcels) + $shipping['price_consignment'],$data['markup_shipping']); // return shipping data return $shipping; } // return price on application $shipping['price_total'] = 'POA'; // return shipping data return $shipping; } /** * @param array $price_breaks * @param array $data * @return array */ public static function breaks_floatvals(array $price_breaks, array $data): array { // if we have custom qty and custom unit if($data['custom_qty'] && $data['custom_unit']) { // set our custom price break $price_breaks = [ [ "quantity" => $data['custom_qty'], "unit_price" => $data['custom_unit'] ] ]; } else { // loop through price breaks and convert break values to float foreach ($price_breaks as &$break) { $break["quantity"] = floatval($break["quantity"]); //$break["unit_price"] = floatval(self::markup($break["unit_price"],$data['markup_unit'])); $break["unit_price"] = self::currency(self::markup($break["unit_price"],$data['markup_unit']),$data['currency']); } } // return price breaks return $price_breaks; } /** * @param array $data * @return array|bool */ public static function get_pricing_data(array $data): array|bool { // if data country is set and is not empty if(isset($data['country']) && $data['country']) { // get the quanity measurement type $unit_measure = get_field('product_weight_kg',$data['product_id']); // if we have a measure $data['unit_measure'] = $unit_measure ? 'kg' : 'qty'; // get the origination promo setting $promo_origination = get_field('promo_origination_charges','option'); // if company id is set and is false if(isset($data['company_id']) && !$data['company_id']) { // get the company id $data['company_id'] = Company::id(User::id()); } // get the shipping country pricing setting switch (Shipping::$settings['countries'][$data['country']]['pricing']) { case 'uk': // update data with pricing UK code $data['pricing'] = 'uk'; // get the UK origination $uk_origination = get_field('product_origination',$data['product_id']); // if promo origination is on then set origination to 0 else set it to the UK origination including any markup $data['origination'] = $promo_origination ? 0 : self::currency(self::markup_add($uk_origination,$data['markup_origination']),$data['currency']); // get UK price breaks $uk_price_breaks = self::get_price_breaks($data); // if we have UK price breaks if($uk_price_breaks) { // set the price breaks $data['price_breaks'] = self::breaks_floatvals($uk_price_breaks,$data); } else { // no price breaks found self::error_logger(false,'No UK price breaks found for <a href="'.get_edit_post_link($data['product_id']).'" target="_blank">product</a>.'); } break; case 'eu': // update data with pricing EU code $data['pricing'] = 'eu'; // get the EU origination $eu_origination = get_field('product_eu_origination',$data['product_id']); // if promo origination is on then set origination to 0 else set it to the EU origination $data['origination'] = $promo_origination ? 0 : self::currency(self::markup_add($eu_origination,$data['markup_origination']),$data['currency']); // get EU price breaks $eu_price_breaks = self::get_price_breaks($data); // if we have EU price breaks if($eu_price_breaks) { // set the price breaks $data['price_breaks'] = self::breaks_floatvals($eu_price_breaks,$data); } else { // no price breaks found self::error_logger(false,'No EU price breaks found for <a href="'.get_edit_post_link($data['product_id']).'" target="_blank">product</a>.'); } break; default: // no regional price code found self::error_logger(false,'No regional pricing code found in shipping settings.'); } } else { // no product id is set in data self::error_logger(false,'No shipping country is set in data.'); } // if our custom origination is set and is a float if(is_float($data['custom_origination'])) { // overide the origination with custom origination no matter what $data['origination'] = $data['custom_origination']; } // return data return $data; } /** * @param array $data * @return array|bool */ public static function get_price_breaks(array $data): array|bool { // if product id is set and is not empty if(isset($data['product_id']) && $data['product_id']) { // get the customer pricing $customer_pricing = get_field('product_custom_prices',$data['product_id']); // get the pricing code switch ($data['pricing']) { case 'uk': // if we have customer pricing if($customer_pricing) { // loop through customer pricing foreach ($customer_pricing as $customer) { // if customer id is set and is not empty if(isset($customer['customer']) && $customer['customer']) { // if customer id is the same as the data customer id if($customer['customer'] === $data['company_id']) { // if we have customer UK price breaks if(isset($customer['product_prices']) && $customer['product_prices']) { // return the customer UK price breaks return $customer['product_prices']; } } } } } // get default UK price breaks return get_field('product_prices',$data['product_id']); break; case 'eu': // if we have customer pricing if($customer_pricing) { // loop through customer pricing foreach ($customer_pricing as $customer) { // if customer id is set and is not empty if(isset($customer['customer']) && $customer['customer']) { // if customer id is the same as the data customer id if($customer['customer'] === $data['company_id']) { // if we have customer EU price breaks if(isset($customer['product_eu_prices']) && $customer['product_eu_prices']) { // return the customer EU price breaks return $customer['product_eu_prices']; } } } } } // check for eu price breaks $eu_price_breaks = get_field('product_eu_prices',$data['product_id']); // if we have eu price breaks if($eu_price_breaks) { // return eu price breaks return $eu_price_breaks; } // else return uk price breaks as fallback return get_field('product_prices',$data['product_id']); break; default: // no regional price code found self::error_logger(false,'No regional pricing code found in pricing data.'); } } // return false if no return return false; } /** * @return void */ public static function render_currency_select(): void { ?> <div class="form-group ml-auto"> <select class="form-control" style="padding-right:2.25rem;" name="pricing_currency"> <option value="gbp" <?=self::$default_currency==='gbp'?'selected':null?>>(£) GBP <span class="fi fi-gb"></span></option> <option value="eur" <?=self::$default_currency==='eur'?'selected':null?>>(€) EUR <span class="fi fi-eu"></span></option> </select> </div> <?php } /** * @return void */ public function default_currency(): void { // check if we have eu shipping cookie if (isset($_COOKIE['pricing_currency'])) { // set our currency variable self::$default_currency = $_COOKIE['pricing_currency']; } } /** * @param int|float $value * @return int|float */ public static function round_up_currency(int|float $value): int|float { // round up currency value to 2 decimal places return ceil($value * 100) / 100; } /** * @param int|float $value * @param bool|string $currency * @return int|float */ public static function currency(int|float $value, bool|string $currency = false): int|float { // if we have a currency switch($currency) { case 'gbp': // return GBP value return (float)number_format($value,2,'.',''); break; case 'eur': // get the EUR rate from shipping options $eur_rate = get_field('currency_rate_eur','option'); // return EUR value return self::round_up_currency($value * $eur_rate); break; default: // return GBP value return (float)number_format($value,2,'.',''); } } /** * @param int|float|string $value * @param string $currency * @return string */ public static function value(int|float|string $value, string $currency = 'gbp'): string { // if value is a string if(is_string($value)) { // if value is price on application if($value === 'POA') { // update abbrediated price on application value $value = '<abbr data-toggle="tooltip" data-placement="top" data-html="true" title="<strong>Price on application</strong>" class="font-weight-bold">'.$value.'</abbr>'; } // return string value return $value; } else { // convert value to 2 decimal place add decimal point and thousands seperator $value_format = number_format($value,2); // return matched currency with value return match ($currency) { 'eur' => $value_format . ' €', default => '£' . $value_format }; } // return value return $value; } /** * @param int|float $value * @param bool|float $markup * @return float|int */ public static function markup(int|float $value, bool|float $markup): float|int { // if we have a markup if($markup) { // convert markup percentage to decimal $markup = (float)($markup/100)+1; // price markup $value = $value * $markup; } // return price with parse markup return $value; } /** * @param int|float $value * @param bool|float $markup * @return float|int */ public static function markup_add(int|float $value, bool|float $markup): float|int { // if we have a markup if($markup) { // add markup to value $value = $value + $markup; } // return price with parse markup return $value; } /** * @return void */ #[NoReturn] public function ajax_load_json_price_breaks(): void { // set data array $data = [ 'product_id' => $_GET['product_id'] ? (int)$_GET['product_id'] : false, 'company_id' => $_GET['company_id'] ? (int)$_GET['company_id'] : false, 'country' => $_GET['country'] ?? false, 'currency' => $_GET['currency'] ?? false, 'markup_unit' => false, 'markup_shipping' => false, 'markup_origination' => false ]; // get pricing $data = self::get_pricing_data($data); // if we have errors if(isset($data['error']) && is_array($data['error'])) { // send json error wp_send_json_error($data); } else { // else send json success wp_send_json_success($data); } // death is the beginning die(); } } new PricingV2(); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,411 @@ <?php /** * @author Josh Cranwell <[email protected]> * @copyright The Sweet People * @version 1.0 * @link https://www.thesweetpeople.com/ * @since April 2023 * * @property int id * @property string quote_hash * @property int user_id * @property int product_id * @property bool|int company_id * @property string country * @property string service * @property string currency * @property bool|array quote_data * @property string created_at */ class QuotesV2 { public static string $expiryDuration = '30 DAYS'; protected $data = [], $isLoaded = false, $filterDuration = '1 DAY'; protected function tableName(): string { global $wpdb; return $wpdb->prefix . 'quotes_v2'; } public function __get($name) { // check if we have a special method for formatting our data $getterAttribute = 'get' . ucfirst($name) . 'Attribute'; // is this a valid property if (property_exists($this->data, $name)) { // return our default data return $this->data->{$name}; } // is this a valid method? if (method_exists($this, $getterAttribute)) { return $this->$getterAttribute(); } // not found, return false return false; } /** * Forms constructor. */ public function __construct() { global $wpdb; // create table if not already created $wpdb->query("CREATE TABLE IF NOT EXISTS `{$this->tableName()}` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `quote_hash` VARCHAR(32) NOT NULL , `user_id` INT(10) UNSIGNED NOT NULL , `company_id` INT(10) UNSIGNED, `product_id` INT(10) UNSIGNED NOT NULL , `quote_data` LONGTEXT NULL, `country` VARCHAR(255) NULL , `service` VARCHAR(255) NULL , `currency` VARCHAR(255) NULL , `created_at` DATETIME NOT NULL, PRIMARY KEY (`id`), INDEX (`quote_hash`, `user_id`, `product_id`, `company_id`), INDEX (`created_at`) ) ENGINE = InnoDB; "); } /** * @param int $user_id * @param $product_id * @param $company_id * @param $country * @param $service * @param $currency * @param array $quote_data * @return $this */ public function newQuote(int $user_id, $product_id, $company_id, $country, $service, $currency, array $quote_data = []): static { global $wpdb; // calculate our quote hash $quoteHash = md5(implode(':', [ // base on user id & post id $user_id, $product_id, $company_id, // and delivery country / type $country, $service, // and currency $currency, // also if our pricing has changed, generate new quote serialize($quote_data) ])); // get quotes with current quote hash created in the last day $quotes = $wpdb->get_results(" SELECT * FROM `{$this->tableName()}` WHERE quote_hash = '$quoteHash' AND user_id = $user_id AND product_id = $product_id AND created_at >= DATE_SUB(NOW(), INTERVAL {$this->filterDuration}) "); // check if we've got our quotes... if (is_array($quotes) && count($quotes) > 0) { $this->data = $quotes[0]; return $this; } // create our new record $wpdb->insert($this->tableName(), [ 'quote_hash' => $quoteHash, 'user_id' => $user_id, 'product_id' => $product_id, 'company_id' => $company_id, 'country' => $country, 'service' => $service, 'currency' => $currency, 'quote_data' => json_encode($quote_data), 'created_at' => (new DateTime())->format('Y-m-d H:i:s'), ]); // reload the quote back on this object $this->loadQuote($wpdb->insert_id); // create action to let WordPress know, we've created a new quote do_action('quote_created_v2', $this); return $this; } /** * Get our quotes filename * * @return string */ public function filename(): string { return sprintf('quote-%s.pdf', $this->quote_number()); } /** * Is loaded * @return bool */ public function isLoaded(): bool { return $this->isLoaded; } /** * Check if a quote has expired * * @return bool */ public function hasExpired() { // get today's date and determine our expiry date $dateNow = \Carbon\Carbon::now('GMT'); $expiryDate = $this->created_at()->add(self::$expiryDuration); // return our comparison return $dateNow->gt($expiryDate); } /** * Return our $pricing property * * @return mixed */ protected function getPricingAttribute(): mixed { // return our json decoded pricing return json_decode($this->quote_data, true); } /** * Load quote * @param $quote_id * @return QuotesV2 */ public function loadQuote($quote_id): static { global $wpdb; // get our quote with id #$quote_id $result = $wpdb->get_row("SELECT * FROM {$this->tableName()} WHERE id = $quote_id"); // check we have a result if ($result) { $this->saturate($result); } // return instance of our quote return $this; } /** * Saturate a Quote object * @param stdClass $data * @return QuotesV2 */ public function saturate(stdClass $data): static { // fill our model with our data $this->isLoaded = true; $this->data = $data; return $this; } /** * Retrieve all quotes from the database * * @param int $page The page to load * @param int $limit The number of pages to load * @param int $totalPages Referenced value, the total number of pages * * @return QuotesV2[] */ public function allQuotes(int $page = 1, int $limit = 50, int &$totalPages = 1): array { global $wpdb; if ($limit > 0) { // get our total number of items $count = $wpdb->get_var("SELECT COUNT(*) FROM `{$this->tableName()}`"); // determine our total number of pages $totalPages = $count > 0 ? 1 : ceil($count / $limit); // check our page is a valid positive integer $page = $page > 0 ? $page : 1; // if we are exceeding our page count, return empty if ($totalPages < $page) { return []; } // create our mysql limit string $limitString = 'LIMIT ' . ($page - 1) . ',' . $limit; } else { // we're not enforcing a limit... $limitString = null; } // get all quotes from the database $results = $wpdb->get_results("SELECT * FROM `{$this->tableName()}` WHERE created_at > NOW() - INTERVAL 30 DAY ORDER BY id DESC $limitString"); //dd($results); $results = array_map(function ($result) { return (new QuotesV2())->saturate($result); }, $results); // return all of our results return $results; } /** * Get quotes for a specific user * @param $user_id * @param int $limit * @return QuotesV2[] */ public function get_quotes($user_id, int $limit = 50): array { global $wpdb; // get quotes from current user, current product, created within the last day $results = $wpdb->get_results("SELECT id FROM `{$this->tableName()}` WHERE user_id = {$user_id} ORDER BY id DESC LIMIT {$limit}"); // for each result, create a quote object $results = array_map(function ($result) { return (new QuotesV2())->loadQuote($result->id); }, $results); // return our results return $results; } /** * Get quotes number * @return string Number */ public function quote_number(): string { // format our quote number return str_pad($this->data->id, 4, '0', STR_PAD_LEFT); } /** * Get quotes id * @return int */ public function quote_id(): int { // format our quote number return $this->data->id; } /** * Get delivery type * @return string */ public function country(): string { return Shipping::$countries[strtoupper($this->data->country)]; } /** * Get delivery type * @return string */ public function service(): string { return Shipping::$services[$this->data->service]; } /** * Return our user object * @return WP_User */ public function user(): WP_User { // get user object return get_user_by('ID',$this->data->user_id); } /** * Return our product object * @return WP_Post */ public function product(): WP_Post { // get product object return get_post($this->data->product_id); } /** * Return our created at data * @return \Carbon\Carbon */ public function created_at(): \Carbon\Carbon { return \Carbon\Carbon::make($this->data->created_at, 'GMT'); } /** * Return our product object * @param $quote_id * @return string */ public function download_url($quote_id): string { // quote url return get_bloginfo('url') . '/generate/quote?id=' . $quote_id; } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,830 @@ <?php use JetBrains\PhpStorm\NoReturn; /** * @author Josh Cranwell <[email protected]> * @copyright The Sweet People * @version 1.0 * @link https://www.thesweetpeople.com/ * @since March 2023 */ class Shipping { /** * @return $ */ public static array $countries = [ 'AT' => 'Austria', 'BE' => 'Belgium', 'BG' => 'Bulgaria', 'GB-CHA' => 'Channel Islands', 'HR' => 'Croatia', 'CY' => 'Republic of Cyprus', 'CZ' => 'Czech Republic', 'DK' => 'Denmark', 'EE' => 'Estonia', 'FI' => 'Finland', 'FR' => 'France', 'DE' => 'Germany', 'GR' => 'Greece', 'HU' => 'Hungary', 'IE' => 'Ireland', 'IT' => 'Italy', 'GB-IOM' => 'Isle of Man', 'GB-IOW' => 'Isle of Wight', 'LV' => 'Latvia', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'MT' => 'Malta', 'NL' => 'Netherlands', 'PL' => 'Poland', 'PT' => 'Portugal', 'RO' => 'Romania', 'SK' => 'Slovakia', 'SI' => 'Slovenia', 'ES' => 'Spain', 'SE' => 'Sweden', 'GB' => 'United Kingdom' ]; public static array $uk_couriers = [ "Royal Mail", "DHL Express UK", "Hermes", "DPD UK", "UPS UK", "TNT UK", "Parcelforce Worldwide", "Yodel", "UK Mail", "DX" ]; public static array $eu_couriers = [ "DHL Express", "UPS", "FedEx", "GLS", "DPD", "TNT", "Chronopost", "La Poste", "PostNord", "Bpost" ]; public static array $services = [ "standard" => "Standard", "pre-1200" => "Pre 12:00", "pre-1030" => "Pre 10:30" ]; public static array $settings = []; public static array|bool $enabled = false; public static array|bool $individual = false; #[NoReturn] public function __construct () { // load acf choices shipping settings add_filter('acf/load_field/key=field_6418de2d5394b', [ $this, 'load_country_choices' ]); add_filter('acf/load_field/key=field_6418e1805394e', [ $this, 'load_courier_choices' ]); add_filter('acf/load_field/key=field_6418fbf07a998', [ $this, 'load_courier_choices' ]); add_filter('acf/load_field/key=field_6418efa64b7c1', [ $this, 'load_service_choices' ]); add_filter('acf/load_field/key=field_6418fbf07a999', [ $this, 'load_service_choices' ]); // set our settings varible array $this->set_settings(); // loadding shipping form service add_action('wp_ajax_nopriv_load_shipping_services', [ $this, 'ajax_load_shipping_services' ], 20 ); add_action('wp_ajax_load_shipping_services', [ $this, 'ajax_load_shipping_services' ], 20 ); } /** * @param array $data * @return array */ public static function get_packaging_id(array $data): array { // check we have a product id if(isset($data['product_id']) && $data['product_id']) { // if product is pre packed $pre_packed = get_field('product_is_pre_packed',$data['product_id']); // if product is pre packed if($pre_packed) { // get confectionery id $data['packaging_id'] = get_field('product_confectionery',$data['product_id']); // if no confectionery packaging id is set the log error PricingV2::error_logger($data['packaging_id'],'No pre-packed confectionery selected for <a href="'.get_edit_post_link($data['product_id']).'" target="_blank">product</a>.'); } else { // get packaging id $data['packaging_id'] = get_field('product_packaging',$data['product_id']); // if no packaging id is set the log error PricingV2::error_logger($data['packaging_id'],'No packaging selected for <a href="'.get_edit_post_link($data['product_id']).'" target="_blank">product</a>.'); } } else { // no product id is set in data PricingV2::error_logger(false,'No product id is set in data.'); } // return data aray return $data; } /** * @param array $data * @return array */ public static function get_parcel_data(array $data): array { // check we have a packaging id if(isset($data['packaging_id']) && $data['packaging_id']) { // get parcel data $packaging = get_term_by('term_taxonomy_id',$data['packaging_id'],'','ARRAY_A'); // check we have taxonomoy set if(isset($packaging['taxonomy']) && is_string($packaging['taxonomy'])) { // if we have a product id if (isset($data['pricing']) && $data['pricing']) { // get the shipping packaging setting switch ($data['pricing']) { case 'uk': // check if we have a custom shipping $custom_uk_shipping = get_field('product_custom_shipping',$data['product_id']); // if we have custom shipping if($custom_uk_shipping) { // get parcel box id from product $data['parcel_id'] = (int)get_field('packaging_shipping',$data['product_id'],false); // get parcel max items from product $data['parcel_max_items'] = (int)get_field('packaging_shipping_content',$data['product_id']); // get custom pallet max parcels $data['pallet_parcels_max'] = (int)get_field('shipping_pallet_qty',$data['product_id']); } else { // get parcel box id $data['parcel_id'] = (int)get_field('packaging_shipping',$packaging['taxonomy'] . '_' . $packaging['term_id'],false); // no uk parcel id is found log PricingV2::error_logger($data['parcel_id'],'No UK parcel found in <a href="'.get_edit_term_link($packaging['term_id'],$packaging['taxonomy']).'" target="_blank">'.$packaging['taxonomy'].'</a>.'); // get parcel max items $data['parcel_max_items'] = (int)get_field('packaging_shipping_content',$packaging['taxonomy'] . '_' . $packaging['term_id']); // no uk parcel max items is found log PricingV2::error_logger($data['parcel_max_items'],'No UK parcel max items is found in <a href="'.get_edit_term_link($packaging['term_id'],$packaging['taxonomy']).'" target="_blank">'.$packaging['taxonomy'].'</a>.'); } break; case 'eu': // check if we have a custom eu shipping $custom_eu_shipping = get_field('product_custom_eu_shipping',$data['product_id']); // if we have custom shipping if($custom_eu_shipping) { // get parcel box id from product $data['parcel_id'] = (int)get_field('packaging_eu_shipping',$data['product_id'],false); // get parcel max items from product $data['parcel_max_items'] = (int)get_field('packaging_eu_shipping_content',$data['product_id']); // get custom pallet max parcels $data['pallet_parcels_max'] = (int)get_field('eu_shipping_pallet_qty',$data['product_id']); } else { // get parcel box id $data['parcel_id'] = (int)get_field('packaging_eu_shipping',$packaging['taxonomy'] . '_' . $packaging['term_id'],false); // no eu parcel id is found log PricingV2::error_logger($data['parcel_id'],'No EU parcel found in <a href="'.get_edit_term_link($packaging['term_id'],$packaging['taxonomy']).'" target="_blank">'.$packaging['taxonomy'].'</a>.'); // get parcel content $data['parcel_max_items'] = (int)get_field('packaging_eu_shipping_content',$packaging['taxonomy'] . '_' . $packaging['term_id']); // no eu parcel max items is found log PricingV2::error_logger($data['parcel_max_items'],'No EU parcel max items is found in <a href="'.get_edit_term_link($packaging['term_id'],$packaging['taxonomy']).'" target="_blank">'.$packaging['taxonomy'].'</a>.'); } break; } } } } // return data aray return $data; } public static function get_pallet_data(array $data): array { // check we have a packaging id if(isset($data['parcel_id']) && $data['parcel_id']) { // check we have outer box $parcel_outer_parcel = get_field('shipping_outer_box','shipping_' . $data['parcel_id']); // if we have outer parcel if($parcel_outer_parcel) { // set outer parcel id $data['parcel_outer_id'] = $parcel_outer_parcel; // if is not set and is false if(!isset($data['pallet_parcels_max']) || !$data['pallet_parcels_max']) { // pallet max parcels $data['pallet_parcels_max'] = (int)get_field('shipping_pallet_qty','shipping_' . $parcel_outer_parcel); // max outer parcels per pallet error PricingV2::error_logger($data['pallet_parcels_max'],'Maximum outer boxes per pallet not set in <a href="'.get_edit_term_link($parcel_outer_parcel,'shipping').'" target="_blank">shipping</a>.'); } // pallet max parcels //$data['pallet_parcels_layer'] = (int)get_field('shipping_pallet_qty_layer','shipping_' . $parcel_outer_parcel); // pallet outer parcel single layer error //PricingV2::error_logger($data['pallet_parcels_layer'],'Pallet single outer box layer quantity not set in <a href="'.get_edit_term_link($parcel_outer_parcel,'shipping').'" target="_blank">shipping</a>.'); } else { // if is not set or is false if(!isset($data['pallet_parcels_max']) || !$data['pallet_parcels_max']) { // pallet max parcels $data['pallet_parcels_max'] = (int)get_field('shipping_pallet_qty','shipping_' . $data['parcel_id']); // max parcels per pallet error PricingV2::error_logger($data['pallet_parcels_max'],'Maximum boxes per pallet not set in <a href="'.get_edit_term_link($data['parcel_id'],'shipping').'" target="_blank">shipping</a>.'); } // pallet max parcels //$data['pallet_parcels_layer'] = (int)get_field('shipping_pallet_qty_layer','shipping_' . $data['parcel_id']); // pallet parcel single layer error //PricingV2::error_logger($data['pallet_parcels_layer'],'Pallet single box layer quantity not set in <a href="'.get_edit_term_link($data['parcel_id'],'shipping').'" target="_blank">shipping</a>.'); } } // return data aray return $data; } /** * @return mixed|void */ public static function get_default() { // if uk pricing is enabled if(self::$enabled['uk']) { // set default services using gb code return self::$settings['countries']['gb']['services']; } else { // get the first country in list $default_country = reset(self::$settings['countries']); // set default services using gb code return $default_country['services']; } } /** * @param array $data * @return array */ public static function get_unit_weight(array $data): array { // add unit weight to data $data['unit_weight'] = Product::unit_weight($data['product_id']); // return array return $data; } /** * @return no-return */ public static function render_country_select() { // if countries is set and is an array if (isset(self::$settings['countries']) && is_array(self::$settings['countries'])) { ?> <div class="form-group mr-sm-3"> <select class="form-control" style="padding-right:2.25rem;" name="shipping_country"> <?php foreach (self::$settings['countries'] as $code => $country) { ?> <?php if (self::$enabled['uk'] && $country['pricing'] === 'uk') { ?> <option value="<?=$code?>" <?=$code==='gb'?'selected':null?>><?=$country['country']?></option> <?php } ?> <?php if (self::$enabled['eu'] && $country['pricing'] === 'eu') { ?> <option value="<?=$code?>" ><?=$country['country']?></option> <?php } ?> <?php } ?> </select> </div> <?php } } /** * @param bool|string $code * @return no-return */ public static function render_service_radio(bool|string $code = false) { // if countries is set and is an array if (isset(self::$settings['countries']) && is_array(self::$settings['countries'])) { // if we have country code if ($code) { // set services using code $services = self::$settings['countries'][ $code ]['services']; } else { // get default $services = self::get_default(); } // if we have services if (is_array($services)) { $uid = rand(); ?> <div data-group="services" class="form-group"> <?php $i = 0; foreach ($services as $value => $service) { $i++ ?> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="shipping_service" id="service_<?=$value?>_<?=$uid?>" value="<?=$value?>" <?=$i===1?'checked':false?>/> <label class="form-check-label" for="service_<?=$value?>_<?=$uid?>"><?=$service['service']?></label> </div> <?php } ?> </div> <?php } } } /** * @return void */ #[NoReturn] public function ajax_load_shipping_services(): void { // get the country code $code = $_GET['country']; // render service radios for country self::render_service_radio($code); // die die(); } /** * @param bool|int $post_id * @return void */ #[NoReturn] public static function set_product_vars(bool|int $post_id = false): void { // check if post id is set if(!$post_id) { // global post global $post; // set post id $post_id = $post->ID; } // check if we are on the right post type and post is set if($post_id || is_singular('product') && isset($post)) { // check if product shipping has been enabled $enabled_uk = get_field('product_uk_shipping',$post_id); $enabled_eu = get_field('product_eu_shipping',$post_id); // update our indivual shipping var self::$enabled['uk'] = $enabled_uk ?? false; self::$enabled['eu'] = $enabled_eu ?? false; // let check for indiviual settings $individual = get_field('product_individual_shipping',$post_id); // if individual shipping if($individual) { // individual price fields $price_uk = get_field('product_individual_shipping_cost',$post_id); $price_eu = get_field('product_individual_eu_shipping_cost',$post_id); // update our indivual shipping var self::$individual['uk'] = $price_uk ?? false; self::$individual['eu'] = $price_eu ?? false; } } } /** * @return void */ #[NoReturn] public function set_settings(): void { // get our shipping options $globals = get_field('shipping_globals','options'); $countries = get_field('shipping_countries','options'); // if globals is an array if( is_array($globals) ) { // for each globals as global foreach( $globals as $key => $value ) { // if key string starts with uk if( str_starts_with($key, 'uk') ) { // set uk global settings as integer self::$settings['globals']['uk'][ str_replace('uk_','',$key) ] = (int)$value; continue; } // if key string starts with eu if( str_starts_with($key, 'eu') ) { // set eu global settings as integer self::$settings['globals']['eu'][ str_replace('eu_','',$key) ] = (int)$value; } } } // if countries is an array if( is_array($countries) ) { // for each countries as country foreach( $countries as $country ) { // country code $code = $country['country']['value']; // set country in settings array self::$settings['countries'][ $code ]['country'] = $country['country']['label']; // set pricing in settings array self::$settings['countries'][ $code ]['pricing'] = $country['pricing']['value']; // if parcel services is an array if( is_array($country['services_parcel']) ) { // for each service's parcel as service parcel foreach( $country['services_parcel'] as $service_parcel ) { // parcel service value $service = $service_parcel['service']['value']; // set the service name label self::$settings['countries'][ $code ]['services'][ $service ]['service'] = $service_parcel['service']['label']; // set parcel service in settings array self::$settings['countries'][ $code ]['services'][ $service ]['method']['parcel'] = $service_parcel; // remove service array to label value unset(self::$settings['countries'][ $code ]['services'][ $service ]['method']['parcel']['service']); } } // if pallet services is an array if( is_array($country['services_pallet']) ) { // for each service's pallet as service pallet foreach( $country['services_pallet'] as $service_pallet ) { // pallet service value $service = $service_pallet['service']['value']; // set pallet service in settings array self::$settings['countries'][ $code ]['services'][ $service ]['method']['pallet'] = $service_pallet; // remove service array to label value unset(self::$settings['countries'][ $code ]['services'][ $service ]['method']['pallet']['service']); } } } } // get the countries sub-array $countries = self::$settings['countries'] ?? false; // if countries is an array if(is_array($countries)) { // extract the country sub-element as a separate array for sorting $country_list = array_column($countries,'country'); // sort the country sub-element array in alphabetical order array_multisort($country_list,SORT_ASC, SORT_STRING, $countries); // update the original array with the sorted countries sub-array self::$settings['countries'] = $countries; } } /** * @param $field array * @return array */ public function load_country_choices( array $field ): array { // reset choices $field['choices'] = []; // select country default null $field['choices'][''] = 'Select Country...'; // loop through array and add to field 'choices' if( is_array(self::$countries) ) { // change keys to lower case $countries = array_change_key_case(self::$countries); // for each contries as code => country foreach( $countries as $code => $country ) { // add country and code to $field['choices'][ $code ] = $country; } } // return the field return $field; } /** * @param $field array * @return array */ public function load_courier_choices( array $field ): array { // reset choices $field['choices'] = []; // select courier default null $field['choices'][''] = 'Select Courier...'; // loop through array and add to field 'choices' if( is_array(self::$uk_couriers) ) { // for each uk couriers as uk courier foreach( self::$uk_couriers as $uk_courier ) { // add uk courier to option group $field['choices']['UK Couriers'][ $uk_courier ] = $uk_courier; } } // loop through array and add to field 'choices' if( is_array(self::$eu_couriers) ) { // for each eu couriers as eu courier foreach( self::$eu_couriers as $eu_courier ) { // add eu courier option group $field['choices']['EU Couriers'][ $eu_courier ] = $eu_courier; } } // return the field return $field; } /** * @param $field array * @return array */ public function load_service_choices( array $field ): array { // reset choices $field['choices'] = []; // select services default null $field['choices'][''] = 'Select Service...'; // loop through array and add to field 'choices' if( is_array(self::$services) ) { // for each service as value => label foreach( self::$services as $value => $label ) { // add value and label to choices $field['choices'][ $value ] = $label; } } // return the field return $field; } /** * @param int|float $value * @return string */ public static function value_kg(int|float $value): string { // return kg value return (float)($value / 1000) . ' kg'; } /** * @param int|float $value * @param string|bool $measure * @return string */ public static function value_unit(int|float $value, string|bool $measure): string { // return matched measure with value return match ($measure) { 'kg' => $value . ' kg', default => $value }; } /** * @param array|bool $break * @return string */ public static function packing_icon(array|bool $break): string { // set icon $icon = ''; // if break is an array if(is_array($break)) { // tooltip html $tooltip = ''; // get parcel weight $parcel_weight = $break['shipping_weight_parcel'] ?? false; // set tooltip parcel weight $tooltip .= $parcel_weight ? 'Parcel Weight: <strong>' . self::value_kg($parcel_weight). '</strong></br>' : ''; // get consignment weight $consignment_weight = $break['shipping_weight_consignment'] ?? false; // set tooltip consignment weight $tooltip .= $consignment_weight ? 'Consignment Weight: <strong>' . self::value_kg($consignment_weight). '</strong></br>' : ''; // if shipping method is set $method = $break['shipping_method'] ?? false; // set parcel count $parcels = $break['shipping_parcels'] ?? false; // set tooltip parcel count $tooltip .= $parcels ? 'Parcels: <strong>x' . $parcels . '</strong></br>' : ''; // if method is set switch ($method) { case 'parcel': // if parcel count is 1 if($parcels == 1) { // single parcel icon $icon = '<abbr data-toggle="tooltip" data-html="true" title="'.$tooltip.'" class="mr-2"><i class="fas fa-box fa-fw color-sweets"></i></abbr>'; // else if parcel count is greater than 1 } else if ($parcels > 1) { // multiple parcels icon $icon = '<abbr data-toggle="tooltip" data-html="true" title="'.$tooltip.'" class="mr-2"><i class="fas fa-boxes fa-fw color-sweets"></i></abbr>'; } break; case 'pallet': // set pallet count $pallets = $break['shipping_pallets'] ?? false; // set tooltip pallet count $tooltip .= $pallets ? 'Pallets: <strong>x' . $pallets . '</strong></br>' : ''; // if we have pallets if($pallets) { // single or multiple pallets icon $icon = '<abbr data-toggle="tooltip" data-html="true" title="'.$tooltip.'" class="mr-2"><i class="fas fa-pallet fa-fw color-sweets"></i></abbr>'; } break; default: break; } } // return icon string return $icon; } /** * @param bool|string $service * @return string */ public static function service_icon(bool|string $service): string { // return matched service icon return match ($service) { 'standard' => '<abbr data-toggle="tooltip" data-placement="top" data-html="true" title="Service: <strong>'.self::$services[$service].'</strong>" class="mr-2"><i class="fas fa-shipping-fast fa-fw color-sweets"></i></abbr>', default => '<abbr data-toggle="tooltip" data-placement="top" data-html="true" title="Service: <strong>'.self::$services[$service].'</strong>" class="mr-2"><i class="fas fa-shipping-timed fa-fw color-sweets"></i></abbr>' }; } } // initiate the shipping class $shipping = new Shipping(); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,646 @@ /** * @author Josh Cranwell <[email protected]> * @copyright The Sweet People * @version 1.0 * @link https://www.thesweetpeople.com/ * @since March 2023 */ // jQuery on ready jQuery(function($) { /** * @param value */ function format_money(value) { // Round the value to two decimal places value = parseFloat(value.toFixed(2)); // Add a comma separator for the thousands place value = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); // Add a leading zero if necessary if (value.indexOf('.') === -1) { value += '.00'; } else if (value.indexOf('.') === value.length - 2) { value += '0'; } // return the value return value; } // all pricing blocks let product_pricing = $('.product-pricing-v2', document); // run pricing form events pricing_form_events(product_pricing); // winodw loaded $(window).on('load',function() { // detect when a new pricing form is added to the DOM $(document).on('DOMNodeInserted','#quicklook_modal_body',function(e) { //console.log(e); // if the newly added element has the pri if($(e.target).hasClass('product-single')) { // get the newly added pricing form let product_pricing = $('.product-pricing-v2', e.target); // run pricing form events on newly added pricing form pricing_form_events(product_pricing); } }); }); /** * @param product_pricing */ function pricing_form_events(product_pricing) { // pricing form object $(product_pricing) // pricing form country select on change .on('input','[name="shipping_country"]',function(e) { // load shipping servicess load_shipping_services(e.currentTarget); }) // pricing form service selcect on change .on('input', '[name="shipping_service"]', function (e) { // load pricing table load_pricing_table(e.currentTarget); }) // pricing form currency select on change .on('input', '[name="pricing_currency"]', function (e) { // get selected currecy value let currency = e.currentTarget.value; // set currency cookie Cookies.set('pricing_currency', currency, {expires: 365}); // load pricing table load_pricing_table(e.currentTarget); }) } /** * @param target */ function load_shipping_services(target) { // get selected country code let code = target.value; // get the current pricing form let product_pricing = $(target).closest('.product-pricing-v2'); // disable all shipping services $('[name="shipping_service"]', product_pricing).prop('disabled',true); // ajax call $.ajax({ cache: false, timeout: 30000, url: admin_ajax_url, type: 'GET', data: { action: 'load_shipping_services', country: code } }).done(function (data) { // update shipping services group $('[data-group="services"]', product_pricing).replaceWith(data); }).then(function () { // load pricing table load_pricing_table(product_pricing); }); } /** * @param target */ window.load_pricing_table = function(target) { // get the current pricing form let product_pricing = $(target).closest('.product-pricing-v2'); // get our shipping form variable let data = { product_id: $(product_pricing).data('product-id'), company_id: $(product_pricing).data('company-id'), country: $('[name="shipping_country"]', product_pricing).val(), service: $('[name="shipping_service"]:checked', product_pricing).val(), currency: $('[name="pricing_currency"]', product_pricing).val(), markup_unit: $(product_pricing).data('markup-unit'), markup_shipping: $(product_pricing).data('markup-shipping'), markup_origination: $(product_pricing).data('markup-origination'), }; // enable quote download button $('.download-product-quote-v2', product_pricing) .addClass('disabled') .attr('href','#'); // fade out pricing table $('.pricing-table-v2', product_pricing).find('.table-responsive') .fadeTo(0,0.5); // ajax call $.ajax({ cache: false, timeout: 30000, url: admin_ajax_url, type: 'GET', data: { action: 'load_pricing_table', product_id: data.product_id, company_id: data.company_id, country: data.country, service: data.service, currency: data.currency, markup_unit: data.markup_unit, markup_shipping: data.markup_shipping, markup_origination: data.markup_origination } }).done(function (response) { // render the pricing table html $('.pricing-table-v2', product_pricing).find('.table-responsive') .html(response.data) .fadeTo(0,1); // initialize the tooltips again $('[data-toggle="tooltip"]').tooltip(); // if success is true if(response.success) { // get the download quote button href params let params = $.param(data); // enable quote download button and set the href $('.download-product-quote-v2', product_pricing) .attr('href','/generate/quote?' + params) .removeClass('disabled'); } }).then(function () { // do nothing }); } // get custom pricing calculator let calculator_pricing = $('.custom-pricing-calculator', document); // pricing form object $(calculator_pricing) // pricing form country select on change .on('input','[name="shipping_country"]',function(e) { // load shipping servicess load_calculator_shipping_services(e.currentTarget); }) // pricing form currency select on change .on('input', '[name="pricing_currency"]', function (e) { // get selected currecy value let currency = e.currentTarget.value; // set currency cookie Cookies.set('pricing_currency', currency, {expires: 365}); // load calculator price breaks json load_calculator_json_price_breaks(calculator_pricing); }) // calculator qty input on change .on('input', '[name="calculator_qty"]', function (e) { // get the add unit price input let add_unit = $('[name="add_calculator_unit"]', calculator_pricing); // if add unit price is not checked if(!add_unit.prop('checked')) { // get the current calculator pricing data let data = JSON.parse($(calculator_pricing).attr('data-pricing')); // check if json data has the price_breaks property if (data.hasOwnProperty('price_breaks')) { // set our quantity input and unit price input let qty_input = e.currentTarget.value; let unit_price_input = $('[name="calculator_unit"]', calculator_pricing); // set the entered quantity and unit price let entered_qty = parseInt(qty_input, 10); let unit_price; // if enter quanity is a number if (!isNaN(entered_qty)) { // set the unit_price to the lowest unit price initially unit_price = data.price_breaks[0].unit_price; // loop through the price_breaks array for (let i = 0; i < data.price_breaks.length; i++) { // check if the entered_qty is greater than or equal to the current price_break's quantity if (entered_qty >= data.price_breaks[i].quantity) { // update the unit_price variable with the unit_price of the current price_break unit_price = data.price_breaks[i].unit_price; } else { // else if the entered_qty is less than the current price_break's quantity, // stop looping and keep the last matched unit_price break; } } } // update the unit price input value unit_price_input.val(unit_price ? unit_price.toFixed(2) : ''); } } }) // check when the calculator qty input is blurred .on('blur', '[name="calculator_qty"]', function (e) { // lets get the min value let min = parseInt(e.currentTarget.min,10); // // if current value is empty if(!e.currentTarget.value) { // reset the value to min $(e.currentTarget).val(e.currentTarget.min).trigger('input'); // if current value is less than min } else if (e.currentTarget.value < min) { // set the current value to the min $(e.currentTarget).val(e.currentTarget.min).trigger('input'); } }) // add calculator unit price checkbox on change .on('input', '[name="add_calculator_unit"]', function (e) { // get the calculator unit price input let input_unit = $('[name="calculator_unit"]', calculator_pricing); // if the custom add unit price is checked if(e.currentTarget.checked) { // disable the unit price input input_unit.prop('disabled',false); } else { // disable the unit price input input_unit.prop('disabled',true); } }) // add calculator origination checkbox on change .on('input', '[name="add_calculator_origination"]', function (e) { // get the calculator origination input let input_unit = $('[name="calculator_origination"]', calculator_pricing); // if the custom add origination is checked if(e.currentTarget.checked) { // disable the origination input input_unit.prop('disabled',false); } else { // disable the origination input input_unit.prop('disabled',true); } }) // check when the calculator unit and origination input is blurred .on('blur', '[name="calculator_unit"], [name="calculator_origination"]', function (e) { // lets get current target value let value = parseFloat(e.currentTarget.value); // format the value $(e.currentTarget).val(format_money(value)); }) /** * @param target */ function load_calculator_shipping_services(target) { // get selected country code let code = target.value; // get the custom pricing calculator let calculator_pricing = $(target).closest('.custom-pricing-calculator'); // disable all custom pricing calculator shipping services $('[name="shipping_service"]', calculator_pricing).prop('disabled',true); // ajax call $.ajax({ cache: false, timeout: 30000, url: admin_ajax_url, type: 'GET', data: { action: 'load_shipping_services', country: code } }).done(function (data) { // update shipping services group $('[data-group="services"]', calculator_pricing).replaceWith(data); }).then(function () { // load calculor price breaks json load_calculator_json_price_breaks(calculator_pricing); }); } /** * @param target */ function load_calculator_json_price_breaks(target) { // get the custom pricing calculator let calculator_pricing = $(target); // set our data object let data = { product_id: $(calculator_pricing).data('product-id'), company_id: $(calculator_pricing).data('company-id'), country: $('[name="shipping_country"]', calculator_pricing).val(), currency: $('[name="pricing_currency"]', calculator_pricing).val() }; // get our add values let add = { unit_price: $('[name="add_calculator_unit"]', calculator_pricing).prop('checked'), origination: $('[name="add_calculator_origination"]', calculator_pricing).prop('checked'), shipping: $('[name="add_calculator_shipping"]', calculator_pricing).prop('checked') } // disable all calculator unit inputs $('[name="calculator_qty"], [name="calculator_unit"], [name="calculator_origination"], [type="submit"]', calculator_pricing).prop('disabled',true); // ajax call $.ajax({ cache: false, timeout: 30000, url: admin_ajax_url, type: 'GET', data: { action: 'load_json_price_breaks', product_id: data.product_id, company_id: data.company_id, country: data.country, currency: data.currency } }).done(function (response) { // if success is true if(response.success) { // set data json with stringified response data let data_json = JSON.stringify(response.data); // add json to calculator data pricing $(calculator_pricing).attr('data-pricing', data_json); // update calculator unit price and origination currency symbol $('[name="calculator_unit"], [name="calculator_origination"]', calculator_pricing) .closest('.input-group') .find('.input-group-text') .html(data.currency === 'eur' ? '€' : '£'); // check if response.data has the price_breaks property if(response.data.hasOwnProperty('price_breaks')) { // set price breaks let price_breaks = response.data.price_breaks; // is array of price breaks if(Array.isArray(price_breaks) && price_breaks[0]) { // set calculation unit to first price break unit let first_price_break = price_breaks[0]; // if we have first priced break unit if(first_price_break.unit_price) { // set quantity input let input_qty = $('[name="calculator_qty"]', calculator_pricing); // set value on quantity input and enable input_qty.val(first_price_break.quantity).prop('disabled',false); // if the input qty min is undefined if(!input_qty.attr('min')) { // set the min value input_qty.attr('min', first_price_break.quantity); } // set input unit price let input_unit = $('[name="calculator_unit"]', calculator_pricing); // enable all calculator unit inputs input_unit.val(format_money(first_price_break.unit_price)); // if we have an add unit price if(add.unit_price) { // enable unit price input input_unit.prop('disabled',false); } } } } // check if response.data has the price_breaks property if(response.data.hasOwnProperty('origination')) { // set origination let orgination = response.data.origination; // if we have origination if(orgination) { // set input origination let input_origination = $('[name="calculator_origination"]', calculator_pricing); // enable all calculator unit inputs input_origination.val(format_money(orgination)); // if we have an add unit price if(add.origination) { // enable input origination input_origination.prop('disabled',false); } } } } }).then(function (response) { // if success is true if(response.success) { // enable the calculator submit button for calcaltion $('[type="submit"]', calculator_pricing).prop('disabled',false); } }); } // load calculator price breaks json load_calculator_json_price_breaks(calculator_pricing); // custom calculator form submit $('.custom-pricing-calculator-form').on('submit', function (e) { // prevent default e.preventDefault(); // set our data object let data = { product_id: $(calculator_pricing).data('product-id'), company_id: $(calculator_pricing).data('company-id'), country: $('[name="shipping_country"]', calculator_pricing).val(), service: $('[name="shipping_service"]:checked', calculator_pricing).val(), currency: $('[name="pricing_currency"]', calculator_pricing).val(), custom_qty: $('[name="calculator_qty"]', calculator_pricing).val(), custom_unit: $('[name="calculator_unit"]', calculator_pricing).val(), custom_origination: $('[name="calculator_origination"]', calculator_pricing).val() } // remove the pricing calculation display nonw class $('.custom-pricing-calculation', calculator_pricing).removeClass('d-none'); // enable quote download button $('.download-product-quote-v2', calculator_pricing) .addClass('disabled') .attr('href','#'); // fade out pricing table $('.custom-pricing-calculation-table', calculator_pricing).find('.table-responsive') .fadeTo(0,0.5); // ajax call $.ajax({ cache: false, timeout: 30000, url: admin_ajax_url, type: 'GET', data: { action: 'load_pricing_table', product_id: data.product_id, company_id: data.company_id, country: data.country, service: data.service, currency: data.currency, custom_qty: data.custom_qty, custom_unit: data.custom_unit, custom_origination: data.custom_origination } }).done(function (response) { // render the pricing table html $('.custom-pricing-calculation-table', calculator_pricing).find('.table-responsive') .html(response.data) .fadeTo(0,1); // initialize the tooltips again $('[data-toggle="tooltip"]').tooltip(); // if success is true if(response.success) { // get the download quote button href params let params = $.param(data); // enable quote download button and set the href $('.download-product-quote-v2', calculator_pricing) .attr('href', '/generate/quote?' + params) .removeClass('disabled'); } }).then(function (response) { // then }); }); })