*/ class MEC_book extends MEC_base { /** * @var array */ public $settings; /** * @var MEC_main */ public $main; /** * Post Type Slug * @var string */ public $PT; /** * @var MEC_partial */ public $partial_payment; /** * Constructor method * @author Webnus */ public function __construct() { // Import MEC Main $this->main = $this->getMain(); // MEC Book Post Type Name $this->PT = $this->main->get_book_post_type(); // MEC Settings $this->settings = $this->main->get_settings(); // MEC Partial Payment $this->partial_payment = $this->getPartialPayment(); } /** * Get invoice (Ticket price + Fees) based on tickets * @param array $tickets * @param int $event_id * @param array $event_tickets * @param array $variations * @param array $timestamps * @param boolean $apply_fees * @return array * @author Webnus */ public function get_price_details($tickets, $event_id, $event_tickets, $variations = [], $timestamps = [], $apply_fees = true) { $total_tickets_amount = 0; $total_tickets_count = 0; $total_variations_amount = 0; $total_fee_amount = 0; $variation_details = []; $fee_details = []; $details = []; foreach ($timestamps as $timestamp) { $date_tickets_amount = 0; $date_tickets_count = 0; $date_variations_amount = 0; $date_fee_amount = 0; $timestamp_ex = explode(':', $timestamp); $timestamp = $timestamp_ex[0]; foreach ($tickets as $ticket_id => $count) { if (!$count) continue; if (!isset($event_tickets[$ticket_id])) continue; $date_tickets_count += $count; $t_price = isset($event_tickets[$ticket_id]['price']) ? $this->get_ticket_price($event_tickets[$ticket_id], current_time('Y-m-d'), $event_id, $timestamp) : 0; if (!is_numeric($t_price)) $t_price = 0; $date_tickets_amount = $date_tickets_amount + ($t_price * $count); // Variations module is enabled and some variations bought if (isset($this->settings['ticket_variations_status']) and $this->settings['ticket_variations_status'] and is_array($variations) and count($variations)) { $ticket_variations = $this->main->ticket_variations($event_id, $ticket_id); foreach ($ticket_variations as $key => $ticket_variation) { if (!is_numeric($key)) continue; if (!isset($ticket_variation['title']) or !trim($ticket_variation['title'])) continue; $booked_variations = isset($variations[$ticket_id]) && is_array($variations[$ticket_id]) ? $variations[$ticket_id] : []; $variation_count = $booked_variations[$key] ?? 0; if (!$variation_count or $variation_count < 0) continue; $v_price = (isset($ticket_variation['price']) and trim($ticket_variation['price']) != '') ? $ticket_variation['price'] : 0; $variation_amount = $v_price * $variation_count; $variation_title = $ticket_variation['title'] . ' (' . esc_html($variation_count) . ')'; // Add To Total $date_variations_amount += $variation_amount; // Price Details if (!isset($variation_details[$key])) $variation_details[$key] = ['amount' => $variation_amount, 'description' => __($variation_title, 'modern-events-calendar-lite'), 'type' => 'variation', 'count' => $variation_count]; else { $variation_details[$key]['amount'] += $variation_amount; $new_count = ((int) $variation_details[$key]['count'] + $variation_count); $variation_details[$key]['count'] = $new_count; $variation_details[$key]['description'] = esc_html__($ticket_variation['title'] . ' (' . $new_count . ')', 'modern-events-calendar-lite'); } } } } $total_tickets_amount += $date_tickets_amount; $total_variations_amount += $date_variations_amount; $total_tickets_count += $date_tickets_count; // Fees module is enabled if ($apply_fees and isset($this->settings['taxes_fees_status']) and $this->settings['taxes_fees_status']) { $fees = $this->get_fees($event_id); foreach ($fees as $key => $fee) { $fee_amount = 0; if (!is_numeric($key)) continue; if ($fee['type'] == 'amount_per_date') $fee_amount += $fee['amount']; else continue; // Add to Total $date_fee_amount += $fee_amount; // Price Details if (!isset($fee_details[$key])) $fee_details[$key] = ['amount' => $fee_amount, 'description' => __($fee['title'], 'modern-events-calendar-lite'), 'type' => 'fee', 'fee_type' => $fee['type'], 'fee_amount' => $fee['amount']]; else $fee_details[$key]['amount'] += $fee_amount; } } $total_fee_amount += $date_fee_amount; } $tax_inclusion_type = isset($this->settings['tax_inclusion']) && trim($this->settings['tax_inclusion']) ? $this->settings['tax_inclusion'] : 'excluded'; $total_ticket_percent_fee_amount = 0; // Fees module is enabled if ($apply_fees && isset($this->settings['taxes_fees_status']) && $this->settings['taxes_fees_status']) { $fees = $this->get_fees($event_id); $rest_fee_amount = 0; // Fee Per Booking foreach ($fees as $key => $fee) { if (!is_numeric($key)) continue; $fee_amount_config = isset($fee['amount']) ? (float) $fee['amount'] : 0; $fee_amount = 0; if ($fee['type'] == 'percent') { if ($tax_inclusion_type === 'included') { $fee_amount += ($total_tickets_amount + $total_variations_amount) - (($total_tickets_amount + $total_variations_amount) / (1 + ($fee_amount_config / 100))); // Add to Ticket Percent Fee $total_ticket_percent_fee_amount += $total_tickets_amount - ($total_tickets_amount / (1 + ($fee_amount_config / 100))); // Variation Details if (count($variation_details)) { $new_variation_details = []; foreach ($variation_details as $variation_detail) { $variation_fee_amount = $variation_detail['amount'] - ($variation_detail['amount'] / (1 + ($fee_amount_config / 100))); $variation_detail['amount'] = $variation_detail['amount'] - $variation_fee_amount; $total_variations_amount = $total_variations_amount - $variation_fee_amount; $new_variation_details[] = $variation_detail; } $variation_details = $new_variation_details; } } else $fee_amount += (($total_tickets_amount + $total_variations_amount) * $fee_amount_config) / 100; } else if ($fee['type'] == 'amount') $fee_amount += ($total_tickets_count * $fee_amount_config); else if ($fee['type'] == 'amount_per_booking') $fee_amount += $fee_amount_config; else continue; // Add to Total $rest_fee_amount += $fee_amount; // Price Details if (!isset($fee_details[$key])) $fee_details[$key] = ['amount' => $fee_amount, 'description' => __($fee['title'], 'modern-events-calendar-lite'), 'type' => 'fee', 'fee_type' => $fee['type'], 'fee_amount' => $fee_amount_config]; else $fee_details[$key]['amount'] += $fee_amount; } $total_fee_amount += $rest_fee_amount; } if ($tax_inclusion_type === 'included') { $total_tickets_amount = $total_tickets_amount - $total_ticket_percent_fee_amount; } // Ticket Details $details[] = ['amount' => $total_tickets_amount, 'description' => esc_html__('Subtotal', 'modern-events-calendar-lite'), 'type' => 'tickets']; // Variation Details foreach ($variation_details as $variation_detail) $details[] = $variation_detail; // Fee Details foreach ($fee_details as $fee_detail) $details[] = $fee_detail; $total = $total_tickets_amount + $total_fee_amount + $total_variations_amount; $payable = $total; // Calculate Payable if ($this->partial_payment->is_enabled()) $payable = $this->partial_payment->calculate($total, $event_id); return [ 'total' => $total, 'payable' => $payable, 'details' => $details, ]; } /** * Get fees of a certain event * @param int $event_id * @return array * @author Webnus */ public function get_fees($event_id) { $fees_global_inheritance = get_post_meta($event_id, 'mec_fees_global_inheritance', true); if (is_string($fees_global_inheritance) and trim($fees_global_inheritance) == '') $fees_global_inheritance = 1; // Event fees $fees = get_post_meta($event_id, 'mec_fees', true); // Get fees from global options if ($fees_global_inheritance) $fees = $this->settings['fees'] ?? []; // Clean if (isset($fees[':i:'])) unset($fees[':i:']); return $fees; } /** * Save a temporary booking * @param array $data * @return int * @author Webnus */ public function temporary($data = []) { $transaction = new Transaction(0, $data); return $transaction->update_data(); } /** * Generate a transaction id for bookings * @return string * @author Webnus */ public function get_transaction_id()//TODO: remove { return Transaction::generate_transaction_id(); } /** * Get transaction data * @param string $transaction_id * @return array * @author Webnus */ public function get_transaction($transaction_id) { return get_option($transaction_id, []); } /** * @param $transaction_id * @return MEC_transaction */ public function get_TO($transaction_id) { MEC::import('app.libraries.transaction'); return (new MEC_transaction($transaction_id)); } /** * Update a transaction * @param string $transaction_id * @param array $data * @author Webnus */ public function update_transaction($transaction_id, $data) { update_option($transaction_id, $data, false); if ($transaction_id) { $transactionObject = new Transaction($transaction_id); $transactionObject->reset_cache_tickets_details(); } } /** * Add a booking * @param array $values * @param string $transaction_id * @param int $ticket_ids * @return int|boolean * @author Webnus */ public function add($values, $transaction_id, $ticket_ids) { // Check Transaction State $db = $this->main->getDB(); $db_transaction_ids = $db->select("SELECT `post_id` FROM `#__postmeta` WHERE `meta_key` = 'mec_transaction_id' AND `meta_value` = '$transaction_id'"); foreach ($db_transaction_ids as $db_transaction_id) { $book_status = get_post_status($db_transaction_id->post_id); if (trim($book_status) == 'trash') unset($db_transaction_ids[$db_transaction_id->post_id]); } if (count($db_transaction_ids)) return false; // Transaction Data $transaction = $this->get_transaction($transaction_id); $event_id = $transaction['event_id']; $attention_date = $transaction['date'] ?? ''; $attention_times = explode(':', $attention_date); // Default values if (!isset($values['post_date'])) $values['post_date'] = date('Y-m-d H:i:s', trim($attention_times[0])); if (!isset($values['post_status'])) $values['post_status'] = 'publish'; $book_id = wp_insert_post($values); // Delete Cache delete_transient('all_sold_tickets_' . $event_id); // Update transaction id after insert book for prevent repeat reservation books. update_post_meta($book_id, 'mec_transaction_id', $transaction_id); // Payment Gateway if (isset($values['mec_gateway']) and isset($values['mec_gateway_label'])) { update_post_meta($book_id, 'mec_gateway', $values['mec_gateway']); update_post_meta($book_id, 'mec_gateway_label', $values['mec_gateway_label']); } $transaction['booking_id'] = $book_id; $transaction['invoice_key'] = md5(time() . mt_rand(10000, 99999)); if (isset($values['mec_gateway'])) { $transaction['gateway'] = $values['mec_gateway']; } $this->update_transaction($transaction_id, $transaction); // Publish it wp_publish_post($book_id); // Assign User if (isset($values['post_author']) and $values['post_author']) { $u = $this->getUser(); $u->assign($book_id, $values['post_author']); } update_post_meta($book_id, 'mec_verified', 0); update_post_meta($book_id, 'mec_verification_key', md5(time() . mt_rand(10000, 99999))); update_post_meta($book_id, 'mec_cancellation_key', md5(time() . mt_rand(10000, 99999))); update_post_meta($book_id, 'mec_confirmed', 0); update_post_meta($book_id, 'mec_event_id', $event_id); update_post_meta($book_id, 'mec_date', $transaction['date']); update_post_meta($book_id, 'mec_ticket_id', $ticket_ids); update_post_meta($book_id, 'mec_booking_time', current_time('Y-m-d H:i:s')); update_post_meta($book_id, 'mec_locale', $transaction['locale'] ?? ''); // Multiple Dates if (isset($transaction['all_dates']) and is_array($transaction['all_dates'])) update_post_meta($book_id, 'mec_all_dates', $transaction['all_dates']); if (isset($transaction['other_dates']) and is_array($transaction['other_dates'])) update_post_meta($book_id, 'mec_other_dates', $transaction['other_dates']); update_post_meta($book_id, 'mec_attention_time', $attention_date); update_post_meta($book_id, 'mec_attention_time_start', $attention_times[0]); update_post_meta($book_id, 'mec_attention_time_end', $attention_times[1]); // For Badge Bubble Notification Alert Count From It. update_post_meta($book_id, 'mec_book_date_submit', date('YmdHis', current_time('timestamp'))); $location_id = $this->main->get_master_location_id($event_id, $attention_times[0]); if (!empty($location_id)) update_post_meta($book_id, 'mec_booking_location', $location_id); // Event Tickets $tickets = get_post_meta($event_id, 'mec_tickets', true); if (isset($values['mec_attendees'])) { foreach ($values['mec_attendees'] as $k => $mec_attendee) { if (!is_numeric($k)) continue; $values['mec_attendees'][$k]['buyerip'] = $this->main->get_client_ip(); $ticket_id = $mec_attendee['id'] ?? 0; $ticket_price = (isset($tickets[$ticket_id]) ? $tickets[$ticket_id]['price'] : 0); update_post_meta($book_id, 'mec_ticket_price_' . $ticket_id, $ticket_price); } update_post_meta($book_id, 'mec_attendees', $values['mec_attendees']); } $price = $transaction['price'] ?? ($transaction['total'] ?? 0); update_post_meta($book_id, 'mec_price', $price); $payable = $transaction['payable'] ?? $price; update_post_meta($book_id, 'mec_payable', $payable); // A coupon applied if (isset($transaction['coupon'])) { $coupon_id = $this->coupon_get_id($transaction['coupon']); if ($coupon_id) { wp_set_object_terms($book_id, $coupon_id, 'mec_coupon'); update_post_meta($book_id, 'mec_coupon_code', $transaction['coupon']); } } // Local Data update_post_meta($book_id, 'mec_local_timezone', $this->main->get_timezone_by_ip()); // Booking Record $this->getBookingRecord()->insert($book_id); // Fires after adding a new booking to send notifications etc do_action('mec_booking_added', $book_id); list($auto_verify_free, $auto_verify_paid) = $this->get_auto_verification_status($event_id, $book_id); list($auto_confirm_free, $auto_confirm_paid) = $this->get_auto_confirmation_status($event_id, $book_id); $verified = false; // Auto verification for free bookings is enabled if ($price <= 0 and $auto_verify_free) { $this->verify($book_id); $verified = true; } // Auto verification for paid bookings is enabled if ($price > 0 and $auto_verify_paid) { $this->verify($book_id); $verified = true; } // Auto confirmation for free bookings is enabled if ($price <= 0 and $auto_confirm_free and $verified) { $this->confirm($book_id, 'auto'); } // Auto confirmation for paid bookings is enabled if ($price > 0 and $auto_confirm_paid and $verified) { // Work or don't work auto confirmation when pay through pay locally payment. $gateways_settings = get_option('mec_options', []); $gateway_key = null; $can_auto_confirm = true; $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : false; switch ($action) { case 'mec_do_transaction_pay_locally': case 'mec_cart_do_transaction_pay_locally': $gateway_key = 1; break; case 'mec_do_transaction_bank_transfer': case 'mec_cart_do_transaction_bank_transfer': $gateway_key = 8; break; } if (!is_null($gateway_key) && isset($gateways_settings['gateways'][$gateway_key]['disable_auto_confirmation']) && trim($gateways_settings['gateways'][$gateway_key]['disable_auto_confirmation'])) { $can_auto_confirm = false; } if ($can_auto_confirm) $this->confirm($book_id, 'auto'); } // Latest Booking Date & Time update_option('mec_latest_booking_datetime', current_time('YmdHis'), false); return $book_id; } /** * Confirm a booking * @param int $book_id * @param string $mode * @return boolean * @author Webnus */ public function confirm($book_id, $mode = 'manually') { update_post_meta($book_id, 'mec_confirmed', 1); // Fires after confirming a booking to send notifications etc. do_action('mec_booking_confirmed', $book_id, $mode); $event_id = get_post_meta($book_id, 'mec_event_id', true); $date = get_post_meta($book_id, 'mec_date', true); $timestamps = explode(':', $date); // Booking Records $this->getBookingRecord()->confirm($book_id); // Disable Cache $cache = $this->getCache(); $cache->disable(); // Event is sold-out so fire the event $soldout = $this->main->is_sold($event_id, $timestamps[0]); if ($soldout) do_action('mec_event_soldout', $event_id, $book_id); // Enable Cache $cache->enable(); return true; } /** * Reject a booking * @param int $book_id * @return boolean * @author Webnus */ public function reject($book_id) { update_post_meta($book_id, 'mec_confirmed', -1); // Booking Records $this->getBookingRecord()->reject($book_id); // Fires after rejecting a booking to send notifications etc. do_action('mec_booking_rejected', $book_id); return true; } /** * Make a booking pending * @param int $book_id * @return boolean * @author Webnus */ public function pending($book_id) { update_post_meta($book_id, 'mec_confirmed', 0); // Booking Records $this->getBookingRecord()->pending($book_id); // Fires after pending a booking to send notifications etc. do_action('mec_booking_pended', $book_id); return true; } /** * Verify a booking * @param int $book_id * @return boolean * @author Webnus */ public function verify($book_id) { update_post_meta($book_id, 'mec_verified', 1); // Booking Records $this->getBookingRecord()->verify($book_id); // Fires after verifying a booking to send notifications etc. do_action('mec_booking_verified', $book_id); return true; } /** * Cancel a booking * @param int $book_id * @param ?bool $refund * @return boolean * @author Webnus */ public function cancel($book_id, $refund = null) { $verified = -1; $verified = apply_filters('mec_verified_value', $verified, $book_id); if ($verified != -1) return true; update_post_meta($book_id, 'mec_verified', -1); update_post_meta($book_id, 'mec_cancelled_date', date('Y-m-d H:i:s', current_time('timestamp'))); if (is_null($refund)) $refund = (isset($this->settings['booking_auto_refund']) and $this->settings['booking_auto_refund']); $gateway = get_post_meta($book_id, 'mec_gateway', true); if ($refund and $gateway == 'MEC_gateway_stripe') { $stripe = new MEC_gateway_stripe(); $stripe->refund($book_id); // Actions do_action('mec_booking_refunded', $book_id); } // Booking Records $this->getBookingRecord()->cancel($book_id); // Fires after canceling a booking to send notifications etc. do_action('mec_booking_canceled', $book_id); return true; } public function move($booking_id, $from_start, $to_start) { // Database $db = $this->getDB(); // Event $event_id = get_post_meta($booking_id, 'mec_event_id', true); $offset = (int) $this->main->get_gmt_offset_seconds($event_id, $to_start); $to_start_datetime = wp_date('Y-m-d H:i:s', $to_start - $offset); $to_end = (int) $db->select("SELECT `tend` FROM `#__mec_dates` WHERE `post_id`='" . esc_sql($event_id) . "' AND `tstart`='" . esc_sql($to_start) . "'", 'loadResult'); $from_end = (int) $db->select("SELECT `tend` FROM `#__mec_dates` WHERE `post_id`='" . esc_sql($event_id) . "' AND `tstart`='" . esc_sql($from_start) . "'", 'loadResult'); // Update Booking Post $db->q("UPDATE `#__posts` SET `post_date`='" . esc_sql($to_start_datetime) . "', `post_date_gmt`='" . get_gmt_from_date($to_start_datetime) . "' WHERE `ID`='" . esc_sql($booking_id) . "'"); // Update Booking Meta foreach ([ 'mec_date' => $to_start . ':' . $to_end, 'mec_attention_time' => $to_start . ':' . $to_end, 'mec_attention_time_start' => $to_start, 'mec_attention_time_end' => $to_end, ] as $key => $value) { // Keep Previous Data $prev = get_post_meta($booking_id, $key, true); update_post_meta($booking_id, $key . '_prev', $prev); update_post_meta($booking_id, $key, $value); } // All Dates $all_dates = get_post_meta($booking_id, 'mec_all_dates', true); if (!is_array($all_dates)) $all_dates = []; $k = array_search($from_start . ':' . $from_end, $all_dates); if ($k !== false) { $all_dates[$k] = $to_start . ':' . $to_end; } update_post_meta($booking_id, 'mec_all_dates', $all_dates); // Update Transaction $transaction_id = get_post_meta($booking_id, 'mec_transaction_id', true); $transaction = $this->get_transaction($transaction_id); if (isset($transaction['timestamps']) and is_array($transaction['timestamps'])) { $k = array_search($transaction['date'], $transaction['timestamps']); if ($k !== false) $transaction['timestamps'][$k] = $to_start . ':' . $to_end; } $transaction['date'] = $to_start . ':' . $to_end; $this->update_transaction($transaction_id, $transaction); // Update booking Record $db->q("UPDATE `#__mec_bookings` SET `timestamp`='" . esc_sql($to_start) . "', `date`='" . esc_sql($to_start_datetime) . "' WHERE `booking_id`='" . esc_sql($booking_id) . "' AND `event_id`='" . esc_sql($event_id) . "' AND `timestamp`='" . esc_sql($from_start) . "'"); // Delete Cache clean_post_cache($booking_id); // Fires after moving a booking to send notifications etc. do_action('mec_booking_moved', $booking_id, $to_start); } public function move_notify($booking_id, $moved_to) { // Fires after moving a booking to send notifications etc. do_action('mec_booking_moved_notify', $booking_id, $moved_to); } /** * Waiting a booking * @param int $book_id * @return boolean * @author Webnus */ public function waiting($book_id) { update_post_meta($book_id, 'mec_verified', 0); // Booking Records $this->getBookingRecord()->waiting($book_id); // Fires after waiting a booking to send notifications etc. do_action('mec_booking_waiting', $book_id); return true; } /** * Returns ticket availabilities of an event for a certain date * @param int $event_id * @param int $timestamp * @param string $mode * @return array|integer * @author Webnus */ public function get_tickets_availability($event_id, $timestamp, $mode = 'availability') { $ex = explode(':', $timestamp); $timestamp = $ex[0]; if (!is_numeric($timestamp)) $timestamp = strtotime($timestamp); $availability = []; $tickets = get_post_meta($event_id, 'mec_tickets', true); // No Ticket Found! if (!is_array($tickets) or !count($tickets)) { if ($mode == 'reservation') return 0; else return $availability; } $booking_options = get_post_meta($event_id, 'mec_booking', true); if (!is_array($booking_options)) $booking_options = []; $total_bookings_limit = (isset($booking_options['bookings_limit']) and trim($booking_options['bookings_limit']) !== '') ? $booking_options['bookings_limit'] : 100; $bookings_limit_unlimited = $booking_options['bookings_limit_unlimited'] ?? 0; $book_all_occurrences = isset($booking_options['bookings_all_occurrences']) ? (int) $booking_options['bookings_all_occurrences'] : 0; if ($bookings_limit_unlimited == '1') $total_bookings_limit = '-1'; // Get Per Occurrence $total_bookings_limit = MEC_feature_occurrences::param($event_id, $timestamp, 'bookings_limit', $total_bookings_limit); // Total Booking Limit $total_bookings_limit_original = $total_bookings_limit; // Ticket Selling Stop $event_date = date('Y-m-d h:i a', $timestamp); if (!$book_all_occurrences && trim($timestamp)) $date_query = " AND `timestamp`=" . $timestamp; else $date_query = ""; // Database $db = $this->getDB(); // Cache $cache = $this->getCache(); $booked = 0; foreach ($tickets as $ticket_id => $ticket) { $limit = (isset($ticket['limit']) and trim($ticket['limit']) != '') ? $ticket['limit'] : -1; $ticket_seats = (isset($ticket['seats']) and is_numeric($ticket['seats'])) ? (int) $ticket['seats'] : 1; $ticket_seats = max(1, $ticket_seats); $records = $cache->rememberOnce($event_id . ':' . $ticket_id . ':' . $timestamp, function () use ($db, $event_id, $ticket_id, $date_query) { return $db->select("SELECT `id`,`ticket_ids` FROM `#__mec_bookings` WHERE `event_id`=" . $event_id . " AND `ticket_ids` LIKE '%," . $ticket_id . ",%' AND `status` IN ('publish', 'pending', 'draft', 'future', 'private') AND `confirmed`!='-1' AND `verified`!='-1'" . $date_query); }); $bookings = 0; $booked_seats = 0; foreach ($records as $record) { $ticket_ids = explode(',', trim($record->ticket_ids, ', ')); $ticket_ids_count = array_count_values($ticket_ids); if (isset($ticket_ids_count[$ticket_id]) and is_numeric($ticket_ids_count[$ticket_id])) { $bookings += $ticket_ids_count[$ticket_id]; $booked_seats += ($ticket_ids_count[$ticket_id] * $ticket_seats); } } if ($total_bookings_limit > 0) $total_bookings_limit = max(($total_bookings_limit - $booked_seats), 0); $booked += $bookings; $availability_date_start = $ticket['availability_start'] ?? ''; $availability_date_end = $ticket['availability_end'] ?? ''; if (trim($availability_date_start) && trim($availability_date_end)) { if (!(strtotime($availability_date_start) <= $timestamp && strtotime($availability_date_end) >= $timestamp)) { $availability[$ticket_id] = 0; $availability['not_available_' . $ticket_id] = true; $availability['seats_' . $ticket_id] = $ticket_seats; continue; } } // Ticket Selling Stop $stop_selling_value = isset($ticket['stop_selling_value']) ? trim($ticket['stop_selling_value']) : 0; $stop_selling_type = isset($ticket['stop_selling_type']) ? trim($ticket['stop_selling_type']) : 'day'; if ($stop_selling_value > 0 and $this->main->check_date_time_validation('Y-m-d h:i a', strtolower($event_date))) { if (strtotime("-" . $stop_selling_value . $stop_selling_type, strtotime($event_date)) <= current_time('timestamp')) { $availability[$ticket_id] = 0; $availability['stop_selling_' . $ticket_id] = true; $availability['seats_' . $ticket_id] = $ticket_seats; continue; } } // Few Seats if ($ticket_seats > 1 and $total_bookings_limit > 0 and $total_bookings_limit < $ticket_seats) { $availability[$ticket_id] = 0; $availability['seats_' . $ticket_id] = $ticket_seats; continue; } // Ticket is Unlimited if ((isset($ticket['unlimited']) and $ticket['unlimited'] == 1) or $limit == -1) { $availability[$ticket_id] = $total_bookings_limit > 0 ? floor($total_bookings_limit / $ticket_seats) : -1; $availability['seats_' . $ticket_id] = $ticket_seats; continue; } if ($limit == '') $limit = 0; $limit = (int) $limit; $booked_seats = (int) $booked_seats; // Unlimited Total if ($total_bookings_limit == '-1') $ticket_availability = $limit - $booked_seats; else $ticket_availability = min(($limit - $booked_seats), max($total_bookings_limit, 0)); $availability[$ticket_id] = $ticket_availability >= 0 ? floor($ticket_availability / $ticket_seats) : 0; $availability['seats_' . $ticket_id] = $ticket_seats; } // For the time being set reservation parameter if ($mode == 'reservation') return $booked; // Set Total Booking Limit $availability['total'] = $total_bookings_limit; // Do not send higher limit for tickets compared to total limit if ($total_bookings_limit != '-1' and $total_bookings_limit > 0) { $new_availability = []; foreach ($availability as $ticket_id => $limit) { if (is_numeric($ticket_id)) { $ticket_seats = $availability['seats_' . $ticket_id]; $ticket_seats = max(1, $ticket_seats); $seats = $limit * $ticket_seats; $new_availability[$ticket_id] = floor(min($seats, $total_bookings_limit) / $ticket_seats); } else $new_availability[$ticket_id] = $limit; } return apply_filters('mec_get_tickets_availability', $new_availability, $event_id, $timestamp, $mode, $tickets); } // Total Booking Limit Reached if ($total_bookings_limit_original != -1 and $booked >= $total_bookings_limit_original) { $new_availability = []; foreach ($availability as $ticket_id => $limit) { if (is_numeric($ticket_id)) $new_availability[$ticket_id] = 0; else $new_availability[$ticket_id] = $limit; } return apply_filters('mec_get_tickets_availability', $new_availability, $event_id, $timestamp, $mode, $tickets); } return apply_filters('mec_get_tickets_availability', $availability, $event_id, $timestamp, $mode, $tickets); } /** * Returns ticket availabilities of an event for a certain date * @param int $event_id * @param array $dates * @return array * @author Webnus */ public function get_tickets_availability_multiple($event_id, $dates) { $availability = []; foreach ($dates as $date) { $ex = explode(':', sanitize_text_field($date)); $date = $ex[0]; $a = $this->get_tickets_availability($event_id, $date); if (!is_array($a)) continue; // Fill Compatibility if (!count($availability)) $availability = $a; // Minimum Availability foreach ($availability as $k => $v) { if (isset($a[$k])) $availability[$k] = min($a[$k], $v); } } return $availability; } /** * Check validity of a coupon * @param string $coupon * @param int $event_id * @param array $transaction * @return int * @author Webnus */ public function coupon_check_validity($coupon, $event_id, $transaction) { $term = get_term_by('name', $coupon, 'mec_coupon'); $coupon_id = $term->term_id ?? 0; // Coupon is not exists if (!$coupon_id) return 0; // Usage Limit $usage_limit = get_term_meta($coupon_id, 'usage_limit', true); $status = ($usage_limit == '-1' or (int) $usage_limit > $term->count) ? 1 : -1; // Expiration Date if ($status === 1) { $expiration_date = get_term_meta($coupon_id, 'expiration_date', true); if (trim($expiration_date) and strtotime($expiration_date) < strtotime(date('Y-m-d'))) { $status = -2; } } // Event Specification if ($status === 1) { $all_events = get_term_meta($coupon_id, 'target_event', true); if (trim($all_events) == '') $all_events = 1; $target_events = get_term_meta($coupon_id, 'target_events', true); if (!is_array($target_events)) { $target_events = []; if ($all_events and $all_events != 1) $target_events[] = $all_events; } if (!$all_events and is_array($target_events) and count($target_events) and !in_array($event_id, $target_events)) { $status = -3; } } // Category Specification if ($status === 1) { $all_target_categories = get_term_meta($coupon_id, 'target_category', true); if (trim($all_target_categories) == '') $all_target_categories = 1; $target_categories = get_term_meta($coupon_id, 'target_categories', true); if (!$all_target_categories and is_array($target_categories) and count($target_categories)) { $event_categories = wp_get_post_terms($event_id, 'mec_category', ['fields' => 'ids']); $found = false; foreach ($target_categories as $target_category) { if (in_array($target_category, $event_categories)) { $found = true; break; } } if (!$found) $status = -6; } } // Minimum Tickets if ($status === 1) { $ticket_minimum = get_term_meta($coupon_id, 'ticket_minimum', true); if (!trim($ticket_minimum)) $ticket_minimum = 1; $tickets = $transaction['tickets'] ?? []; if (isset($tickets['attachments'])) unset($tickets['attachments']); if (count($tickets) < $ticket_minimum) { $status = -4; } } // Maximum Tickets if ($status === 1) { $ticket_maximum = get_term_meta($coupon_id, 'ticket_maximum', true); if (trim($ticket_maximum)) { $tickets = $transaction['tickets'] ?? []; if (isset($tickets['attachments'])) unset($tickets['attachments']); if (count($tickets) > $ticket_maximum) { $status = -5; } } } $all_dates = (isset($transaction['all_dates']) and is_array($transaction['all_dates'])) ? $transaction['all_dates'] : []; // Minimum Dates if ($status === 1 and count($all_dates) >= 1) { $date_minimum = get_term_meta($coupon_id, 'date_minimum', true); if (!trim($date_minimum)) $date_minimum = 1; if (count($all_dates) < $date_minimum) { $status = -7; } } // Maximum Dates if ($status === 1 and count($all_dates) >= 1) { $date_maximum = get_term_meta($coupon_id, 'date_maximum', true); if (trim($date_maximum)) { if (count($all_dates) > $date_maximum) { $status = -8; } } } return $status; } /** * Apply a coupon to a transaction * @param string $coupon * @param int $transaction_id * @return int * @author Webnus */ public function coupon_apply($coupon, $transaction_id) { $transaction = $this->get_transaction($transaction_id); $event_id = $transaction['event_id'] ?? null; // Verify validity of coupon if ($this->coupon_check_validity($coupon, $event_id, $transaction) != 1) return 0; $total = $transaction['total']; $discount = $this->coupon_get_discount($coupon, $total); $after_discount = $total - $discount; $transaction['price_details']['total'] = $after_discount; $price_details = $transaction['price_details']['details']; foreach ($price_details as $i => $price_detail) { if (isset($price_detail['type']) and $price_detail['type'] == 'discount' and isset($price_detail['coupon'])) unset($price_details[$i]); } $price_details[] = ['amount' => $discount, 'description' => __('Discount', 'modern-events-calendar-lite'), 'type' => 'discount', 'coupon' => $coupon]; $transaction['price_details']['details'] = $price_details; $transaction['discount'] = $discount; $transaction['price'] = $after_discount; $transaction['coupon'] = $coupon; // Re-calculate $transaction = $this->recalculate($transaction); // Update Transaction $this->update_transaction($transaction_id, $transaction); return $transaction['discount'] ?? $discount; } /** * Get discount of a coupon * @param string $coupon * @param int $total * @return int * @author Webnus */ public function coupon_get_discount($coupon, $total) { $coupon_id = $this->coupon_get_id($coupon); // Coupon is not exists if (!$coupon_id) return 0; $discount_type = get_term_meta($coupon_id, 'discount_type', true); $discount = get_term_meta($coupon_id, 'discount', true); if ($discount_type == 'percent') { $discount_amount = ($total * $discount) / 100; $discount_max = get_term_meta($coupon_id, 'maximum_discount', true); if (trim($discount_max) and is_numeric($discount_max)) $discount_amount = min($discount_amount, $discount_max); } else $discount_amount = min($discount, $total); return $discount_amount; } /** * Get id of a coupon by coupon number * @param string $coupon * @return int * @author Webnus */ public function coupon_get_id($coupon) { $term = get_term_by('name', $coupon, 'mec_coupon'); return $term->term_id ?? 0; } /** * Is coupon 100 percent? * @param string $coupon * @return bool * @author Webnus */ public function coupon_is_100percent($coupon) { $coupon_id = $this->coupon_get_id($coupon); // Coupon is not exists if (!$coupon_id) return false; $discount_type = get_term_meta($coupon_id, 'discount_type', true); $discount = (int) get_term_meta($coupon_id, 'discount', true); return ($discount_type === 'percent' and $discount === 100); } public function recalculate($transaction) { $price_details = $transaction['price_details']['details']; $other_dates = (isset($transaction['other_dates']) and is_array($transaction['other_dates'])) ? $transaction['other_dates'] : []; $dates_count = count($other_dates) + 1; $booked_tickets = $transaction['tickets']; if (isset($booked_tickets['attachments'])) unset($booked_tickets['attachments']); $total_tickets_count = (count($booked_tickets) * $dates_count); $total_fee_amount = 0; $taxable = 0; $total_discount = 0; $fees_to_apply = []; $discounts_to_apply = []; foreach ($price_details as $i => $item) { $type = $item['type'] ?? ''; $amount = $item['amount'] ?? 0; if ($type == 'fee') $fees_to_apply[] = $item; else if ($type == 'discount') $discounts_to_apply[] = $item; else $taxable += $amount; // Remove Fee and Discount Items if (in_array($type, ['fee', 'discount'])) unset($price_details[$i]); } $total = $taxable; $fee_discount = 0; $fee_discount_type = 'percent'; // Apply Discounts foreach ($discounts_to_apply as $discount_item) { $coupon_id = $this->coupon_get_id($discount_item['coupon']); if ($coupon_id && get_term_meta($coupon_id, 'apply_on_fees', true)) { $fee_discount_type = get_term_meta($coupon_id, 'discount_type', true); $fee_discount = get_term_meta($coupon_id, 'discount', true); } $discount = $this->coupon_get_discount($discount_item['coupon'], $taxable); $taxable = max(0, ($taxable - $discount)); $total_discount += $discount; $price_details[] = ['amount' => $discount, 'description' => __('Discount', 'modern-events-calendar-lite'), 'type' => 'discount', 'coupon' => $discount_item['coupon']]; } // Apply Fees foreach ($fees_to_apply as $fee_item) { $fee_amount = 0; if ($fee_item['fee_type'] == 'percent') $fee_amount += ($taxable * $fee_item['fee_amount']) / 100; else if ($fee_item['fee_type'] == 'amount') $fee_amount += ($total_tickets_count * $fee_item['fee_amount']); else if ($fee_item['fee_type'] == 'amount_per_date') $fee_amount += ($dates_count * $fee_item['fee_amount']); else if ($fee_item['fee_type'] == 'amount_per_booking') $fee_amount += $fee_item['fee_amount']; // Apply Discount on Fee if ($fee_discount && in_array($fee_item['fee_type'], ['amount', 'amount_per_date', 'amount_per_booking'])) { if ($fee_discount_type === 'percent') $fee_discount_amount = (float) ($fee_amount * min($fee_discount, 100)) / 100; else $fee_discount_amount = (float) max($fee_discount, 0); $fee_discount_amount = min($fee_amount, $fee_discount_amount); $total_discount += $fee_discount_amount; $fee_amount = max($fee_amount - $fee_discount_amount, 0); } $total_fee_amount += $fee_amount; $price_details[] = ['amount' => $fee_amount, 'description' => __($fee_item['description'], 'modern-events-calendar-lite'), 'type' => 'fee', 'fee_type' => $fee_item['fee_type'], 'fee_amount' => $fee_item['fee_amount']]; } $total += $total_fee_amount; $price = ($taxable + $total_fee_amount); $payable = $price; // Calculate Payable if ($this->partial_payment->is_enabled()) $payable = $this->partial_payment->calculate($price, $transaction['event_id']); $transaction['price_details']['total'] = $price; $transaction['price_details']['payable'] = $payable; $transaction['price_details']['details'] = $price_details; $transaction['discount'] = $total_discount; $transaction['total'] = $total; $transaction['price'] = $price; $transaction['payable'] = $payable; return $transaction; } /** * Get invoice link for certain transaction * @param $transaction_id * @return string * @author Webnus */ public function get_invoice_link($transaction_id) { if (isset($this->settings['booking_invoice']) and !$this->settings['booking_invoice']) return ''; $main = $this->getMain(); $url = $main->URL(); $url = $main->add_qs_var('method', 'mec-invoice', $url); // Invoice Key $transaction = $this->get_transaction($transaction_id); if (isset($transaction['invoice_key'])) $url = $main->add_qs_var('mec-key', $transaction['invoice_key'], $url); return apply_filters('mec_booking_invoice_url', $main->add_qs_var('id', $transaction_id, $url), $transaction_id); } /** * Get Downloadable file link for certain transaction * @param $book_id * @return string * @author Webnus */ public function get_dl_file_link($book_id) { if (!isset($this->settings['downloadable_file_status']) or (isset($this->settings['downloadable_file_status']) and !$this->settings['downloadable_file_status'])) return ''; $event_id = get_post_meta($book_id, 'mec_event_id', true); $dl_file_id = get_post_meta($event_id, 'mec_dl_file', true); return apply_filters('mec_booking_dl_file_url', ($dl_file_id ? wp_get_attachment_url($dl_file_id) : ''), $book_id); } public function get_bookings_by_transaction_id($transaction_id) { $main = $this->getMain(); return get_posts([ 'posts_per_page' => -1, 'post_type' => $main->get_book_post_type(), 'meta_key' => 'mec_transaction_id', 'meta_value' => $transaction_id, ]); } public function get_thankyou_page($page_id, $transaction_id = null, $cart_id = null) { $main = $this->getMain(); $page = get_permalink($page_id); if ($transaction_id) $page = $main->add_qs_var('transaction', $transaction_id, $page); if ($cart_id) $page = $main->add_qs_var('cart', $cart_id, $page); return $page; } public function invoice_link_shortcode() { $transaction = isset($_GET['transaction']) ? sanitize_text_field($_GET['transaction']) : null; if (!$transaction) return null; $book = $this->getBook(); return '' . esc_html__('Download Invoice', 'modern-events-calendar-lite') . ''; } public function get_total_attendees($book_id) { $attendees = get_post_meta($book_id, 'mec_attendees', true); $count = 0; if (is_array($attendees)) { foreach ($attendees as $key => $attendee) { if ($key === 'attachments') continue; if (!isset($attendee[0]['MEC_TYPE_OF_DATA'])) $count++; else if ($attendee[0]['MEC_TYPE_OF_DATA'] != 'attachment') $count++; } } return $count; } public function get_attendees($book_id) { $attendees = get_post_meta($book_id, 'mec_attendees', true); $clean = []; if (is_array($attendees)) { foreach ($attendees as $key => $attendee) { if ($key === 'attachments') continue; $clean[$key] = $attendee; } } return $clean; } public function get_transaction_id_book_id($book_id) { return get_post_meta($book_id, 'mec_transaction_id', true); } public function get_book_id_transaction_id($transaction_id) { $db = $this->getDB(); return $db->select("SELECT `post_id` FROM `#__postmeta` WHERE `meta_key`='mec_transaction_id' AND `meta_value`='" . $db->escape($transaction_id) . "'", 'loadResult'); } public function get_ticket_price_label($ticket, $current_date, $event_id, $timestamp = null) { return $this->get_ticket_price_key($ticket, $current_date, $event_id, 'price_label', $timestamp); } public function get_ticket_price($ticket, $current_date, $event_id, $timestamp = null) { $price = $this->get_ticket_price_key($ticket, $current_date, $event_id, 'price', $timestamp); return trim($price) ? $price : 0; } public function get_ticket_regular_price_key($ticket, $current_date, $event_id, $key, $timestamp = null) { $data = $ticket[$key] ?? null; if ($timestamp and isset($ticket['id']) and trim($ticket['id']) !== '') { $occ_tickets = MEC_feature_occurrences::param($event_id, $timestamp, 'tickets', []); if (is_array($occ_tickets) and isset($occ_tickets[$ticket['id']]) and is_array($occ_tickets[$ticket['id']]) and isset($occ_tickets[$ticket['id']][$key]) and trim($occ_tickets[$ticket['id']][$key]) !== '') { $data = $occ_tickets[$ticket['id']][$key]; } } $price_dates = (isset($ticket['dates']) and is_array($ticket['dates'])) ? $ticket['dates'] : []; if (count($price_dates)) { $current_time = strtotime($current_date); foreach ($price_dates as $k => $price_date) { if (!is_numeric($k)) continue; $start = $price_date['start']; $end = $price_date['end']; if ($current_time >= strtotime($start) and $current_time <= strtotime($end)) { if ($key == 'price_label') $data = $price_date['label']; else $data = $price_date[$key]; } } } return $data; } public function get_ticket_price_key($ticket, $current_date, $event_id, $key, $timestamp = null) { $data = $this->get_ticket_regular_price_key($ticket, $current_date, $event_id, $key, $timestamp); return $this->get_price_for_loggedin_users($event_id, $data, $key); } /** * Returns tickets prices of an event for a certain date * @param int $event_id * @param string $date * @param string $key * @param int $timestamp * @return array * @author Webnus */ public function get_tickets_prices($event_id, $date, $key = 'price', $timestamp = null) { $prices = []; $tickets = get_post_meta($event_id, 'mec_tickets', true); // No Ticket Found! if (!is_array($tickets) || !count($tickets)) return $prices; foreach ($tickets as $ticket_id => $ticket) { $price = $this->get_ticket_price_key($ticket, $date, $event_id, $key, $timestamp); $prices[$ticket_id] = apply_filters('mec_filter_ticket_price', $price, $ticket, $event_id, $this); } return $prices; } public function get_price_for_loggedin_users($event_id, $price, $type = 'price') { if (!is_user_logged_in()) return $price; $user_id = get_current_user_id(); return $this->get_price_for_users($event_id, $price, $user_id, $type); } public function get_price_for_users($event_id, $price, $user_id, $type = 'price') { // Guest User if (!$user_id) return $price; $booking_options = get_post_meta($event_id, 'mec_booking', true); if (!is_array($booking_options)) $booking_options = []; // User $user = get_user_by('id', $user_id); // Invalid User ID if (!$user || !isset($user->roles)) return $price; $roles = $user->roles; $loggedin_discount = $booking_options['loggedin_discount'] ?? 0; $role_discount = $loggedin_discount; // Step through all roles in Array that comes from WordPress Core foreach ($roles as $role) { // If role discount is higher than the preset role OR a previous roles discount, set it to the new higher discount if (isset($booking_options['roles_discount_' . $role]) and is_numeric($booking_options['roles_discount_' . $role]) and $booking_options['roles_discount_' . $role] > $role_discount) $role_discount = $booking_options['roles_discount_' . $role]; } if (trim($role_discount) and is_numeric($role_discount)) { if ($type === 'price_label' and !is_numeric($price)) { $numeric = preg_replace("/[^0-9.]/", '', $price); if (is_numeric($numeric)) $price = $this->main->render_price(($numeric - (($numeric * $role_discount) / 100)), $event_id); } else { $price = $price - (($price * $role_discount) / 100); } } // Render Numeric Price if ($type === 'price_label' and is_numeric($price)) $price = $this->main->render_price($price, $event_id); return $price; } public function get_user_booking_limit($event_id) { $unlimited = false; $limit = 12; $mec_settings = $this->main->get_settings(); $booking_options = get_post_meta($event_id, 'mec_booking', true); // Total user booking limited if (isset($booking_options['bookings_user_limit_unlimited']) and !trim($booking_options['bookings_user_limit_unlimited'])) { $limit = (isset($booking_options['bookings_user_limit']) and trim($booking_options['bookings_user_limit'])) ? trim($booking_options['bookings_user_limit']) : $limit; } else { // If Inherit from global options activate if (!isset($mec_settings['booking_limit']) or !trim($mec_settings['booking_limit'])) $unlimited = true; else $limit = trim($mec_settings['booking_limit']); } return [$limit, $unlimited]; } public function get_minimum_tickets_per_booking($event_id) { $booking_options = get_post_meta($event_id, 'mec_booking', true); $bookings_minimum_per_booking = (isset($booking_options['bookings_minimum_per_booking']) and trim($booking_options['bookings_minimum_per_booking'])) ? (int) $booking_options['bookings_minimum_per_booking'] : 1; return max($bookings_minimum_per_booking, 1); } public function timestamp($start, $end) { // Timestamp is already available if (isset($start['timestamp']) && isset($end['timestamp'])) { return $start['timestamp'] . ':' . $end['timestamp']; } $s_hour = $start['hour']; if (strtoupper($start['ampm']) == 'AM' and $s_hour == '0') $s_hour = 12; $e_hour = $end['hour']; if (strtoupper($end['ampm']) == 'AM' and $e_hour == '0') $e_hour = 12; $start_time = $start['date'] . ' ' . sprintf("%02d", $s_hour) . ':' . sprintf("%02d", $start['minutes']) . ' ' . $start['ampm']; $end_time = $end['date'] . ' ' . sprintf("%02d", $e_hour) . ':' . sprintf("%02d", $end['minutes']) . ' ' . $end['ampm']; return strtotime($start_time) . ':' . strtotime($end_time); } public function get_event_id_by_transaction_id($transaction_id) { $transaction = $this->get_transaction($transaction_id); return $transaction['event_id'] ?? 0; } public function get_attendee_price($transaction, $email) { if (!is_array($transaction)) $transaction = $this->get_transaction($transaction); // No Attendees found! if (!isset($transaction['tickets']) || !is_array($transaction['tickets'])) return false; $attendee = []; foreach ($transaction['tickets'] as $key => $ticket) { if (!is_numeric($key)) continue; if ($ticket['email'] == $email) { $attendee = $ticket; break; } } // Attendee not found if (!count($attendee)) return false; $event_id = $transaction['event_id'] ?? 0; if (!$event_id) return false; $tickets = get_post_meta($event_id, 'mec_tickets', true); $dates = explode(':', $transaction['date']); $ticket_price = isset($tickets[$attendee['id']]) ? $this->get_ticket_price($tickets[$attendee['id']], date('Y-m-d', $dates[0]), $event_id, $dates[0]) : 0; if (!$ticket_price) return false; $variation_price = 0; // Ticket Variations if (isset($attendee['variations']) and is_array($attendee['variations']) and count($attendee['variations'])) { $ticket_variations = $this->main->ticket_variations($event_id, $attendee['id']); foreach ($attendee['variations'] as $variation_id => $variation_count) { if (!$variation_count || $variation_count < 0) continue; $variation_price += ((isset($ticket_variations[$variation_id]['price']) and is_numeric($ticket_variations[$variation_id]['price'])) ? $ticket_variations[$variation_id]['price'] : 0); } } return ($ticket_price + $variation_price); } public function get_auto_verification_status($event_id, $book_id) { // Booking Options $BO = get_post_meta($event_id, 'mec_booking', true); if (!is_array($BO)) $BO = []; $event_auto_verify = (isset($BO['auto_verify']) and trim($BO['auto_verify']) != '') ? $BO['auto_verify'] : 'global'; if (is_numeric($event_auto_verify)) $event_auto_verify = (int) $event_auto_verify; if ($event_auto_verify === 'global') { $auto_verify_free = $this->settings['booking_auto_verify_free'] ?? 0; $auto_verify_paid = $this->settings['booking_auto_verify_paid'] ?? 0; } else { $auto_verify_free = $event_auto_verify; $auto_verify_paid = $event_auto_verify; } return apply_filters('mec_get_auto_verification_status', [$auto_verify_free, $auto_verify_paid], $event_id, $book_id); } public function get_auto_confirmation_status($event_id, $book_id) { // Booking Options $BO = get_post_meta($event_id, 'mec_booking', true); if (!is_array($BO)) $BO = []; $event_auto_confirm = (isset($BO['auto_confirm']) and trim($BO['auto_confirm']) != '') ? $BO['auto_confirm'] : 'global'; if (is_numeric($event_auto_confirm)) $event_auto_confirm = (int) $event_auto_confirm; if ($event_auto_confirm === 'global') { $auto_confirm_free = $this->settings['booking_auto_confirm_free'] ?? 0; $auto_confirm_paid = $this->settings['booking_auto_confirm_paid'] ?? 0; } else { $auto_confirm_free = $event_auto_confirm; $auto_confirm_paid = $event_auto_confirm; } return apply_filters('mec_get_auto_confirmation_status', [$auto_confirm_free, $auto_confirm_paid], $event_id, $book_id); } public function get_total_tickets($event_id) { $booking_options = get_post_meta($event_id, 'mec_booking', true); if (!is_array($booking_options)) $booking_options = []; $tickets = get_post_meta($event_id, 'mec_tickets', true); $total_bookings_limit = (isset($booking_options['bookings_limit']) and trim($booking_options['bookings_limit']) !== '') ? $booking_options['bookings_limit'] : 100; $bookings_limit_unlimited = $booking_options['bookings_limit_unlimited'] ?? 0; if ($bookings_limit_unlimited == '1') $total_bookings_limit = '-1'; $all_ticket_availability = -1; foreach ($tickets as $ticket_id => $ticket) { $limit = (isset($ticket['limit']) and trim($ticket['limit']) != '') ? $ticket['limit'] : -1; $ticket_seats = (isset($ticket['seats']) and is_numeric($ticket['seats'])) ? (int) $ticket['seats'] : 1; $ticket_seats = max(1, $ticket_seats); // Ticket is Unlimited if ((isset($ticket['unlimited']) && $ticket['unlimited'] == 1) || $limit == -1) continue; // Unlimited Total if ($total_bookings_limit == '-1') $ticket_availability = $limit; else $ticket_availability = min($limit, max($total_bookings_limit, 0)); $all_ticket_availability = max($all_ticket_availability, 0); $all_ticket_availability += $ticket_availability >= 0 ? floor($ticket_availability / $ticket_seats) : 0; } $final = $all_ticket_availability != '-1' ? $all_ticket_availability : $total_bookings_limit; return $final == '-1' ? __('Unlimited', 'modern-events-calendar-lite') : $final; } public function get_all_sold_tickets($event_id) { // Transient Key $transient_key = 'all_sold_tickets_' . $event_id; // Cached Data $cached = get_transient($transient_key); // Return Cached Data if ($cached) return $cached; $query = new WP_Query([ 'post_type' => $this->PT, 'posts_per_page' => -1, 'post_status' => ['publish', 'pending', 'draft', 'future', 'private'], 'meta_query' => [ ['key' => 'mec_event_id', 'value' => $event_id, 'compare' => '='], ['key' => 'mec_confirmed', 'value' => 1, 'compare' => '='], ['key' => 'mec_verified', 'value' => 1, 'compare' => '='], ], ]); $sold = 0; if ($query->have_posts()) { // The Loop while ($query->have_posts()) { $query->the_post(); $sold += $this->get_total_attendees(get_the_ID()); } // Restore original Post Data wp_reset_postdata(); } // Set Transient set_transient($transient_key, $sold, 24 * HOUR_IN_SECONDS); return $sold; } public function get_ticket_total_price($transaction, $attendee, $booking_id) { $event_id = $transaction['event_id']; $all_attendees = get_post_meta($booking_id, 'mec_attendees', true); if (!is_array($all_attendees) || !count($all_attendees)) $all_attendees = [get_post_meta($booking_id, 'mec_attendee', true)]; if (isset($all_attendees['attachments'])) unset($all_attendees['attachments']); $total_price = get_post_meta($booking_id, 'mec_price', true); if (count($all_attendees) == 1) return $total_price; $tickets = get_post_meta($event_id, 'mec_tickets', true); $ticket_id = $attendee['id']; $ticket_variations = $this->main->ticket_variations($event_id, $ticket_id); $ticket_price_booking_saved = get_post_meta($booking_id, 'mec_ticket_price_' . $ticket_id, true); if (trim($ticket_price_booking_saved) === '') $ticket_price_booking_saved = 0; $ticket_price = (isset($tickets[$ticket_id]) ? $tickets[$ticket_id]['price'] : $ticket_price_booking_saved); $user = $this->getUser(); $booking_user = $user->booking($booking_id); $ticket_price = $this->get_price_for_users($event_id, $ticket_price, $booking_user->ID); // Price Per Date if (isset($tickets[$ticket_id]['dates']) and is_array($tickets[$ticket_id]['dates']) and count($tickets[$ticket_id]['dates'])) { $book_time = strtotime(get_post_meta($booking_id, 'mec_booking_time', true)); if ($book_time) { $pdates = $tickets[$ticket_id]['dates']; foreach ($pdates as $pdate) { if (!isset($pdate['start']) or !isset($pdate['end'])) continue; $t_start = strtotime($pdate['start']); $t_end = strtotime($pdate['end']); if ($book_time >= $t_start and $book_time <= $t_end and isset($pdate['price'])) { $ticket_price = $pdate['price']; break; } } } } $variation_price = 0; if (isset($attendee['variations']) and is_array($attendee['variations']) and count($attendee['variations'])) { foreach ($attendee['variations'] as $variation_id => $count) { if (!trim($count)) continue; if (!isset($ticket_variations[$variation_id])) continue; $p = $ticket_variations[$variation_id]['price']; if (is_numeric($p) and is_numeric($count)) $variation_price += ($p * $count); } } // Fees $total_fees = 0; // Discounts $discounts = 0; if (isset($transaction['price_details']) and isset($transaction['price_details']['details']) and is_array($transaction['price_details']['details']) and count($transaction['price_details']['details'])) { foreach ($transaction['price_details']['details'] as $detail) { if (!isset($detail['type'])) continue; if ($detail['type'] == 'fee' and isset($detail['amount']) and is_numeric($detail['amount'])) $total_fees += $detail['amount']; if ($detail['type'] == 'discount' and isset($detail['amount']) and is_numeric($detail['amount'])) $discounts += $detail['amount']; } } $ticket_total_price = null; if (is_numeric($ticket_price) and is_numeric($variation_price) and is_numeric($total_fees) and is_numeric($discounts)) { $ticket_total_price = ($ticket_price + $variation_price + ($total_fees / count($all_attendees))) - ($discounts / count($all_attendees)); } return (!is_null($ticket_total_price) ? $ticket_total_price : $total_price); } /** * Remove Fees From a Transaction * @param int $transaction_id * @return boolean * @author Webnus */ public function remove_fees($transaction_id) { $transaction = $this->get_transaction($transaction_id); $price_details = $transaction['price_details']['details']; $removed_fees = []; foreach ($price_details as $i => $price_detail) { if (isset($price_detail['type']) and $price_detail['type'] == 'fee') { $removed_fees[] = $price_detail; unset($price_details[$i]); } } $transaction['price_details']['details'] = $price_details; $transaction['removed_fees_status'] = 1; $transaction['removed_fees'] = (count($removed_fees) ? $removed_fees : ($transaction['removed_fees'] ?? [])); // Re-calculate $transaction = $this->recalculate($transaction); // Update Transaction $this->update_transaction($transaction_id, $transaction); return true; } /** * Re-Add Fees To a Transaction * @param int $transaction_id * @return boolean * @author Webnus */ public function readd_fees($transaction_id) { $transaction = $this->get_transaction($transaction_id); $is_removed = $transaction['removed_fees_status'] ?? 0; if (!$is_removed) return false; $price_details = $transaction['price_details']['details']; $removed_fees = (isset($transaction['removed_fees']) and is_array($transaction['removed_fees'])) ? $transaction['removed_fees'] : []; foreach ($removed_fees as $removed_fee) $price_details[] = $removed_fee; $transaction['price_details']['details'] = $price_details; unset($transaction['removed_fees_status']); unset($transaction['removed_fees']); // Re-calculate $transaction = $this->recalculate($transaction); // Update Transaction $this->update_transaction($transaction_id, $transaction); return true; } public function get_cancel_url($event, $booking_id) { $event_url = trim(get_permalink($event), '/ '); $cancelation_key = get_post_meta($booking_id, 'mec_cancellation_key', true); if (strpos($event_url, '?') === false) return $event_url . '/cancel/' . $cancelation_key . '/'; else return $this->main->add_qs_var('cancel', $cancelation_key, $event_url); } }