/** * Adjusts global stock levels for any of the products that were just purchased. * * Expects to fire during the 'event_tickets_edd_tickets_purchased_inventory_recorded' action. * * @param array $quantities */ public function adjust_stock_levels(array $quantities) { foreach ($quantities as $ticket_id => $amount_purchased) { $ticket_id = absint($ticket_id); $event = tribe_events_get_ticket_event($ticket_id); $global_stock = new Tribe__Tickets__Global_Stock($event->ID); // We're only interested if the ticket utilizes global stock if (!$global_stock->is_enabled()) { continue; } // Try to load the actual ticket object $tickets = $this->get_event_tickets($event->ID); // Move on if we couldn't obtain the ticket object if (empty($tickets[$ticket_id])) { continue; } switch ($tickets[$ticket_id]->global_stock_mode()) { // Reduce the cap in line with the number of capped tickets that were purchased, if any case Tribe__Tickets__Global_Stock::CAPPED_STOCK_MODE: $original_level = $tickets[$ticket_id]->global_stock_cap(); update_post_meta($ticket_id, '_global_stock_cap', $original_level - $amount_purchased); // Fall-through is deliberate - capped sales still draw from the global inventory pool // Fall-through is deliberate - capped sales still draw from the global inventory pool case Tribe__Tickets__Global_Stock::GLOBAL_STOCK_MODE: $original_level = $global_stock->get_stock_level(); $global_stock->set_stock_level($original_level - $amount_purchased); break; } } }
/** * Save the current global stock properties for this event. * * @param int $post_id */ public function save_global_stock($post_id) { if (!(isset($_POST['tribe-tickets-post-settings']) && wp_verify_nonce($_POST['tribe-tickets-post-settings'], 'tribe-tickets-meta-box'))) { return; } // Bail on autosaves/bulk updates if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) { return; } $enable = !empty($_POST['tribe-tickets-enable-global-stock']); $stock = (int) @$_POST['tribe-tickets-global-stock']; $post_global_stock = new Tribe__Tickets__Global_Stock($post_id); $post_global_stock->enable($enable); $post_global_stock->set_stock_level($stock); }
/** * When WooCommerce reduces stock levels during order processing we need to look * for global stock tickets and ensure we "equalize" the total stock levels/sales * caps as appropriate. * * @param WC_Order $order */ public function stock_equalize(WC_Order $order) { $woo_tickets = Tribe__Tickets_Plus__Commerce__WooCommerce__Main::get_instance(); $total_ordered = array(); $capped_tickets = array(); // Get the total quantity of global stock ordered per event foreach ($order->get_items() as $item) { $product = $order->get_product_from_item($item); $event = $woo_tickets->get_event_for_ticket($product->id); $global_stock = new Tribe__Tickets__Global_Stock($event->ID); // Skip non-tickets or tickets that do not utilize global stock if (!$event || !$global_stock->is_enabled() || !$product->managing_stock()) { continue; } $ticket = $woo_tickets->get_ticket($event->ID, $product->id); switch ($ticket->global_stock_mode()) { case Tribe__Tickets__Global_Stock::CAPPED_STOCK_MODE: $capped_tickets[$product->id] = (int) $item['qty']; // Deliberate fallthrough - $total_ordered should accumulate capped *and* global quantities // Deliberate fallthrough - $total_ordered should accumulate capped *and* global quantities case Tribe__Tickets__Global_Stock::GLOBAL_STOCK_MODE: $total_ordered[$event->ID] += (int) $item['qty']; } } // For each ticket product that utilizes global stock, adjust the product inventory foreach ($total_ordered as $event_id => $quantity_ordered) { $global_stock = new Tribe__Tickets__Global_Stock($event_id); $level = $global_stock->get_stock_level(); $new_level = $level - $quantity_ordered; $global_stock->set_stock_level($new_level); $this->stock_update_global_tickets($event_id, $new_level); } // Now adjust sale caps foreach ($capped_tickets as $ticket_id => $reduce_by) { $event = $woo_tickets->get_event_for_ticket($ticket_id); $ticket = $woo_tickets->get_ticket($event->ID, $ticket_id); $current = $ticket->global_stock_cap(); $new_cap = $current - $reduce_by; $ticket->global_stock_cap($new_cap); update_post_meta($ticket_id, '_global_stock_cap', $new_cap); } }
/** * Takes any global stock data and makes it available via a wp_localize_script() call. */ public static function enqueue_frontend_stock_data() { $data = array('tickets' => array(), 'events' => array()); foreach (self::$frontend_ticket_data as $ticket) { /** * @var Tribe__Tickets__Ticket_Object $ticket */ $event_id = $ticket->get_event()->ID; $global_stock = new Tribe__Tickets__Global_Stock($event_id); $stock_mode = $ticket->global_stock_mode(); $data['tickets'][$ticket->ID] = array('event_id' => $event_id, 'mode' => $stock_mode); if (Tribe__Tickets__Global_Stock::CAPPED_STOCK_MODE === $stock_mode) { $data['tickets'][$ticket->ID]['cap'] = $ticket->global_stock_cap(); } if (Tribe__Tickets__Global_Stock::OWN_STOCK_MODE === $stock_mode && $ticket->managing_stock()) { $data['tickets'][$ticket->ID]['stock'] = $ticket->stock(); } $data['events'][$event_id] = array('stock' => $global_stock->get_stock_level()); } wp_localize_script('tribe_tickets_frontend_tickets', 'tribe_tickets_stock_data', $data); }
/** * Gets the "tickets sold" message for a given ticket * * @param Tribe__Tickets__Ticket_Object $ticket Ticket to analyze * * @return string */ function tribe_tickets_get_ticket_stock_message(Tribe__Tickets__Ticket_Object $ticket) { $stock = $ticket->stock(); $sold = $ticket->qty_sold(); $cancelled = $ticket->qty_cancelled(); $pending = $ticket->qty_pending(); $event = Tribe__Tickets__Tickets::find_matching_event($ticket); $global_stock = new Tribe__Tickets__Global_Stock($event->ID); $is_global = Tribe__Tickets__Global_Stock::GLOBAL_STOCK_MODE === $ticket->global_stock_mode(); $is_capped = Tribe__Tickets__Global_Stock::CAPPED_STOCK_MODE === $ticket->global_stock_mode(); $stock_cap = $ticket->global_stock_cap(); // If ticket sales are capped, do not suggest that more than the cap amount are available if ($is_capped && $stock > $stock_cap) { $stock = $stock_cap; } // If it is a global-stock ticket but the global stock level has not yet been set for the event // then return something better than just '0' as the available stock if ($is_global && 0 === $stock && !$global_stock->is_enabled()) { $stock = '<i>' . __('global inventory', 'event-tickets') . '</i>'; } // There may not be a fixed inventory - in which case just report the number actually sold so far if (empty($stock) && $stock !== 0) { $message = sprintf(esc_html__('Sold %d', 'event-tickets'), esc_html($sold)); } else { $cancelled_count = empty($cancelled) ? '' : esc_html(sprintf(_x(' cancelled: %1$d', 'ticket stock message (cancelled stock)', 'event-tickets'), (int) $cancelled)); $pending_count = $pending < 1 ? '' : esc_html(sprintf(__(' pending: %1$d', 'ticket stock message (pending stock)', 'event-tickets'), (int) $pending)); $message = sprintf(esc_html__('Sold %1$d (units remaining: %2$s%3$s%4$s)', 'event-tickets'), esc_html($sold), $stock, $cancelled_count, $pending_count); } return $message; }
/** * Returns the amount of global stock set for the event. * * A positive value does not necessarily mean global stock is currently in effect; * always combine a call to this method with a call to $this->uses_global_stock()! * * @param int $event_id * * @return int */ protected function global_stock_level($event_id) { // In some cases (version mismatch with Event Tickets) the Global Stock class may not be available if (!class_exists('Tribe__Tickets__Global_Stock')) { return 0; } $global_stock = new Tribe__Tickets__Global_Stock($event_id); return $global_stock->get_stock_level(); }