Created
April 12, 2023 16:40
-
-
Save joshmoto/28adfa359834022cc6e30ff07b8ce494 to your computer and use it in GitHub Desktop.
This file contains 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 characters
/** | |
* @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 | |
}); | |
}); | |
}) |
This file contains 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 characters
<?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 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 characters
<?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 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 characters
<?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(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment