@ -0,0 +1,45 @@ |
|||||
|
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
Show Booking Management |
||||
|
======================= |
||||
|
A module for show booking management. Admin can manage shows and Users |
||||
|
can book shows easily through Website by selecting date, screen, time and seats. |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
- No configuration needed |
||||
|
|
||||
|
License |
||||
|
======= |
||||
|
Affero General Public License v3.0 (AGPL v3) |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
* Developer: (V17) Ashwin A, Contact: odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
* Website : http://www.cybrosys.com |
||||
|
|
||||
|
Bug Tracker |
||||
|
----------- |
||||
|
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
||||
|
|
||||
|
Maintainer |
||||
|
========== |
||||
|
.. image:: https://cybrosys.com/images/logo.png |
||||
|
:target: https://cybrosys.com |
||||
|
This module is maintained by Cybrosys Technologies. |
||||
|
For support and more information, please visit https://www.cybrosys.com |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import controller |
||||
|
from . import models |
@ -0,0 +1,70 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
{ |
||||
|
'name': "Show Booking Management", |
||||
|
'version': '17.0.1.0.0', |
||||
|
'category': 'Services', |
||||
|
'summary': "Manage shows and book shows easily.", |
||||
|
'description': """A module for show booking management. Admin can manage shows |
||||
|
and Users can book shows easily through Website by selecting date, |
||||
|
screen, time and seats.""", |
||||
|
'author': "Cybrosys Techno Solutions", |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://www.cybrosys.com", |
||||
|
'depends': ['stock', 'website_sale', 'account', 'portal'], |
||||
|
'data': [ |
||||
|
'security/show_booking_management_groups.xml', |
||||
|
'security/ir.model.access.csv', |
||||
|
'data/product_product_data.xml', |
||||
|
'data/report_paperformat_data.xml', |
||||
|
'data/mail_template_data.xml', |
||||
|
'data/ir_sequence_data.xml', |
||||
|
'data/website_menu_data.xml', |
||||
|
'views/movie_movie_views.xml', |
||||
|
'views/show_type_views.xml', |
||||
|
'views/cast_type_views.xml', |
||||
|
'views/movie_cast_views.xml', |
||||
|
'views/time_slots_views.xml', |
||||
|
'views/movie_screen_views.xml', |
||||
|
'views/movie_registration_views.xml', |
||||
|
'views/website_templates.xml', |
||||
|
'views/portal_template.xml', |
||||
|
'report/movie_registration_templates.xml', |
||||
|
], |
||||
|
'assets': { |
||||
|
'web.assets_backend': [ |
||||
|
'show_booking_management/static/src/js/time_widget.js', |
||||
|
'show_booking_management/static/src/xml/**/*.xml', |
||||
|
], |
||||
|
'web.assets_frontend': [ |
||||
|
'show_booking_management/static/src/js/bookShow.js', |
||||
|
'show_booking_management/static/src/js/selectSeat.js', |
||||
|
'show_booking_management/static/src/css/show_booking_management.css', |
||||
|
], |
||||
|
}, |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': True, |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import payment |
||||
|
from . import portal |
||||
|
from . import show_booking_management |
@ -0,0 +1,102 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
import base64 |
||||
|
import json |
||||
|
from io import BytesIO |
||||
|
import qrcode |
||||
|
from odoo import http |
||||
|
from odoo.http import request |
||||
|
from odoo.addons.payment.controllers.post_processing import PaymentPostProcessing |
||||
|
|
||||
|
|
||||
|
class PaymentPost(PaymentPostProcessing): |
||||
|
""" |
||||
|
Inherit the poll status method to handle payment status completion. |
||||
|
Creates movie registration and movie seats records if payment status is 'done'. |
||||
|
""" |
||||
|
@http.route() |
||||
|
def poll_status(self, **kwargs): |
||||
|
""" Inheriting the poll status and if the status of payment is done |
||||
|
it will create records movie registration and movie seats.""" |
||||
|
res = super(PaymentPost, self).poll_status(**kwargs) |
||||
|
movie_booking_data = request.session.get('movie_booking_data') |
||||
|
if movie_booking_data and res['state'] == 'done': |
||||
|
movie_booking_data = json.loads(movie_booking_data) |
||||
|
# Creating movie.registration record |
||||
|
movie_ticket = request.env['movie.registration'].create({ |
||||
|
'movie_id': movie_booking_data['movie_id'], |
||||
|
'screen_id': movie_booking_data['screen_id'], |
||||
|
'time_slot_id': movie_booking_data['time_slot_id'], |
||||
|
'date': movie_booking_data['booking_date'], |
||||
|
'no_of_tickets': len(movie_booking_data['selected_seats']), |
||||
|
'partner_id': request.env.user.partner_id.id, |
||||
|
'state': 'invoiced' |
||||
|
}) |
||||
|
# Creating QR code |
||||
|
qr_data = f"Ticket : {movie_ticket.name}\n" \ |
||||
|
f"Movie: {movie_ticket.movie_id.name}\n" \ |
||||
|
f"Date: {movie_ticket.date}\n" \ |
||||
|
f"Time: {movie_ticket.time_slot_id.name}\n" \ |
||||
|
f"Screen: {movie_ticket.screen_id.name}\n" \ |
||||
|
f"Seats: {', '.join(movie_booking_data['selected_seats'])}" |
||||
|
qr = qrcode.QRCode(version=1, box_size=10, border=5) |
||||
|
qr.add_data(qr_data) |
||||
|
qr.make(fit=True) |
||||
|
img = qr.make_image() |
||||
|
buffer = BytesIO() |
||||
|
img.save(buffer, format="PNG") |
||||
|
qr_image = base64.b64encode(buffer.getvalue()).decode() |
||||
|
movie_ticket.write({'qr_code': qr_image}) |
||||
|
|
||||
|
# Creating movie.seats records |
||||
|
for seat in movie_booking_data['selected_seats']: |
||||
|
request.env['movie.seats'].create({ |
||||
|
'screen_id': movie_booking_data['screen_id'], |
||||
|
'time_slot_id': movie_booking_data['time_slot_id'], |
||||
|
'movie_registration_id': movie_ticket.id, |
||||
|
'date': movie_booking_data['booking_date'], |
||||
|
'seat': seat, |
||||
|
'is_booked': True |
||||
|
}) |
||||
|
invoice = request.env['account.move'].browse( |
||||
|
movie_booking_data['invoice_id']) |
||||
|
invoice.movie_ticket_id = movie_ticket.id |
||||
|
movie_admin_users = request.env['res.users'].search([ |
||||
|
('groups_id', 'in', |
||||
|
request.env.ref('show_booking_management.show_booking_management_group_admin').id) |
||||
|
]) |
||||
|
template = request.env.ref( |
||||
|
'show_booking_management.email_template_movie_ticket') |
||||
|
attachment_id = request.env['ir.attachment'].create({ |
||||
|
'name': 'Movie_Ticket_QR_Code.png', |
||||
|
'type': 'binary', |
||||
|
'datas': qr_image, |
||||
|
'res_model': 'movie.registration', |
||||
|
'res_id': movie_ticket.id, |
||||
|
'mimetype': 'image/png' |
||||
|
}) |
||||
|
email_values = { |
||||
|
'email_from': movie_admin_users[0].email, |
||||
|
'attachment_ids': [(6, 0, [attachment_id.id])], |
||||
|
} |
||||
|
template.send_mail(movie_ticket.id, email_values=email_values, force_send=True) |
||||
|
return res |
@ -0,0 +1,33 @@ |
|||||
|
from odoo import http |
||||
|
from odoo.http import request |
||||
|
from odoo.addons.portal.controllers.portal import CustomerPortal |
||||
|
|
||||
|
|
||||
|
class ShowPortal(CustomerPortal): |
||||
|
""" Prepare values for the home portal and retrieve shows based on user permissions.""" |
||||
|
def _prepare_home_portal_values(self, counters): |
||||
|
""" |
||||
|
Prepare values for the home portal and retrieve the count |
||||
|
of shows available based on user permissions. |
||||
|
""" |
||||
|
values = super()._prepare_home_portal_values(counters) |
||||
|
if 'shows_count' in counters: |
||||
|
shows_count = request.env['movie.registration'].search_count([]) |
||||
|
|
||||
|
values['shows_count'] = shows_count |
||||
|
return values |
||||
|
|
||||
|
@http.route('/my/shows', type='http', auth="user", website=True) |
||||
|
def my_subscription(self, **kw): |
||||
|
""" |
||||
|
Retrieve shows for the user based on permissions and render them on the portal. |
||||
|
""" |
||||
|
user = request.env.user |
||||
|
if user.has_group('base.group_system'): |
||||
|
shows = request.env['movie.registration'].sudo().search([]) |
||||
|
else: |
||||
|
shows = request.env['movie.registration'].sudo().search([('partner_id', '=', user.partner_id.id)]) |
||||
|
values = { |
||||
|
'shows': shows, |
||||
|
} |
||||
|
return request.render('show_booking_management.portal_my_shows', values) |
@ -0,0 +1,108 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
import json |
||||
|
from werkzeug.urls import url_encode |
||||
|
from odoo import http, fields |
||||
|
from odoo.http import request |
||||
|
|
||||
|
|
||||
|
class MovieShow(http.Controller): |
||||
|
""" This class defines the HTTP routes and movie shows. |
||||
|
It provides functionality to render the movie template for user |
||||
|
interaction.""" |
||||
|
@http.route(['/show'], type='http', auth="public", csrf=False, website=True) |
||||
|
def show_movies(self): |
||||
|
""" Function for rendering show page.""" |
||||
|
movies = request.env['movie.movie'].search([('state', '=', 'ongoing')]) |
||||
|
return http.request.render('show_booking_management.show_movie', |
||||
|
{'movies': movies}) |
||||
|
|
||||
|
@http.route(['/book_now/<int:movie_id>'], type='http', auth="public", |
||||
|
csrf=False, website=True) |
||||
|
def book_now(self, movie_id): |
||||
|
""" Function for rendering booking page while clicking the button book now.""" |
||||
|
movie = request.env['movie.movie'].browse(movie_id) |
||||
|
return http.request.render('show_booking_management.book_movie', |
||||
|
{'movie': movie}) |
||||
|
|
||||
|
@http.route('/movie/book_ticket', type='http', auth='public', website=True, |
||||
|
methods=['POST'], csrf=False) |
||||
|
def book_ticket(self, **kwargs): |
||||
|
""" Function for submitting the form and rendering the seat selection chart.""" |
||||
|
movie_id = request.env['movie.movie'].browse( |
||||
|
int(kwargs.get('movie_id'))) |
||||
|
screen_id = request.env['movie.screen'].browse( |
||||
|
int(kwargs.get('screen'))) |
||||
|
time_slot_id = request.env['time.slots'].browse( |
||||
|
int(kwargs.get('time_slots'))) |
||||
|
booked_seats = request.env['movie.seats'].search([ |
||||
|
('screen_id', '=', screen_id.id), |
||||
|
('time_slot_id', '=', int(kwargs.get('time_slots'))), |
||||
|
('date', '=', kwargs.get('show_date')), |
||||
|
('is_booked', '=', True) |
||||
|
]).mapped('seat') |
||||
|
|
||||
|
return http.request.render( |
||||
|
'show_booking_management.seat_selection_template', |
||||
|
{'screen': screen_id, 'movie': movie_id, |
||||
|
'time_slot_id': time_slot_id, 'booked_seats': booked_seats, |
||||
|
'booked_seats_count': len(booked_seats), |
||||
|
'available_seats_count': screen_id.total_seat_count - len(booked_seats), |
||||
|
'booking_date': kwargs.get('show_date')}) |
||||
|
|
||||
|
@http.route('/movie/confirm_booking', type='http', auth='public', |
||||
|
website=True, methods=['POST'], csrf=True) |
||||
|
def confirm_booking(self, **post): |
||||
|
""" Function for confirming the seats selection and creating invoice.""" |
||||
|
selected_seats = request.httprequest.form.getlist('selected_seats') |
||||
|
movie = request.env['movie.movie'].browse(int(post.get('movie_id'))) |
||||
|
product = request.env.ref('show_booking_management.product_1') |
||||
|
invoice = request.env['account.move'].sudo().create({ |
||||
|
'move_type': 'out_invoice', |
||||
|
'invoice_origin': 'Movie', |
||||
|
'partner_id': request.env.user.partner_id.id, |
||||
|
'invoice_date': fields.Date.today(), |
||||
|
'state': 'draft', |
||||
|
'invoice_line_ids': [(0, 0, { |
||||
|
'name': f"Ticket for {movie.name} on {post.get('booking_date')}", |
||||
|
'product_id': product.id, |
||||
|
'quantity': len(selected_seats), |
||||
|
'price_unit': movie.price, |
||||
|
})], |
||||
|
}) |
||||
|
if invoice: |
||||
|
invoice.sudo().action_post() |
||||
|
access_token = invoice._portal_ensure_token() |
||||
|
booking_data = { |
||||
|
'invoice_id': invoice.id, |
||||
|
'movie_id': int(post.get('movie_id')), |
||||
|
'screen_id': int(post.get('screen_id')), |
||||
|
'time_slot_id': int(post.get('time_slot_id')), |
||||
|
'booking_date': post.get('booking_date'), |
||||
|
'selected_seats': selected_seats, |
||||
|
} |
||||
|
request.session['movie_booking_data'] = json.dumps(booking_data) |
||||
|
params = { |
||||
|
'access_token': access_token, |
||||
|
'payment_method_id': post.get('payment_method_id'), |
||||
|
} |
||||
|
return request.redirect(f'/my/invoices/{invoice.id}?{url_encode(params)}') |
@ -0,0 +1,15 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- Movie Registration sequence --> |
||||
|
<record id="movie_registration_sequence" model="ir.sequence"> |
||||
|
<field name="name">Movie Registration</field> |
||||
|
<field name="code">movie.registration</field> |
||||
|
<field name="prefix">MV</field> |
||||
|
<field name="padding">5</field> |
||||
|
<field name="number_next">1</field> |
||||
|
<field name="number_increment">1</field> |
||||
|
<field name="company_id" eval="False"/> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,61 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<!-- Movie ticket email template --> |
||||
|
<record id="email_template_movie_ticket" model="mail.template"> |
||||
|
<field name="name">Movie Ticket Booking Confirmation</field> |
||||
|
<field name="model_id" ref="show_booking_management.model_movie_registration"/> |
||||
|
<field name="subject">Movie Ticket : {{object.movie_id.name}} On {{object.date}}</field> |
||||
|
<field name="email_to">{{object.partner_id.email}}</field> |
||||
|
<field name="body_html" type="html"> |
||||
|
<p>Your movie ticket booking for |
||||
|
<strong> |
||||
|
<t t-out="object.movie_id.name"/> |
||||
|
</strong> |
||||
|
has been confirmed. |
||||
|
</p> |
||||
|
<h3>Movie Ticket Details:</h3> |
||||
|
|
||||
|
<table style="border-collapse: collapse; width: 50%;"> |
||||
|
<tr> |
||||
|
<th style="border: 1px solid black; padding: 8px; text-align: left;">Movie</th> |
||||
|
<td style="border: 1px solid black; padding: 8px;"> |
||||
|
<strong><t t-out="object.movie_id.name"/></strong> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th style="border: 1px solid black; padding: 8px; text-align: left;">Screen</th> |
||||
|
<td style="border: 1px solid black; padding: 8px;"> |
||||
|
<strong><t t-out="object.screen_id.name"/></strong> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th style="border: 1px solid black; padding: 8px; text-align: left;">Time Slot</th> |
||||
|
<td style="border: 1px solid black; padding: 8px;"> |
||||
|
<strong><t t-out="object.time_slot_id.name"/></strong> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th style="border: 1px solid black; padding: 8px; text-align: left;">Date</th> |
||||
|
<td style="border: 1px solid black; padding: 8px;"> |
||||
|
<strong><t t-out="object.date"/></strong> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th style="border: 1px solid black; padding: 8px; text-align: left;">Number of Tickets</th> |
||||
|
<td style="border: 1px solid black; padding: 8px;"> |
||||
|
<strong><t t-out="object.no_of_tickets"/></strong> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<th style="border: 1px solid black; padding: 8px; text-align: left;">Seats</th> |
||||
|
<td style="border: 1px solid black; padding: 8px;"> |
||||
|
<strong><t t-out="', '.join(object.seat_ids.mapped('seat'))"/></strong> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
<p>Thank you for booking with us.</p> |
||||
|
</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,10 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<!-- Movie Ticket Service Product --> |
||||
|
<record id="product_1" model="product.product"> |
||||
|
<field name="name">Movie Ticket</field> |
||||
|
<field name="type">service</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,17 @@ |
|||||
|
<odoo> |
||||
|
<data> |
||||
|
<!-- Movie ticket receipt paper format --> |
||||
|
<record id="paper_format_movie_ticket" model="report.paperformat"> |
||||
|
<field name="name">Movie Ticket Receipt</field> |
||||
|
<field name="default" eval="False"/> |
||||
|
<field name="format">custom</field> |
||||
|
<field name="page_height">200</field> |
||||
|
<field name="page_width">80</field> |
||||
|
<field name="orientation">Portrait</field> |
||||
|
<field name="margin_top">10</field> |
||||
|
<field name="margin_bottom">10</field> |
||||
|
<field name="margin_left">10</field> |
||||
|
<field name="margin_right">10</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- Show menu in Website --> |
||||
|
<record id="website_movie_show" model="website.menu"> |
||||
|
<field name="name">Show</field> |
||||
|
<field name="url">/show</field> |
||||
|
<field name="parent_id" ref="website.main_menu"/> |
||||
|
<field name="sequence" type="int">50</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,6 @@ |
|||||
|
## Module <show_booking_management> |
||||
|
|
||||
|
#### 06.09.2024 |
||||
|
#### Version 17.0.1.0.0 |
||||
|
##### ADD |
||||
|
-Initial Commit for Show Booking Management |
@ -0,0 +1,30 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from . import account_move |
||||
|
from . import cast_type |
||||
|
from . import movie_cast |
||||
|
from . import movie_movie |
||||
|
from . import movie_registration |
||||
|
from . import movie_screen |
||||
|
from . import movie_seats |
||||
|
from . import show_type |
||||
|
from . import time_slots |
@ -0,0 +1,33 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class AccountMove(models.Model): |
||||
|
""" |
||||
|
Add a Many2one field to link movie tickets with account moves. |
||||
|
""" |
||||
|
_inherit = 'account.move' |
||||
|
|
||||
|
movie_ticket_id = fields.Many2one('account.move', |
||||
|
string='Movie Ticket ID', |
||||
|
help='Movie ticket ID') |
@ -0,0 +1,32 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class CastType(models.Model): |
||||
|
""" |
||||
|
Defines a model for Cast Type. |
||||
|
""" |
||||
|
_name = 'cast.type' |
||||
|
_description = 'Cast Type' |
||||
|
|
||||
|
name = fields.Char(string='Cast Type', help='Mention the Cast type') |
@ -0,0 +1,34 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class MovieCast(models.Model): |
||||
|
""" |
||||
|
Movie Cast model for managing information about movie casts. |
||||
|
""" |
||||
|
_name = 'movie.cast' |
||||
|
_description = 'Movie Cast' |
||||
|
|
||||
|
name = fields.Char(string='Name', help='Name of the movie cast') |
||||
|
cast_id = fields.Many2one('cast.type', string='Cast Type', help='Id of the cast type') |
||||
|
cast_image = fields.Binary(String='Image', help='Image of the movie cast') |
@ -0,0 +1,157 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import api, fields, models |
||||
|
from odoo.exceptions import ValidationError |
||||
|
|
||||
|
|
||||
|
class MovieMovie(models.Model): |
||||
|
""" |
||||
|
Model for managing movie details including name, duration, |
||||
|
release date, show type, language, poster, time slots, cast, |
||||
|
screens, price, overview, currency, and status. |
||||
|
""" |
||||
|
_name = 'movie.movie' |
||||
|
_description = 'Movie Movie' |
||||
|
|
||||
|
name = fields.Char(string='Name', help='Name of the Movie') |
||||
|
duration = fields.Float(string='Duration of the movie', |
||||
|
help='Duration of the Movie') |
||||
|
release_date = fields.Date(string='Release Date', |
||||
|
help='Release date of the Movie') |
||||
|
show_type_ids = fields.Many2many('show.type', |
||||
|
string='Show Type', |
||||
|
help='Show type of the movie') |
||||
|
movie_language_id = fields.Many2one('res.lang', |
||||
|
string='Movie Language', |
||||
|
help='Language of the movie') |
||||
|
movie_poster = fields.Binary(string='Movie Poster', help='Poster of the Movie') |
||||
|
available_time_slots_ids = fields.Many2many('time.slots', |
||||
|
string='Time Slots', |
||||
|
help='Time slots of the movie') |
||||
|
movie_cast_ids = fields.Many2many('movie.cast', |
||||
|
string='Movie Cast', |
||||
|
help='Mention the movie casts') |
||||
|
available_screens_ids = fields.Many2many('movie.screen', |
||||
|
string='Screens', |
||||
|
help='Mention the screens') |
||||
|
show_start_date = fields.Date(string='Show Start Date', |
||||
|
help='Mention the show start date') |
||||
|
show_end_date = fields.Date(string='Show End Date', |
||||
|
help='Mention the show end date') |
||||
|
price = fields.Monetary(currency_field='currency_id', string='Price', |
||||
|
help='Mention the ticket price') |
||||
|
about_movie = fields.Text(string='About Movie', help='Overview of the movie.') |
||||
|
currency_id = fields.Many2one('res.currency', |
||||
|
string='Currency', |
||||
|
help="Currency", |
||||
|
required=True, |
||||
|
default=lambda |
||||
|
self: self.env.user.company_id.currency_id) |
||||
|
state = fields.Selection([('draft', 'Draft'), |
||||
|
('ongoing', 'Ongoing'), |
||||
|
('cancel', 'Cancelled')], |
||||
|
string='Status', default='draft', |
||||
|
help='') |
||||
|
|
||||
|
@api.constrains('show_start_date', 'show_end_date', 'release_date') |
||||
|
def _check_show_start_date(self): |
||||
|
""" Function for validating show start date and end date """ |
||||
|
for record in self: |
||||
|
if (record.show_start_date and record.release_date and |
||||
|
record.show_start_date < record.release_date): |
||||
|
raise ValidationError( |
||||
|
'Show Start date must be on or after the Release Date') |
||||
|
if (record.show_end_date and record.show_end_date and |
||||
|
record.show_start_date > record.show_end_date): |
||||
|
raise ValidationError( |
||||
|
'Show End date must be on or after the Show Start date') |
||||
|
|
||||
|
@api.constrains('available_screens_ids', 'show_start_date', 'show_end_date') |
||||
|
def _check_screen_availability(self): |
||||
|
""" Function for checking the screen availability if the |
||||
|
screen is already booked for another movie it raises error""" |
||||
|
for record in self: |
||||
|
if record.show_start_date and record.show_end_date: |
||||
|
overlapping_movies = self.env['movie.movie'].search([ |
||||
|
('id', '!=', record.id), |
||||
|
('available_screens_ids', 'in', |
||||
|
record.available_screens_ids.ids), |
||||
|
('show_start_date', '<=', record.show_end_date), |
||||
|
('show_end_date', '>=', record.show_start_date), |
||||
|
]) |
||||
|
if overlapping_movies: |
||||
|
raise ValidationError( |
||||
|
'One or more of the selected screens are already booked ' |
||||
|
'for another movie during this period.') |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
"""Supering create function to check screen availability""" |
||||
|
res = super(MovieMovie, self).create(vals) |
||||
|
res._check_screen_availability() |
||||
|
return res |
||||
|
|
||||
|
@api.model |
||||
|
def write(self, vals): |
||||
|
"""Supering create function to check screen availability""" |
||||
|
res = super(MovieMovie, self).write(vals) |
||||
|
self._check_screen_availability() |
||||
|
return res |
||||
|
|
||||
|
@api.model |
||||
|
def check_shows_on_date(self, date, selected_movie): |
||||
|
""" Function for searching the movies based on the date from js.""" |
||||
|
movie = self.search([('id', '=', selected_movie), |
||||
|
('show_start_date', '<=', date), |
||||
|
('show_end_date', '>=', date)]) |
||||
|
return bool(movie) |
||||
|
|
||||
|
def action_start_show(self): |
||||
|
""" Function for changing the state into ongoing.""" |
||||
|
for rec in self: |
||||
|
if fields.Date.today() >= rec.show_start_date: |
||||
|
self.write({'state': 'ongoing'}) |
||||
|
else: |
||||
|
raise ValidationError( |
||||
|
'Show Starts only based on the Show Start Date') |
||||
|
|
||||
|
def action_cancel_show(self): |
||||
|
""" Function for changing the state into cancel.""" |
||||
|
self.write({'state': 'cancel'}) |
||||
|
|
||||
|
@api.model |
||||
|
def update_seats(self, screen_id, time_slot_id, booking_date): |
||||
|
""" |
||||
|
Update the seats availability based on the screen, time slot, and booking date. |
||||
|
""" |
||||
|
booked_seats = self.env['movie.seats'].search([ |
||||
|
('screen_id', '=', int(screen_id)), |
||||
|
('time_slot_id', '=', int(time_slot_id)), |
||||
|
('date', '=', booking_date), |
||||
|
('is_booked', '=', True) |
||||
|
]).mapped('seat') |
||||
|
time_slot = self.env['time.slots'].browse(int(time_slot_id)).name |
||||
|
screen = self.env['movie.screen'].browse(int(screen_id)) |
||||
|
return {'booked_seats': booked_seats, |
||||
|
'time_slot': time_slot, |
||||
|
'booked_seats_count': len(booked_seats), |
||||
|
'available_seats_count': screen.total_seat_count - len(booked_seats)} |
@ -0,0 +1,213 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import api, fields, models |
||||
|
from odoo.exceptions import ValidationError |
||||
|
|
||||
|
|
||||
|
class MovieRegistration(models.Model): |
||||
|
""" |
||||
|
Model for managing movie registrations including details like partner, |
||||
|
movie, date, time slot, screen, tickets, etc. |
||||
|
""" |
||||
|
_name = 'movie.registration' |
||||
|
_description = 'Movie Registration' |
||||
|
|
||||
|
name = fields.Char(required=True, copy=False, |
||||
|
default='New', readonly=True, |
||||
|
help='Name of the Movie Ticket') |
||||
|
partner_id = fields.Many2one('res.partner', string='Select Partner', |
||||
|
help='Mention the partner') |
||||
|
movie_id = fields.Many2one('movie.movie', string='Select Movie', |
||||
|
domain="[('id', 'in', available_movie_ids)]", |
||||
|
required=True, help='Mention the movie id') |
||||
|
movie_type = fields.Many2many('show.type', related='movie_id.show_type_ids', |
||||
|
help='Show type of the movie') |
||||
|
movie_lang = fields.Many2one('res.lang', string='Movie Langauge', |
||||
|
related='movie_id.movie_language_id', |
||||
|
help='Language of the movie') |
||||
|
date = fields.Date(string='Date', default=fields.Date.today(), |
||||
|
required=True, help='Mention the date for booking.') |
||||
|
time_slot_id = fields.Many2one('time.slots', |
||||
|
string='Select time slot', |
||||
|
domain="[('id', 'in', available_time_slot_ids)]", |
||||
|
required=True, help='Mention the time slots of the movie') |
||||
|
screen_id = fields.Many2one('movie.screen', string='Select Screen', |
||||
|
domain="[('id', 'in', available_screens_ids)]", |
||||
|
required=True, help='Mention the screen of the movie') |
||||
|
available_movie_ids = fields.Many2many('movie.movie', |
||||
|
string='Available movies', |
||||
|
help='Mention the available movies') |
||||
|
available_time_slot_ids = fields.Many2many('time.slots', |
||||
|
string='Available time slots', |
||||
|
compute='_compute_available_time_slot_ids', |
||||
|
help='Mention the available time slots') |
||||
|
available_screens_ids = fields.Many2many('movie.screen', |
||||
|
string='Available screens', |
||||
|
help='Mention the available screen') |
||||
|
movie_price = fields.Monetary(string='Movie Price', |
||||
|
related='movie_id.price', |
||||
|
help='Price of the movie ticket') |
||||
|
currency_id = fields.Many2one('res.currency', string='Currency', |
||||
|
help="Currency", |
||||
|
required=True, |
||||
|
default=lambda |
||||
|
self: self.env.user.company_id.currency_id) |
||||
|
no_of_tickets = fields.Integer(string='Number of tickets', default=1, |
||||
|
help='Mention the number of tickets') |
||||
|
movie_poster = fields.Binary(related='movie_id.movie_poster', |
||||
|
string='Movie poster', |
||||
|
help='Poster of the movie.') |
||||
|
movie_cast_ids = fields.Many2many(related='movie_id.movie_cast_ids', |
||||
|
string='Movie Cast', readonly=True, |
||||
|
help='Movie casts') |
||||
|
seat_ids = fields.One2many('movie.seats', 'movie_registration_id', |
||||
|
string="Seats", help='Mention the seat ids') |
||||
|
qr_code = fields.Binary(string='Qr Code', help='Qr code containing ticket details') |
||||
|
state = fields.Selection([('draft', 'Draft'), |
||||
|
('done', 'Done'), |
||||
|
('invoiced', 'Invoiced')], string='Status', |
||||
|
default='draft', help='Status of the movie registration') |
||||
|
|
||||
|
def action_submit(self): |
||||
|
""" Function for writing the state into done.""" |
||||
|
self.write({'state': 'done'}) |
||||
|
|
||||
|
def action_invoice(self): |
||||
|
""" Function for creating invoice.""" |
||||
|
product_id = self.env.ref('show_booking_management.product_1') |
||||
|
try: |
||||
|
move = self.env['account.move'].create([ |
||||
|
{ |
||||
|
'move_type': 'out_invoice', |
||||
|
'partner_id': self.partner_id.id, |
||||
|
'movie_ticket_id': self.id, |
||||
|
'date': self.date, |
||||
|
'invoice_date': fields.Date.today(), |
||||
|
'invoice_line_ids': [ |
||||
|
(0, 0, |
||||
|
{ |
||||
|
'product_id': product_id.id, |
||||
|
'name': product_id.name, |
||||
|
'quantity': self.no_of_tickets, |
||||
|
'price_unit': self.movie_id.price, |
||||
|
})], |
||||
|
}, ]) |
||||
|
self.write({'state': 'invoiced'}) |
||||
|
move.action_post() |
||||
|
return { |
||||
|
'name': 'Invoice', |
||||
|
'res_id': move.id, |
||||
|
'res_model': 'account.move', |
||||
|
'view_id': False, |
||||
|
'view_mode': 'form', |
||||
|
'type': 'ir.actions.act_window', |
||||
|
} |
||||
|
except: |
||||
|
raise ValidationError('Invoice Creation Failed!') |
||||
|
|
||||
|
@api.depends('movie_id') |
||||
|
def _compute_available_time_slot_ids(self): |
||||
|
""" Function for computing time slots and screens.""" |
||||
|
for record in self: |
||||
|
record.available_time_slot_ids = record.movie_id.available_time_slots_ids.ids |
||||
|
record.available_screens_ids = record.movie_id.available_screens_ids.ids |
||||
|
|
||||
|
@api.onchange('date') |
||||
|
def fetch_movies(self): |
||||
|
""" Function for validating date and fetching movies based on the date.""" |
||||
|
for record in self: |
||||
|
if record.date < fields.Date.today(): |
||||
|
raise ValidationError('The date must be greater than or equal to today\'s date.') |
||||
|
record.movie_id = None |
||||
|
record.available_movie_ids = None |
||||
|
movies_list = self.env['movie.movie'].search([ |
||||
|
('show_start_date', '<=', record.date), |
||||
|
('show_end_date', '>=', record.date), |
||||
|
]).ids |
||||
|
record.available_movie_ids = movies_list |
||||
|
|
||||
|
def check_seat_availability(self): |
||||
|
""" Function for checking seat availability""" |
||||
|
reserved_seats = sum(self.search([ |
||||
|
('date', '=', self.date), |
||||
|
('time_slot_id', '=', self.time_slot_id.id), |
||||
|
('screen_id', '=', self.screen_id.id), |
||||
|
('state', '=', 'invoiced') |
||||
|
]).mapped('no_of_tickets')) + self.no_of_tickets |
||||
|
if reserved_seats > self.screen_id.total_seat_count: |
||||
|
raise ValidationError('Selected screen is already full') |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
"""Supering create function to check screen availability""" |
||||
|
if vals.get('name', 'New') == 'New': |
||||
|
vals['name'] = self.env['ir.sequence'].next_by_code( |
||||
|
'movie.registration') |
||||
|
res = super(MovieRegistration, self).create(vals) |
||||
|
res.check_seat_availability() |
||||
|
return res |
||||
|
|
||||
|
@api.constrains('no_of_tickets') |
||||
|
def check_seat(self): |
||||
|
""" Function for checking seat availability based on the number of tickets""" |
||||
|
self.check_seat_availability() |
||||
|
|
||||
|
@api.onchange('movie_id') |
||||
|
def set_values(self): |
||||
|
""" Function for resetting time slot and screen while changing movie.""" |
||||
|
for record in self: |
||||
|
record.update({'time_slot_id': None, 'screen_id': None}) |
||||
|
|
||||
|
def action_generate_ticket_pdf(self): |
||||
|
""" Function for downloading ticket pdf.""" |
||||
|
return self.env.ref( |
||||
|
'show_booking_management.action_report_movie_ticket').report_action(self) |
||||
|
|
||||
|
@api.model |
||||
|
def check_seat_available(self, date, time_slot_id, screen_id, ticket_count): |
||||
|
""" Function for updating the status of the seats availability based |
||||
|
on the movie time slot and screen selected""" |
||||
|
screen = self.env['movie.screen'].browse(int(screen_id)) |
||||
|
reserved_seats = sum(self.search([ |
||||
|
('date', '=', date), |
||||
|
('time_slot_id', '=', int(time_slot_id)), |
||||
|
('screen_id', '=', int(screen_id)), |
||||
|
('state', '=', 'invoiced') |
||||
|
]).mapped('no_of_tickets')) |
||||
|
if reserved_seats + int(ticket_count) > screen.total_seat_count: |
||||
|
return { |
||||
|
'Status': 'Failed', |
||||
|
'Error': f"The selected screen has only " |
||||
|
f"{screen.total_seat_count - reserved_seats} seats left!" |
||||
|
} |
||||
|
return {'Status': 'Success'} |
||||
|
|
||||
|
def action_open_invoices(self): |
||||
|
""" Function for viewing created invoices""" |
||||
|
return { |
||||
|
'name': 'Invoice', |
||||
|
'domain': [('movie_ticket_id', '=', self.id)], |
||||
|
'res_model': 'account.move', |
||||
|
'view_id': False, |
||||
|
'view_mode': 'tree,form', |
||||
|
'type': 'ir.actions.act_window', |
||||
|
} |
@ -0,0 +1,50 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
|
||||
|
class MovieScreen(models.Model): |
||||
|
""" |
||||
|
Model for managing movie screens with details like name, total rows, |
||||
|
seats per row, and total seat count. |
||||
|
""" |
||||
|
_name = 'movie.screen' |
||||
|
_description = 'Movie Screen' |
||||
|
|
||||
|
name = fields.Char(string='Name of the screen', |
||||
|
help='Mention the name of the screen') |
||||
|
total_rows = fields.Integer(string='Number of total Rows', |
||||
|
help='Mention the number of total rows.') |
||||
|
no_of_seat_row = fields.Integer(string='Number of seat per row', |
||||
|
help='Mention the number of seats per row') |
||||
|
total_seat_count = fields.Integer(string='Total seats', |
||||
|
compute='_compute_total_seat_count', |
||||
|
help='Calculates the total seats') |
||||
|
|
||||
|
@api.constrains('total_rows', 'no_of_seat_row') |
||||
|
def _compute_total_seat_count(self): |
||||
|
""" Function for computing total seats in the screen.""" |
||||
|
for record in self: |
||||
|
if record.total_rows and record.no_of_seat_row: |
||||
|
record.total_seat_count = record.total_rows * record.no_of_seat_row |
||||
|
else: |
||||
|
record.total_seat_count = 0 |
@ -0,0 +1,41 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class MovieSeats(models.Model): |
||||
|
""" |
||||
|
Movie Seats model for managing seat bookings. |
||||
|
""" |
||||
|
_name = 'movie.seats' |
||||
|
_description = 'Movie Seats' |
||||
|
|
||||
|
screen_id = fields.Many2one('movie.screen', 'Screen', |
||||
|
required=True, help='Mention the screen id') |
||||
|
time_slot_id = fields.Many2one('time.slots', 'Time Slot', |
||||
|
required=True, help='Mention the time slot id') |
||||
|
movie_registration_id = fields.Many2one('movie.registration', |
||||
|
'Booking ID', required=True, |
||||
|
help='Mention the movie registration id') |
||||
|
date = fields.Date(string='Date', help='Mention the date', required=True) |
||||
|
seat = fields.Char(string='Seat', required=True, help='Mention the seats') |
||||
|
is_booked = fields.Boolean('Is booked', help='Check if is booked true') |
@ -0,0 +1,34 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from odoo import fields, models |
||||
|
|
||||
|
|
||||
|
class ShowType(models.Model): |
||||
|
""" |
||||
|
Show Type model for managing different types of shows. |
||||
|
""" |
||||
|
_name = 'show.type' |
||||
|
_description = 'Show Type' |
||||
|
|
||||
|
name = fields.Char(string='Show Type', |
||||
|
required=True, |
||||
|
help='Name of the Show type') |
@ -0,0 +1,60 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################# |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
||||
|
# |
||||
|
# You can modify it under the terms of the GNU AFFERO |
||||
|
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
||||
|
# |
||||
|
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
|
# (AGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################# |
||||
|
from datetime import datetime |
||||
|
from odoo import api, fields, models |
||||
|
from odoo.exceptions import ValidationError |
||||
|
|
||||
|
|
||||
|
class TimeSlots(models.Model): |
||||
|
""" |
||||
|
Time slots model for managing different time slots for show. |
||||
|
""" |
||||
|
_name = 'time.slots' |
||||
|
_description = 'Time Slots' |
||||
|
|
||||
|
name = fields.Char(string='Time Slot', default='New', |
||||
|
readonly=True, help='Mention the name of the Time slots') |
||||
|
movie_time = fields.Char(string='Movie Time', help='Mention the slot time') |
||||
|
|
||||
|
_sql_constraints = [ |
||||
|
('name_uniq', 'unique(name)', "Name should be unique") |
||||
|
] |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
"""Supering create function to update name.""" |
||||
|
if vals['movie_time']: |
||||
|
vals['name'] = datetime.strptime(vals['movie_time'], "%H:%M").strftime("%I:%M %p") |
||||
|
vals['movie_time'] = vals['movie_time'].replace(":", ".") |
||||
|
else: |
||||
|
raise ValidationError('Please mention time!!') |
||||
|
return super().create(vals) |
||||
|
|
||||
|
@api.model |
||||
|
def write(self, vals): |
||||
|
"""Supering write function to update name.""" |
||||
|
if vals['movie_time']: |
||||
|
vals['name'] = datetime.strptime(vals['movie_time'], |
||||
|
"%H:%M").strftime("%I:%M %p") |
||||
|
vals['movie_time'] = vals['movie_time'].replace(":", ".") |
||||
|
res = super(TimeSlots, self).write(vals) |
||||
|
return res |
@ -0,0 +1,77 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Movie ticket report template --> |
||||
|
<template id="report_movie_ticket"> |
||||
|
<t t-call="web.basic_layout"> |
||||
|
<div class="page"> |
||||
|
<div class="oe_structure"/> |
||||
|
<div class="pos-receipt" style="width:80mm;padding:10px;font-size:10pt;"> |
||||
|
<t t-foreach="docs" t-as="doc"> |
||||
|
<div class="receipt-header" style="text-align: center;margin-bottom: 10px;"> |
||||
|
<h2>Movie Ticket</h2> |
||||
|
<h3><t t-esc="doc.name"/></h3> |
||||
|
</div> |
||||
|
<br/> |
||||
|
<div style="margin-top:10px;text-align: center;border-top: 1px solid #000;border-bottom: 1px solid #000;"> |
||||
|
<div class="receipt-center-align" style="text-align: center;padding: 5px;"> |
||||
|
<img t-attf-src="data:image/png;base64,#{doc.qr_code}" style="width:100px; height:100px;"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="receipt-ticket-data" style="margin-top: 10px;"> |
||||
|
<div style="text-align: center;margin:10px;"> |
||||
|
<img t-attf-src="data:image/png;base64,#{doc.movie_id.movie_poster}" style="width:150px; height:200px;"/> |
||||
|
</div> |
||||
|
<table class="table table-hover" style="width:100%;margin-top: 10px;display: flex;"> |
||||
|
<tr style="display: flex;"> |
||||
|
<td style="padding: 5px;">Movie:</td> |
||||
|
<td style="padding: 5px;"><strong><t t-esc="doc.movie_id.name"/></strong></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="padding: 5px;">Date:</td> |
||||
|
<td style="padding: 5px;"><strong><t t-esc="doc.date"/></strong></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="padding: 5px;">Time:</td> |
||||
|
<td style="padding: 5px;"><strong><t t-esc="doc.time_slot_id.name"/></strong></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="padding: 5px;">Screen:</td> |
||||
|
<td style="padding: 5px;"><strong><t t-esc="doc.screen_id.name"/></strong></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="padding: 5px;">No of Tickets:</td> |
||||
|
<td style="padding: 5px;"><strong><t t-esc="doc.no_of_tickets"/></strong></td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="padding: 5px;">Seats:</td> |
||||
|
<td> |
||||
|
<t t-foreach="doc.seat_ids" t-as="seat"> |
||||
|
<span style="padding: 5px;background:#7ec4ed;border-radius: 10px;"><strong><t t-esc="seat.seat"/></strong></span> |
||||
|
</t> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
|
</div> |
||||
|
<div class="receipt-footer" style="margin-top:20px;text-align: center;"> |
||||
|
<div class="receipt-center-align" style="text-align: center;padding-top: 5px;"> |
||||
|
<strong>Enjoy Your Movie!</strong> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
</template> |
||||
|
<!-- Action report movie ticket --> |
||||
|
<record id="action_report_movie_ticket" model="ir.actions.report"> |
||||
|
<field name="name">Movie Ticket</field> |
||||
|
<field name="model">movie.registration</field> |
||||
|
<field name="report_type">qweb-pdf</field> |
||||
|
<field name="report_name">show_booking_management.report_movie_ticket</field> |
||||
|
<field name="report_file">show_booking_management.report_movie_ticket</field> |
||||
|
<field name="binding_model_id" |
||||
|
ref="show_booking_management.model_movie_registration"/> |
||||
|
<field name="paperformat_id" |
||||
|
ref="show_booking_management.paper_format_movie_ticket"/> |
||||
|
</record> |
||||
|
</odoo> |
|
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Group Movie Admin --> |
||||
|
<record id="show_booking_management_group_admin" model="res.groups"> |
||||
|
<field name="name">Show Booking Management Admin</field> |
||||
|
<field name="users" eval="[(4, ref('base.user_admin'))]"/> |
||||
|
</record> |
||||
|
</odoo> |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 912 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 256 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 185 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 196 KiB |
After Width: | Height: | Size: 389 KiB |
After Width: | Height: | Size: 880 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 4.3 KiB |
@ -0,0 +1,137 @@ |
|||||
|
.movie_details{ |
||||
|
display: -webkit-box; |
||||
|
} |
||||
|
|
||||
|
.param{ |
||||
|
margin-left:10px; |
||||
|
} |
||||
|
|
||||
|
.sidebar{ |
||||
|
max-width:280px; |
||||
|
margin-left: 20px; |
||||
|
margin-right:20px; |
||||
|
margin-bottom:20px; |
||||
|
padding: 20px; |
||||
|
background-color: #c9e3fd; |
||||
|
border: 1px solid #ddd; |
||||
|
border-radius: 5px; |
||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.seating-chart-container { |
||||
|
position: relative; |
||||
|
display: ruby-text; |
||||
|
max-width: 100%; |
||||
|
overflow-x: auto; |
||||
|
user-select: none; |
||||
|
} |
||||
|
|
||||
|
.seating-chart { |
||||
|
flex-grow: 1; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
|
||||
|
.seat-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.seat-row-inner { |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.seat-wrapper { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
.seat-label { |
||||
|
border-radius: 5px; |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
text-align: center; |
||||
|
line-height: 30px; |
||||
|
cursor: pointer; |
||||
|
background-color: #d3d3d3; |
||||
|
} |
||||
|
|
||||
|
.seat-checkbox { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.seat-checkbox:checked + .seat-label { |
||||
|
background-color: #28a745; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.seat-checkbox:disabled + .seat-label { |
||||
|
background-color: #dc3545; |
||||
|
color: white; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
.screen{ |
||||
|
text-align: center; |
||||
|
background: darkseagreen; |
||||
|
padding: 10px; |
||||
|
margin-top: 5rem; |
||||
|
margin-bottom: 10px; |
||||
|
border-radius: 15px; |
||||
|
} |
||||
|
|
||||
|
.button-container { |
||||
|
position: absolute; |
||||
|
right: 25%; |
||||
|
} |
||||
|
|
||||
|
.seat-demo-booked{ |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
border-radius: 5px; |
||||
|
text-align: center; |
||||
|
line-height: 30px; |
||||
|
background-color: #dc3545; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.seat-demo-selected{ |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
border-radius: 5px; |
||||
|
text-align: center; |
||||
|
line-height: 30px; |
||||
|
background-color: #28a745; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.seat-demo-available{ |
||||
|
width: 30px; |
||||
|
height: 30px; |
||||
|
border-radius: 5px; |
||||
|
text-align: center; |
||||
|
line-height: 30px; |
||||
|
background-color: #d3d3d3; |
||||
|
} |
||||
|
|
||||
|
.card{ |
||||
|
min-width: 300px; |
||||
|
margin-right: 15px; |
||||
|
margin-top: 20px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.card-body{ |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.text{ |
||||
|
font-family: system-ui; |
||||
|
font-size: larger; |
||||
|
} |
||||
|
|
||||
|
.time-slot-btn { |
||||
|
margin: 5px; |
||||
|
cursor: pointer; |
||||
|
} |
After Width: | Height: | Size: 998 B |
@ -0,0 +1,51 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
import publicWidget from "@web/legacy/js/public/public_widget"; |
||||
|
import { jsonrpc } from "@web/core/network/rpc_service"; |
||||
|
|
||||
|
publicWidget.registry.bookShow = publicWidget.Widget.extend({ |
||||
|
/* Extending widget and creating book show */ |
||||
|
selector: '.book_show', |
||||
|
events: { |
||||
|
'change #choose_date' : 'CheckShows', |
||||
|
}, |
||||
|
CheckShows: function (ev) { |
||||
|
/* Function for validating date. */ |
||||
|
const selectedDate = new Date(ev.currentTarget.value); |
||||
|
const currentDate = new Date(); |
||||
|
selectedDate.setHours(0, 0, 0, 0); |
||||
|
currentDate.setHours(0, 0, 0, 0); |
||||
|
const movieId = this.$el.find('form')[0].dataset.movieId |
||||
|
this.$el.find('form')[0][4].value = '' |
||||
|
if (selectedDate < currentDate){ |
||||
|
ev.currentTarget.value = '' |
||||
|
this.$el.find('#error_box').text('Please select a date in the future!') |
||||
|
this.$el.find('#error_box').show() |
||||
|
} |
||||
|
else{ |
||||
|
this.$el.find('#error_box').hide() |
||||
|
this.checkShowsOnDate(ev, selectedDate, movieId); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
checkShowsOnDate: function (ev, date, movieId) { |
||||
|
/* Function for checking shows on the selected date. */ |
||||
|
const formattedDate = new Date(date.getTime() - (date.getTimezoneOffset() * 60000)) |
||||
|
.toISOString().split('T')[0]; |
||||
|
return jsonrpc("/web/dataset/call_kw", { |
||||
|
model: 'movie.movie', |
||||
|
method: 'check_shows_on_date', |
||||
|
args: [formattedDate,movieId], |
||||
|
kwargs: {} |
||||
|
}).then((result) => { |
||||
|
if (!result){ |
||||
|
ev.currentTarget.value = '' |
||||
|
this.$el.find('#error_box').text('There is no shows on the selected date!!') |
||||
|
this.$el.find('#error_box').show() |
||||
|
this.$el.find('.tickets_section').hide() |
||||
|
} |
||||
|
else{ |
||||
|
this.$el.find('.tickets_section').show() |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
}); |
@ -0,0 +1,57 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
import publicWidget from "@web/legacy/js/public/public_widget"; |
||||
|
import { jsonrpc } from "@web/core/network/rpc_service"; |
||||
|
|
||||
|
publicWidget.registry.selectSeat = publicWidget.Widget.extend({ |
||||
|
/* Extending widget and creating selectSeat */ |
||||
|
selector: '.seat-selection', |
||||
|
events: { |
||||
|
'change .seat-checkbox': 'UpdateSeatCount', |
||||
|
'click .time-slot-btn': 'UpdateSeatChart', |
||||
|
}, |
||||
|
UpdateSeatCount: function (ev) { |
||||
|
/* Function for hiding and showing the |
||||
|
confirm button based on selecting seats. */ |
||||
|
var checked_seats = this.$el.find('.seat-checkbox:checked:not(:disabled)') |
||||
|
if (checked_seats.length > 0){ |
||||
|
this.$el.find('.button-container').show() |
||||
|
this.$el.find('.selected-seats').show() |
||||
|
this.$el.find('.seat-demo-selected')[0].innerText = checked_seats.length |
||||
|
}else{ |
||||
|
this.$el.find('.button-container').hide() |
||||
|
this.$el.find('.selected-seats').hide() |
||||
|
} |
||||
|
}, |
||||
|
UpdateSeatChart: function(ev){ |
||||
|
/* Function for updating the seat chart and side bar contents |
||||
|
based on the clicking the time slots */ |
||||
|
this.$el.find('.time-slot-btn').removeClass('active'); |
||||
|
ev.currentTarget.classList.add("active") |
||||
|
this.$el.find('.seat-demo-selected')[0].innerText = 0; |
||||
|
const screenId = this.$el.find('input[name="screen_id"]').val(); |
||||
|
const timeSlotId = ev.currentTarget.dataset.timeSlotId |
||||
|
const booking_date = this.$el.find('input[name="booking_date"]').val(); |
||||
|
this.$el.find('input[name="time_slot_id"]').val(timeSlotId); |
||||
|
jsonrpc("/web/dataset/call_kw", { |
||||
|
model: 'movie.movie', |
||||
|
method: 'update_seats', |
||||
|
args: [screenId,timeSlotId,booking_date], |
||||
|
kwargs: {} |
||||
|
}).then((result) => { |
||||
|
this.$el.siblings().find('.sidebar_timeslot')[0].innerText = result.time_slot; |
||||
|
this.$el.siblings().find('.seat-demo-booked')[0].innerText = result.booked_seats_count; |
||||
|
this.$el.siblings().find('.seat-demo-available')[0].innerText = result.available_seats_count; |
||||
|
var bookedSeats = result.booked_seats; |
||||
|
this.$el.find('.seat-checkbox').each(function() { |
||||
|
var seatId = this.value; |
||||
|
if (bookedSeats.includes(seatId)) { |
||||
|
this.checked = true; |
||||
|
this.disabled = true; |
||||
|
} else { |
||||
|
this.checked = false; |
||||
|
this.disabled = false; |
||||
|
} |
||||
|
}); |
||||
|
}) |
||||
|
}, |
||||
|
}) |
@ -0,0 +1,35 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
import { registry } from "@web/core/registry"; |
||||
|
import { useInputField } from "@web/views/fields/input_field_hook"; |
||||
|
const { Component, useRef } = owl; |
||||
|
/** |
||||
|
* We define this module for the function of creating a time picker widget |
||||
|
* |
||||
|
*/ |
||||
|
export class FieldTimePicker extends Component { |
||||
|
setup() { |
||||
|
this.input = useRef('input_time'); |
||||
|
useInputField({ getValue: () => this.props.record.data[this.props.name] || "", refName: "input_time" }); |
||||
|
} |
||||
|
|
||||
|
onBlur(){ |
||||
|
/** |
||||
|
* Handle the blur event for the timepicker input field. |
||||
|
* |
||||
|
* This function is responsible for handling the blur event on the timepicker input field. |
||||
|
* It checks if the close button is present in the timepicker, and if so, it adds a click event |
||||
|
* listener to it to handle the closing of the timepicker. |
||||
|
* |
||||
|
* @returns {void} |
||||
|
*/ |
||||
|
this.props.record.update({ [this.props.name] : this.input.el?.value}) |
||||
|
} |
||||
|
} |
||||
|
// Set the template for the FieldTimePicker component
|
||||
|
FieldTimePicker.template = 'FieldTimePicker'; |
||||
|
FieldTimePicker.supportedTypes = ["char"] |
||||
|
export const timepicker = { |
||||
|
component: FieldTimePicker |
||||
|
} |
||||
|
// Add the timepicker to the fields category
|
||||
|
registry.category("fields").add("timepicker_time", timepicker); |
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<templates xml:space="preserve"> |
||||
|
<t t-name="FieldTimePicker" owl="1"> |
||||
|
<!-- FieldTimePicker template to add input field --> |
||||
|
<div class="input-group timepicker-component"> |
||||
|
<input type="time" class="time-input" t-ref="input_time" t-on-blur="onBlur"/> |
||||
|
</div> |
||||
|
</t> |
||||
|
</templates> |
@ -0,0 +1,22 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Cast type tree view --> |
||||
|
<record id="cast_type_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">cast.type.view.tree</field> |
||||
|
<field name="model">cast.type</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree editable="bottom"> |
||||
|
<field name="name" required="1"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Action Cast Type --> |
||||
|
<record id="cast_type_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Cast Type</field> |
||||
|
<field name="res_model">cast.type</field> |
||||
|
<field name="view_mode">tree</field> |
||||
|
</record> |
||||
|
<!-- MenuItem for cast type --> |
||||
|
<menuitem id="movie_cast_type" name="Cast Type" |
||||
|
parent="show_booking_management.movie_configuration" action="cast_type_action"/> |
||||
|
</odoo> |
@ -0,0 +1,45 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Movie Cast Form View --> |
||||
|
<record id="movie_cast_view_form" model="ir.ui.view"> |
||||
|
<field name="name">movie.cast.view.form</field> |
||||
|
<field name="model">movie.cast</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Movie Cast"> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="name" required="1"/> |
||||
|
<field name="cast_id" required="1"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="cast_image" widget='image' nolabel="1"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Movie Cast Tree View --> |
||||
|
<record id="movie_cast_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">movie.cast.view.tree</field> |
||||
|
<field name="model">movie.cast</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="name"/> |
||||
|
<field name="cast_id"/> |
||||
|
<field name="cast_image" widget="image" options="{'size': [100, 100]}"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Action Movie Cast --> |
||||
|
<record id="movie_cast_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Show Cast</field> |
||||
|
<field name="res_model">movie.cast</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
<!-- MenuItem for movie cast --> |
||||
|
<menuitem id="movie_movie_cast" name="Show Cast" |
||||
|
parent="show_booking_management.movie_configuration" |
||||
|
action="movie_cast_action"/> |
||||
|
</odoo> |
@ -0,0 +1,158 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Movie Search View --> |
||||
|
<record id="movie_movie_view_search" model="ir.ui.view"> |
||||
|
<field name="name">movie.movie.view.search</field> |
||||
|
<field name="model">movie.movie</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<search string="Search Tips"> |
||||
|
<field name="name" string="Name"/> |
||||
|
<filter name="filter_movies_ongoing" string="Ongoing" domain="[('state','=','ongoing')]"/> |
||||
|
<filter name="filter_movies_draft" string="Draft" domain="[('state','=','draft')]"/> |
||||
|
<filter name="filter_movies_cancel" string="Cancelled" domain="[('state','=','cancel')]"/> |
||||
|
<group expand="0" string="Group By"> |
||||
|
<filter name="State" string="State" |
||||
|
context="{'group_by':'state'}"/> |
||||
|
<filter name="Language" string="Language" |
||||
|
context="{'group_by':'movie_language_id'}"/> |
||||
|
<filter name="Release Date" string="Release Date" |
||||
|
context="{'group_by':'release_date'}"/> |
||||
|
</group> |
||||
|
</search> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Movie Kanban View --> |
||||
|
<record id="movie_movie_view_kanban" model="ir.ui.view"> |
||||
|
<field name="name">movie.movie.view.kanban</field> |
||||
|
<field name="model">movie.movie</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<kanban sample="1"> |
||||
|
<field name="id"/> |
||||
|
<field name="movie_language_id"/> |
||||
|
<field name="movie_poster"/> |
||||
|
<field name="show_type_ids"/> |
||||
|
<field name="available_time_slots_ids"/> |
||||
|
<field name="price"/> |
||||
|
<templates> |
||||
|
<t t-name="kanban-box"> |
||||
|
<div class="oe_kanban_global_click"> |
||||
|
<div class="o_kanban_image"> |
||||
|
<img t-att-src="kanban_image('movie.movie', 'movie_poster', record.id.raw_value)" |
||||
|
alt="Movie Poster"/> |
||||
|
</div> |
||||
|
<div class="oe_kanban_details"> |
||||
|
<strong class="o_kanban_record_title"> |
||||
|
<field name="name"/> |
||||
|
</strong> |
||||
|
<ul> |
||||
|
<li> |
||||
|
<strong>Language: |
||||
|
<field name="movie_language_id"/> |
||||
|
</strong> |
||||
|
</li> |
||||
|
<li> |
||||
|
<strong>Price: |
||||
|
<field name="price"/> |
||||
|
</strong> |
||||
|
</li> |
||||
|
<li> |
||||
|
<strong>Show Type: |
||||
|
<field name="show_type_ids" widget="many2many_tags"/> |
||||
|
</strong> |
||||
|
</li> |
||||
|
<li> |
||||
|
<strong>Time Slots: |
||||
|
<field name="available_time_slots_ids" widget="many2many_tags"/> |
||||
|
</strong> |
||||
|
</li> |
||||
|
<li> |
||||
|
<strong>Screen: |
||||
|
<field name="available_screens_ids" widget="many2many_tags"/> |
||||
|
</strong> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
</templates> |
||||
|
</kanban> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Movie Form View --> |
||||
|
<record id="movie_movie_view_form" model="ir.ui.view"> |
||||
|
<field name="name">movie.movie.view.form</field> |
||||
|
<field name="model">movie.movie</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Movie Movie"> |
||||
|
<header> |
||||
|
<button name="action_start_show" type="object" string="Start Show" |
||||
|
invisible="state not in ['draft']" class="btn-primary"/> |
||||
|
<button name="action_cancel_show" type="object" string="Cancel Show" |
||||
|
invisible="state not in ['ongoing']" class="btn-primary"/> |
||||
|
<field name="state" widget="statusbar"/> |
||||
|
</header> |
||||
|
<sheet> |
||||
|
<div class="oe_title"> |
||||
|
<h1> |
||||
|
<field name="name" required="1"/> |
||||
|
</h1> |
||||
|
</div> |
||||
|
<group> |
||||
|
<group> |
||||
|
<field name="duration" required="1"/> |
||||
|
<field name="release_date" required="1"/> |
||||
|
<field name="show_type_ids" |
||||
|
widget="many2many_tags" required="1"/> |
||||
|
<field name="movie_language_id"/> |
||||
|
<field name="available_time_slots_ids" |
||||
|
widget="many2many_tags" required="1"/> |
||||
|
<field name="available_screens_ids" |
||||
|
widget="many2many_tags" required="1"/> |
||||
|
<field name="show_start_date" required="1"/> |
||||
|
<field name="show_end_date" required="1"/> |
||||
|
<field name="price" required="1"/> |
||||
|
<field name="currency_id" invisible="1"/> |
||||
|
</group> |
||||
|
<group> |
||||
|
<field name="movie_poster" widget="image" options="{'size': [185, 280]}" required="1"/> |
||||
|
</group> |
||||
|
</group> |
||||
|
<notebook> |
||||
|
<page string="About"> |
||||
|
<field name="about_movie" placeholder="Overview of the movie"/> |
||||
|
</page> |
||||
|
<page string="Cast"> |
||||
|
<field name="movie_cast_ids"/> |
||||
|
</page> |
||||
|
</notebook> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Movie Tree View --> |
||||
|
<record id="movie_movie_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">movie.movie.view.tree</field> |
||||
|
<field name="model">movie.movie</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="name"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Action Movie --> |
||||
|
<record id="movie_movie_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Shows</field> |
||||
|
<field name="res_model">movie.movie</field> |
||||
|
<field name="view_mode">kanban,form,tree</field> |
||||
|
</record> |
||||
|
<!-- MenuItem for movie, registration, configuration --> |
||||
|
<menuitem id="movie" name="Shows" sequence="1" |
||||
|
groups="show_booking_management.show_booking_management_group_admin" |
||||
|
web_icon="show_booking_management,static/description/icon.png"/> |
||||
|
<menuitem id="movie_ticket" name="Shows" parent="show_booking_management.movie" |
||||
|
action="movie_movie_action" sequence="1"/> |
||||
|
<menuitem id="movie_registration" name="Registration" |
||||
|
parent="show_booking_management.movie"/> |
||||
|
<menuitem id="movie_configuration" name="Configuration" |
||||
|
parent="show_booking_management.movie" sequence="5"/> |
||||
|
</odoo> |