Skip to content

Instantly share code, notes, and snippets.

@joshmoto
Created April 12, 2023 16:40
Show Gist options
  • Save joshmoto/28adfa359834022cc6e30ff07b8ce494 to your computer and use it in GitHub Desktop.
Save joshmoto/28adfa359834022cc6e30ff07b8ce494 to your computer and use it in GitHub Desktop.
/**
* @author Josh Cranwell <josh.cranwell@gmail.com>
* @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
});
});
})
<?php
use JetBrains\PhpStorm\NoReturn;
/**
* @author Josh Cranwell <josh.cranwell@gmail.com>
* @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();
<?php
/**
* @author Josh Cranwell <josh.cranwell@gmail.com>
* @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;
}
}
<?php
use JetBrains\PhpStorm\NoReturn;
/**
* @author Josh Cranwell <josh.cranwell@gmail.com>
* @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