Xây dựng website đặt phòng khách sạn với Meta Box (Phần 2) – Tạo trang booking ở backend

xây dựng website đặt phòng khách sạn với meta bõ - phần 2 - tạo trang booking ở backend

phần 1 của series này, mình đã hướng dẫn các bạn tạo trang giới thiệu thông tin về các loại phòng của một khách sạn. Thông thường thì tại trang giới thiệu phòng này sẽ luôn có nút điều hướng đến phần đặt phòng để khách hàng có để đặt trực tiếp trên website. Tuy nhiên, với một số khách sạn phát triển kênh bán hàng trực tiếp nhiều, thì ngoài việc để khách hàng đặt trực tiếp như vậy, việc nhân viên kinh doanh tự tạo booking thay cho khách là việc khá phổ biến.

Ngoài ra, để tiện cho bạn hình dung việc xây dựng được trang đặt booking ngoài frontend sau này, mình sẽ hướng dẫn luôn các bạn cách tạo một trang đặt booking ở backend để nhân viên sử dụng nội bộ trong bài viết số 2 này. Phần tạo booking ngoài frontend mình sẽ để dành sang bài số 3.

Các plugin bạn cần cài đặt và chuẩn bị thì vẫn tương tự như mình đã hướng dẫn ở bài số 1 thôi nhé.

Bây giờ, bắt tay vào thực hiện thôi!

Tạo một Custom Post Type mới

Chúng ta sẽ tạo một post type mới có tên là “Booking”. Mỗi một đơn đặt phòng được tạo sau này sẽ tương ứng với một bài viết thuộc loại post type này nhé.

Bạn tìm tới menu Meta Box > Post Types > New Post Type trong admin dashboard để tạo một custom post type mới nhé.

Sau đó, bạn điền các thông tin và tùy chọn hiển thị cho post type. Trong đó:

  • Tên của post type là: Booking;
  • Supports: mình bỏ chọn các field mặc định và chỉ chọn Revision, tức cho phép xem lại các lần chỉnh sửa bài viết thuộc loại post type này;
  • Menu position after: chọn vị trí hiển thị của loại post type trên menu trong dashboard.
Tạo một custom post type mới có tên là Booking

Tạo một custom post type mới có tên là Booking

Đến đây, bạn có thể nhấn Publish để lưu post type và đi tiếp đến mục Meta Box > Custom Fields > Add New, hoặc nhấn luôn Add Custom Fields ngay tại trang chỉnh sửa thông tin post type này để thực hiện bước tiếp theo.

Nhấn Add Custom Fields để tạo custom fields cho post type Booking

Nhấn Add Custom Fields để tạo custom fields cho post type Booking

Tạo Custom Fields cho post type “Booking” vừa tạo

Ở trang tạo Booking này, chúng ta sẽ cần tạo khá nhiều field với các loại field khác nhau. Bạn nên định hướng rõ ngay từ đầu xem các fields bạn cần là gì, điền thông tin như nào, thứ tự và cách hiển thị dữ liệu, chức năng như nào để thuận tiện hơn cho quá trình tạo field. Như bài toán của mình, thì các thông tin sẽ như sau:

