get_total_refunded(); self::cancel_booking_on_refund( $booking_id, $total_refunded, __( 'Order fully refunded', 'wp-bnb' ) ); } /** * Check if the order is fully refunded. * * @param \WC_Order $order WooCommerce order. * @return bool */ public static function is_full_refund( \WC_Order $order ): bool { $order_total = floatval( $order->get_total() ); $total_refunded = floatval( $order->get_total_refunded() ); // Consider it full refund if refunded amount >= order total. return $total_refunded >= $order_total; } /** * Cancel booking on full refund. * * @param int $booking_id Booking ID. * @param float $refund_amount Refund amount. * @param string $reason Refund reason. * @return void */ public static function cancel_booking_on_refund( int $booking_id, float $refund_amount, string $reason ): void { // Get current status. $old_status = get_post_meta( $booking_id, '_bnb_booking_status', true ); // Don't cancel if already cancelled. if ( 'cancelled' === $old_status ) { // Just update refund info. self::record_refund_meta( $booking_id, $refund_amount, $reason ); return; } /** * Filter whether to cancel booking on refund. * * @param bool $cancel Whether to cancel. * @param \WC_Order $order WooCommerce order. * @param float $refund_amount Refund amount. */ $should_cancel = apply_filters( 'wp_bnb_wc_should_cancel_on_refund', true, $booking_id, $refund_amount ); if ( ! $should_cancel ) { self::record_partial_refund( $booking_id, $refund_amount, $reason ); return; } // Update booking status to cancelled. update_post_meta( $booking_id, '_bnb_booking_status', 'cancelled' ); // Store refund information. self::record_refund_meta( $booking_id, $refund_amount, $reason ); // Add cancellation note. $note = sprintf( /* translators: %s: Refund amount */ __( 'Booking cancelled due to WooCommerce refund (%s)', 'wp-bnb' ), wc_price( $refund_amount ) ); $existing_notes = get_post_meta( $booking_id, '_bnb_booking_notes', true ); $new_notes = $existing_notes ? $existing_notes . "\n\n" . $note : $note; update_post_meta( $booking_id, '_bnb_booking_notes', $new_notes ); /** * Fires when booking status changes (triggers email notifications). * * @param int $booking_id Booking ID. * @param string $old_status Old status. * @param string $new_status New status. */ do_action( 'wp_bnb_booking_status_changed', $booking_id, $old_status, 'cancelled' ); } /** * Record partial refund without cancelling. * * @param int $booking_id Booking ID. * @param float $refund_amount Refund amount. * @param string $reason Refund reason. * @return void */ private static function record_partial_refund( int $booking_id, float $refund_amount, string $reason ): void { // Get existing refund amount and add to it. $existing_refund = floatval( get_post_meta( $booking_id, self::REFUND_AMOUNT_META, true ) ); $total_refund = $existing_refund + $refund_amount; // Update refund meta. self::record_refund_meta( $booking_id, $total_refund, $reason ); // Add note about partial refund. $note = sprintf( /* translators: %s: Refund amount */ __( 'Partial refund processed: %s', 'wp-bnb' ), wc_price( $refund_amount ) ); $existing_notes = get_post_meta( $booking_id, '_bnb_booking_notes', true ); $new_notes = $existing_notes ? $existing_notes . "\n\n" . $note : $note; update_post_meta( $booking_id, '_bnb_booking_notes', $new_notes ); } /** * Record refund metadata. * * @param int $booking_id Booking ID. * @param float $refund_amount Total refund amount. * @param string $reason Refund reason. * @return void */ private static function record_refund_meta( int $booking_id, float $refund_amount, string $reason ): void { update_post_meta( $booking_id, self::REFUND_AMOUNT_META, $refund_amount ); update_post_meta( $booking_id, self::REFUND_DATE_META, current_time( 'mysql' ) ); if ( $reason ) { update_post_meta( $booking_id, self::REFUND_REASON_META, $reason ); } } /** * Calculate refund amount for a booking. * * @param int $booking_id Booking ID. * @param string $type Refund type: 'full' or 'nights_remaining'. * @return float Refund amount. */ public static function calculate_refund_amount( int $booking_id, string $type = 'full' ): float { $calculated_price = floatval( get_post_meta( $booking_id, '_bnb_booking_calculated_price', true ) ); if ( 'full' === $type ) { return $calculated_price; } // Calculate pro-rata based on nights remaining. $check_in = get_post_meta( $booking_id, '_bnb_booking_check_in', true ); $check_out = get_post_meta( $booking_id, '_bnb_booking_check_out', true ); if ( ! $check_in || ! $check_out ) { return $calculated_price; } $today = new \DateTime( 'today' ); $check_in_date = \DateTime::createFromFormat( 'Y-m-d', $check_in ); $check_out_date = \DateTime::createFromFormat( 'Y-m-d', $check_out ); if ( ! $check_in_date || ! $check_out_date ) { return $calculated_price; } // If check-in hasn't happened, full refund. if ( $today < $check_in_date ) { return $calculated_price; } // If check-out has passed, no refund. if ( $today >= $check_out_date ) { return 0.0; } // Calculate remaining nights. $total_nights = $check_in_date->diff( $check_out_date )->days; $nights_used = $check_in_date->diff( $today )->days; $nights_remaining = $total_nights - $nights_used; if ( $total_nights <= 0 ) { return 0.0; } // Pro-rata refund. $nightly_rate = $calculated_price / $total_nights; return $nightly_rate * $nights_remaining; } /** * Get refund info for a booking. * * @param int $booking_id Booking ID. * @return array|null Refund info or null. */ public static function get_booking_refund_info( int $booking_id ): ?array { $amount = get_post_meta( $booking_id, self::REFUND_AMOUNT_META, true ); if ( ! $amount ) { return null; } return array( 'amount' => floatval( $amount ), 'reason' => get_post_meta( $booking_id, self::REFUND_REASON_META, true ), 'date' => get_post_meta( $booking_id, self::REFUND_DATE_META, true ), ); } /** * Add booking refund notice in admin order page. * * @param int $order_id Order ID. * @return void */ public static function add_booking_refund_notice( int $order_id ): void { $order = wc_get_order( $order_id ); if ( ! $order instanceof \WC_Order ) { return; } $booking_id = Manager::get_booking_for_order( $order ); if ( ! $booking_id ) { return; } $refund_info = self::get_booking_refund_info( $booking_id ); if ( ! $refund_info ) { return; } $booking_status = get_post_meta( $booking_id, '_bnb_booking_status', true ); ?> : ' . esc_html__( 'View booking', 'wp-bnb' ) . '' ); ?>