diff --git a/table_reservation_on_website/README.rst b/table_reservation_on_website/README.rst index 9d856c3b3..fa402a0ba 100644 --- a/table_reservation_on_website/README.rst +++ b/table_reservation_on_website/README.rst @@ -2,9 +2,9 @@ :target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html :alt: License: LGPL-3 -Table Reservation on Website +Table Reservation On POS And Website ============================ -This module allows to reserve tables in POS from website. +This module allows to reserve tables in POS and website. Configuration ============= diff --git a/table_reservation_on_website/__manifest__.py b/table_reservation_on_website/__manifest__.py index e84598766..16c4bedc2 100644 --- a/table_reservation_on_website/__manifest__.py +++ b/table_reservation_on_website/__manifest__.py @@ -20,8 +20,8 @@ # ############################################################################### { - 'name': 'Table Reservation on Website', - 'version': '17.0.1.0.2', + 'name': 'Table Reservation On POS And Website', + 'version': '17.0.1.1.3', 'category': 'eCommerce,Point of Sale', 'summary': 'Reserve tables in POS from website', 'description': """This module enables to reserve tables in POS from website. @@ -30,9 +30,10 @@ 'company': 'Cybrosys Techno Solutions', 'maintainer': 'Cybrosys Techno Solutions', 'website': 'https://www.cybrosys.com', - 'depends': ['base', 'website_sale', 'pos_restaurant'], + 'depends': ['pos_restaurant', 'base', 'website_sale', 'sale_management'], 'data': [ 'security/ir.model.access.csv', + 'data/automatic_invoice.xml', 'data/table_reservation_data.xml', 'data/product_product_data.xml', 'views/table_reservation_templates.xml', @@ -46,13 +47,29 @@ ], 'assets': { 'point_of_sale._assets_pos': [ - 'table_reservation_on_website/static/src/js/PaymentScreen.js', - 'table_reservation_on_website/static/src/js/FloorScreen.js', - 'table_reservation_on_website/static/src/xml/FloorScreen.xml', + 'table_reservation_on_website/static/src/app/screens/floor_screen' + '/floor_screen.js', + 'table_reservation_on_website/static/src/app/screens/floor_screen' + '/floor_screen.xml', + 'table_reservation_on_website/static/src/app/screens' + '/product_screen/product_screen.js', + 'table_reservation_on_website/static/src/app/screens' + '/reservation_screen/reservation_screen.js', + 'table_reservation_on_website/static/src/app/screens' + '/reservation_screen/reservation_screen.xml', + 'table_reservation_on_website/static/src/app/booking_popup' + '/editBookingPopup.js', + 'table_reservation_on_website/static/src/app/booking_popup' + '/editBookingPopup.xml', + 'table_reservation_on_website/static/src/app/booking_popup' + '/createBookingPopup.js', + 'table_reservation_on_website/static/src/app/booking_popup' + '/createBookingPopup.xml', 'table_reservation_on_website/static/src/scss/style.css', ], 'web.assets_frontend': [ 'table_reservation_on_website/static/src/js/table_reservation.js', + 'table_reservation_on_website/static/src/js/reservation.js', 'table_reservation_on_website/static/src/js/reservation_floor.js', ], }, diff --git a/table_reservation_on_website/controllers/__init__.py b/table_reservation_on_website/controllers/__init__.py index 8a870456e..882148379 100644 --- a/table_reservation_on_website/controllers/__init__.py +++ b/table_reservation_on_website/controllers/__init__.py @@ -19,5 +19,5 @@ # If not, see . # ############################################################################### -from . import main +from . import table_reservation_on_website_website_sale from . import table_reservation_on_website diff --git a/table_reservation_on_website/controllers/main.py b/table_reservation_on_website/controllers/main.py deleted file mode 100644 index fefa378c0..000000000 --- a/table_reservation_on_website/controllers/main.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Cybrosys Technologies Pvt. Ltd. -# -# Copyright (C) 2024-TODAY Cybrosys Technologies() -# Author: Aysha Shalin (odoo@cybrosys.com) -# -# You can modify it under the terms of the GNU LESSER -# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. -# -# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE -# (LGPL v3) along with this program. -# If not, see . -# -############################################################################### -from odoo import http -from odoo.exceptions import ValidationError -from odoo.http import request -from odoo.addons.website_sale.controllers.main import WebsiteSale -from odoo import SUPERUSER_ID - - -class WebsiteSalePayment(WebsiteSale): - """ For creating new record for table reservation """ - @http.route('/shop/payment/validate', type='http', auth="public", - website=True, sitemap=False) - def shop_payment_validate(self, sale_order_id=None, **post): - """ Payment Validate page """ - if sale_order_id is None: - order = request.website.sale_get_order() - if not order and 'sale_last_order_id' in request.session: - last_order_id = request.session['sale_last_order_id'] - order = request.env['sale.order'].sudo().browse( - last_order_id).exists() - else: - order = request.env['sale.order'].sudo().browse(sale_order_id) - assert order.id == request.session.get('sale_last_order_id') - errors = self._get_shop_payment_errors(order) - if errors: - first_error = errors[0] # only display first error - error_msg = f"{first_error[0]}\n{first_error[1]}" - raise ValidationError(error_msg) - tx_sudo = order.get_portal_last_transaction() if order else order.env['payment.transaction'] - if order: - reservation = request.env['table.reservation'].sudo().create({ - "customer_id": request.env.user.partner_id.id, - "booked_tables_ids": order.tables_ids, - "floor_id": order.floors, - "date": order.date, - "starting_at": order.starting_at, - "ending_at": order.ending_at, - 'booking_amount': order.booking_amount, - 'state': 'reserved', - }) - order.table_reservation_id = reservation.id - if not order or (order.amount_total and not tx_sudo): - return request.redirect('/shop') - if order and not order.amount_total and not tx_sudo: - order.with_context(send_email=True).with_user(SUPERUSER_ID).action_confirm() - return request.redirect(order.get_portal_url()) - request.website.sale_reset() - if tx_sudo and tx_sudo.state == 'draft': - return request.redirect('/shop') - return request.redirect('/shop/confirmation') diff --git a/table_reservation_on_website/controllers/table_reservation_on_website.py b/table_reservation_on_website/controllers/table_reservation_on_website.py index 2036f388a..d306944b7 100644 --- a/table_reservation_on_website/controllers/table_reservation_on_website.py +++ b/table_reservation_on_website/controllers/table_reservation_on_website.py @@ -20,7 +20,8 @@ # ############################################################################### from datetime import datetime, timedelta -from odoo import http +from odoo import http, _ +from odoo.exceptions import ValidationError from odoo.http import request @@ -65,11 +66,15 @@ class TableReservation(http.Controller): 'date', '=', datetime.strptime(kwargs.get('date'), "%Y-%m-%d")), ( 'state', '=', 'reserved')]) - start_time_new = datetime.strptime(kwargs.get("start").strip(), "%H:%M").time() + start_time_new = datetime.strptime(kwargs.get("start").strip(), + "%H:%M").time() for rec in reserved: - start_at = datetime.strptime(rec.starting_at, "%H:%M").time() + start_time = datetime.strptime(rec.starting_at, "%H:%M") + start_at = start_time - timedelta( + hours=int(rec.lead_time), + minutes=int((rec.lead_time % 1) * 100)) end_at = datetime.strptime(rec.ending_at, "%H:%M").time() - if start_at <= start_time_new <= end_at: + if start_at.time() <= start_time_new <= end_at: for table in rec.booked_tables_ids: table_inbetween.append(table.id) data_tables = {} @@ -94,58 +99,147 @@ class TableReservation(http.Controller): def booking_confirm(self, **kwargs): """ For booking tables """ company = request.env.company - list_tables = [rec for rec in kwargs.get("tables").split(',')] - record_tables = request.env['restaurant.table'].sudo().search( - [('id', 'in', list_tables)]) - amount = [rec.rate for rec in record_tables] - payment = request.env['ir.config_parameter'].sudo().get_param( - "table_reservation_on_website.reservation_charge") - if payment: - table = request.env.ref( - 'table_reservation_on_website' - '.product_product_table_booking') - table.write({ - 'list_price': sum(amount) - }) - sale_order = request.website.sale_get_order(force_create=True) - if sale_order.state != 'draft': - request.session['sale_order_id'] = None + if kwargs.get("tables"): + list_tables = [rec for rec in kwargs.get("tables").split(',')] + record_tables = request.env['restaurant.table'].sudo().search( + [('id', 'in', list_tables)]) + amount = [rec.rate for rec in record_tables] + payment = request.env['ir.config_parameter'].sudo().get_param( + "table_reservation_on_website.reservation_charge") + if payment: + table = request.env.ref( + 'table_reservation_on_website' + '.product_product_table_booking').sudo() + table.write({ + 'list_price': sum(amount) + }) sale_order = request.website.sale_get_order(force_create=True) - sale_order.sudo().write({ - 'tables_ids': record_tables, - 'floors': kwargs.get('floors'), - 'date': kwargs.get('date'), - 'starting_at': kwargs.get('start_time'), - "ending_at": kwargs.get('end_time'), - 'booking_amount': sum(amount), - 'order_line': [ - (0, 0, { - 'name': request.env.ref( - 'table_reservation_on_website' - '.product_product_table_booking').name, - 'product_id': request.env.ref( - 'table_reservation_on_website' - '.product_product_table_booking').id, - 'product_uom_qty': 1, - 'price_unit': sum(amount), - })], - }) - sale_order.website_id = request.env['website'].sudo().search( - [('company_id', '=', company.id)], limit=1) - return request.redirect("/shop/cart") - else: - request.env['table.reservation'].sudo().create({ - "customer_id": request.env.user.partner_id.id, - "booked_tables_ids": record_tables, - "floor_id": kwargs.get('floors'), - "date": kwargs.get('date'), - "starting_at": kwargs.get('start_time'), - "ending_at": kwargs.get('end_time'), - 'booking_amount': 0, - 'state': 'reserved', - }) + if sale_order.state != 'draft': + request.session['sale_order_id'] = None + sale_order = request.website.sale_get_order( + force_create=True) + sale_order.sudo().write({ + 'tables_ids': record_tables, + 'floors': kwargs.get('floors'), + 'date': kwargs.get('date'), + 'starting_at': kwargs.get('start_time'), + "ending_at": kwargs.get('end_time'), + 'booking_amount': sum(amount), + 'order_line': [ + (0, 0, { + 'name': request.env.ref( + 'table_reservation_on_website' + '.product_product_table_booking').name, + 'product_id': request.env.ref( + 'table_reservation_on_website' + '.product_product_table_booking').id, + 'product_uom_qty': 1, + 'price_unit': sum(amount), + })], + }) + sale_order.website_id = request.env['website'].sudo().search( + [('company_id', '=', company.id)], limit=1) + return request.redirect("/shop/cart") + else: + reservation = request.env['table.reservation'].sudo().create({ + "customer_id": request.env.user.partner_id.id, + "booked_tables_ids": record_tables, + "floor_id": kwargs.get('floors'), + "date": kwargs.get('date'), + "starting_at": kwargs.get('start_time'), + "ending_at": kwargs.get('end_time'), + 'booking_amount': 0, + 'state': 'reserved', + 'type': 'website', + }) + string = f'The reservation amount for the selected table is {reservation.booking_amount}.' if reservation.booking_amount > 0 else '' + list_of_tables = record_tables.mapped('name') + if len(list_of_tables) > 1: + tables_sentence = ', '.join( + list_of_tables[:-1]) + ', and ' + list_of_tables[-1] + else: + tables_sentence = list_of_tables[0] + final_sentence = string + " You have reserved " + tables_sentence + "." + request.env['mail.mail'].sudo().create({ + 'subject': "Table reservation", + 'email_to': request.env.user.login, + 'recipient_ids': [request.env.user.partner_id.id], + 'body_html': + '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '
' + '' + '' + '' + '' + '' + '''''' + '
' + ''+reservation.sequence+'
' + '' + 'Table Reservation' + '' + '
' + '' + '
' + '
' + '
' + '
' + '' + '' + '' + '' + '' + '' + '' + '
' + '
' + 'Dear' + ' ' + request.env.user.name + ',' '
''
' + 'Your table booking at ' + request.env['restaurant.floor'].browse(int(kwargs.get('floors'))).name + ' ' + 'has been confirmed on '+str(reservation.date)+' for '+reservation.starting_at+' to '+reservation.ending_at + '.' + final_sentence + + '
''
' + '' + '
' + '
' + 'Best regards''
' + '' + '
' + '
' + '
' + '
' + '' + '' + '' + '
' + + request.env.company.name + + '
' + + request.env.company.phone + + '
' + '' + '' + + request.env.company.email + + '' + '' + '
' + '' + '
' + }).send() return request.render( "table_reservation_on_website.table_reserved_template") + else: + raise ValidationError(_("Please select table.")) @http.route(['/table/reservation/pos'], type='json', auth='user', website=True) @@ -170,6 +264,7 @@ class TableReservation(http.Controller): 'ending_at': end_time, 'booking_amount': table.rate, 'state': 'reserved', + 'type': 'pos' }) else: request.env['table.reservation'].sudo().create({ @@ -180,6 +275,7 @@ class TableReservation(http.Controller): 'ending_at': end_time, 'booking_amount': 0, 'state': 'reserved', + 'type': 'pos' }) return @@ -188,17 +284,28 @@ class TableReservation(http.Controller): def active_floor_tables(self, floor_id): """ To get active floors """ table_inbetween = [] + product_id = request.env.ref( + 'table_reservation_on_website.' + 'product_product_table_booking_pos') + for rec in request.env['pos.category'].sudo().search([]): + if rec: + product_id.pos_categ_ids = [(4, rec.id, 0)] + table_reservation = request.env['table.reservation'].sudo().search([( 'floor_id', "=", floor_id), ('date', '=', datetime.now().date()), ('state', '=', 'reserved')]) for rec in table_reservation: - start_at = datetime.strptime(rec.starting_at, "%H:%M").time() + start_time = datetime.strptime(rec.starting_at, "%H:%M") + start_at = start_time - timedelta( + hours=int(rec.lead_time), + minutes=int((rec.lead_time % 1) * 100)) end_at = datetime.strptime(rec.ending_at, "%H:%M").time() now = ( (datetime.now() + timedelta(hours=5, minutes=30)).time()).strftime( "%H:%M") - if start_at <= datetime.strptime(now, "%H:%M").time() <= end_at: + if start_at.time() <= datetime.strptime( + now, "%H:%M").time() <= end_at: for table in rec.booked_tables_ids: table_inbetween.append(table.id) return table_inbetween diff --git a/table_reservation_on_website/controllers/table_reservation_on_website_website_sale.py b/table_reservation_on_website/controllers/table_reservation_on_website_website_sale.py new file mode 100644 index 000000000..36975fcc4 --- /dev/null +++ b/table_reservation_on_website/controllers/table_reservation_on_website_website_sale.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import http +from odoo.exceptions import ValidationError +from odoo.http import request +from odoo.addons.website_sale.controllers.main import WebsiteSale +from odoo import SUPERUSER_ID + + +class WebsiteSalePayment(WebsiteSale): + """ For creating new record for table reservation """ + @http.route('/shop/payment/validate', type='http', auth="public", + website=True, sitemap=False) + def shop_payment_validate(self, sale_order_id=None, **post): + """ Payment Validate page """ + if sale_order_id is None: + order = request.website.sale_get_order() + if not order and 'sale_last_order_id' in request.session: + last_order_id = request.session['sale_last_order_id'] + order = request.env['sale.order'].sudo().browse( + last_order_id).exists() + else: + order = request.env['sale.order'].sudo().browse(sale_order_id) + assert order.id == request.session.get('sale_last_order_id') + errors = self._get_shop_payment_errors(order) + if errors: + first_error = errors[0] # only display first error + error_msg = f"{first_error[0]}\n{first_error[1]}" + raise ValidationError(error_msg) + tx_sudo = order.get_portal_last_transaction() if order else order.env['payment.transaction'] + if order.tables_ids: + reservation = request.env['table.reservation'].sudo().create({ + "customer_id": request.env.user.partner_id.id, + "booked_tables_ids": order.tables_ids, + "floor_id": order.floors, + "date": order.date, + "starting_at": order.starting_at, + "ending_at": order.ending_at, + 'booking_amount': order.booking_amount, + 'state': 'reserved', + 'type': 'website' + }) + string = f'The reservation amount for the selected table is {order.order_line[0].price_unit}.' if order.order_line[0].price_unit > 0 else '' + list_of_tables = reservation.booked_tables_ids.mapped('name') + if len(list_of_tables) > 1: + tables_sentence = ', '.join(list_of_tables[:-1]) + ', and ' + \ + list_of_tables[-1] + else: + tables_sentence = list_of_tables[0] + final_sentence = string + " You have reserved table " + tables_sentence + "." + request.env['mail.mail'].sudo().create({ + 'subject': "Table reservation", + 'email_to': request.env.user.login, + 'recipient_ids': [request.env.user.partner_id.id], + 'body_html': + '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '
' + '' + '' + '' + '' + '' + '''''' + '
' + ''+reservation.sequence+'
' + '' + 'Table Reservation' + '' + '
' + '' + '
' + '
' + '
' + '
' + '' + '' + '' + '' + '' + '' + '' + '
' + '
' + 'Dear' + ' ' + request.env.user.name + ',' '
''
' + 'Your table booking at ' + request.env['restaurant.floor'].browse(int(order.floors)).name + ' ' + 'has been confirmed on '+str(reservation.date)+' for '+reservation.starting_at+' to '+reservation.ending_at + '.' + final_sentence + + '
''
' + '' + '
' + '
' + 'Best regards''
' + '' + '
' + '
' + '
' + '
' + '' + '' + '' + '
' + + request.env.company.name + + '
' + + request.env.company.phone + + '
' + '' + '' + + request.env.company.email + + '' + '' + '
' + '' + '
' + }).send() + order.table_reservation_id = reservation.id + if not order or (order.amount_total and not tx_sudo): + return request.redirect('/shop') + if order and not order.amount_total and not tx_sudo: + order.with_context(send_email=True).with_user(SUPERUSER_ID).action_confirm() + return request.redirect(order.get_portal_url()) + request.website.sale_reset() + if tx_sudo and tx_sudo.state == 'draft': + return request.redirect('/shop') + return request.redirect('/shop/confirmation') diff --git a/table_reservation_on_website/data/automatic_invoice.xml b/table_reservation_on_website/data/automatic_invoice.xml new file mode 100644 index 000000000..48eafbd91 --- /dev/null +++ b/table_reservation_on_website/data/automatic_invoice.xml @@ -0,0 +1,10 @@ + + + + + + sale.automatic_invoice + True + + + diff --git a/table_reservation_on_website/data/product_product_data.xml b/table_reservation_on_website/data/product_product_data.xml index c300d56f3..c8e074682 100644 --- a/table_reservation_on_website/data/product_product_data.xml +++ b/table_reservation_on_website/data/product_product_data.xml @@ -5,11 +5,25 @@ Table Booking 0 0 - product + service Table Booking TB - + + + + + + Table Booking[POS] + 0 + 0 + service + + + Table Booking + TB + true diff --git a/table_reservation_on_website/doc/RELEASE_NOTES.md b/table_reservation_on_website/doc/RELEASE_NOTES.md index c635f7872..9acd544b8 100644 --- a/table_reservation_on_website/doc/RELEASE_NOTES.md +++ b/table_reservation_on_website/doc/RELEASE_NOTES.md @@ -3,14 +3,19 @@ #### 17.04.2024 #### Version 17.0.1.0.0 ##### ADD -- Initial commit for Table Reservation on Website +- Initial commit for Table Reservation On POS And Website #### 27.05.2024 #### Version 17.0.1.0.1 -##### UPDT +##### UPDATE - Issue solved on the pos payment screen. #### 19.06.2024 -#### Version 17.0.1.0.1 +#### Version 17.0.1.0.2 ##### BUGFIX - Fixed issues in table booking from website. + +#### 25.07.2024 +#### Version 17.0.1.1.3 +##### BUGFIX +- Updated the module work flow. diff --git a/table_reservation_on_website/models/__init__.py b/table_reservation_on_website/models/__init__.py index 47575509d..8bd5b782a 100644 --- a/table_reservation_on_website/models/__init__.py +++ b/table_reservation_on_website/models/__init__.py @@ -19,7 +19,10 @@ # If not, see . # ############################################################################### +from . import pos_config from . import res_config_settings from . import restaurant_table from . import sale_order from . import table_reservation +from . import restaurant_floor +from . import pos_session diff --git a/table_reservation_on_website/models/pos_config.py b/table_reservation_on_website/models/pos_config.py new file mode 100644 index 000000000..9788b55cc --- /dev/null +++ b/table_reservation_on_website/models/pos_config.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import fields, models + + +class PosConfig(models.Model): + """ Inherited model for adding new field to configuration settings + that allows to add lead time to reservations """ + + _inherit = 'pos.config' + + has_lead_time = fields.Boolean( + string='Lead Time', + compute="_compute_has_lead_time", + help="Enable to set lead time for reservations") + has_reservation_charge = fields.Boolean( + string="Reservation Charge", + compute="_compute_has_reservation_charge", + help="Enable to apply charge for reservations.") + + def _compute_has_lead_time(self): + """ To check whether lead time is enabled from settings """ + if self.env['ir.config_parameter'].sudo().get_param( + 'table_reservation_on_website.is_lead_time'): + self.has_lead_time = True + else: + self.has_lead_time = False + + def _compute_has_reservation_charge(self): + """ To check whether reservation charge is enabled from settings """ + if self.env['ir.config_parameter'].sudo().get_param( + 'table_reservation_on_website.reservation_charge'): + self.has_reservation_charge = True + else: + self.has_reservation_charge = False diff --git a/table_reservation_on_website/models/pos_session.py b/table_reservation_on_website/models/pos_session.py new file mode 100644 index 000000000..f3bb37bcb --- /dev/null +++ b/table_reservation_on_website/models/pos_session.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import models + + +class PosSession(models.Model): + """inherited pos session to load the product product""" + _inherit = 'pos.session' + + def _get_pos_ui_product_product(self, params): + """load product to pos""" + result = super()._get_pos_ui_product_product(params) + product = self.env.ref('table_reservation_on_website' + '.product_product_table_booking') + + data = product.read(fields=params['search_params']['fields']) + append_data = data[0] + append_data['categ'] = {'id': product.categ_id.id, + 'name': product.categ_id.name, + 'parent_id': product.categ_id.parent_id.id, + 'parent': None} + result.append(append_data) + return result diff --git a/table_reservation_on_website/models/res_config_settings.py b/table_reservation_on_website/models/res_config_settings.py index 6ad31c2a5..fbee5b44a 100644 --- a/table_reservation_on_website/models/res_config_settings.py +++ b/table_reservation_on_website/models/res_config_settings.py @@ -32,14 +32,28 @@ class ResConfigSettings(models.TransientModel): "reservation_on_" "website.reservation" "_charge") - refund = fields.Text(string="No Refund Notes", - help="No refund notes to display in website") + refund = fields.Text(string="Notes", + help="You can display this notes in Website table " + "booking") + is_lead_time = fields.Boolean( + string="Lead Time", + help="Enable to set lead time for reservations") + reservation_lead_time = fields.Float( + string="Reservation Lead Time", + help="The order should be reserved hours" + "before the booking start time.") def set_values(self): """ To set the value for fields in config setting """ + res = super(ResConfigSettings, self).set_values() self.env['ir.config_parameter'].set_param( 'table_reservation_on_website.refund', self.refund) - return super(ResConfigSettings, self).set_values() + self.env['ir.config_parameter'].set_param( + 'table_reservation_on_website.is_lead_time', self.is_lead_time) + self.env['ir.config_parameter'].sudo().set_param( + 'table_reservation_on_website.reservation_lead_time', + self.reservation_lead_time) + return res def get_values(self): """ To get the value in config settings """ @@ -47,4 +61,12 @@ class ResConfigSettings(models.TransientModel): refund = self.env['ir.config_parameter'].sudo().get_param( 'table_reservation_on_website.refund') res.update(refund=refund if refund else False) + is_lead_time = self.env['ir.config_parameter'].sudo().get_param( + 'table_reservation_on_website.is_lead_time') + res.update(is_lead_time=is_lead_time if is_lead_time else False) + reservation_lead_time = self.env['ir.config_parameter'].sudo().get_param( + 'table_reservation_on_website.reservation_lead_time') + res.update( + reservation_lead_time=reservation_lead_time if reservation_lead_time + else 0.0) return res diff --git a/table_reservation_on_website/models/restaurant_floor.py b/table_reservation_on_website/models/restaurant_floor.py new file mode 100644 index 000000000..b3375d9db --- /dev/null +++ b/table_reservation_on_website/models/restaurant_floor.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import api, fields, models + + +class RestaurantFloor(models.Model): + """ Inherit restaurant table for adding is_show_field field """ + _inherit = 'restaurant.floor' + + is_show_field = fields.Boolean(string='Show field', + compute='_compute_is_show_field', + help='Depends on the field value field ' + 'rate visibility is determined') + + @api.depends('name') + def _compute_is_show_field(self): + """Compute field rate visibility using this function""" + for record in self: + if record.env['ir.config_parameter'].get_param( + 'table_reservation_on_website.reservation_charge'): + record.is_show_field = True + else: + record.is_show_field = False diff --git a/table_reservation_on_website/models/restaurant_table.py b/table_reservation_on_website/models/restaurant_table.py index bc97baad6..3854e56bf 100644 --- a/table_reservation_on_website/models/restaurant_table.py +++ b/table_reservation_on_website/models/restaurant_table.py @@ -19,7 +19,7 @@ # If not, see . # ############################################################################### -from odoo import fields, models +from odoo import api, fields, models class RestaurantTable(models.Model): @@ -28,3 +28,18 @@ class RestaurantTable(models.Model): rate = fields.Float(string="Amount", help="Amount for reservation of this table") + + is_show_field = fields.Boolean(string='Show field', + compute='_compute_is_show_field', + help='Depends on the field value field ' + 'rate visibility is determined') + + @api.depends('name') + def _compute_is_show_field(self): + """Compute field rate visibility using this function""" + for record in self: + if record.env['ir.config_parameter'].get_param( + 'table_reservation_on_website.reservation_charge'): + record.is_show_field = True + else: + record.is_show_field = False diff --git a/table_reservation_on_website/models/table_reservation.py b/table_reservation_on_website/models/table_reservation.py index 7a44599ff..d142793e7 100644 --- a/table_reservation_on_website/models/table_reservation.py +++ b/table_reservation_on_website/models/table_reservation.py @@ -19,9 +19,9 @@ # If not, see . # ############################################################################### -from datetime import datetime +from datetime import date, datetime, timedelta import re -from odoo import _, api, fields, models +from odoo import api, fields, models, _ from odoo.exceptions import UserError @@ -51,12 +51,39 @@ class TableReservation(models.Model): help="Ending time of reservation", required=True) booking_amount = fields.Float(string="Booking Charge", - help="Amount for booking", + help="Amount for booking", store=True, compute="_compute_booking_amount") state = fields.Selection([('draft', "Draft"), ('reserved', 'Reserved'), ('done', "Done"), ("cancel", "Cancelled")], default='draft', string="Status", help="State for records") + type = fields.Selection(string='Order Type', + selection=[('website', 'Website'), ('pos', 'POS')], + help="The type of Order") + lead_time = fields.Float(string='Lead time', + compute="_compute_lead_time", + store=True, readonly=False, + help="The order should be reserved hours before" + " the booking start time") + lead_time_computed = fields.Boolean(string="Lead Time Computed", + default=False) + order_name = fields.Char(string='Order Name', + help='pos order name') + + @api.depends('lead_time_computed') + def _compute_lead_time(self): + """ Default lead time for reservations """ + for rec in self: + if not rec.lead_time_computed: + is_lead_time = self.env['ir.config_parameter'].sudo().get_param( + 'table_reservation_on_website.is_lead_time') + if is_lead_time and rec.lead_time == 0: + lead_times = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'table_reservation_on_website.reservation_lead_time') + if lead_times: + rec.lead_time = lead_times + rec.lead_time_computed = True @api.model def create(self, vals): @@ -86,9 +113,8 @@ class TableReservation(models.Model): tables = self.env['restaurant.table'].search([('floor_id', '=', self.floor_id.id)]) table_inbetween = [] - reserved = self.search([('floor_id', '=', self.floor_id.id), ('date', - '=', - self.date), + reserved = self.search([('floor_id', '=', self.floor_id.id), + ('date', '=', self.date), ('state', '=', 'reserved')]) if self.starting_at: start_time_new = datetime.strptime( @@ -135,3 +161,189 @@ class TableReservation(models.Model): self.write({ 'state': 'done' }) + + def table_reservations(self): + """ To show reservations in pos product screen """ + today = date.today() + reservations = self.search_read([('date', '>=', today), + ('state', '=', 'reserved')]) + return reservations + + @api.model + def edit_reservations(self, booking_id, date, customer, start_time, + end_time, floor, table_ids, lead, order_name=None): + """ For editing reservations from pos """ + time_float = 0 + is_lead_time = self.env['ir.config_parameter'].sudo().get_param( + "table_reservation_on_website.is_lead_time") + default_lead_time = self.env['ir.config_parameter'].sudo().get_param( + "table_reservation_on_website.reservation_lead_time") + if is_lead_time: + if lead: + if isinstance(lead, str): + hours, minutes = map(int, lead.split(':')) + time_float = hours + minutes / 100.0 + else: + time_float = lead + else: + time_float = default_lead_time + if isinstance(table_ids, str): + table_ids_list = [int(rec) for rec in table_ids.split(',')] + else: + table_ids_list = table_ids + reservation = self.browse(booking_id) + customer_id = self.env['res.partner'].browse(int(customer)) + floor_id = self.env['restaurant.floor'].browse(floor) + reservation.update({ + 'lead_time': time_float, + 'date': datetime.strptime(date, "%Y-%m-%d"), + 'customer_id': customer_id.id, + 'starting_at': start_time, + 'order_name': order_name, + 'ending_at': end_time, + 'floor_id': floor_id.id, + 'booked_tables_ids': [(6, 0, [rec for rec in table_ids_list])], + }) + product_id = self.env.ref( + 'table_reservation_on_website.' + 'product_product_table_booking_pos') + return product_id.id + + @api.model + def get_table_details(self, floor_id, date, start_time, end_time, + booked_table_id=None): + """ To get un-reserved table details """ + table_inbetween = [] + tables = self.env['restaurant.table'].sudo().search( + [('floor_id', '=', int(floor_id))]) + reservations = self.env['table.reservation'].sudo().search( + [('floor_id', '=', int(floor_id)), ( + 'date', '=', datetime.strptime(date, "%Y-%m-%d")), + ('state', '=', 'reserved')]) + start_time = datetime.strptime(start_time, "%H:%M").time() + end_time = datetime.strptime(end_time, "%H:%M").time() + if reservations: + for rec in reservations: + starting_time = datetime.strptime( + rec.starting_at, "%H:%M") + reservation_start = starting_time - timedelta( + hours=int(rec.lead_time), + minutes=int((rec.lead_time % 1) * 100)) + reservation_end = datetime.strptime(rec.ending_at, + "%H:%M").time() + if reservation_start.time() <= start_time <= reservation_end or reservation_start.time() <= end_time < reservation_end: + for table in rec.booked_tables_ids: + table_inbetween.append(table.id) + elif start_time <= reservation_start.time() <= end_time or start_time <= reservation_end < end_time: + for table in rec.booked_tables_ids: + table_inbetween.append(table.id) + data_tables = [] + for rec in tables: + if rec.id not in table_inbetween: + data_tables.append({ + 'id': rec.id, + 'name': rec.name + }) + if booked_table_id: + for id in booked_table_id: + if self.env['restaurant.table'].browse(id).floor_id.id == int( + floor_id): + data_tables.append({ + 'id': id, + 'name': self.env['restaurant.table'].browse(id).name + }) + return data_tables + + @api.model + def get_reservation_amount(self, table_id=None): + """ For fetching the reservation amount details of tables """ + amount = 0 + if table_id: + payment = self.env['ir.config_parameter'].sudo().get_param( + "table_reservation_on_website.reservation_charge") + table_id_list = [int(num) for num in table_id.split(',')] + tables = self.env['restaurant.table'].search([ + ('id', 'in', table_id_list) + ]) + if payment and table_id: + if table_id: + sum_amount = [rec.rate for rec in tables] + amount = sum(sum_amount) + else: + amount = amount + return amount + return amount + + @api.model + def create_table_reservation(self, table_id, date, start_time, end_time, + partner, lead_time, floor_id, order_name=None): + """ For pos table booking """ + time_float = 0 + table_id_list = [int(num) for num in table_id.split(',')] + is_lead_time = self.env['ir.config_parameter'].sudo().get_param( + "table_reservation_on_website.is_lead_time") + default_lead_time = self.env['ir.config_parameter'].sudo().get_param( + "table_reservation_on_website.reservation_lead_time") + if is_lead_time: + if lead_time: + hours, minutes = map(int, lead_time.split(':')) + time_float = hours + minutes / 100.0 + else: + time_float = default_lead_time + else: + time_float = time_float + partner_id = self.env['res.partner'].browse(int(partner)) + self.env['table.reservation'].create({ + 'customer_id': partner_id.id, + 'floor_id': floor_id, + 'booked_tables_ids': [(6, 0, [rec for rec in table_id_list])], + 'date': date, + 'starting_at': start_time, + 'ending_at': end_time, + 'state': 'reserved', + 'type': 'pos', + 'lead_time': time_float, + 'order_name': order_name, + }) + product_id = self.env.ref( + 'table_reservation_on_website.' + 'product_product_table_booking_pos') + return product_id.id + + @api.model + def get_avail_table(self, floor_id, date, start_time, end_time, table_ids): + """To check if table is available while editing reservations from pos""" + table_ids_list = [] + available = True + if table_ids and isinstance(table_ids, str): + table_ids_list = [int(rec) for rec in table_ids.split(',')] + reservations = self.env['table.reservation'].search([ + ('floor_id', '=', int(floor_id)), + ('date', '=', datetime.strptime(date, "%Y-%m-%d")), + ('booked_tables_ids', 'in', table_ids_list), + ('state', '=', 'reserved') + ]) + start_time = datetime.strptime(start_time, "%H:%M").time() + end_time = datetime.strptime(end_time, "%H:%M").time() + if reservations: + for rec in reservations: + starting_time = datetime.strptime( + rec.starting_at, "%H:%M") + reservation_start = starting_time - timedelta( + hours=int(rec.lead_time), + minutes=int((rec.lead_time % 1) * 100)) + reservation_end = datetime.strptime(rec.ending_at, + "%H:%M").time() + if reservation_start.time() <= start_time <= reservation_end: + available = False + elif start_time <= reservation_start.time() <= end_time: + available = False + return available + + @api.model + def cancel_reservations(self, res_id): + """ Cancel reservations from pos screen """ + res = self.browse(int(res_id)) + res.update({ + 'state': 'cancel' + }) diff --git a/table_reservation_on_website/static/description/assets/screenshots/1.png b/table_reservation_on_website/static/description/assets/screenshots/1.png index 6093b173a..b403f65a4 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/1.png and b/table_reservation_on_website/static/description/assets/screenshots/1.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/10.png b/table_reservation_on_website/static/description/assets/screenshots/10.png index 95d630ac4..6a606327c 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/10.png and b/table_reservation_on_website/static/description/assets/screenshots/10.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/11.png b/table_reservation_on_website/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..0dafce447 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/11.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/12.png b/table_reservation_on_website/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..57dd90c90 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/12.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/13.png b/table_reservation_on_website/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..f341dfdcc Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/13.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/14.png b/table_reservation_on_website/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..18848e428 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/14.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/15.png b/table_reservation_on_website/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..e0285fef6 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/15.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/16.png b/table_reservation_on_website/static/description/assets/screenshots/16.png new file mode 100644 index 000000000..ce7c0f292 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/16.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/17.png b/table_reservation_on_website/static/description/assets/screenshots/17.png new file mode 100644 index 000000000..f5daf0b74 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/17.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/18.png b/table_reservation_on_website/static/description/assets/screenshots/18.png new file mode 100644 index 000000000..c9c40ae73 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/18.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/19.png b/table_reservation_on_website/static/description/assets/screenshots/19.png new file mode 100644 index 000000000..a9f5c4c0d Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/19.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/2.png b/table_reservation_on_website/static/description/assets/screenshots/2.png index 20e7cd691..b386e6a5f 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/2.png and b/table_reservation_on_website/static/description/assets/screenshots/2.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/20.png b/table_reservation_on_website/static/description/assets/screenshots/20.png new file mode 100644 index 000000000..46a4b75a0 Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/20.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/21.png b/table_reservation_on_website/static/description/assets/screenshots/21.png new file mode 100644 index 000000000..7ee9397ed Binary files /dev/null and b/table_reservation_on_website/static/description/assets/screenshots/21.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/3.png b/table_reservation_on_website/static/description/assets/screenshots/3.png index 857c81557..f844893fc 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/3.png and b/table_reservation_on_website/static/description/assets/screenshots/3.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/4.png b/table_reservation_on_website/static/description/assets/screenshots/4.png index 3a3223d2d..950bca073 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/4.png and b/table_reservation_on_website/static/description/assets/screenshots/4.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/5.png b/table_reservation_on_website/static/description/assets/screenshots/5.png index 2cb4d1e51..07ee582af 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/5.png and b/table_reservation_on_website/static/description/assets/screenshots/5.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/6.png b/table_reservation_on_website/static/description/assets/screenshots/6.png index 826335f2a..ca10982a2 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/6.png and b/table_reservation_on_website/static/description/assets/screenshots/6.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/7.png b/table_reservation_on_website/static/description/assets/screenshots/7.png index 7cde7967d..673b3f33a 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/7.png and b/table_reservation_on_website/static/description/assets/screenshots/7.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/8.png b/table_reservation_on_website/static/description/assets/screenshots/8.png index 90fefae15..01180133f 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/8.png and b/table_reservation_on_website/static/description/assets/screenshots/8.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/9.png b/table_reservation_on_website/static/description/assets/screenshots/9.png index c7f682c49..5ce5ade25 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/9.png and b/table_reservation_on_website/static/description/assets/screenshots/9.png differ diff --git a/table_reservation_on_website/static/description/assets/screenshots/hero.gif b/table_reservation_on_website/static/description/assets/screenshots/hero.gif index 3ab0406bc..50e816372 100644 Binary files a/table_reservation_on_website/static/description/assets/screenshots/hero.gif and b/table_reservation_on_website/static/description/assets/screenshots/hero.gif differ diff --git a/table_reservation_on_website/static/description/index.html b/table_reservation_on_website/static/description/index.html index 99b43bc11..9c49969f6 100644 --- a/table_reservation_on_website/static/description/index.html +++ b/table_reservation_on_website/static/description/index.html @@ -26,14 +26,6 @@ style="background-color:#017E84 !important;font-size: 0.8rem !important; color:#fff !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;"> Community -
- Enterprise -
-
- Odoo.sh -
@@ -42,10 +34,10 @@ style="margin: 80px 0px !important;">

- Table Reservation on Website

+ Table Reservation On POS And Website

- 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. +

@@ -217,7 +276,7 @@ style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> Cart.

- Redirect to Cart page while Clicking the Button 'Booking Confirm'. + Redirect to Cart page while Clicking the Button 'Booking Confirm' and Make the Payment.

@@ -233,7 +292,139 @@ style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important"> Sale Order.

- 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 @@ + + + + + + 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 @@ + + + + + + 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('
' +data[i]['name'] + - '


'+ data[i]['seats']+ - '
' - + data[i]['rate'] + - '/Slot

'); + var date = this.$el.find("#date_booking").text().trim() + var start = this.$el.find("#booking_start").text() + if (document.getElementById('count_table')){ + document.getElementById('count_table').innerText = 0; + } + if (document.getElementById('total_amount')){ + document.getElementById('total_amount').innerText = 0; + } + var self = this + if (floors && date && start) { + jsonrpc("/restaurant/floors/tables", {'floors_id' : floors, + 'date': date, 'start':start,}) + .then(function (data) { + if(floors == 0){ + self.$el.find('#table_container_row').empty(); + self.$el.find('#info').hide(); } - } - }); + else{ + self.$el.find('#table_container_row').empty(); + self.$el.find('#info').show(); + for (let i in data){ + if (Object.keys(data).length > 1) { + let amount = ''; + if (data[i]['rate'] != 0) { + amount = '
' + data[i]['rate'] + '/Slot'; + } + self.$el.find('#table_container_row').append('
' +data[i]['name'] + + '


'+ data[i]['seats']+ + '' + amount + '

'); + } + else { + let amount = ''; + if (data[i]['rate'] != 0) { + amount = '
' + data[i]['rate'] + '/Slot'; + } + self.$el.find('#table_container_row').append('
' +data[i]['name'] + + '


'+ data[i]['seats']+ + '' + amount + '

'); + } + } + } + }); + } }, }); diff --git a/table_reservation_on_website/views/res_config_settings_views.xml b/table_reservation_on_website/views/res_config_settings_views.xml index c4cb9bbc3..3d876806a 100644 --- a/table_reservation_on_website/views/res_config_settings_views.xml +++ b/table_reservation_on_website/views/res_config_settings_views.xml @@ -14,7 +14,7 @@
+
+
+ +
+
+
+
+ +
+
diff --git a/table_reservation_on_website/views/restaurant_floor_views.xml b/table_reservation_on_website/views/restaurant_floor_views.xml index c2702a71b..787a8949d 100644 --- a/table_reservation_on_website/views/restaurant_floor_views.xml +++ b/table_reservation_on_website/views/restaurant_floor_views.xml @@ -7,9 +7,12 @@ + + + - + diff --git a/table_reservation_on_website/views/restaurant_table_views.xml b/table_reservation_on_website/views/restaurant_table_views.xml index 0ff6c6c35..7bf31906d 100644 --- a/table_reservation_on_website/views/restaurant_table_views.xml +++ b/table_reservation_on_website/views/restaurant_table_views.xml @@ -7,7 +7,8 @@ - + + diff --git a/table_reservation_on_website/views/table_reservation_templates.xml b/table_reservation_on_website/views/table_reservation_templates.xml index 438b30a6b..f3a0b66e0 100644 --- a/table_reservation_on_website/views/table_reservation_templates.xml +++ b/table_reservation_on_website/views/table_reservation_templates.xml @@ -1,34 +1,32 @@ - + - - +