Tên field Loại field Chức năng
Số order number Là một dãy số duy nhất và tự sinh ra mỗi khi có booking
Ngày tạo booking date Ngày tạo booking
Nhân viên phụ trách user Chọn một từ trong danh sách các thành viên (users) trong WordPress
Thông tin người đặt heading Ngăn cách nội dung các fields thông tin khách hàng
·  Họ và tên text Tên khách hàng
·  Số điện thoại number Số điện thoại khách hàng
·  Email email Email của khách hàng
·  Địa chỉ text Địa chỉ của khách hàng
·  Ghi chú textarea Ghi chú nếu có
Thông tin Booking heading Ngăn cách nội dung các fields thông tin Booking
Chi tiết Đặt phòng  group Nhóm các fields về thông tin phòng được đặt. Nhóm field này có thể nhân bản được (dùng trong trường hợp: 1 khách hàng đặt nhiều phòng cùng một lúc)
· Phòng post Chọn một từ trong danh sách phòng hiện có
· Giá  number
· Số người lớn number Người dùng tự nhập
· Số trẻ em number Người dùng tự nhập, mặc định là 0
· Tuổi của trẻ em number Số tuổi của từng trẻ. Tự động thêm 1 trường này khi có thêm 1 trẻ.
· Số giường thêm (extra bed) number Số giường kê thêm nếu có
· Ngày vào ở date
· Ngày trả phòng date
· Tổng số đêm ở number Tự động tính theo ngày vào ở và ngày trả phòng
Số lượng phòng number Tự động tính ra số lượng phòng từ số lượng Group phòng ở trên
Yêu cầu khác textarea Ghi chú các yêu cầu khác nếu có của khách hàng
Thông tin thanh toán heading Ngăn cách nội dung các fields thông tin Thanh toán
· Tổng tiền thanh toán number Tự tính theo số phòng và loại phòng + số giường thêm
· Số tiền đã thanh toán number Cho sales nhập vào số tiền đã thanh toán hoặc cọc trước
· Số tiền còn lại number = Tổng tiền thanh toán – Số tiền đã thanh toán
Tình trạng đơn hàng heading
· Tình trạng đơn hàng select Chọn một trong các nội dung: Đã hủy, Đã thanh toán, Chưa thanh toán
· Số tiền hoàn lại cho khách (nếu hủy) select Các mức hoàn lại cho khách nếu hủy: 100%, 70%, 50%, 0%

Vì số lượng field khá nhiều, nên mình sẽ chỉ hướng dẫn tạo một số field đặc trưng. Do mình sử dụng Meta Box Builder nên việc tạo các field khá đơn giản và thao tác gần tương tự như nhau. Bạn thao tác lần lượt như sau nhé:

Tạo Field Group

Trước khi tạo custom fields bất kỳ, chúng ta cần tạo Field Group (chính là custom meta box) để chứa custom field đó.

Như mình nói ở trên, bạn có thể đến mục Meta Box > Custom Fields > Add New trong admin dashboard, hoặc nhấn luôn Add Custom Fields ngay tại trang chỉnh sửa thông tin post type “Booking” để tạo Field Group.

Tạo Field Group

Bạn điền các thông tin cho Field Group và ở tab Settings của giao diện tạo Field Group, bạn chọn loại post type là Booking ở mục Post Types nhé.

Cho phép Field Group hiển thị với loại Post Types là Booking

Cho phép Field Group hiển thị với loại Post Types là Booking

Tiếp sau đây, bạn quay trở lại với tab Fields của giao diện tạo Field Group để tạo từng custom fields mà bạn cần nhé.

Tạo field Number cho thông tin Số Order

Vẫn ở tab Fields trong phần Edit Field Group, bạn chọn Number ở mục Basic phía bên cột trái để tạo một field loại Number nhé.

Vì trường thông tin số order (mã số của đơn hàng) là một trường thông tin do hệ thống tự động sinh ra, và người dùng không tự thay đổi được, nên mình chọn tick vào ô Read Only và ô Disable.

Tạo field cho trường thông tin Số Order

Tạo field cho trường thông tin Số Order

Tạo Field cho trường thông tin Nhân viên phụ trách

Các nhân viên phụ trách cho đơn hàng được nhập chính là nhân viên sale của khách sạn. Mỗi người này sẽ có một tài khoản trong WordPress admin (gọi là một user). Vậy nên, chúng ta sẽ tạo một field cho phép hiển thị ra danh sách các user trong WordPress để chọn một trong số họ làm người phụ trách cho đơn hàng.

Ở phía bên cột trái, bạn chọn User ở mục WordPress là được nhé.

Tạo user field

Tạo user field

Tạo heading để phân tách các mục trong đơn hàng

Heading thực ra là một field thuộc mục Layout. Nó khác ở chỗ là không cho phép người dùng nhập giá trị và cũng không có ID. Trường này chỉ hiển thị như một heading giúp phân tách các khu vực, nhóm nội dung khác nhau. Các trường heading này sẽ giúp form điền thông tin của bạn dễ nhìn hơn.

