Add pricing system with tiers, seasons, and calculator (v0.2.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m19s

- Create PricingTier enum for short/mid/long-term pricing
- Add Season class for seasonal pricing with date ranges
- Implement Calculator for price calculations with breakdown
- Add pricing meta box to Room post type
- Create Seasons admin page for managing seasonal pricing
- Add Pricing settings tab with tier thresholds
- Support weekend surcharges and configurable weekend days
- Add price column to room list admin

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 14:10:30 +01:00
parent f24a347bb1
commit dabfe1e826
12 changed files with 1911 additions and 25 deletions

View File

@@ -11,6 +11,8 @@ declare( strict_types=1 );
namespace Magdev\WpBnb\PostTypes;
use Magdev\WpBnb\Pricing\Calculator;
use Magdev\WpBnb\Pricing\PricingTier;
use Magdev\WpBnb\Taxonomies\Amenity;
use Magdev\WpBnb\Taxonomies\RoomType;
@@ -147,6 +149,15 @@ final class Room {
'normal',
'default'
);
add_meta_box(
'bnb_room_pricing',
__( 'Room Pricing', 'wp-bnb' ),
array( self::class, 'render_pricing_meta_box' ),
self::POST_TYPE,
'normal',
'high'
);
}
/**
@@ -327,6 +338,87 @@ final class Room {
<?php
}
/**
* Render pricing meta box.
*
* @param \WP_Post $post Current post object.
* @return void
*/
public static function render_pricing_meta_box( \WP_Post $post ): void {
$pricing = Calculator::getRoomPricing( $post->ID );
$currency = get_option( 'wp_bnb_currency', 'CHF' );
?>
<table class="form-table bnb-pricing-table">
<tr>
<th scope="row" colspan="2">
<h4><?php esc_html_e( 'Base Prices', 'wp-bnb' ); ?></h4>
</th>
</tr>
<?php foreach ( PricingTier::cases() as $tier ) : ?>
<tr>
<th scope="row">
<label for="bnb_room_price_<?php echo esc_attr( $tier->value ); ?>">
<?php echo esc_html( $tier->label() ); ?>
</label>
</th>
<td>
<div class="bnb-price-input-wrapper">
<input type="number"
id="bnb_room_price_<?php echo esc_attr( $tier->value ); ?>"
name="bnb_room_price_<?php echo esc_attr( $tier->value ); ?>"
value="<?php echo esc_attr( $pricing[ $tier->value ]['price'] ?? '' ); ?>"
class="small-text"
min="0"
step="0.01">
<span class="bnb-price-unit">
<?php echo esc_html( $currency ); ?> <?php echo esc_html( $tier->unit() ); ?>
</span>
</div>
</td>
</tr>
<?php endforeach; ?>
<tr>
<th scope="row" colspan="2">
<h4><?php esc_html_e( 'Adjustments', 'wp-bnb' ); ?></h4>
</th>
</tr>
<tr>
<th scope="row">
<label for="bnb_room_price_weekend_surcharge">
<?php esc_html_e( 'Weekend Surcharge', 'wp-bnb' ); ?>
</label>
</th>
<td>
<div class="bnb-price-input-wrapper">
<input type="number"
id="bnb_room_price_weekend_surcharge"
name="bnb_room_price_weekend_surcharge"
value="<?php echo esc_attr( $pricing['weekend_surcharge']['price'] ?? '' ); ?>"
class="small-text"
min="0"
step="0.01">
<span class="bnb-price-unit">
<?php echo esc_html( $currency ); ?> <?php esc_html_e( 'per night', 'wp-bnb' ); ?>
</span>
</div>
<p class="description">
<?php esc_html_e( 'Additional amount charged for weekend nights (Friday/Saturday by default).', 'wp-bnb' ); ?>
</p>
</td>
</tr>
</table>
<p class="description">
<?php
printf(
/* translators: %s: Link to pricing settings */
esc_html__( 'Seasonal pricing and tier thresholds can be configured in %s.', 'wp-bnb' ),
'<a href="' . esc_url( admin_url( 'admin.php?page=wp-bnb-settings&tab=pricing' ) ) . '">' . esc_html__( 'Pricing Settings', 'wp-bnb' ) . '</a>'
);
?>
</p>
<?php
}
/**
* Save post meta.
*
@@ -410,6 +502,23 @@ final class Room {
implode( ',', $ids )
);
}
// Pricing fields.
$pricing_data = array();
foreach ( PricingTier::cases() as $tier ) {
$key = 'bnb_room_price_' . $tier->value;
if ( isset( $_POST[ $key ] ) && '' !== $_POST[ $key ] ) {
$pricing_data[ $tier->value ] = floatval( $_POST[ $key ] );
}
}
if ( isset( $_POST['bnb_room_price_weekend_surcharge'] ) ) {
$pricing_data['weekend_surcharge'] = floatval( $_POST['bnb_room_price_weekend_surcharge'] );
}
if ( ! empty( $pricing_data ) ) {
Calculator::saveRoomPricing( $post_id, $pricing_data );
}
}
/**
@@ -433,6 +542,7 @@ final class Room {
$new_columns['room_number'] = __( 'Room #', 'wp-bnb' );
$new_columns['taxonomy-bnb_room_type'] = __( 'Room Type', 'wp-bnb' );
$new_columns['capacity'] = __( 'Capacity', 'wp-bnb' );
$new_columns['price'] = __( 'Price/Night', 'wp-bnb' );
$new_columns['status'] = __( 'Status', 'wp-bnb' );
}
}
@@ -483,6 +593,16 @@ final class Room {
}
break;
case 'price':
$pricing = Calculator::getRoomPricing( $post_id );
$nightly = $pricing[ PricingTier::SHORT_TERM->value ]['price'] ?? null;
if ( $nightly ) {
echo esc_html( Calculator::formatPrice( $nightly ) );
} else {
echo '<span class="bnb-no-price">' . esc_html__( 'Not set', 'wp-bnb' ) . '</span>';
}
break;
case 'status':
$status = get_post_meta( $post_id, self::META_PREFIX . 'status', true ) ?: 'available';
$statuses = self::get_room_statuses();