concretecms-community-store / community_store

An open, free and community developed eCommerce system for Concrete CMS
https://concretecms-community-store.github.io/community_store/
MIT License
106 stars 66 forks source link

Booking system with CS #871

Open cahueya opened 5 months ago

cahueya commented 5 months ago

Hello peeps,

I have a request to implement a booking system for a small guesthouse and I would like to do that with CommunityStore, because I love it :-)

I'm here to spin ideas and/or experiences.

What I THINK to do it bulk-produce a bunch of products for each room ("room1, room2") where each product has a date attribute which is the date of check-in.

So when a customer select their stay, an bunch of products (room1_day1, room1_day2, room1_day3) is selected and then purchased.

In that case, I only have to build a custom product list block to display/select in a proper way and everything else would be standard CommunityStore functionality.

What do you think about that? Or has any of you fine people done a similar thing?

Thanks!

Mesuva commented 5 months ago

I've done a bit to do with linking event tickets, but not so much for accommodation.

The way I think I'd approach is:

Then when an order is placed, I think an event hook may then need to be used to look at the order, work out the product, the number of dates and the dates involved, and then programatically adjust the calendar to block out that period. This bit is perhaps the hardest bit involved, as it would need to handle cancellations, holiday periods, etc.

Anything with dates and booking can get very complicated, very quickly, with lots of little considerations and exception cases. Personally I'd be very hesitant to build something myself - I'd make sure that you check out lots of the third-party platforms for guesthouses/B&Bs, as there's lots to chose from - and they've been built to handle all the complexity. The cost to develop something, versus the ongoing third-party cost would be worth weighing up.

cahueya commented 5 months ago

Thanks for the thinking!

What do you think about the suggestion above? With the ability to import a product by CSV, I can easily build 365 products for each room type where each product represents one day at one room type. Quantity is always just 1.

Each product contains a attribute that contains the check-in date (03-05-2024) with which I can build hook into a calendar display.

This way, the "difficult part" of blocking availability is gone and all I need to customize is the "how to book a room"-display.

So for this small place (12) rooms, it is 365 * 12 products per year with all-predefined description and attributes through CSV.

cahueya commented 5 months ago

The problem with the third-party options is, that they do not hook easily into all the other custom stuff we already have available through concreteCMS. So I can either focus on customizing the third-party integration OR I can focus on making bookings through CommunityStore working and enjoy all the other things we already have available :-)

Mesuva commented 5 months ago

I've actually built a booking add-on for Concrete, one that we've used for booking training sessions (and doing thing like reserving squash courts). It's not related to Community Store, but it uses a similar sort of mechanism to what you've described.

Now this add-on isn't really going to work for accommodation, as it's very much about booking one session/timeslot at a time, but it works well from a management point of view.

I think what you've described could work, having a few thousand products in there shouldn't really break anything as such. The bit that doesn't feel right to me though is that creating a new product, just for the purpose of restricting it, seems like it's the wrong place to handle it. It also would make reporting much more difficult as well (whereas if you have one or two products, you can easily count the quantity sold over a period of time to work out how many nights have been sold).

I reckon if you're going to be building something to handle the calendar display and selection, you probably should just build some sort of basic calendar storage, where you import the CSV of room availability and populate a custom database table.

Each room/date entry could then map against a product, with that in the CSV really just being the product ID.

It also mean you can do things like change the rates easily, without having to remove and re-create a whole number of products.

cahueya commented 5 months ago

Yeah, sounds good too. Well, creating 12 products for 12 rooms really isnt the way to go, more like "2 products for 2 room categories" and the Quantity of each product represents the quantity of availability for that given day. So in this case it would be 2 products with Quantity of 6 each for each day of the year, which makes the reporting display much more useful...

and isn't the pDateAvailableEnd exactly the field that represents the checkout date so pDateAvailableEnd -1 would be the checkin date... So in theory I would build a job that clears out all products after where pDateAvailableEnd < today?

It looks like most of what needs to be there is already available in the products table.

I have to think about the differences of your last suggestion, which is adding more complexity/fragility or which is too limiting. I would love to keep the customization at the display level but maybe I'm thinking too simplistic.

cahueya commented 4 months ago

Alright, I want to keep you up to date in the process and maybe get feedback or warnings about my thought process.