Tạo heading để trình bày form đơn đặt hàng cho dễ nhìn hơn

Tạo heading để trình bày form đơn đặt hàng cho dễ nhìn hơn

Tạo một group các field về thông tin chi tiết đặt phòng

Nhóm các field này bao gồm thông tin loại phòng, số người ở, số lượng trẻ em, số lượng extra bed. Việc tạo từng field lẻ này khá là dễ dàng và cơ bản, bạn chỉ cần chọn loại field tương ứng từ phía bên cột trái trong giao diện tạo field là được. Nhưng để nhóm chúng lại thành một nhóm và có thể nhân bản được thì bạn sẽ cần đến sự trợ giúp của Meta Box Group.

Ở mục Layout, bạn chọn Group nhé:

Tạo một group các field về thông tin chi tiết đặt phòng

Ở group các field này, mình chọn đặt Custom CSS Class để ở bước sau mình sẽ xử lý cho phép bắt sự kiện click và update số lượng phòng trong JS.

Tạo Custom CSS Class cho một nhóm các field

Tạo Custom CSS Class cho một nhóm các field

Ngoài ra, với mỗi sub-field bên trong group này, mình có các lưu ý như sau:

1. Field cho tên loại phòng:

Mình chọn loại field type là Post và chọn hiển thị tất cả các post có trong loại post type là Room (các loại phòng mà mình đã tạo ở bài số 1).

Field cho tên loại phòng

2. Ở trường thông tin Trẻ em:

Người dùng sẽ cần điền số lượng trẻ em sẽ ở phòng đang đặt vào ô này. Tùy theo quy định của mỗi khách sạn, thì độ tuổi của trẻ em sẽ quyết định việc có tính thêm tiền vào giá phòng hay không. Vì thế nên nếu người dùng chọn số lượng trẻ em từ 1 trở lên, thì sẽ có thêm các trường thông tin để người dùng điền tuổi của trẻ vào, mình đặt các trường này tên là Age Children 1 và Age Children 2.

Số lượng trường để điền tuổi sẽ tương ứng với số lượng trẻ em được nhập vào ô “Trẻ em”. Ở đây, mình cũng có thêm giới hạn là mỗi phòng chỉ cho phép tối đa 2 trẻ em.

Để làm được việc này, mình sử dụng Meta Box Conditional Logic để quy định khi nào thì hiển thị các field độ tuổi.

Giới hạn số lượng trẻ em tối đa

Giới hạn số lượng trẻ em tối đa

Đặt điều kiện để trường thông tin độ tuổi hiển thị

Đặt điều kiện để trường thông tin độ tuổi hiển thị

Trên đây mình tạo điều kiện cho field độ tuổi của trẻ số 1 là Children >=1, bạn có thể tự đặt điều kiện cho field độ tuổi của trẻ số 2 hiển thị khi Children >=2 nhé.

3. Trường thông tin ngày vào ở và ngày trả phòng:

Hai trường này mình tạo field dưới dạng ngày tháng thông thường và hiển thị lịch cơ bản ra. Ở bài cuối cùng trong series này, mình sẽ hướng dẫn các bạn đưa các thông tin ngày còn phòng trống lên lịch này sau nhé.

Ở mục Advanced bên cột trái, chọn Date. Field này sẽ hiển thị lịch để bạn chọn ngày thay vì tự nhập.

Tạo field dạng lịch để chọn ngày vào ở và trả phòng

Tạo field dạng lịch để chọn ngày vào ở và trả phòng

Tạo các field cho các trường thông tin khác

Bạn cũng áp dụng cách chọn một loại field từ phía bên cột trái để tạo field tương ứng cho loại thông tin bạn cần nhé.

Riêng với các trường thông tin về giá và thanh toán, bạn cứ tạo như bình thường. Chúng ta sẽ xử lý ở bước sau để các trường thông tin này tự động tính giá dựa trên các thông tin điền vào ở các trường thông tin đặt phòng.

Cuối cùng, bạn sẽ thấy ở trang đặt phòng sẽ có các field hiển thị như sau:

Các trường thông tin của form đặt phòng trong backend

Các trường thông tin của form đặt phòng trong backend

