PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.64
Timetics – Appointment Booking Calendar & Scheduling v1.0.64
1.0.64 1.0.62 1.0.63 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 All 65 releases
← All changes | core/bookings/booking.php +70 -0 1.0.62 → 1.0.64 View file →
@@ -716,8 +716,78 @@
716 716 update_post_meta( $this->id, $this->meta_prefix . 'slot_released', 1 );
717 717 }
718 718
719 719 /**
720 + * Take back the slot this booking released.
721 + *
722 + * A declined payment marks the booking failed and releases its slot, so an
723 + * attempt the customer walks away from does not block the time forever. When
724 + * the customer retries on that same booking the slot has to be taken back
725 + * before any money moves, otherwise the booking finalizes while the slot still
726 + * reads as free and the next customer can book straight over it.
727 + *
728 + * @return bool True when the slot is held, false when it is no longer free.
729 + */
730 + public function reserve_slot() {
731 + if ( ! $this->is_booking() ) {
732 + return false;
733 + }
734 +
735 + // Nothing was released, so the slot is still held from booking creation.
736 + if ( ! $this->get_prop( 'slot_released' ) ) {
737 + return true;
738 + }
739 +
740 + $meeting = new Appointment( $this->get_appointment() );
741 + $entry = $this->find_slot_entry( $meeting );
742 + $seats = ! empty( $this->get_seat() ) ? (array) $this->get_seat() : [];
743 + $taking = max( 1, count( $seats ) );
744 +
745 + if ( $entry ) {
746 + $booked = intval( $entry->get_booked() ) + $taking;
747 +
748 + // Someone else took the time while this booking was in the failed state.
749 + if ( $booked > $meeting->get_effective_capacity() ) {
750 + return false;
751 + }
752 +
753 + $existing = ! empty( $entry->get_seats() ) ? (array) $entry->get_seats() : [];
754 +
755 + $entry->update(
756 + [
757 + 'booked' => $booked,
758 + 'seats' => array_values( array_unique( array_merge( $existing, $seats ) ) ),
759 + ]
760 + );
761 + } else {
762 + // A one-to-one release deletes the entry outright, so recreate it. Entries
763 + // are stored in the meeting's timezone, the booking's in the customer's.
764 + $start = timetics_convert_timezone( $this->get_start_date() . ' ' . $this->get_start_time(), $this->get_timezone(), $meeting->get_timezone() );
765 + $end = timetics_convert_timezone( $this->get_start_date() . ' ' . $this->get_end_time(), $this->get_timezone(), $meeting->get_timezone() );
766 +
767 + $booking_entry = new Booking_Entry();
768 +
769 + $booking_entry->create(
770 + [
771 + 'meeting_id' => $meeting->get_id(),
772 + 'staff_id' => $this->get_staff_id(),
773 + 'customer_id' => $this->get_customer(),
774 + 'booking_id' => $this->id,
775 + 'booked' => $taking,
776 + 'date' => $start->format( 'Y-m-d' ),
777 + 'start' => $start->format( 'h:i a' ),
778 + 'end' => $end->format( 'h:i a' ),
779 + 'seats' => $seats,
780 + ]
781 + );
782 + }
783 +
784 + delete_post_meta( $this->id, $this->meta_prefix . 'slot_released' );
785 +
786 + return true;
787 + }
788 +
789 + /**
720 790 * Release the entry for one named slot of this booking.
721 791 *
722 792 * Rescheduling saves the new time first, so the old slot has to be named
723 793 * rather than read off the booking. No idempotency guard: the booking lives