I did some playing with the CSV Import to products and it works fine and with a little customisation does exactly what I need it for.

It's also working well for updating products in bulk. If the SKU already exists, it just updates the attributes and that's it. So my customer will just have CSV template for every room type with all the dates and values and can use that to create update sheets.

So I will stick with the plan to create a product for each room category for each day of the year. I just like the idea of using all the functionality that already comes with the Product Class.

I will use the SKU and endDateAvailable field to identify the Rooms (SKU: DATE-ROOMTYPE e.g. 05132024-A, 13052024-A ).

On the frontend, I want to use https://lopezb.com/hoteldatepicker/ because it looks amazing and does exactly what I need. I'm still struggling with getting it to work, but that's because I'm the Jon Snow of Javascript, I know nuthin about it.

The hoteldatepicker will (once working) return an array of dates to the controller of my custom block. With those dates I will run a query through the Products table and return arrays for each day, holding all the available room types of those days.

So if the customer selects array(05-10-2024, 05-11-2024, 05-12-2024),

I want to grab those dates and run a foreach loop:

foreach $dates as $date {
modifiedbookingdate('Y-m-d', strtotime($date. ' + 1 day')); $bookingdate=SELECT * FROM Products WHERE endDateAvailable=$modifiedbookingdate}

I shift the requested date to match the endDateAvailable, where a room is no more available the day after its availability.

This should give me an array with available Rooms/Products for each of the requested days

array{
    05-10-2024{
        05102024-A, 
        05102024-B
    }
    05-11-2024{
        05112024-A, 
        05112024-B, 
        05112024-C
    }
    05-12-2024{
        05122024-A, 
        05122024-B
        }
    }

I haven't thought much further but then I want to show the room types to the customer, and only those room types that are available on all the selected dates, and then they choose one and I have to figure out how to "bulk-add-to-cart" the items but I think well do that along the way.

Somewhere before that I also need to ask of course how many beds are needed and each room-product contains the number available for that specific roomtype and we'll filter through that too.

Am I somewhere completely wrong in my thinking? Please tell me if I am :-)

cahueya commented 4 months ago

Ok, I have the whole display and filtering going to the point where the controller returns an array of $pID that match the critera like daterange, numerofpeople and roomcategory.

Now while working on submitting that to the Cart, I've realized that I don't see how that is working and if it would be possible to submit an array of products.

I see the form that includes that $pID but I didnt find where it submits to and what the rules are here. Can you help with that, @Mesuva ?

Basically my plan is to merge all matching products into $booking_products arrays where each Item contains the $pID of all the daterange and bedspace matching products and the various arrays are differentiated by the room types, which I have set as product groups.

So in the end there would be a list of booking_products where each booking_product contains an array of all the daterange matching products. The submit button stays with each booking_product and will submit the array of $pID to the cart.

But is it possible or do I need to loop through?

Thanks for hints!

Mesuva commented 4 months ago

The normal add-to-cart process calls a URL mapped to this function: https://github.com/concretecms-community-store/community_store/blob/master/controllers/single_page/cart.php#L150

But it's the StoreCart::add($data) call that actually does the work of adding the product to the cart session: https://github.com/concretecms-community-store/community_store/blob/master/src/CommunityStore/Cart/Cart.php#L254

In $data (which is just the POST array), it's expecting a pID value, a quantity value as a minimum. Then you can also pass it other attributes (if the product has options).

So in your case, you're effectively wanting to call StoreCart::add($data) multiple times, looping through the products you're POST-ing.

What I'd suggest is in your own package, create an endpoint similar to /cart/add, that can receive POST data with multiple products and quantities, loop through it there, passing it through to StoreCart::add. What is perhaps the fiddliest bit is deciding what then happens - you might want to show your own custom popup/cart, showing what has happened, or do something like redirect to the /cart page.

cahueya commented 4 months ago

Aaaah great thank you! Yeah, the plan is to show the cart with each single day shown and then just submit the order.

Next step will be a custom singlepage for a Reports view with the booking shown in a booking table. But submission first. Thank you so much!

cahueya commented 4 months ago

Nice, the logic works. Now I'll fiddle with the display and some extra attributes and report-singlepages. So basically it works as a filtering interface for the products and helps bulk-adding them to the Cart.

Once done, I'll push it to Github for the world to critisize :-D

Thank you for that well-structured, easy extendable code!