Xử lý chức năng cho các fields của trang booking

Ở bước này, chúng ta sẽ xử lý thêm để tự động sinh giá trị cho một số fields đặc biệt, cụ thể:

  • Tự động sinh ra số order cho mỗi đơn đặt phòng;
  • Tự đếm số phòng dựa trên số lần nhân bản nhóm field về thông tin đặt phòng (Số lần nhấn nút “Add Room” (tức “Thêm phòng”) để điền vào ô số lượng phòng đặt;
  • Tự động hiển thị giá phòng tương ứng sau khi chọn loại phòng;
  • Tự động tính tổng số đêm đặt từng phòng;
  • Tự động tính tổng tiền cần thanh toán;

Riêng phần này, chúng ta sẽ cần dùng đến code nhé.

Tự động sinh ra số order cho mỗi đơn đặt phòng

Mỗi bài viết có loại post type là Booking thì đều không có tiêu đề (Title) nên đường dẫn (permalink) cũng không tự sinh ra được, và sẽ ở dạng Auto Draft sau khi lưu. Vì thế, mình sẽ lấy ID của bài viết đó làm tiêu đề và làm đường dẫn luôn cho nó nhé.

Đây là bài viết được lưu khi mình chưa tạo tiêu đề cho nó:

Tự động sinh ra số order cho mỗi đơn đặt phòng

Bạn thêm đoạn code sau vào file functions.php trong thư mục theme của bạn nhé:

function update_post( $post_id ) {
    $post = array(
        'ID' => $post_id,
        'post_title' => '#'.$post_id,
        'post_name' => $post_id,
    );
    wp_update_post( $post );
    update_post_meta($post_id, 'order', $post_id);
}
add_action('save_post_booking', 'update_post', 20);

Trong đó, update_post_meta($post_id, 'order', $post_id) là để tự động đặt post ID làm giá trị của trường số order.

Lưu file functions.php và bạn thử tạo một đơn đặt phòng mới, bạn sẽ thấy tiêu đề của mỗi đơn đặt phòng như sau:

ID của bài viết được tự động đặt làm tiêu đề và đường dẫn của đơn đặt phòng

ID của bài viết được tự động đặt làm tiêu đề và đường dẫn của đơn đặt phòng

Mã số của đơn đặt phòng tự động được sinh theo ID của bài viết

Mã số của đơn đặt phòng tự động được sinh theo ID của bài viết

Tự đếm số phòng dựa trên số lần nhân bản nhóm field về thông tin đặt phòng

Mặc định trường số lượng phòng sẽ có giá trị là 1, tương đương với 1 group field ban đầu cho 1 phòng. Khi người dùng ấn vào nút Add More hay Add Room (thêm phòng) thì ta sẽ cộng giá trị cho trường này thêm 1 đơn vị.

Việc này cần được xử lý bằng Javascript ở backend như sau:

Copy đoạn code sau vào file functions.php. Đoạn code này được sử dụng nhằm nhập file có đường dẫn là /js/booking.js vào trang post-new.php của loại post type là Booking.

function add_admin_scripts( $hook ) {
    global $post;
    if ( $hook == 'post-new.php' && 'booking' === $post->post_type) {
    wp_enqueue_script( 'booking-js', get_stylesheet_directory_uri().'/js/booking.js' );
    }
}
add_action( 'admin_enqueue_scripts', 'add_admin_scripts' );

Tiếp theo, bạn tạo một file có tên là booking.js trong thư mục js. Đường dẫn cụ thể như sau:

Tự đếm số phòng dựa trên số lần nhân bản nhóm field về thông tin đặt phòng

Tiếp theo, bạn mở file booking.js vừa tạo ra và dán đoạn code sau vào file này:

jQuery( document ).ready( function( $ ) {
    $('.group-bookings .add-clone').on('click', function(e){
        setTimeout(function(){
            var rooms = $('.group-bookings .rwmb-group-clone').length;
            $('input#amount').val(rooms);
            update_js();
        }, 100);
    });

    function update_js(){
        $('.group-bookings .remove-clone').on('click', function(e){
            setTimeout(function(){
                var rooms = $('.group-bookings .rwmb-group-clone').length;
                $('input#amount').val(rooms);
            }, 100);
        });
    }
});

Đoạn js trên sẽ giúp bạn cập nhật số lượng phòng mỗi khi người dùng click vào nút Add Room (thêm phòng) hoặc Remove.

Trong đó:

  • ‘group-booking’: là Custom CSS Class của group mà ta đã đặt ở bước tạo một nhóm các fields cho thông tin đặt phòng;
  • ‘amount’: là ID của field tổng số lượng phòng;
  • Add-clone, remove-clone: là Custom CSS Class của 2 nút thêm và xóa phòng. Để xem thông tin này, bạn có thể nhấn nút F12:
Xem class của 2 nút thêm và xóa phòng

Xem class của 2 nút thêm và xóa phòng

Tự động hiển thị giá phòng tương ứng sau khi chọn loại phòng

Đầu tiên, chúng ta cần tạo một field loại Number để hiển thị giá phòng. Mình đặt field này ở dạng Read only và thêm điều kiện hiển thị là khi giá trị của trường loại phòng (room) không phải (khác) “rỗng”. Tức khi field tên loại phòng (Room) có một giá trị bất kỳ (không để trống) thì trường giá phòng mới hiển thị.

Tạo trường thông tin cho giá phòng

Tạo trường thông tin cho giá phòng

Tiếp theo, mình sẽ tạo 1 biến dạng global trong js. Biến này sẽ là một mảng chứa tất cả các thông tin như giá phòng và ID của phòng đó. Mỗi khi người dùng chọn một phòng, biến này sẽ được lôi ra để kiểm tra, nếu ID của loại phòng được chọn bằng với ID nào trong mảng, thì giá phòng của ID đó sẽ được hiển thị.

Để tạo được biến này, ta mở file functions.php, thay đoạn code nhập file booking.js lúc nãy bằng đoạn code sau nhé.

function add_admin_scripts( $hook ) {
    global $post;
        if ( $hook == 'post-new.php' || $hook == 'post.php' ) {
            if ( 'booking' === $post->post_type ) { 
            wp_register_script( 'booking-js', get_stylesheet_directory_uri().'/js/booking.js' ); 
            $rooms = get_posts(array(
                'post_type' => 'room',
                'posts_per_page'=> -1
            ));
            $arr_rooms = array();
            foreach ($rooms as $room) {
                array_push($arr_rooms, array('id'=>$room->ID, 'price'=>rwmb_get_value('price','', $room->ID)));
            }
            wp_localize_script( 'booking-js', 'rooms_data', $arr_rooms);
            wp_enqueue_script( 'booking-js' );
        }
    }
}
add_action( 'admin_enqueue_scripts', 'add_admin_scripts', 10, 1 );

Sau đó, copy đoạn code dưới đây và đưa vào file booking.js:

$( ".group-bookings .rwmb-field select[name*='[room]']" ).on('change', function(){
    var curr = $(this).val();
    var price_unit = $(this).closest('.rwmb-field').siblings().find("input[name*='[price_unit]']");
        rooms_data.forEach(function(val, i){
            if (curr == val['id']){
                price_unit.val(parseInt(val['price']));
            }
        });
});

Bạn sẽ thấy kết quả như này:

Giá phòng được hiển thị tự động ngay sau khi chọn một loại phòng bất kỳv

Giá phòng được hiển thị tự động ngay sau khi chọn một loại phòng bất kỳ

Tính tổng số đêm dựa vào ngày vào ở và ngày trả phòng

Trước khi tính tổng số đêm khách đặt, mình sẽ làm một chức năng liên quan ở đây nữa là đặt điều kiện ngày trả phòng phải là ngày sau ngày vào ở. Mình sẽ sử dụng js để xử lý minDate cho trường thông tin ngày trả phòng bằng với giá trị của trường thông tin ngày vào ở cộng thêm 1 đơn vị.

Bạn copy đoạn code dưới đây và đặt vào file booking.js nhé:

$( ".group-bookings .rwmb-field input[name*='[check_in]']" ).on('change', function(){
    var d = new Date($(this).val());
    var af_date = d.getDate() + 1;
    var af_month = d.getMonth() + 1;
    var af_year = d.getFullYear();
    var min_date = af_year + '-' + af_month + '-' + af_date;
$(this).closest('.rwmb-field').siblings().find("input[name*='[check_out]']").datepicker('option', 'minDate', min_date);
});

Bây giờ, để tính số đêm mà khách sẽ ở tự động dựa theo ngày vào ở và ngày trả phòng, bạn thêm đoạn code sau vào file booking.js nhé:

$( ".group-bookings .rwmb-field input[name*='[check_out]']" ).on('change', function(){
var total = calculate_total_day($(this).closest('.rwmb-field').siblings().find("input[name*='[check_in]']").val(), $(this).val());
$(this).closest('.rwmb-field').siblings().find("input[name*='[total_nights_stay]']").val(total);
});
function calculate_total_day(check_in, check_out){
    var date1 = new Date(check_in); 
    var date2 = new Date(check_out);
    return (date2.getTime() - date1.getTime()) / (1000 * 3600 * 24);
}

Bạn sẽ thấy field Tổng số đêm ở của bạn đã tự động nhảy số khi bạn chọn ngày:

Tự động tính số đêm khách đặt phòng

Tự động tính số đêm khách đặt phòng

Tính tổng tiền cần thanh toán

Công thức tính tổng tiền cần thanh toán như sau:

Tổng tiền cần thanh toán = (Giá phòng 1 + số extra bed phòng 1 * giá) * số đêm
                                         + (Giá phòng 2 + số extra bed phòng 2 * giá) * số đêm
                                         + …
                                         + (Giá phòng N + số extra bed phòng N * giá) * số đêm

Trong đó, mình coi extra bed như một loại phòng và có mức giá cố định luôn, đồng thời, mình sẽ tạo thêm một field là Tổng giá mỗi phòng để tiện cho việc tính toán.

Tổng giá mỗi phòng = (Giá phòng + số extra bed * giá extra bed) * số đêm

Cuối cùng bạn chỉ cần cộng tổng giá trị các field tổng giá mỗi phòng là ra tổng tiền cần thanh toán.

Bạn cần đưa đoạn code này vào file booking.js để tính tổng giá của mỗi phòng:

$( ".group-bookings .rwmb-field input[name*='[check_out]']" ).on('change', function(){
var total = $(this).closest('.rwmb-field').siblings().find("input[name*='[total_nights_stay]']").val();
    var price_unit = $(this).closest('.rwmb-field').siblings().find("input[name*='[price_unit]']").val();
    var extra = $(this).closest('.rwmb-field').siblings().find("input[name*='[extra_bed]']").val();
    rooms_data.forEach(function(val, i){
        if (498 == val['id']){
            total_extra = extra*parseInt(val['price']);
        }
    });
$(this).closest('.rwmb-field').siblings().find("input[name*='[total_amount]']").val((parseInt(price_unit)+parseInt(total_extra))*total);
});

Và đưa tiếp đoạn code này vào file booking.js để tính tổng giá trị tất cả các phòng:

function update_total_payment(){
    var total_payment = 0;
    $( ".group-bookings .rwmb-field input[name*='[total_amount]']" ).each(function(){
        total_payment = parseInt(total_payment) + parseInt($(this).val());
    })
    $('#total').val(total_payment);
}

Bạn sẽ thấy các trường trong form đặt booking của bạn hoạt động đúng logic như sau:

Tính tổng tiền cần thanh toán

Dưới đây là toàn bộ phần code của file booking.js mà mình đã làm để các bạn tham khảo:

Lời cuối

Trong phần hướng dẫn này, mình tự đặt giả thiết về logic đặt phòng nên khi bạn vẫn nên điều chỉnh đôi chút cho phù hợp thực tế sử dụng của bạn. Riêng với phần đặt phòng từ frontend để cho phép khách hàng tự tạo booking, mình sẽ hướng dẫn bạn thực hiện ở bài tiếp theo. Hãy chờ đón đọc bài viết mới của mình nhé!

Gửi phản hồi