CMXX - Checkout and Placeorder
This commit is contained in:
@@ -3086,4 +3086,411 @@ function calculateTotalPrice($product_data, $selected_options) {
|
||||
'total_price' => $total_price,
|
||||
'selected_items' => implode(', ', $selected_item_names)
|
||||
];
|
||||
}
|
||||
|
||||
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// ShoppingCartCalulator ++++++++++++++
|
||||
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
class ShoppingCartCalculator {
|
||||
private $products;
|
||||
private $selected_country;
|
||||
private $selected_shipping_method;
|
||||
private $business_type;
|
||||
private $discount_code;
|
||||
private $db;
|
||||
private $discount_message;
|
||||
private $tax_rate;
|
||||
|
||||
public function __construct($products, $selected_country, $selected_shipping_method, $business_type, $discount_code, $db) {
|
||||
$this->products = $products;
|
||||
$this->selected_country = $selected_country;
|
||||
$this->selected_shipping_method = $selected_shipping_method;
|
||||
$this->business_type = strtolower($business_type);
|
||||
$this->discount_code = $discount_code;
|
||||
$this->db = $db;
|
||||
$this->discount_message = '';
|
||||
$this->tax_rate = $this->getTaxRate();
|
||||
}
|
||||
|
||||
public function calculateTotals() {
|
||||
// Calculate basic totals
|
||||
$subtotal = $this->calculateSubtotal();
|
||||
$weighttotal = $this->calculateWeightTotal();
|
||||
$shippingtotal = $this->calculateShippingTotal($subtotal, $weighttotal,$this->selected_shipping_method);
|
||||
$discounttotal = $this->calculateDiscountTotal();
|
||||
$taxtotal = $this->calculateTaxTotal($subtotal - $discounttotal + $shippingtotal);
|
||||
|
||||
// Calculate final total based on business type
|
||||
$total = $this->calculateFinalTotal($subtotal, $shippingtotal, $discounttotal, $taxtotal);
|
||||
|
||||
|
||||
return [
|
||||
'cart_details' => [
|
||||
'products' => $this->products,
|
||||
'selected_country' => $this->selected_country,
|
||||
'selected_shipping_method' => $this->selected_shipping_method,
|
||||
'business_type' => $this->business_type,
|
||||
'discount_code' => $this->discount_code
|
||||
|
||||
],
|
||||
'totals' => [
|
||||
'subtotal' => number_format($subtotal, 2, '.', ''),
|
||||
'weighttotal' => number_format($weighttotal, 2, '.', ''),
|
||||
'shippingtotal' => number_format($shippingtotal, 2, '.', ''),
|
||||
'discounttotal' => number_format($discounttotal, 2, '.', ''),
|
||||
'discount_message' => $this->discount_message,
|
||||
'tax_rate' => number_format($this->tax_rate, 2, '.', '') . '%',
|
||||
'taxtotal' => number_format($taxtotal, 2, '.', ''),
|
||||
'total' => number_format($total, 2, '.', '')
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
private function getTaxRate() {
|
||||
$sql = "SELECT rate FROM taxes WHERE country = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$this->selected_country]);
|
||||
$tax = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $tax ? floatval($tax['rate']) : 0;
|
||||
}
|
||||
|
||||
private function calculateSubtotal() {
|
||||
$subtotal = 0;
|
||||
foreach ($this->products as $product) {
|
||||
$product_price = floatval(str_replace(',', '.', $product['options_price']));
|
||||
$subtotal += $product_price * $product['quantity'];
|
||||
}
|
||||
return $subtotal;
|
||||
}
|
||||
|
||||
private function calculateWeightTotal() {
|
||||
$weighttotal = 0;
|
||||
foreach ($this->products as $product) {
|
||||
$options_weight = floatval($product['options_weight']);
|
||||
$weighttotal += $options_weight * $product['quantity'];
|
||||
}
|
||||
return $weighttotal;
|
||||
}
|
||||
|
||||
private function calculateDiscountTotal() {
|
||||
if (empty($this->discount_code)) {
|
||||
$this->discount_message = '';
|
||||
return 0;
|
||||
}
|
||||
|
||||
$current_date = date('Y-m-d H:i:s');
|
||||
|
||||
// First check if discount code exists and is valid
|
||||
$sql = "SELECT * FROM discounts WHERE discount_code = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$this->discount_code]);
|
||||
$discount = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$discount) {
|
||||
$this->discount_message = 'Invalid discount code';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check date validity
|
||||
if ($current_date < $discount['start_date']) {
|
||||
$this->discount_message = 'Discount code not yet active';
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($current_date > $discount['end_date']) {
|
||||
$this->discount_message = 'Discount code expired';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert string of IDs to arrays
|
||||
$discount_product_ids = !empty($discount['product_ids']) ?
|
||||
array_map('trim', explode(',', $discount['product_ids'])) : [];
|
||||
$discount_category_ids = !empty($discount['category_ids']) ?
|
||||
array_map('trim', explode(',', $discount['category_ids'])) : [];
|
||||
|
||||
$discounttotal = 0;
|
||||
$eligible_products_found = false;
|
||||
$total_eligible_price = 0;
|
||||
|
||||
// Calculate total eligible price
|
||||
foreach ($this->products as $product) {
|
||||
if ($this->isProductEligibleForDiscount($product, $discount_product_ids, $discount_category_ids)) {
|
||||
$eligible_products_found = true;
|
||||
$product_price = floatval(str_replace(',', '.', $product['options_price'])) * $product['quantity'];
|
||||
$total_eligible_price += $product_price;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate discount if eligible products found
|
||||
if ($eligible_products_found) {
|
||||
if ($discount['discount_type'] == 1) {
|
||||
// Percentage discount
|
||||
$discounttotal = $total_eligible_price * ($discount['discount_value'] / 100);
|
||||
} else {
|
||||
// Fixed amount discount
|
||||
$discounttotal = min($discount['discount_value'], $total_eligible_price);
|
||||
}
|
||||
|
||||
$discount_type = $discount['discount_type'] == 1 ?
|
||||
$discount['discount_value'] . '% discount' :
|
||||
'€' . number_format($discount['discount_value'], 2) . ' discount';
|
||||
$this->discount_message = "Discount applied successfully: " . $discount_type;
|
||||
} else {
|
||||
$this->discount_message = 'No eligible products for this discount code';
|
||||
$discounttotal = 0;
|
||||
}
|
||||
|
||||
return $discounttotal;
|
||||
}
|
||||
|
||||
private function isProductEligibleForDiscount($product, $discount_product_ids, $discount_category_ids) {
|
||||
// If no specific products or categories are set, discount applies to all products
|
||||
if (empty($discount_product_ids) && empty($discount_category_ids)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$product_match = false;
|
||||
$category_match = false;
|
||||
|
||||
// Check product ID match
|
||||
if (!empty($discount_product_ids)) {
|
||||
$product_match = in_array($product['id'], $discount_product_ids);
|
||||
|
||||
// If only product IDs are specified (no categories), return the product match result
|
||||
if (empty($discount_category_ids)) {
|
||||
return $product_match;
|
||||
}
|
||||
} else {
|
||||
// If no product IDs specified, set product_match to true
|
||||
$product_match = true;
|
||||
}
|
||||
|
||||
// Check category match
|
||||
if (!empty($discount_category_ids)) {
|
||||
if (isset($product['meta']['category_ids'])) {
|
||||
$product_categories = is_array($product['meta']['category_ids']) ?
|
||||
$product['meta']['category_ids'] :
|
||||
array_map('trim', explode(',', $product['meta']['category_ids']));
|
||||
|
||||
$category_match = !empty(array_intersect($product_categories, $discount_category_ids));
|
||||
} else {
|
||||
$category_match = false;
|
||||
}
|
||||
|
||||
// If only categories are specified (no products), return the category match result
|
||||
if (empty($discount_product_ids)) {
|
||||
return $category_match;
|
||||
}
|
||||
} else {
|
||||
// If no categories specified, set category_match to true
|
||||
$category_match = true;
|
||||
}
|
||||
|
||||
// If both product IDs and categories are specified, both must match
|
||||
return $product_match && $category_match;
|
||||
}
|
||||
|
||||
private function calculateShippingTotal($subtotal, $weighttotal,$selected_shipping_method) {
|
||||
|
||||
|
||||
//USER PROVIDED SHIPMENT METHOD
|
||||
$sql = "SELECT price FROM shipping WHERE ID = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$this->selected_shipping_method]);
|
||||
|
||||
$shipping = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $shipping ? floatval($shipping['price']) : 0;
|
||||
}
|
||||
|
||||
private function calculateTaxTotal($amount_to_tax) {
|
||||
$sql = "SELECT rate FROM taxes WHERE country = ?";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([$this->selected_country]);
|
||||
$tax = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
return $tax ? ($amount_to_tax * ($tax['rate'] / 100)) : 0;
|
||||
}
|
||||
|
||||
private function calculateFinalTotal($subtotal, $shippingtotal, $discounttotal, $taxtotal) {
|
||||
$base = $subtotal - $discounttotal + $shippingtotal;
|
||||
|
||||
if ($this->business_type === 'b2c') {
|
||||
// Tax is included in final price
|
||||
return $base;
|
||||
} else {
|
||||
// Tax is added on top for B2B
|
||||
return $base + $taxtotal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateCheckoutData($post_content) {
|
||||
$errors = [];
|
||||
|
||||
// Required fields for checkout input
|
||||
$required_checkout_fields = [
|
||||
'cart' => 'Products',
|
||||
'checkout_input.selected_country' => 'Country',
|
||||
'checkout_input.selected_shipment_method' => 'Shipping method',
|
||||
'checkout_input.business_type' => 'Business type',
|
||||
'checkout_input.payment_method' => 'Payment method'
|
||||
];
|
||||
|
||||
// Required fields for customer details
|
||||
$required_customer_fields = [
|
||||
'customer_details.email' => 'Email',
|
||||
'customer_details.first_name' => 'First name',
|
||||
'customer_details.last_name' => 'Last name',
|
||||
'customer_details.address_street' => 'Street address',
|
||||
'customer_details.address_city' => 'City',
|
||||
'customer_details.address_zip' => 'ZIP code',
|
||||
'customer_details.address_country' => 'Country',
|
||||
'customer_details.address_phone' => 'Phone number'
|
||||
];
|
||||
|
||||
// Validate checkout input fields
|
||||
foreach ($required_checkout_fields as $field => $label) {
|
||||
$keys = explode('.', $field);
|
||||
if (count($keys) === 1) {
|
||||
if (!isset($post_content[$keys[0]]) || empty($post_content[$keys[0]])) {
|
||||
$errors[] = "$label is required";
|
||||
}
|
||||
} else {
|
||||
if (!isset($post_content[$keys[0]][$keys[1]]) || empty($post_content[$keys[0]][$keys[1]])) {
|
||||
$errors[] = "$label is required";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate customer details fields
|
||||
foreach ($required_customer_fields as $field => $label) {
|
||||
$keys = explode('.', $field);
|
||||
if (!isset($post_content[$keys[0]][$keys[1]]) || empty($post_content[$keys[0]][$keys[1]])) {
|
||||
$errors[] = "$label is required";
|
||||
}
|
||||
}
|
||||
|
||||
// Additional validation for email format
|
||||
if (isset($post_content['customer_details']['email']) && !empty($post_content['customer_details']['email'])) {
|
||||
if (!filter_var($post_content['customer_details']['email'], FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[] = "Invalid email format";
|
||||
}
|
||||
}
|
||||
|
||||
// Additional validation for phone number (basic format check)
|
||||
if (isset($post_content['customer_details']['address_phone']) && !empty($post_content['customer_details']['address_phone'])) {
|
||||
if (!preg_match("/^[0-9\-\(\)\/\+\s]*$/", $post_content['customer_details']['address_phone'])) {
|
||||
$errors[] = "Invalid phone number format";
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
function validateTransactionData($post_content) {
|
||||
$errors = [];
|
||||
|
||||
// Required fields for customer details
|
||||
$required_fields = [
|
||||
'customer_details.email' => 'Email',
|
||||
'customer_details.first_name' => 'First name',
|
||||
'customer_details.last_name' => 'Last name',
|
||||
'customer_details.address_street' => 'Street address',
|
||||
'customer_details.address_city' => 'City',
|
||||
'customer_details.address_zip' => 'ZIP code',
|
||||
'customer_details.address_country' => 'Country',
|
||||
'total.payment_amount' => 'Payment_amount',
|
||||
|
||||
];
|
||||
|
||||
// Validate customer details fields
|
||||
foreach ($required_fields as $field => $label) {
|
||||
$keys = explode('.', $field);
|
||||
if (!isset($post_content[$keys[0]][$keys[1]]) || empty($post_content[$keys[0]][$keys[1]])) {
|
||||
$errors[] = "$label is required";
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
function getCountryNamesByIds($countries, $idString) {
|
||||
// Create a lookup array where ID is the key and country name is the value
|
||||
$countryMap = array_column($countries, 'country', 'id');
|
||||
|
||||
// Convert comma-separated string to array
|
||||
$ids = explode(',', $idString);
|
||||
|
||||
// Get country names for each ID
|
||||
$countryNames = [];
|
||||
foreach ($ids as $id) {
|
||||
$id = trim($id);
|
||||
if (isset($countryMap[$id])) {
|
||||
$countryNames[] = $countryMap[$id];
|
||||
}
|
||||
}
|
||||
|
||||
return $countryNames;
|
||||
}
|
||||
|
||||
function transformOrderData(array $orderData): array {
|
||||
// Initialize the result array with the first row's common data
|
||||
$firstRow = $orderData[0];
|
||||
|
||||
$result = [
|
||||
'customer' => [
|
||||
'email' => $firstRow['payer_email'],
|
||||
'name' => $firstRow['first_name'] . ' ' . $firstRow['last_name'],
|
||||
'street' => $firstRow['address_street'],
|
||||
'zip' => $firstRow['address_zip'],
|
||||
'city' => $firstRow['address_city'],
|
||||
'country' => $firstRow['address_country']
|
||||
|
||||
],
|
||||
'products' => [],
|
||||
'invoice' => [
|
||||
'id' => $firstRow['invoice'],
|
||||
'created' => $firstRow['invoice_created'],
|
||||
'payment_status' => $firstRow['payment_status']
|
||||
],
|
||||
'pricing' => [
|
||||
'subtotal' => 0,
|
||||
'shipping_total' => $firstRow['shipping_amount'],
|
||||
'tax_total' => $firstRow['tax_amount'],
|
||||
'discount_total' => $firstRow['discount_amount'],
|
||||
'payment_amount' => $firstRow['payment_amount']
|
||||
]
|
||||
];
|
||||
|
||||
// Process products from all rows
|
||||
foreach ($orderData as $row) {
|
||||
// Decode JSON string for item options
|
||||
$itemOptions = json_decode($row['item_options'], true) ?? [];
|
||||
|
||||
// Calculate line total
|
||||
$lineTotal = floatval($row['item_price']) * intval($row['item_quantity']);
|
||||
|
||||
// Add to subtotal
|
||||
$result['pricing']['subtotal'] += $lineTotal;
|
||||
|
||||
// Add product information
|
||||
$result['products'][] = [
|
||||
'item_id' => $row['item_id'],
|
||||
'product_name' => $row['productname'],
|
||||
'options' => $itemOptions,
|
||||
'quantity' => $row['item_quantity'],
|
||||
'price' => $row['item_price'],
|
||||
'line_total' => number_format($lineTotal, 2, '.', '')
|
||||
];
|
||||
}
|
||||
|
||||
// Format monetary values
|
||||
$result['pricing']['subtotal'] = number_format($result['pricing']['subtotal'], 2, '.', '');
|
||||
$result['pricing']['shipping_total'] = number_format(floatval($result['pricing']['shipping_total']), 2, '.', '');
|
||||
$result['pricing']['tax_total'] = number_format(floatval($result['pricing']['tax_total']), 2, '.', '');
|
||||
$result['pricing']['discount_total'] = number_format(floatval($result['pricing']['discount_total']), 2, '.', '');
|
||||
$result['pricing']['payment_amount'] = number_format(floatval($result['pricing']['payment_amount']), 2, '.', '');
|
||||
|
||||
return $result;
|
||||
}
|
||||
Reference in New Issue
Block a user