- Reserve Tables in POS through Website.
+ Reserve POS Tables Through Website And Pos.
@@ -133,6 +125,23 @@
+
+
+
+
+
+
+
+ Lead Time.
+
+ Enable the Lead Time if the Table should be Reserved a Certain Amount of Time before the Booking Start Time.
+ You can Edit the Time for Each Reservation Separately from POS.
+
+
+
+
@@ -146,6 +155,10 @@
Add Reservation Amount for POS Tables in Floors.
+
+ Go to Configuration -> Floor Plans, Select Floor and Add Reservation Amount for
+ Tables.
+
@@ -158,7 +171,8 @@
- Select Booking Date and Time.
+ Booking Table from Website.
+
Select Booking Date and Time.
@@ -190,19 +204,64 @@
+
+
+
+
+
+
+
+ Table is Reserved.
+
+
+
-
+
+
+ Table Reservation in Backend.
+
+ New Reservation will be created in the Backend. Go to Configuration -> Table Reservation.
+
+
+
+
+
+
+
Table Reservation Details.
+
+ Order Type should be 'Website' if the Reservation is created from Website and 'POS' if it is created from POS.
+
+
+
+
+
+
+
+
+
+
+
+ Table Booking with Reservation Amount.
+
+ If the Reservation Charge is Enabled then Booking Amount will be Displayed.
+
- Redirect to Cart page while Clicking the Button 'Booking Confirm'.
+ Redirect to Cart page while Clicking the Button 'Booking Confirm' and Make the Payment.
- In sale order we can see the Table Reservation details.
+ In Sale Order we can see the Table Reservation Details.
+
+
+
+
+
+
+
+
+
+
+
+ Book Table from POS.
+
+ Click on 'Book table' to Display the Reservations.
+
+
+
+
+
+
+
+
+
+
+
+ Reservation Screen in POS.
+
+ Displays Current and Upcoming Reservations in POS. You can Create and Edit reservations from here.
+
+
+
+
+
+
+
+
+
+
+
+ Create New Reservations.
+
+ You can Create New Reservations by Clicking on 'Create' Button and Fill the Details.
+ Select any Floor to Choose the Available Tables of Corresponding Floor. If Reservation Charge is
+ not enabled, then you can Confirm your Booking on Clicking 'Confirm' Button.
+
+
+
+
+
+
+
+
+
+
+
+ Charge for Reservations from POS.
+
+ If Reservation Charge is enabled, the Amount will be visible, and you need to Pay the Reservation
+ Amount for Booking by Clicking on 'Pay'.
+
+
+
+
+
+
+
+
+
+
+
+ Reservation Payment.
+
+ Clicking on the Pay Button will create an Order in th POS for the Product 'Table Booking' with a
+ Unit Price equal to the Reservation Amount of the selected Tables, and you can then make the Payment.
+
+
+
+
+
+
+
+
+
+
+
+ Edit the Reservations.
+
+ You can Edit Reservations by Clicking on 'Edit' Button and Edit the Details.
+
+
+
+
+
+
+
+
+
+
+
+ Cancel the Reservations.
+
+ Able to Cancel the Reservations by clicking on 'Cancel' button.
+
+
+
+
+
+
+
+
+
+
+
+ Displayed Reserved Label on Tables in POS Floor Screen.
+
+ The Label 'Reserved' will be Visible on Tables in POS Floor Screen During the Reservation Period.
@@ -275,7 +466,7 @@
- Initial Commit for Table Reservation on Website.
+ Initial Commit for Table Reservation On POS And Website.
diff --git a/table_reservation_on_website/static/src/app/booking_popup/createBookingPopup.js b/table_reservation_on_website/static/src/app/booking_popup/createBookingPopup.js
new file mode 100644
index 000000000..8ed345414
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/booking_popup/createBookingPopup.js
@@ -0,0 +1,213 @@
+/**@odoo-module **/
+import { AbstractAwaitablePopup } from "@point_of_sale/app/popup/abstract_awaitable_popup";
+import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup";
+import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
+import { usePos } from "@point_of_sale/app/store/pos_hook";
+import { useService } from "@web/core/utils/hooks";
+import { useState } from "@odoo/owl";
+import { _t } from "@web/core/l10n/translation";
+
+export class createBookingPopup extends AbstractAwaitablePopup {
+ setup() {
+ super.setup();
+ this.orm = useService('orm');
+ this.pos = usePos();
+ this.popup = useService("popup");
+ this.state = useState({
+ customers: this.env.services.pos.partners,
+ partner: '',
+ floors: this.env.services.pos.floors,
+ floor: '',
+ date: '',
+ start_time: '',
+ end_time: '',
+ tables: [],
+ table: '',
+ amount: '',
+ lead_time: '',
+ Table: '',
+ table_details_header: false,
+ });
+ }
+ // Filter tables according to floor selected
+ async onSelectFloor(ev) {
+ this.state.amount = ''
+ const selectedFloorText = ev.target.options[ev.target.selectedIndex].text;
+ if (ev.target.options[ev.target.selectedIndex].text != 'Select Floor'){
+ var table_data = []
+ this.state.table_details_header = true
+ this.state.Table = ''
+ var floor_id = this.state.floor
+ var date = this.state.date
+ var start_time = this.state.start_time
+ var end_time = this.state.end_time
+ if (start_time > end_time){
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time can't be greater than end time."),
+ });
+ }
+ if ((start_time && end_time) && (start_time === end_time)) {
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time and end time can't be same."),
+ });
+ }
+ if (date && start_time && end_time){
+ var table_data = await this.orm.call('table.reservation', 'get_table_details', [
+ floor_id, date, start_time, end_time])
+ this.state.tables = table_data
+ }
+ }
+ }
+ // To Check selected date is valid one
+ async onChangeDate() {
+ var selectedDate = new Date(this.state.date);
+ const currentDate = new Date();
+ if (selectedDate < currentDate.setHours(0, 0, 0, 0)){
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Date"),
+ body: _t("Please select a valid date."),
+ }).then(() => {
+ this.state.date = null;
+ });
+ }
+ this.onChangeTime()
+ }
+ // To check selected time is not past one
+ onChangeTime() {
+ let now = new Date();
+ let currentHours = now.getHours().toString().padStart(2, '0');
+ let currentMinutes = now.getMinutes().toString().padStart(2, '0');
+ let currentTime = `${currentHours}:${currentMinutes}`;
+ // Get the current date
+ const currentDate = new Date();
+ const year = currentDate.getFullYear();
+ const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ const day = String(currentDate.getDate()).padStart(2, '0');
+ // Format the date as YYYY-MM-DD
+ const formattedDate = `${year}-${month}-${day}`;
+ if (this.state.date == formattedDate){
+ if (this.state.start_time && this.state.start_time < currentTime) {
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Time"),
+ body: _t("You can't select past time."),
+ }).then(() => {
+ this.state.start_time = null;
+ });
+ }
+ else if (this.state.end_time && this.state.end_time < currentTime) {
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Time"),
+ body: _t("You can't select past time."),
+ }).then(() => {
+ this.state.end_time = null;
+ });
+ }
+ }
+ // Check start time is not greater than end time
+ if ((this.state.start_time && this.state.end_time) && (this.state.start_time > this.state.end_time)){
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time can't be greater than end time."),
+ }).then(() => {
+ this.state.start_time = null;
+ this.state.end_time = null;
+ });
+ }
+ // Check start and end time not same
+ if ((this.state.start_time && this.state.end_time) && (this.state.start_time === this.state.end_time)) {
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time and end time can't be same."),
+ }).then(() => {
+ this.state.start_time = null;
+ this.state.end_time = null;
+ });
+ }
+ }
+ // Select tables for booking
+ async onSelectTable(ev) {
+ var table_div = ev.target.closest('.card_table');
+ var tableId = table_div.getAttribute('data-id');
+ if (table_div.style.backgroundColor === 'green') {
+ table_div.style.backgroundColor = '#96ccd5';
+ this.state.Table = this.state.Table.split(',').filter(id => id !== tableId).join(',');
+ } else {
+ table_div.style.backgroundColor = 'green';
+ if (this.state.Table.length > 0) {
+ this.state.Table += ',' + tableId;
+ } else {
+ this.state.Table = tableId;
+ }
+ }
+ if (this.state.floor && this.state.Table !== '') {
+ var reservation_amount = await this.orm.call('table.reservation', 'get_reservation_amount', [this.state.Table]);
+ this.state.amount = reservation_amount;
+ } else {
+ this.state.amount = 0;
+ }
+ }
+ // Create new reservation
+ createReservation() {
+ this.onChangeTime()
+ if (this.state.partner && this.state.date && this.state.start_time && this.state.end_time
+ && this.state.floor && this.state.Table) {
+ this.orm.call('table.reservation', 'create_table_reservation', [
+ this.state.Table, this.state.date, this.state.start_time, this.state.end_time,
+ this.state.partner, this.state.lead_time, this.state.floor])
+ location.reload()
+ }
+ else{
+ this.popup.add(ErrorPopup, {
+ title: _t("Alert"),
+ body: _t("Please fill all the required details."),
+ });
+ }
+ }
+ // Create new reservation and make payments if reservation charge enabled
+ async createReservationPayment(ev) {
+ this.onChangeTime()
+ if (this.state.start_time > this.state.end_time){
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time can't be greater than end time."),
+ });
+ }
+ if ((this.state.start_time && this.state.end_time) && (this.state.start_time === this.state.end_time)) {
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time and end time can't be same."),
+ });
+ }
+ if (this.state.partner && this.state.partner != 'Select Customer') {
+ if (this.state.date && this.state.start_time && this.state.end_time
+ && this.state.floor && this.state.Table) {
+ var data = await this.orm.call('table.reservation', 'create_table_reservation', [
+ this.state.Table, this.state.date, this.state.start_time, this.state.end_time,
+ this.state.partner, this.state.lead_time, this.state.floor, this.pos.get_order().name])
+ this.cancel();
+ this.pos.showScreen('ProductScreen');
+ var product = this.pos.db.product_by_id[data]
+ product['lst_price'] = this.state.amount
+ this.pos.get_order().set_partner(this.pos.db.partner_by_id[parseInt(this.state.partner)])
+ this.pos.get_order().add_product(product, {
+ quantity: 1,
+ });
+ }
+ else{
+ this.popup.add(ErrorPopup, {
+ title: _t("Alert"),
+ body: _t("Please fill all the required details."),
+ });
+ }
+ }
+ else {
+ this.popup.add(ErrorPopup, {
+ title: _t("Alert"),
+ body: _t("Please fill all the required details."),
+ });
+ }
+ }
+}
+createBookingPopup.template = "createBookingPopup";
diff --git a/table_reservation_on_website/static/src/app/booking_popup/createBookingPopup.xml b/table_reservation_on_website/static/src/app/booking_popup/createBookingPopup.xml
new file mode 100644
index 000000000..9731ddaa1
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/booking_popup/createBookingPopup.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+ Reserve Table
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/table_reservation_on_website/static/src/app/booking_popup/editBookingPopup.js b/table_reservation_on_website/static/src/app/booking_popup/editBookingPopup.js
new file mode 100644
index 000000000..ca0fa08b4
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/booking_popup/editBookingPopup.js
@@ -0,0 +1,227 @@
+/**@odoo-module **/
+import { AbstractAwaitablePopup } from "@point_of_sale/app/popup/abstract_awaitable_popup";
+import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup";
+import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
+import { usePos } from "@point_of_sale/app/store/pos_hook";
+import { useService } from "@web/core/utils/hooks";
+import { useState } from "@odoo/owl";
+import { _t } from "@web/core/l10n/translation";
+
+export class EditBookingPopup extends AbstractAwaitablePopup {
+ async setup() {
+ super.setup();
+ this.orm = useService('orm')
+ this.popup = useService("popup")
+ this.pos = usePos()
+ const floors = this.env.services.pos.floors
+ const tables = floors.find(floor => floor.id === this.props.data?.floor_id[0])?.tables || [];
+ const bookedTableIds = this.props.data?.booked_tables_ids || [];
+ const parsedBookedTableIds = typeof bookedTableIds === 'string'
+ ? bookedTableIds.split(',').map(Number).filter(id => !isNaN(id))
+ : [...bookedTableIds];
+ this.state = useState({
+ customerId: this.props.data?.customer_id[0],
+ Date: this.props.data?.date,
+ StartingTime: this.props.data?.starting_at,
+ EndTime: this.props.data?.ending_at,
+ Floor: this.props.data?.floor_id[0],
+ TableList: parsedBookedTableIds,
+ Table: parsedBookedTableIds.join(','),
+ BookingAmount: this.props.data?.booking_amount,
+ OrderType: this.props.data?.type,
+ LeadTime: this.props.data?.lead_time,
+ Partners: this.env.services.pos.partners,
+ floors: this.env.services.pos.floors,
+ tables: [],
+ time:'',
+ table_details_header: false,
+ });
+ if ((this.state.StartingTime && this.state.EndTime) && (this.state.StartingTime === this.state.EndTime)){
+ this.popup.add(ConfirmPopup, {
+ title: _t("Error"),
+ body: _t("Start time and end time can't be same."),
+ });
+ }
+ this.convertDecimalToTime(this.state.LeadTime)
+ var table_data = await this.orm.call('table.reservation', 'get_table_details', [
+ this.state.Floor, this.state.Date, this.state.StartingTime, this.state.EndTime, this.state.TableList])
+ this.state.tables = table_data
+ }
+ // Convert lead time number to string
+ convertDecimalToTime(decimalHours) {
+ const [hours, decimalMinutes] = decimalHours.toString().split('.');
+ const minutes = decimalMinutes ? decimalMinutes.padEnd(2, '0') : '00';
+ const formattedHours = String(hours).padStart(2, '0');
+ const formattedMinutes = String(minutes).padStart(2, '0');
+ this.state.time = `${formattedHours}:${formattedMinutes}`;
+ }
+ // Partner details
+ selectPartner(ev) {
+ this.state.customerId = parseInt(ev.target.value)
+ }
+ // Filter tables according to selected floor
+ async onSelectFloor(ev) {
+ this.state.BookingAmount = ''
+ this.state.TableList = [];
+ if (ev.target.options[ev.target.selectedIndex].text != 'Select Floor'){
+ this.state.table_details_header = true
+ this.state.Floor = parseInt(ev.target.value)
+ var table_data = []
+ var date = this.state.Date
+ var start_time = this.state.StartingTime
+ var end_time = this.state.EndTime
+ var floor_id = this.state.Floor
+ this.state.Table = ''
+ if (start_time > end_time){
+ this.popup.add(ErrorPopup, {
+ title: _t("Error"),
+ body: _t("Start time can't be greater than end time."),
+ });
+ }
+ if (floor_id && date && start_time && end_time){
+ var table_data = await this.orm.call('table.reservation', 'get_table_details', [
+ floor_id, date, start_time, end_time, this.props.data.booked_tables_ids])
+ this.state.tables = table_data
+ }
+ }
+ }
+ // To Check selected date is valid one
+ async onChangeDate() {
+ var selectedDate = new Date($("#date").val());
+ const currentDate = new Date();
+ if (selectedDate < currentDate.setHours(0, 0, 0, 0)){
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Date"),
+ body: _t("Please select a valid date."),
+ }).then(() => {
+ $("#date").val('')
+ });
+ }
+ this.onChangeTime()
+ }
+ // To check selected start time is not past one
+ onChangeTime() {
+ let now = new Date();
+ let currentHours = now.getHours().toString().padStart(2, '0');
+ let currentMinutes = now.getMinutes().toString().padStart(2, '0');
+ let currentTime = `${currentHours}:${currentMinutes}`;
+ // Get the current date
+ const currentDate = new Date();
+ const year = currentDate.getFullYear();
+ const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ const day = String(currentDate.getDate()).padStart(2, '0');
+ // Format the date as YYYY-MM-DD
+ const formattedDate = `${year}-${month}-${day}`;
+ if (this.state.Date == formattedDate){
+ if (this.state.StartingTime && this.state.StartingTime < currentTime) {
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Time"),
+ body: _t("You can't select past time."),
+ }).then(() => {
+ this.state.StartingTime = null;
+ });
+ }
+ if (this.state.EndTime && this.state.EndTime < currentTime) {
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Time"),
+ body: _t("You can't select past time."),
+ }).then(() => {
+ this.state.EndTime = null;
+ });
+ }
+ }
+ if ((this.state.StartingTime && this.state.EndTime) && (this.state.StartingTime === this.state.EndTime)){
+ this.popup.add(ErrorPopup, {
+ title: _t("Error"),
+ body: _t("Start time and end time can't be same."),
+ }).then(() => {
+ this.state.StartingTime = null;
+ this.state.EndTime = null;
+ });
+ }
+ if ((this.state.StartingTime && this.state.EndTime) && (this.state.StartingTime > this.state.EndTime)){
+ this.popup.add(ErrorPopup, {
+ title: _t("Error"),
+ body: _t("Start time can't be greater than end time."),
+ }).then(() => {
+ this.state.StartingTime = null;
+ this.state.EndTime = null;
+ });
+ }
+ }
+ // To Check selected lead time is valid
+ async onChangeLeadTime(ev) {
+ if (isNaN(this.state.LeadTime)) {
+ this.popup.add(ErrorPopup, {
+ title: _t("Invalid Lead Time"),
+ body: _t("Please select a valid lead time."),
+ }).then(() => {
+ this.state.LeadTime = null;
+ });
+ }
+ this.state.LeadTime = ev.target.value
+ this.convertDecimalToTime(this.state.LeadTime)
+ }
+ // Save the edited reservation details
+ async saveData() {
+ var partners = this.env.services.pos.partners
+ var booking_id = this.props.data['id']
+ var date = this.state.Date
+ var customer = this.state.customerId
+ var start_time = this.state.StartingTime
+ var end_time = this.state.EndTime
+ var floor = this.state.Floor
+ var table_ids = this.state.Table
+ var lead_time = this.state.LeadTime
+ this.onChangeTime()
+ if (partners && booking_id && date && customer && start_time && end_time && floor && table_ids.length>0){
+ var data = await this.orm.call('table.reservation', 'edit_reservations', [
+ booking_id, date, customer, start_time, end_time, floor, table_ids, lead_time, this.pos.get_order().name
+ ]);
+ var order = this.pos.orders.find(order => order.name === this.props.data.order_name);
+ if (order){
+ this.pos.removeOrder(order);
+ var product = this.pos.db.product_by_id[data]
+ if (product){
+ product['lst_price'] = this.state.BookingAmount
+ }
+ this.pos.get_order().set_partner(this.pos.db.partner_by_id[parseInt(this.state.customerId)])
+ this.pos.get_order().add_product(product, {
+ quantity: 1,
+ });
+ }
+ location.reload();
+ }
+ else {
+ this.popup.add(ErrorPopup, {
+ title: _t("Alert"),
+ body: _t("Please fill all the required details."),
+ });
+ }
+ }
+ // Select tables for booking
+ async onSelectTable(event) {
+ const tableDiv = event.target.closest('.card_table');
+ const tableId = parseInt(tableDiv.getAttribute('data-id'), 10);
+ let currentTableList = [...this.state.TableList];
+ let currentTable = this.state.Table ? this.state.Table.split(',').map(Number) : [];
+ if (tableDiv.style.backgroundColor == 'green') {
+ tableDiv.style.backgroundColor = '#2980b9';
+ currentTableList = currentTableList.filter(id => id !== tableId);
+ currentTable = currentTable.filter(id => id !== tableId);
+ }
+ else {
+ currentTableList.push(tableId);
+ currentTable.push(tableId);
+ tableDiv.style.backgroundColor = 'green';
+ }
+ // Update state with the new values
+ this.state.TableList = currentTableList;
+ this.state.Table = currentTable.join(',');
+ if(this.state.Floor){
+ var reservation_amount = await this.orm.call('table.reservation', 'get_reservation_amount', [this.state.Table])
+ this.state.BookingAmount = reservation_amount
+ }
+ }
+}
+EditBookingPopup.template = "EditBookingPopup";
diff --git a/table_reservation_on_website/static/src/app/booking_popup/editBookingPopup.xml b/table_reservation_on_website/static/src/app/booking_popup/editBookingPopup.xml
new file mode 100644
index 000000000..ba5782691
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/booking_popup/editBookingPopup.xml
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+ Edit Booking
+
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+ *
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/table_reservation_on_website/static/src/app/screens/floor_screen/floor_screen.js b/table_reservation_on_website/static/src/app/screens/floor_screen/floor_screen.js
new file mode 100644
index 000000000..f6dd2b26b
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/screens/floor_screen/floor_screen.js
@@ -0,0 +1,37 @@
+/** @odoo-module **/
+import { patch } from "@web/core/utils/patch";
+import { FloorScreen } from "@pos_restaurant/app/floor_screen/floor_screen";
+import { jsonrpc } from "@web/core/network/rpc_service";
+patch(FloorScreen.prototype, {
+ async setup() {
+ super.setup(...arguments);
+ await this.fetchActiveTables();
+ },
+ /**
+ For showing reserved tables in pos floor screen
+ **/
+ async fetchActiveTables() {
+ try {
+ var self = this
+ const data = await jsonrpc('/active/floor/tables', { 'floor_id': this.activeFloor.id});
+ this.tables = data;
+ let reserved_tables = [];
+ for (let rec in this.tables) {
+ let new_table = this.activeFloor.tables.find(item => item['id'] == this.tables[rec]);
+ if (new_table) {
+ reserved_tables.push(new_table);
+ }
+ }
+ reserved_tables.forEach(function(record) {
+ record.reserved = true;
+ });
+
+ } catch (error) {
+ console.error('Error fetching active tables:', error);
+ }
+ },
+ get activeTables() {
+ this.fetchActiveTables();
+ return this.activeFloor ? this.activeFloor.tables : null;
+ }
+});
diff --git a/table_reservation_on_website/static/src/xml/FloorScreen.xml b/table_reservation_on_website/static/src/app/screens/floor_screen/floor_screen.xml
similarity index 100%
rename from table_reservation_on_website/static/src/xml/FloorScreen.xml
rename to table_reservation_on_website/static/src/app/screens/floor_screen/floor_screen.xml
diff --git a/table_reservation_on_website/static/src/app/screens/product_screen/product_screen.js b/table_reservation_on_website/static/src/app/screens/product_screen/product_screen.js
new file mode 100644
index 000000000..d532304f5
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/screens/product_screen/product_screen.js
@@ -0,0 +1,10 @@
+/** @odoo-module */
+import { patch } from "@web/core/utils/patch";
+import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen";
+
+patch(ProductScreen.prototype, {
+ // Override the bookTable function for displaying and booking of tables
+ bookTable() {
+ this.pos.showScreen("ReservationsScreen");
+ },
+});
diff --git a/table_reservation_on_website/static/src/app/screens/reservation_screen/reservation_screen.js b/table_reservation_on_website/static/src/app/screens/reservation_screen/reservation_screen.js
new file mode 100644
index 000000000..eeb8dc2d1
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/screens/reservation_screen/reservation_screen.js
@@ -0,0 +1,63 @@
+/** @odoo-module */
+
+import { registry } from "@web/core/registry";
+import { Component, onWillStart } from "@odoo/owl";
+import { useService } from "@web/core/utils/hooks";
+import { useState } from "@odoo/owl";
+import { usePos } from "@point_of_sale/app/store/pos_hook";
+import { _t } from "@web/core/l10n/translation";
+import { EditBookingPopup } from "@table_reservation_on_website/app/booking_popup/editBookingPopup";
+import { createBookingPopup } from "@table_reservation_on_website/app/booking_popup/createBookingPopup";
+
+export class ReservationsScreen extends Component {
+ static template = "table_reservation_on_website.ReservationsScreen";
+ setup() {
+ super.setup(...arguments);
+ this.orm = useService("orm");
+ this.pos = usePos();
+ this.popup = useService("popup");
+ this.state = useState({
+ bookings: [],
+ booking_id: [],
+ });
+ onWillStart(async () => {
+ await this.getReservationList()
+ })
+ }
+ // Displays reservations in reservation screen
+ async getReservationList() {
+ var data = await this.orm.call('table.reservation', 'table_reservations', [[]])
+ this.state.bookings = data
+ const posTables = this.env.services.pos.tables_by_id
+ }
+ // Get reservation details
+ get bookingList() {
+ return this.state.bookings || []
+ }
+ // Popup for editing reservations
+ async onClickEdit(data) {
+ const { confirmed, payload } = await this.popup.add(EditBookingPopup, {
+ title: _t("Edit Reservation"),
+ data
+ });
+ }
+ // For cancelling Reservations
+ async onClickCancel(data){
+ var res_id = data['id']
+ await this.orm.call('table.reservation', 'cancel_reservations', [data['id']])
+ if (data.order_name){
+ var order = this.pos.orders.find(order => order.name === data.order_name);
+ if(order){
+ this.pos.removeOrder(order);
+ }
+ }
+ location.reload()
+ }
+ // Popup for creating reservations
+ async createBooking() {
+ const { confirmed, payload } = await this.popup.add(createBookingPopup, {
+ title: _t("Reserve Table"),
+ });
+ }
+}
+registry.category("pos_screens").add("ReservationsScreen", ReservationsScreen);
diff --git a/table_reservation_on_website/static/src/app/screens/reservation_screen/reservation_screen.xml b/table_reservation_on_website/static/src/app/screens/reservation_screen/reservation_screen.xml
new file mode 100644
index 000000000..345c97d5c
--- /dev/null
+++ b/table_reservation_on_website/static/src/app/screens/reservation_screen/reservation_screen.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+ Discard
+
+
+
+
+
Customer
+
Date
+
Starts at
+
Ends at
+
Floor
+
Table ID
+
Booking Amount
+
Order Type
+
Lead Time
+
Details
+
Cancel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/table_reservation_on_website/static/src/js/FloorScreen.js b/table_reservation_on_website/static/src/js/FloorScreen.js
deleted file mode 100644
index abf3991b0..000000000
--- a/table_reservation_on_website/static/src/js/FloorScreen.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/** @odoo-module **/
-import { patch } from "@web/core/utils/patch";
-import { FloorScreen } from "@pos_restaurant/app/floor_screen/floor_screen";
-import { jsonrpc } from "@web/core/network/rpc_service";
-patch(FloorScreen.prototype, {
- setup() {
- super.setup(...arguments);
- },
- /**
- For payment validation in pos
- **/
- get activeTables() {
- var self = this
- jsonrpc('/active/floor/tables', {'floor_id' : self.activeFloor.id,
- }).then( function(data){
- self.tables = data
- });
- let reserved_tables = []
- for(let rec in self.tables){
- let new_tables = self.activeFloor.tables.find(item => item['id'] == self.tables[rec])
- if (new_tables){
- reserved_tables.push(new_tables)
- }
- }
- reserved_tables.forEach(function(record){
- record.reserved = true;
- });
- return self.activeFloor ? self.activeFloor.tables : null;
- }
-});
diff --git a/table_reservation_on_website/static/src/js/PaymentScreen.js b/table_reservation_on_website/static/src/js/PaymentScreen.js
deleted file mode 100644
index 6ffba1a14..000000000
--- a/table_reservation_on_website/static/src/js/PaymentScreen.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/** @odoo-module **/
-import { _t } from "@web/core/l10n/translation";
-import { PaymentScreen } from "@point_of_sale/app/screens/payment_screen/payment_screen";
-import { jsonrpc } from "@web/core/network/rpc_service";
-import { patch } from "@web/core/utils/patch";
-import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup";
-
-patch(PaymentScreen.prototype, {
- /**
- For payment validation in pos
- **/
- async _finalizeValidation() {
- jsonrpc('/table/reservation/pos',{
- 'table_id': this.currentOrder.tableId
- }).then( function(data){})
- return super._finalizeValidation()
- }
-});
diff --git a/table_reservation_on_website/static/src/js/reservation.js b/table_reservation_on_website/static/src/js/reservation.js
new file mode 100644
index 000000000..34a788e6f
--- /dev/null
+++ b/table_reservation_on_website/static/src/js/reservation.js
@@ -0,0 +1,72 @@
+/** @odoo-module */
+import publicWidget from "@web/legacy/js/public/public_widget";
+
+publicWidget.registry.reservation = publicWidget.Widget.extend({
+ selector: '.container',
+ events: {
+ 'change #date': '_onChangeDate',
+ 'change #start_time': '_onChangeTime',
+ 'change #end_time': '_onChangeTime',
+ 'click .close_btn_alert_modal': '_onClickCloseBtn',
+ 'click .close_btn_time_alert_modal': '_onClickCloseAlertBtn',
+ },
+ // To ensure booking date is a valid one.
+ _onChangeDate: function (ev) {
+ var selectedDate = new Date(this.$el.find("#date").val())
+ const currentDate = new Date();
+ if (selectedDate.setHours(0, 0, 0, 0) < currentDate.setHours(0, 0, 0, 0)) {
+ this.$el.find("#alert_modal").show();
+ this.$el.find("#date").val('')
+ }
+ this._onChangeTime()
+ },
+ // To close the alert modal if invalid date is chosen.
+ _onClickCloseBtn: function() {
+ this.$el.find("#alert_modal").hide();
+ },
+ // Display a modal if invalid start time and end is chosen.
+ _onChangeTime: function() {
+ var start_time = this.$el.find("#start_time")
+ var end_time = this.$el.find("#end_time")
+ let now = new Date();
+ // Get the current time
+ let currentHours = now.getHours().toString().padStart(2, '0');
+ let currentMinutes = now.getMinutes().toString().padStart(2, '0');
+ let currentTime = `${currentHours}:${currentMinutes}`;
+ // Get the current date
+ const currentDate = new Date();
+ const year = currentDate.getFullYear();
+ const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Months are zero-based
+ const day = String(currentDate.getDate()).padStart(2, '0');
+ // Format the date as YYYY-MM-DD
+ const formattedDate = `${year}-${month}-${day}`;
+ if (start_time.val() && end_time.val()) {
+ if (start_time.val() > end_time.val()) {
+ this.$el.find("#time_alert_modal").show()
+ start_time.val('')
+ end_time.val('')
+ }
+ }
+ if (start_time.val() && end_time.val() && (start_time.val() == end_time.val())) {
+ this.$el.find("#time_alert_modal").show()
+ start_time.val('')
+ end_time.val('')
+ }
+ if (formattedDate == this.$el.find("#date").val()){
+ if (start_time.val() && start_time.val() < currentTime) {
+ this.$el.find("#time_alert_modal").show()
+ start_time.val('')
+ end_time.val('')
+ }
+ if (end_time.val() && end_time.val() < currentTime) {
+ this.$el.find("#time_alert_modal").show()
+ start_time.val('')
+ end_time.val('')
+ }
+ }
+ },
+ // To close the alert modal if invalid booking start and end time is chosen.
+ _onClickCloseAlertBtn: function() {
+ this.$el.find("#time_alert_modal").hide()
+ }
+});
diff --git a/table_reservation_on_website/static/src/js/reservation_floor.js b/table_reservation_on_website/static/src/js/reservation_floor.js
index 9873e6d94..a4211ffb5 100644
--- a/table_reservation_on_website/static/src/js/reservation_floor.js
+++ b/table_reservation_on_website/static/src/js/reservation_floor.js
@@ -10,28 +10,51 @@ publicWidget.registry.table_reservation_floor = publicWidget.Widget.extend({
Select table for reservation
**/
_onTableClick: function () {
+ this.$el.find('.submit_button').prop('disabled', false);
var current_div_id = event.target.closest('.card_table')
- var rate = current_div_id.querySelector('#rate').innerText
- var count = this.$el.find('#count_table')[0];
- var amount = this.$el.find('#total_amount')[0];
- var booked = this.$el.find('#tables_input')[0];
+ var rateElement = current_div_id.querySelector('#rate');
+ var countElement = this.$el.find('#count_table')[0];
+ var amountElement = this.$el.find('#total_amount')[0];
+ var bookedElement = this.$el.find('#tables_input')[0];
+ var rate = rateElement ? rateElement.innerText : 0;
if (current_div_id.style.backgroundColor == 'green'){
booked_table.splice(booked_table.indexOf(Number(current_div_id.id)), 1);
current_div_id.style.backgroundColor = '#96ccd5';
- count.innerText = Number(count.innerText) - 1;
- amount.innerText = Number(amount.innerText) - Number(rate)
+ if (countElement) {
+ var countText = countElement.innerText.trim();
+ var count = countText !== '' ? Number(countText) : 0;
+ countElement.innerText = count > 0 ? count - 1 : 0;
+ }
+ if (amountElement) {
+ amountElement.innerText = Number(amountElement.innerText) - Number(rate);
+ }
}
else{
current_div_id.style.backgroundColor = 'green'
- count.innerText = Number(count.innerText) + 1;
+ if (countElement) {
+ var countText = countElement.innerText.trim();
+ var count = countText !== '' ? Number(countText) : 0;
+ countElement.innerText = count + 1;
+ }
booked_table.push(Number(current_div_id.id))
- if (amount.innerText){
- amount.innerText = Number(rate) + Number(amount.innerText)
+ if (amountElement) {
+ if (amountElement.innerText) {
+ amountElement.innerText = Number(rate) + Number(amountElement.innerText);
+ } else {
+ amountElement.innerText = Number(rate);
+ }
+ }
+ }
+ if (bookedElement) {
+ bookedElement.value = booked_table;
+ }
+ if (this.$el.find('#count_table')[0]) {
+ if (Number(this.$el.find('#count_table')[0].innerText.trim()) == 0){
+ this.$el.find('.submit_button').prop('disabled', true);
}
else{
- amount.innerText = Number(rate)
+ this.$el.find('.submit_button').prop('disabled', false);
}
}
- booked.value = booked_table
},
});
diff --git a/table_reservation_on_website/static/src/js/table_reservation.js b/table_reservation_on_website/static/src/js/table_reservation.js
index 4ecfb349a..a2b8b68c1 100644
--- a/table_reservation_on_website/static/src/js/table_reservation.js
+++ b/table_reservation_on_website/static/src/js/table_reservation.js
@@ -2,39 +2,59 @@
import publicWidget from "@web/legacy/js/public/public_widget";
import { jsonrpc } from "@web/core/network/rpc_service";
publicWidget.registry.table_reservation = publicWidget.Widget.extend({
- selector: '#restaurant_floors',
+ selector: '.swa_container',
events: {
'change #floors_rest': '_onFloorChange',
- 'click .card_table': '_onTableClick',
},
/**
To get all tables belongs to the floor
**/
_onFloorChange: function (ev) {
var floors = this.$el.find("#floors_rest")[0].value;
- var date = $("#date_booking").text().trim()
- var start = $("#booking_start").text()
- document.getElementById('count_table').innerText = 0;
- document.getElementById('total_amount').innerText = 0;
- jsonrpc("/restaurant/floors/tables", {'floors_id' : floors,
- 'date': date, 'start':start,})
- .then(function (data) {
- if(floors == 0){
- $('#table_container_row').empty();
- $('#info').hide();
- }
- else{
- $('#table_container_row').empty();
- $('#info').show();
- for (let i in data){
- $('#table_container_row').append('