@ -0,0 +1,57 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
================================ |
||||
|
Venue / Event Booking Management |
||||
|
================================ |
||||
|
The 'Venue / Event Booking Management' is a core module which can manage any type of venue reservation. |
||||
|
|
||||
|
Features |
||||
|
======== |
||||
|
* Venue Booking creation. |
||||
|
* Allocate the Booking to different users. |
||||
|
* Integrated with Accounting module. |
||||
|
* Simple Workflow. |
||||
|
* Attractive Design. |
||||
|
|
||||
|
Configuration |
||||
|
============= |
||||
|
* No additional configurations needed |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
Gnu 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 |
||||
|
------- |
||||
|
* Developers :(V15) Fathima Mazlin AM, |
||||
|
(V16) Risvana AR, |
||||
|
Contact : odoo@cybrosys.com |
||||
|
|
||||
|
Contacts |
||||
|
-------- |
||||
|
* Mail Contact : odoo@cybrosys.com |
||||
|
* Website : https://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 `Our Website <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,25 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 controllers |
||||
|
from . import models |
||||
|
from . import report |
||||
|
from . import wizards |
@ -0,0 +1,77 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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': 'Venue / Event Booking Management', |
||||
|
'version': '15.0.1.0.0', |
||||
|
'summary': """Core Module for Managing Different Types of |
||||
|
Venue/ Event Booking.""", |
||||
|
'description': """Core Module for Managing Different Types of |
||||
|
Venue/ Event Booking, Event Booking, Venue Booking, |
||||
|
Space Booking, Booking, Event, Venue, Wedding, Birthday, |
||||
|
Party, Hall Booking, Room Booking""", |
||||
|
"category": "Industry", |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://www.cybrosys.com", |
||||
|
'depends': ['base', 'account', 'website', ], |
||||
|
'data': [ |
||||
|
'security/venue_booking_management_groups.xml', |
||||
|
'security/ir.model.access.csv', |
||||
|
'data/venue_type_data.xml', |
||||
|
'data/confirmation_email_template_data.xml', |
||||
|
'views/venue_booking_views.xml', |
||||
|
'views/venue_type_views.xml', |
||||
|
'views/amenities_views.xml', |
||||
|
'views/venue_views.xml', |
||||
|
'views/dashboard_views.xml', |
||||
|
'views/res_partner_views.xml', |
||||
|
'views/res_config_settings_views.xml', |
||||
|
'wizards/check_venue_availability_views.xml', |
||||
|
'report/venue_booking_report_views.xml', |
||||
|
'report/venue_booking_report_templates.xml', |
||||
|
'report/venue_booking_reports.xml', |
||||
|
'wizards/venue_booking_analysis_views.xml', |
||||
|
'views/website_venue_booking_templates.xml', |
||||
|
'views/website_portal_templates.xml', |
||||
|
], |
||||
|
'assets': { |
||||
|
'web.assets_frontend': [ |
||||
|
'venue_booking_management/static/src/css/website_page.css', |
||||
|
'venue_booking_management/static/src/js/website_venue_booking.js' |
||||
|
], |
||||
|
'web.assets_backend': [ |
||||
|
'venue_booking_management/static/src/css/venue_dashboard.css', |
||||
|
'venue_booking_management/static/src/scss/venue_booking.scss', |
||||
|
'venue_booking_management/static/src/js/action_manager.js', |
||||
|
'venue_booking_management/static/src/js/lib/chart_bundle.js', |
||||
|
'venue_booking_management/static/src/js/dashboard_action.js', |
||||
|
], |
||||
|
'web.assets_qweb': [ |
||||
|
'venue_booking_management/static/src/xml/dashboard_templates.xml', |
||||
|
], |
||||
|
}, |
||||
|
'images': ['static/description/banner.png'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'application': True, |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 portal |
||||
|
from . import venue_booking_management |
@ -0,0 +1,260 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 collections import OrderedDict |
||||
|
from datetime import timedelta |
||||
|
from odoo import fields,http, _ |
||||
|
from odoo.http import request |
||||
|
from odoo.osv import expression |
||||
|
from odoo.osv.expression import OR |
||||
|
from odoo.addons.portal.controllers import portal |
||||
|
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager |
||||
|
|
||||
|
|
||||
|
class CustomerPortal(portal.CustomerPortal): |
||||
|
"""Class for Venue booking portal that gives the record and counts |
||||
|
of the Bookings""" |
||||
|
def _prepare_home_portal_values(self, counters): |
||||
|
""" Function for finding the number of document """ |
||||
|
values = super()._prepare_home_portal_values(counters) |
||||
|
uid = request.env.user.partner_id.id |
||||
|
venue_booking_count = request.env[ |
||||
|
'venue.booking'].search_count([('partner_id', '=', uid)]) |
||||
|
values.update({'venue_booking_count': venue_booking_count}) |
||||
|
return values |
||||
|
|
||||
|
@http.route(['/my/venue_booking', '/my/venue_booking/page/<int:page>'], |
||||
|
type='http', auth='user', |
||||
|
website=True) |
||||
|
def create_venue_booking_management(self, page=1, date_begin=None, |
||||
|
date_end=None, |
||||
|
sortby=None, filterby=None, |
||||
|
search=None, |
||||
|
search_in='content', ): |
||||
|
""" Function to fetch booking records and pass to |
||||
|
the portal template""" |
||||
|
uid = request.env.user.partner_id.id |
||||
|
venue_booking_management = request.env['venue.booking'].sudo().search( |
||||
|
[('partner_id', '=', uid)]) |
||||
|
values = self._prepare_my_booking_values(page, date_begin, date_end, |
||||
|
sortby, filterby, search, |
||||
|
search_in) |
||||
|
# Pager |
||||
|
pager = portal_pager(**values['pager']) |
||||
|
venue = values['venue'](pager['offset']) |
||||
|
request.session['my_venue_booking_history'] = venue.ids[:100] |
||||
|
values.update({ |
||||
|
'venue_booking_management': venue_booking_management, |
||||
|
'venues': venue, |
||||
|
'pager': pager, |
||||
|
}) |
||||
|
return request.render( |
||||
|
"venue_booking_management.portal_my_venue_booking_documents", |
||||
|
values) |
||||
|
|
||||
|
def _prepare_my_booking_values(self, page, date_begin, date_end, sortby, |
||||
|
filterby, search, search_in, |
||||
|
domain=None, url="/my/venue_booking"): |
||||
|
"""Add all event values to the portal. Which will return the |
||||
|
values event, page, pager, filter, sort, and search""" |
||||
|
values = self._prepare_portal_layout_values() |
||||
|
Venue = request.env['venue.booking'] |
||||
|
domain = expression.AND([ |
||||
|
domain or [], |
||||
|
self._get_booking_domain(), |
||||
|
]) |
||||
|
searchbar_sortings = self._get_venue_booking_searchbar_sortings() |
||||
|
# default sort by order |
||||
|
if not sortby: |
||||
|
sortby = 'date' |
||||
|
order = searchbar_sortings[sortby]['order'] |
||||
|
searchbar_filters = self._get_venue_booking_searchbar_filters() |
||||
|
# default filter by value |
||||
|
if not filterby: |
||||
|
filterby = 'all' |
||||
|
domain += searchbar_filters[filterby]['domain'] |
||||
|
searchbar_inputs = self._get_venue_booking_searchbar_inputs() |
||||
|
if search and search_in: |
||||
|
domain += self._get_venue_booking_search_domain(search_in, search) |
||||
|
if date_begin and date_end: |
||||
|
domain += [('create_date', '>', date_begin), |
||||
|
('create_date', '<=', date_end)] |
||||
|
values.update({ |
||||
|
'date': date_begin, |
||||
|
'venue': lambda pager_offset: self._get_grouped_venues( |
||||
|
Venue, domain, order, pager_offset), |
||||
|
'page_name': 'venue_booking', |
||||
|
'pager': { |
||||
|
"url": url, |
||||
|
"url_args": {'date_begin': date_begin, 'date_end': date_end, |
||||
|
'sortby': sortby, 'search_in': search_in, |
||||
|
'search': search}, |
||||
|
"total": Venue.search_count(domain), |
||||
|
"page": page, |
||||
|
"step": self._items_per_page, |
||||
|
}, |
||||
|
'default_url': url, |
||||
|
'searchbar_sortings': searchbar_sortings, |
||||
|
'sortby': sortby, |
||||
|
'searchbar_filters': OrderedDict( |
||||
|
sorted(searchbar_filters.items())), |
||||
|
'filterby': filterby, |
||||
|
'searchbar_inputs': searchbar_inputs, |
||||
|
'search_in': search_in, |
||||
|
'search': search, |
||||
|
}) |
||||
|
return values |
||||
|
|
||||
|
def _get_venue_page_view_values(self, venue, access_token, **kwargs): |
||||
|
"""Get the page view values""" |
||||
|
values = { |
||||
|
'venue': venue, |
||||
|
'page_name': 'venue_booking', |
||||
|
} |
||||
|
return self._get_page_view_values(venue, access_token, values, |
||||
|
'my_venue_booking_history', False, |
||||
|
**kwargs) |
||||
|
|
||||
|
def _get_booking_domain(self): |
||||
|
"""Returns the booking that are in stage 'cancel' and 'draft'""" |
||||
|
return [('state', 'not in', ('cancel', 'closed'))] |
||||
|
|
||||
|
def _get_venue_booking_searchbar_sortings(self): |
||||
|
"""Sort the booking based on the date and name""" |
||||
|
return { |
||||
|
'date': {'label': _('Date'), 'order': 'create_date desc'}, |
||||
|
'name': {'label': _('Name'), 'order': 'name asc'}, |
||||
|
} |
||||
|
|
||||
|
def _get_venue_booking_searchbar_filters(self): |
||||
|
"""Filter the events by All, Last month, This Month, Last Week, |
||||
|
This Week, Last Year, This Year, Today and This Quarter""" |
||||
|
today = fields.Date.today() |
||||
|
this_month_start = today.replace(day=1) |
||||
|
this_quarter_start = today.replace(day=1, month=(( |
||||
|
today.month - 1) // 3) * 3 + 1) |
||||
|
this_week_start = today - timedelta(days=today.weekday()) |
||||
|
this_year_start = today.replace(month=1, day=1) |
||||
|
return { |
||||
|
'all': {'label': _('All'), 'domain': []}, |
||||
|
'last_month': { |
||||
|
'label': _('Last Month'), |
||||
|
'domain': [('create_date', '>=', |
||||
|
(this_month_start - timedelta(days=30)).strftime( |
||||
|
'%Y-%m-%d')), |
||||
|
('create_date', '<=', |
||||
|
(this_month_start - timedelta(days=1)).strftime( |
||||
|
'%Y-%m-%d'))] |
||||
|
}, |
||||
|
'this_month': { |
||||
|
'label': _('This Month'), |
||||
|
'domain': [ |
||||
|
( |
||||
|
'create_date', '>=', |
||||
|
this_month_start.strftime('%Y-%m-%d')), |
||||
|
('create_date', '<=', today.strftime('%Y-%m-%d'))] |
||||
|
}, |
||||
|
'last_week': { |
||||
|
'label': _('Last Week'), |
||||
|
'domain': [('create_date', '>=', |
||||
|
(this_week_start - timedelta(days=7)).strftime( |
||||
|
'%Y-%m-%d')), |
||||
|
('create_date', '<=', |
||||
|
(this_week_start - timedelta(days=1)).strftime( |
||||
|
'%Y-%m-%d'))] |
||||
|
}, |
||||
|
'this_week': { |
||||
|
'label': _('This Week'), |
||||
|
'domain': [ |
||||
|
( |
||||
|
'create_date', '>=', this_week_start.strftime('%Y-%m-%d')), |
||||
|
('create_date', '<=', today.strftime('%Y-%m-%d'))] |
||||
|
}, |
||||
|
'last_year': { |
||||
|
'label': _('Last Year'), |
||||
|
'domain': [('create_date', '>=', |
||||
|
(this_year_start - timedelta(days=365)).strftime( |
||||
|
'%Y-%m-%d')), |
||||
|
('create_date', '<=', |
||||
|
(this_year_start - timedelta(days=1)).strftime( |
||||
|
'%Y-%m-%d'))] |
||||
|
}, |
||||
|
'this_year': { |
||||
|
'label': _('This Year'), |
||||
|
'domain': [ |
||||
|
( |
||||
|
'create_date', '>=', this_year_start.strftime('%Y-%m-%d')), |
||||
|
('create_date', '<=', today.strftime('%Y-%m-%d'))] |
||||
|
}, |
||||
|
'today': { |
||||
|
'label': _('Today'), |
||||
|
'domain': [('create_date', '=', today.strftime('%Y-%m-%d'))] |
||||
|
}, |
||||
|
'this_quarter': { |
||||
|
'label': _('This Quarter'), |
||||
|
'domain': [ |
||||
|
('create_date', '>=', |
||||
|
this_quarter_start.strftime('%Y-%m-%d')), |
||||
|
('create_date', '<=', today.strftime('%Y-%m-%d'))] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
def _get_venue_booking_search_domain(self, search_in, search): |
||||
|
"""Returns the events for the given search(If we have not entered |
||||
|
the full name which will also gives the output""" |
||||
|
search_domain = [] |
||||
|
if search_in == 'all': |
||||
|
search_domain.append([('name', 'ilike', |
||||
|
f'{search}%')]) |
||||
|
search_domain.append([('phone', 'ilike', |
||||
|
f'{search}%')]) |
||||
|
if search_in in ('venue', 'all'): |
||||
|
search_domain.append([('venue_id', 'ilike', |
||||
|
f'{search}%')]) |
||||
|
return OR(search_domain) |
||||
|
|
||||
|
def _get_venue_booking_searchbar_inputs(self): |
||||
|
"""Which will returns a dictionary of values by the search contents |
||||
|
as Search in All, in Content, Search in states, Search in Venues""" |
||||
|
values = { |
||||
|
'all': {'input': 'all', 'label': _('Search in All'), 'order': 1}, |
||||
|
'venue': {'input': 'venue', 'label': _('Search in Venue'), |
||||
|
'order': 2}, |
||||
|
} |
||||
|
return dict(sorted(values.items(), key=lambda item: item[1]["order"])) |
||||
|
|
||||
|
def _get_grouped_venues(self, Venue, domain, order, pager_offset, ): |
||||
|
"""Returns the grouped venues for a given domain""" |
||||
|
venues = Venue.search(domain, order=order, limit=self._items_per_page, |
||||
|
offset=pager_offset) |
||||
|
return venues |
||||
|
|
||||
|
@http.route(['/my/booking_data/<int:record>'], type='http', |
||||
|
auth="user", website=True) |
||||
|
def portal_my_venue_booking(self, record): |
||||
|
""" Function to fetch data of selected visitors record and pass to |
||||
|
the portal template""" |
||||
|
booking_record = request.env['venue.booking'].sudo().browse(record) |
||||
|
|
||||
|
return http.request.render( |
||||
|
'venue_booking_management.booking_portal_form', |
||||
|
{'booking_record': booking_record, |
||||
|
'page_name': 'venue_booking_management_record'}) |
@ -0,0 +1,93 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 odoo import fields,http,_ |
||||
|
from odoo.http import content_disposition, request |
||||
|
from odoo.http import serialize_exception as _serialize_exception |
||||
|
from odoo.tools import html_escape |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class XLSXReportController(http.Controller): |
||||
|
"""Controller Class for xlsx report""" |
||||
|
@http.route('/venue_xlsx_reports', type='http', auth='user', methods=['POST'], |
||||
|
csrf=False) |
||||
|
def get_report_xlsx(self, model, options, output_format, report_name): |
||||
|
"""Method for passing data to xlsx report""" |
||||
|
uid = request.session.uid |
||||
|
report_obj = request.env[model].with_user(uid) |
||||
|
options = json.loads(options) |
||||
|
try: |
||||
|
if output_format == 'xlsx': |
||||
|
response = request.make_response( |
||||
|
None, |
||||
|
headers=[('Content-Type', 'application/vnd.ms-excel'), ( |
||||
|
'Content-Disposition', |
||||
|
content_disposition(report_name + '.xlsx'))]) |
||||
|
report_obj.get_xlsx_report(options, response) |
||||
|
return response |
||||
|
except Exception as err: |
||||
|
exception = _serialize_exception(err) |
||||
|
error = { |
||||
|
'code': 200, |
||||
|
'message': 'Odoo Server Error', |
||||
|
'data': exception |
||||
|
} |
||||
|
return request.make_response(html_escape(json.dumps(error))) |
||||
|
|
||||
|
|
||||
|
class VenueBookingController(http.Controller): |
||||
|
"""Class to add Venue booking menu in website""" |
||||
|
@http.route('/venue/booking', type='http', auth='public', website=True) |
||||
|
def venue_booking(self): |
||||
|
"""Function to render venue booking values to XML""" |
||||
|
venue_ids = request.env['venue'].sudo().search([]) |
||||
|
state_ids = request.env['res.country.state'].sudo().search([]) |
||||
|
country_ids = request.env['res.country'].sudo().search([]) |
||||
|
return http.request.render('venue_booking_management.venue_booking_page', |
||||
|
{'venue_ids': venue_ids, |
||||
|
'state_ids': state_ids, |
||||
|
'country_ids': country_ids |
||||
|
}) |
||||
|
|
||||
|
@http.route('/booking/submit', type='http', auth='public', website=True) |
||||
|
def booking_success_page(self, **post): |
||||
|
"""Function to create booking and return to success page""" |
||||
|
partner_id = request.env['res.partner'].sudo().create({ |
||||
|
'name': post.get('name'), |
||||
|
'mobile': post.get('mobile_no'), |
||||
|
'state_id': int(post.get('state')), |
||||
|
'country_id': int(post.get('country')), |
||||
|
'city': (post.get('city')), |
||||
|
}) |
||||
|
venue_id = request.env['venue'].browse(int(post.get('venue_type'))) |
||||
|
values = { |
||||
|
'partner_id': partner_id.id, |
||||
|
'venue_id': venue_id.id, |
||||
|
'start_date': post.get('from_date'), |
||||
|
'end_date': post.get('to_date'), |
||||
|
'booking_type': post.get('booking_type'), |
||||
|
'date': fields.Date.today() |
||||
|
} |
||||
|
booking_id = request.env['venue.booking'].sudo().create(values) |
||||
|
return request.render( |
||||
|
'venue_booking_management.venue_booking_success_page') |
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- Email to notify the Customer to Confirm the Venue Booking --> |
||||
|
<record id="mail_template_notify_venue_booking" model="mail.template"> |
||||
|
<field name="name">Confirmed Venue Booking</field> |
||||
|
<field name="subject">Venue booking: Received the Venue Booking for |
||||
|
{{ object.venue_id.name }} |
||||
|
</field> |
||||
|
<field name="model_id" |
||||
|
ref="venue_booking_management.model_venue_booking"/> |
||||
|
<field name="email_from">{{ (object.env.user.login) }} |
||||
|
</field> |
||||
|
<field name="body_html"> |
||||
|
<![CDATA[ |
||||
|
<p> |
||||
|
Dear customer <t t-out="object.partner_id.name"/>,<br/><br/> |
||||
|
We have received a booking for the venue |
||||
|
<strong><t t-out="object.venue_id.name"/></strong>. Please proceed |
||||
|
with necessary actions. |
||||
|
<br/><br/> |
||||
|
Thank You |
||||
|
</p> |
||||
|
]]> |
||||
|
</field> |
||||
|
<field name="lang">{{ object.partner_id.lang or '' }}</field> |
||||
|
<field name="auto_delete" eval="False"/> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,60 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data noupdate="1"> |
||||
|
<!-- Venue Types Records--> |
||||
|
<record id="venue_type1" model="venue.type"> |
||||
|
<field name="name">Conference centers</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type1.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type2" model="venue.type"> |
||||
|
<field name="name">Meeting Room</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type2.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type3" model="venue.type"> |
||||
|
<field name="name">Convention centers</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type3.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type4" model="venue.type"> |
||||
|
<field name="name">Social clubs and lounges</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type4.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type5" model="venue.type"> |
||||
|
<field name="name">Mini Conference centers</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type5.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type6" model="venue.type"> |
||||
|
<field name="name">Stadiums</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type6.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type7" model="venue.type"> |
||||
|
<field name="name">Community centers</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type7.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type8" model="venue.type"> |
||||
|
<field name="name">Resorts</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type8.jpeg"/> |
||||
|
</record> |
||||
|
<record id="venue_type9" model="venue.type"> |
||||
|
<field name="name">Auditorium</field> |
||||
|
<field name="image" type="base64" |
||||
|
file="venue_booking_management/static/img/venue_type9.jpeg"/> |
||||
|
</record> |
||||
|
<!-- Sequence Number Generation for Venue Booking--> |
||||
|
<record id="sequence_venue_booking" model="ir.sequence"> |
||||
|
<field name="name">Venue Booking</field> |
||||
|
<field name="code">venue.booking.sequence</field> |
||||
|
<field name="suffix">%(day)s/%(month)s/%(year)s</field> |
||||
|
<field name="prefix">VENUE-</field> |
||||
|
<field name="number_increment">1</field> |
||||
|
<field name="padding">3</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,7 @@ |
|||||
|
## Module <venue_booking_management> |
||||
|
|
||||
|
#### 16.09.2024 |
||||
|
#### Version 15.0.1.0.0 |
||||
|
#### ADD |
||||
|
|
||||
|
- Initial Commit for Venue / Event Booking Management |
@ -0,0 +1,33 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 amenities |
||||
|
from . import res_config_settings |
||||
|
from . import venue |
||||
|
from . import venue_type |
||||
|
from . import venue_booking |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,32 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 Amenities(models.Model): |
||||
|
"""Model for managing the Amenities""" |
||||
|
_name = 'amenities' |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_description = 'Amenities' |
||||
|
|
||||
|
name = fields.Char(string="Name", help="Name of the Amenities") |
||||
|
amount = fields.Float(string='Amount', help="Amount of the Amenities") |
@ -0,0 +1,35 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 ResConfigSettings(models.TransientModel): |
||||
|
"""Inherit the model res.config.settings to add Additional fields""" |
||||
|
_inherit = 'res.config.settings' |
||||
|
|
||||
|
is_extra = fields.Boolean( |
||||
|
string='Apply Extra Amount', default=False, |
||||
|
config_parameter='venue_booking_management.is_extra', |
||||
|
help="Enable, if extra charge want to add") |
||||
|
extra_amount = fields.Float( |
||||
|
string='Extra Amount', help='Enter extra amount/KM', |
||||
|
config_parameter='venue_booking_management.extra_amount') |
@ -0,0 +1,119 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 Venue(models.Model): |
||||
|
"""Model for managing the Venue that used to add new fields and |
||||
|
functions to create the Venue""" |
||||
|
_name = 'venue' |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_description = 'Venue' |
||||
|
|
||||
|
name = fields.Char(string="Name", help="Name of the venue type") |
||||
|
image = fields.Binary("Image", attachment=True, |
||||
|
help="This field holds the image used as " |
||||
|
"image for the event, limited to 1080x720px.") |
||||
|
venue_type_id = fields.Many2one('venue.type', 'Venue Type', |
||||
|
help='Used to choose the type of the ' |
||||
|
'particular venue') |
||||
|
venue_location = fields.Char(string='Location', required=True, |
||||
|
help='The venue location for Booking') |
||||
|
capacity = fields.Integer(string='Capacity', |
||||
|
help='The capacity of the venue') |
||||
|
seating = fields.Integer(string='Seating', help='The Seating of the venue') |
||||
|
venue_charge_hour = fields.Float(string='Charge Per Hour', |
||||
|
help='The charge per hour of the venue') |
||||
|
venue_charge_day = fields.Float(string='Charge Per Day', |
||||
|
help='The charge per day of the venue') |
||||
|
additional_charge_hour = fields.Float(string=' Additional Charge Per Hour', |
||||
|
help='The charge per hour of the ' |
||||
|
'venue') |
||||
|
additional_charge_day = fields.Float(string='Additional Charge Per Day', |
||||
|
help='The charge per day of' |
||||
|
' the venue') |
||||
|
venue_count = fields.Integer(string="# of Events", |
||||
|
compute='_compute_venue_count', |
||||
|
help='Compute field for calculate the venue ' |
||||
|
'count') |
||||
|
open_time = fields.Float(string=' Open Time', |
||||
|
help='Open time of the venue') |
||||
|
closed_time = fields.Float(string=' Close Time', |
||||
|
help='Close time of the venue') |
||||
|
venue_line_ids = fields.One2many('venue.lines', 'venue_id', |
||||
|
string='Amenities', |
||||
|
help='Amenities for the venue') |
||||
|
price_subtotal = fields.Float(string='Total', |
||||
|
help='Total price of the venue', |
||||
|
compute='_compute_price_subtotal', |
||||
|
readonly=True, store=True) |
||||
|
|
||||
|
@api.depends('venue_line_ids', 'venue_line_ids.sub_total') |
||||
|
def _compute_price_subtotal(self): |
||||
|
"""Compute function for calculating the Amenities Price Subtotal""" |
||||
|
for rec in self: |
||||
|
rec.price_subtotal = sum( |
||||
|
item.sub_total for item in rec.venue_line_ids) |
||||
|
|
||||
|
def _compute_venue_count(self): |
||||
|
"""Compute function for calculating the venue count""" |
||||
|
for records in self: |
||||
|
venues = self.env['venue.booking'].search_count([ |
||||
|
('venue_id', '=', records.id)]) |
||||
|
records.venue_count = venues |
||||
|
|
||||
|
def get_venue_type_action(self): |
||||
|
"""Get the venue type action for the venue bookings""" |
||||
|
return self._get_action( |
||||
|
'venue_booking_management.venue_booking_action_view_kanban') |
||||
|
|
||||
|
|
||||
|
class VenueLines(models.Model): |
||||
|
"""Model for managing the Venue lines""" |
||||
|
_name = 'venue.lines' |
||||
|
_description = 'Venue Lines' |
||||
|
|
||||
|
venue_id = fields.Many2one('venue', string='Venue Lines', |
||||
|
help='The relational field for the venue model') |
||||
|
amenities_id = fields.Many2one('amenities', string='Amenities', |
||||
|
help='The field used to link ' |
||||
|
'the amenities model') |
||||
|
quantity = fields.Float(string="Quantity", default=1, |
||||
|
help="Quantity of the Amenities") |
||||
|
amount = fields.Float(string="Amount", help="Amount of the Amenities", |
||||
|
related='amenities_id.amount') |
||||
|
sub_total = fields.Float(string="Sub Total", compute="_compute_sub_total", |
||||
|
readonly=True, help="Sub Total of the Values") |
||||
|
currency_id = fields.Many2one('res.currency', readonly=True, |
||||
|
string='Currency', |
||||
|
default=lambda self: |
||||
|
self.env.user.company_id.currency_id, |
||||
|
help="Currency value of the Venue") |
||||
|
status = fields.Selection([('open', 'Open'), ('done', 'Done')], |
||||
|
string="Status", default='open', |
||||
|
help="Status of the Venue") |
||||
|
|
||||
|
@api.depends('quantity', 'amount') |
||||
|
def _compute_sub_total(self): |
||||
|
"""Compute the Sub Total of the Venue values""" |
||||
|
for item in self: |
||||
|
item.sub_total = item.quantity * item.amount |
@ -0,0 +1,556 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 UserError, ValidationError |
||||
|
|
||||
|
|
||||
|
class VenueBooking(models.Model): |
||||
|
"""Model for managing the Venue Booking""" |
||||
|
_name = 'venue.booking' |
||||
|
_inherit = ['mail.thread', 'mail.activity.mixin'] |
||||
|
_description = 'Venue Reservation' |
||||
|
|
||||
|
name = fields.Char(string="Name", help="Name of the venue type") |
||||
|
ref = fields.Char(string='Ref', readonly=True, |
||||
|
help="Name of the venue that created as sequencing") |
||||
|
venue_id = fields.Many2one('venue', string='Venue', |
||||
|
help="Venue for the Event", required=True) |
||||
|
venue_type_id = fields.Many2one('venue.type', 'Venue Type', |
||||
|
related='venue_id.venue_type_id', |
||||
|
readonly=True, |
||||
|
help='Used to choose the type of the' |
||||
|
' particular venue') |
||||
|
image = fields.Binary("Image", attachment=True, |
||||
|
related='venue_type_id.image', |
||||
|
help="This field holds the image used as " |
||||
|
"image for the event, limited to 1080x720px.") |
||||
|
partner_id = fields.Many2one('res.partner', string="Customer", |
||||
|
required=True, |
||||
|
help='Used to Choose the Booking Person') |
||||
|
date = fields.Date(string="Date", default=fields.Date.today, required=True, |
||||
|
help='Date field for booking the Venue') |
||||
|
currency_id = fields.Many2one('res.currency', readonly=True, |
||||
|
string='Currency', |
||||
|
default=lambda self: |
||||
|
self.env.user.company_id.currency_id, |
||||
|
help='Currency field for booking Venue') |
||||
|
start_date = fields.Datetime(string="Start date", |
||||
|
default=lambda self: fields.datetime.now(), |
||||
|
required=True, |
||||
|
help='Venue Booking Start Date') |
||||
|
end_date = fields.Datetime(string="End date", required=True, |
||||
|
help='Venue Booking End Date') |
||||
|
state = fields.Selection([('draft', 'Draft'), |
||||
|
('confirm', 'Confirmed'), |
||||
|
('invoice', 'Invoiced'), |
||||
|
('close', 'Close'), ('cancel', 'Canceled')], |
||||
|
string="State", default="draft", |
||||
|
help="State of venue booking") |
||||
|
booking_type = fields.Selection([('day', 'Day'), |
||||
|
('hour', 'Hours')], string='Booking Type', |
||||
|
default='day', help='The selection field ' |
||||
|
'for Booking Type') |
||||
|
venue_booking_line_ids = fields.One2many('venue.booking.line', |
||||
|
'venue_booking_id', |
||||
|
string="Venues", |
||||
|
help='Booking Line for ' |
||||
|
'the given venue') |
||||
|
note = fields.Text(string='Terms and conditions', |
||||
|
help='The note field for Venue Booking') |
||||
|
pending_invoice = fields.Boolean(string="Invoice Pending", |
||||
|
compute='_compute_pending_invoice', |
||||
|
help='Find out is there any ' |
||||
|
'pending invoice') |
||||
|
total = fields.Monetary(string="Total Amount", store=True, |
||||
|
compute='_compute_total_amount', |
||||
|
help='Total amount for the Venue Booking') |
||||
|
booking_charge_per_day = fields.Float(string="Booking Charge Per Day", |
||||
|
related='venue_id.venue_charge_day', |
||||
|
help='Field for adding Booking ' |
||||
|
'Charge Per Day') |
||||
|
booking_charge_per_hour = fields.Float( |
||||
|
string="Booking Charge Per Hour", |
||||
|
related='venue_id.venue_charge_hour', |
||||
|
help='Field for adding Booking Charge Per hour') |
||||
|
booking_charge = fields.Float(string="Venue Amenities Charge", |
||||
|
compute='_compute_booking_charge', |
||||
|
help='Compute the total Booking cost ' |
||||
|
'includes the amenities') |
||||
|
days_difference = fields.Integer(string='Days Difference', |
||||
|
compute='_compute_days_difference', |
||||
|
help='Number of Days to ' |
||||
|
'Booking the venue') |
||||
|
invoice_count = fields.Integer(string="Invoice Count", |
||||
|
compute='_compute_invoice_count', |
||||
|
help='Total invoice count') |
||||
|
is_additional_charge = fields.Boolean(string="Add Extra Charge?", |
||||
|
help='Add additional charge ' |
||||
|
'for the booking') |
||||
|
|
||||
|
@api.constrains('venue_booking_line_ids') |
||||
|
def _check_venue_booking_line_ids(self): |
||||
|
"""Check if the venue bookings line contains already taken amenities""" |
||||
|
amenities_list = [] |
||||
|
name_list = [] |
||||
|
if self.venue_id.venue_line_ids: |
||||
|
amenities = self.venue_id.venue_line_ids.mapped('amenities_id') |
||||
|
for line in self.venue_booking_line_ids: |
||||
|
if line.amenity_id in amenities: |
||||
|
amenities_list.append(line.amenity_id) |
||||
|
name_list.append(line.amenity_id.name) |
||||
|
if amenities_list: |
||||
|
names = ', '.join(name_list) |
||||
|
raise ValidationError( |
||||
|
_("Amenities %s are already Include in Your Venue Booking %s" |
||||
|
% (str(names), str(self.venue_id.name)))) |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, values): |
||||
|
"""Create method for sequencing and checking dates |
||||
|
while Booking the Venues""" |
||||
|
date = values['date'] |
||||
|
partner_name = self.env['res.partner'].browse( |
||||
|
values['partner_id']).name |
||||
|
values['name'] = '%s- %s' % (partner_name, date) |
||||
|
values['ref'] = self.env['ir.sequence'].next_by_code( |
||||
|
'venue.booking.sequence') |
||||
|
res = super().create(values) |
||||
|
return res |
||||
|
|
||||
|
@api.onchange('start_date', 'end_date') |
||||
|
def _onchange_booking_dates(self): |
||||
|
"""Checking dates while Booking the Venues based on the |
||||
|
changes of the Dates""" |
||||
|
if self.venue_id: |
||||
|
booking = self.env['venue.booking'].search( |
||||
|
[('start_date', '<', self.end_date), |
||||
|
('end_date', '>', self.start_date), |
||||
|
('venue_id', '=', self.venue_id.id)]) |
||||
|
if booking: |
||||
|
raise ValidationError( |
||||
|
"Venue is not available for the selected time range.") |
||||
|
|
||||
|
@api.constrains('start_date', 'end_date') |
||||
|
def constrains_dates(self): |
||||
|
for rec in self: |
||||
|
start_date = rec.start_date |
||||
|
end_date = rec.end_date |
||||
|
if start_date >= end_date: |
||||
|
raise UserError(_('Start date must be less than End date')) |
||||
|
|
||||
|
def get_booking_dates(self, start_date, end_date, venue_id): |
||||
|
"""Checking dates while Booking the Venues based on the changes |
||||
|
of the Dates""" |
||||
|
result = 0 |
||||
|
date_result = 0 |
||||
|
venue = self.env['venue'].browse(venue_id) |
||||
|
if start_date >= end_date: |
||||
|
date_result = 1 |
||||
|
else: |
||||
|
date_result = 0 |
||||
|
if date_result == 0: |
||||
|
booking = self.env['venue.booking'].sudo().search( |
||||
|
[('venue_id', '=', venue.id), |
||||
|
('start_date', '<', end_date), |
||||
|
('end_date', '>', start_date), |
||||
|
]) |
||||
|
if booking: |
||||
|
result = 1 |
||||
|
else: |
||||
|
result = 0 |
||||
|
data = { |
||||
|
'result': result, |
||||
|
"date_result": date_result, |
||||
|
} |
||||
|
return data |
||||
|
|
||||
|
@api.depends('start_date', 'end_date') |
||||
|
def _compute_days_difference(self): |
||||
|
"""Compute the difference between start and end dates for |
||||
|
Calculating the days""" |
||||
|
for record in self: |
||||
|
if record.start_date and record.end_date: |
||||
|
delta = record.end_date - record.start_date |
||||
|
record.days_difference = delta.days |
||||
|
else: |
||||
|
record.days_difference = 0 |
||||
|
|
||||
|
@api.depends('booking_charge', 'venue_id') |
||||
|
def _compute_booking_charge(self): |
||||
|
"""Compute booking charge for the given venue with the Amenities""" |
||||
|
for rec in self: |
||||
|
rec.booking_charge = rec.venue_id.price_subtotal if rec.venue_id else 0.0 |
||||
|
|
||||
|
@api.depends('venue_booking_line_ids', 'venue_booking_line_ids.state') |
||||
|
def _compute_pending_invoice(self): |
||||
|
"""Compute function for finding the pending Invoices""" |
||||
|
for pending in self: |
||||
|
pending.pending_invoice = any( |
||||
|
not line.is_invoiced and line.state == "done" for line in |
||||
|
pending.venue_booking_line_ids) |
||||
|
|
||||
|
@api.depends('venue_booking_line_ids.sub_total', 'booking_charge_per_hour', |
||||
|
'booking_charge_per_day', 'is_additional_charge', 'booking_type') |
||||
|
def _compute_total_amount(self): |
||||
|
"""Compute total amount of bookings with the Charge of the |
||||
|
Particular venue""" |
||||
|
total = sum(item.sub_total for item in self.venue_booking_line_ids) |
||||
|
for rec in self: |
||||
|
if rec.booking_type == 'day': |
||||
|
total += (rec.booking_charge_per_day * rec.days_difference) |
||||
|
if rec.is_additional_charge: |
||||
|
if rec.venue_id.additional_charge_day != 0.0: |
||||
|
total += rec.venue_id.additional_charge_day |
||||
|
is_extra = self.env['ir.config_parameter'].sudo(). \ |
||||
|
get_param('venue_booking_management.is_extra') |
||||
|
if is_extra: |
||||
|
amount = self.env['ir.config_parameter'].sudo(). \ |
||||
|
get_param('venue_booking_management.extra_amount') |
||||
|
total += float(amount) |
||||
|
elif rec.booking_type == 'hour': |
||||
|
total += (rec.booking_charge_per_hour * ( |
||||
|
rec.days_difference * 24)) |
||||
|
if rec.is_additional_charge: |
||||
|
if rec.venue_id.additional_charge_hour != 0.0: |
||||
|
total += rec.venue_id.additional_charge_hour |
||||
|
is_extra = self.env['ir.config_parameter'].sudo(). \ |
||||
|
get_param('venue_booking_management.is_extra') |
||||
|
if is_extra: |
||||
|
amount = self.env['ir.config_parameter'].sudo(). \ |
||||
|
get_param( |
||||
|
'venue_booking_management.extra_amount') |
||||
|
total += float(amount) |
||||
|
rec.total = total + rec.booking_charge |
||||
|
|
||||
|
def action_booking_confirm(self): |
||||
|
"""Button action to confirm""" |
||||
|
for booking in self: |
||||
|
bookings = self.env['venue.booking'].search([ |
||||
|
('venue_id', '=', booking.venue_id.id), |
||||
|
('start_date', '<', booking.end_date), |
||||
|
('end_date', '>', booking.start_date), |
||||
|
('id', '!=', booking.id), # Exclude the current record itself |
||||
|
]) |
||||
|
if bookings: |
||||
|
raise ValidationError( |
||||
|
"Booking dates overlap with existing bookings.") |
||||
|
else: |
||||
|
self.state = "confirm" |
||||
|
|
||||
|
def action_reset_to_draft(self): |
||||
|
"""Button action to reset""" |
||||
|
self.state = "draft" |
||||
|
|
||||
|
def action_send_confirmation_mail(self): |
||||
|
"""Button action to send confirmation mail""" |
||||
|
template = self.env.ref( |
||||
|
'venue_booking_management.' |
||||
|
'mail_template_notify_venue_booking').sudo() |
||||
|
template.send_mail(self._origin.id, force_send=True, |
||||
|
email_values={ |
||||
|
'email_to': self.partner_id.email}) |
||||
|
|
||||
|
def action_booking_invoice_create(self): |
||||
|
"""Button action to create related invoice""" |
||||
|
invoice_id = self.env['account.move'].search( |
||||
|
[('invoice_origin', '=', self.ref), ('state', '=', 'draft')]) |
||||
|
amenity_lists = [] |
||||
|
|
||||
|
def add_charge(name, price_unit, quantity=1): |
||||
|
amenity_lists.append({ |
||||
|
'name': name, |
||||
|
'price_unit': price_unit, |
||||
|
'quantity': quantity, |
||||
|
}) |
||||
|
|
||||
|
if self.booking_type == 'day': |
||||
|
if self.is_additional_charge: |
||||
|
total = self.booking_charge_per_day + self.venue_id.\ |
||||
|
additional_charge_day |
||||
|
else: |
||||
|
total = self.booking_charge_per_day |
||||
|
elif self.booking_type == 'hour': |
||||
|
if self.is_additional_charge: |
||||
|
total = (self.booking_charge_per_hour * (self.days_difference * |
||||
|
24)) + \ |
||||
|
self.venue_id.additional_charge_hour |
||||
|
else: |
||||
|
total = (self.booking_charge_per_hour * (self.days_difference * |
||||
|
24)) |
||||
|
else: |
||||
|
total = 0 |
||||
|
add_charge('Amenities charge', self.booking_charge) |
||||
|
add_charge('Booking Charges', total) |
||||
|
for rec in self.venue_booking_line_ids: |
||||
|
add_charge(rec.amenity_id.name, rec.amount, rec.quantity) |
||||
|
if self.is_additional_charge: |
||||
|
is_extra = self.env['ir.config_parameter'].sudo(). \ |
||||
|
get_param('venue_booking_management.is_extra') |
||||
|
if is_extra: |
||||
|
amount = self.env['ir.config_parameter'].sudo(). \ |
||||
|
get_param('venue_booking_management.extra_amount') |
||||
|
amenity_lists.append({ |
||||
|
'name': 'Extra charges', |
||||
|
'price_unit': amount, |
||||
|
'quantity': '1', |
||||
|
}) |
||||
|
invoice_vals = { |
||||
|
'move_type': 'out_invoice', |
||||
|
'partner_id': self.partner_id.id, |
||||
|
'invoice_origin': self.ref, |
||||
|
'invoice_line_ids': [(0, 0, line) for line in amenity_lists], |
||||
|
} |
||||
|
if not invoice_id: |
||||
|
invoice = self.env['account.move'].create([invoice_vals]) |
||||
|
self.state = "invoice" |
||||
|
return { |
||||
|
'name': 'Invoice', |
||||
|
'view_mode': 'form', |
||||
|
'res_id': invoice.id, |
||||
|
'res_model': 'account.move', |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'target': 'current', |
||||
|
} |
||||
|
else: |
||||
|
# Unlink existing lines |
||||
|
invoice_id.invoice_line_ids.unlink() |
||||
|
invoice_id.write( |
||||
|
{'invoice_line_ids': [(0, 0, line) for line in amenity_lists]}) |
||||
|
self.state = "invoice" |
||||
|
return { |
||||
|
'name': 'Invoice', |
||||
|
'view_mode': 'form', |
||||
|
'res_id': invoice_id.id, |
||||
|
'res_model': 'account.move', |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'target': 'current', |
||||
|
} |
||||
|
|
||||
|
def action_view_invoice(self): |
||||
|
"""Smart button to view the Corresponding Invoices for |
||||
|
the Venue Booking""" |
||||
|
return { |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'name': 'Invoice', |
||||
|
'view_mode': 'tree,form', |
||||
|
'res_model': 'account.move', |
||||
|
'target': 'current', |
||||
|
'domain': [('invoice_origin', '=', self.ref)], |
||||
|
'context': {"create": False}, |
||||
|
} |
||||
|
|
||||
|
def _compute_invoice_count(self): |
||||
|
"""Function to count invoice""" |
||||
|
for record in self: |
||||
|
record.invoice_count = self.env['account.move']. \ |
||||
|
search_count([('invoice_origin', '=', self.ref)]) |
||||
|
|
||||
|
def action_booking_cancel(self): |
||||
|
"""Button action to move the cancel state""" |
||||
|
self.state = "cancel" |
||||
|
|
||||
|
def action_booking_close(self): |
||||
|
"""Button action to close the records""" |
||||
|
if any(not line.is_invoiced for line in self.venue_booking_line_ids): |
||||
|
raise ValidationError(_('You can close The Booking only when all ' |
||||
|
'Procedure is Done and Invoiced')) |
||||
|
else: |
||||
|
self.state = "close" |
||||
|
|
||||
|
@api.model |
||||
|
def get_total_booking(self): |
||||
|
"""Function to get total booking, distance and invoice |
||||
|
amount details""" |
||||
|
total_booking = self.env['venue.booking'].search_count([]) |
||||
|
booking_ids = self.env['venue.booking'].search( |
||||
|
[('state', 'not in', ['draft', 'cancel', 'close'])]) |
||||
|
invoice_ids = self.env['venue.booking']. \ |
||||
|
search([('state', '=', 'invoice')]).mapped('total') |
||||
|
venue_ids = self.env['venue'].search_count([]) |
||||
|
return {'total_booking': total_booking, |
||||
|
'total_invoice': sum(invoice_ids), |
||||
|
'total_amount': sum(booking_ids.mapped('total')), |
||||
|
'total_venue': venue_ids} |
||||
|
|
||||
|
@api.model |
||||
|
def get_top_venue(self): |
||||
|
"""Function to return top venue and customer details query to js""" |
||||
|
self.env.cr.execute('''select fv.name,count(tb.name) from venue_booking as tb |
||||
|
inner join venue as fv on fv.id = tb.venue_id |
||||
|
group by fv.name order by count(tb.name) desc limit 10''') |
||||
|
venue = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''select pr.name,count(tb.name) from venue_booking as tb |
||||
|
inner join res_partner as pr on pr.id = tb.partner_id |
||||
|
group by pr.name order by count(tb.name) desc limit 10''') |
||||
|
customer = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''select tb.ref, pr.name, tb.date from |
||||
|
venue_booking as tb |
||||
|
inner join res_partner as pr on pr.id = tb.partner_id |
||||
|
where tb.date >= '%s' and tb.state = 'invoice' |
||||
|
order by tb.date''' % fields.date.today()) |
||||
|
upcoming = self.env.cr.dictfetchall() |
||||
|
return {'venue': venue, 'customer': customer, 'upcoming': upcoming} |
||||
|
|
||||
|
@api.model |
||||
|
def get_booking_analysis(self): |
||||
|
"""Function to return customer details to js for graph view""" |
||||
|
self.env.cr.execute('''select pr.name,sum(tb.total) from venue_booking as tb |
||||
|
inner join res_partner as pr on pr.id = tb.partner_id |
||||
|
group by pr.name order by sum(tb.total)''') |
||||
|
booking = self.env.cr.dictfetchall() |
||||
|
count = [] |
||||
|
customer = [] |
||||
|
for record in booking: |
||||
|
customer.append(record.get('name')) |
||||
|
count.append(record.get('sum')) |
||||
|
value = {'name': customer, 'count': count} |
||||
|
return value |
||||
|
|
||||
|
@api.model |
||||
|
def get_venue_analysis(self): |
||||
|
"""Function to return truck details to js for graph view""" |
||||
|
self.env.cr.execute('''select fv.name,sum(tb.total) from venue_booking as tb |
||||
|
inner join venue as fv on fv.id = tb.venue_id |
||||
|
group by fv.name order by sum(tb.total)''') |
||||
|
booking = self.env.cr.dictfetchall() |
||||
|
count = [] |
||||
|
customer = [] |
||||
|
for record in booking: |
||||
|
customer.append(record.get('name')) |
||||
|
count.append(record.get('sum')) |
||||
|
return {'name': customer, 'count': count} |
||||
|
|
||||
|
@api.model |
||||
|
def get_select_filter(self, option): |
||||
|
"""Function to filter data on the bases of the year""" |
||||
|
if option == 'year': |
||||
|
create_date = '''create_date BETWEEN date_trunc('year', now()) AND now()''' |
||||
|
elif option == 'month': |
||||
|
create_date = '''create_date BETWEEN date_trunc('month', now()) AND now()''' |
||||
|
elif option == 'week': |
||||
|
create_date = '''create_date between date_trunc('week', now()) and now()''' |
||||
|
elif option == 'day': |
||||
|
create_date = '''create_date BETWEEN date_trunc('day', now()) AND now()''' |
||||
|
self.env.cr.execute('''select count(*) from venue_booking |
||||
|
where %s''' % create_date) |
||||
|
booking = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''select sum(total) from venue_booking |
||||
|
where %s''' % create_date) |
||||
|
amount = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''select sum(total) from venue_booking |
||||
|
where state = 'invoice' and %s''' % create_date) |
||||
|
invoice = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''select count(*) from venue |
||||
|
where %s''' % create_date) |
||||
|
venue_count = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''SELECT fv.name, COUNT(tb.name) AS name_count |
||||
|
FROM venue_booking AS tb |
||||
|
INNER JOIN venue AS fv ON fv.id = tb.venue_id |
||||
|
where tb.%s |
||||
|
GROUP BY fv.name |
||||
|
ORDER BY name_count DESC |
||||
|
LIMIT 10''' % create_date) |
||||
|
venue = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''SELECT pr.name, COUNT(tb.name) AS name_count |
||||
|
FROM venue_booking AS tb |
||||
|
INNER JOIN res_partner AS pr ON pr.id = tb.partner_id |
||||
|
where tb.%s |
||||
|
GROUP BY pr.name |
||||
|
ORDER BY name_count DESC |
||||
|
LIMIT 10''' % create_date) |
||||
|
customer = self.env.cr.dictfetchall() |
||||
|
self.env.cr.execute('''SELECT pr.name, COUNT(pr.name) AS count, SUM(tb.total) AS total_sum |
||||
|
FROM venue_booking AS tb |
||||
|
INNER JOIN res_partner AS pr ON pr.id = tb.partner_id |
||||
|
WHERE tb.%s |
||||
|
GROUP BY pr.name |
||||
|
''' % create_date) |
||||
|
cust_invoice = self.env.cr.dictfetchall() |
||||
|
cust_invoice_name = [] |
||||
|
cust_invoice_sum = [] |
||||
|
cust_invoice_count = [] |
||||
|
for record in cust_invoice: |
||||
|
cust_invoice_name.append(record.get('name')) |
||||
|
cust_invoice_count.append(record.get('count')) |
||||
|
cust_invoice_sum.append(record.get('sum')) |
||||
|
self.env.cr.execute('''SELECT fv.name, SUM(tb.total) AS total_sum |
||||
|
FROM venue_booking AS tb |
||||
|
INNER JOIN venue AS fv ON fv.id = tb.venue_id |
||||
|
where tb.%s |
||||
|
GROUP BY fv.name; |
||||
|
''' % create_date) |
||||
|
truck_invoice = self.env.cr.dictfetchall() |
||||
|
truck_invoice_name = [] |
||||
|
truck_invoice_sum = [] |
||||
|
for record in truck_invoice: |
||||
|
truck_invoice_name.append(record.get('name')) |
||||
|
truck_invoice_sum.append(record.get('total_sum')) |
||||
|
return {'booking': booking, 'amount': amount, |
||||
|
'invoice': invoice, 'venue': venue, 'venue_count': venue_count, |
||||
|
'customer': customer, |
||||
|
'cust_invoice_name': cust_invoice_name, |
||||
|
'cust_invoice_count': cust_invoice_count, 'cust_invoice_sum': |
||||
|
cust_invoice_sum, 'truck_invoice_name': truck_invoice_name, |
||||
|
'truck_invoice_sum': truck_invoice_sum, |
||||
|
} |
||||
|
|
||||
|
|
||||
|
class VenueBookingLine(models.Model): |
||||
|
"""Model to manage the Venue Booking lines of the Venue Reservation""" |
||||
|
_name = 'venue.booking.line' |
||||
|
_description = "Venue Booking" |
||||
|
|
||||
|
venue_booking_id = fields.Many2one('venue.booking', |
||||
|
string="Venue Booking", |
||||
|
help='The relation added for the venue Booking ') |
||||
|
state = fields.Selection([('done', 'Done'), ('pending', 'Pending')], |
||||
|
string="State", default="pending", |
||||
|
readonly=True, |
||||
|
help="The state of the venue Booking line") |
||||
|
currency_id = fields.Many2one('res.currency', readonly=True, |
||||
|
default=lambda self: |
||||
|
self.env.user.company_id.currency_id, |
||||
|
string="Currency", |
||||
|
help="The currency of the booking line") |
||||
|
is_invoiced = fields.Boolean(string="Invoiced", readonly=True, |
||||
|
help="The boolean value used for finding the " |
||||
|
"venue booking is invoiced or not") |
||||
|
venue_type_id = fields.Many2one('venue.type', |
||||
|
string="Related Venue Type", |
||||
|
related='venue_booking_id.venue_type_id', |
||||
|
help="The venue type of the booking line") |
||||
|
amenity_id = fields.Many2one('amenities', string='Amenities', |
||||
|
help='The relational field for the booking ' |
||||
|
'line with the amenities model') |
||||
|
quantity = fields.Float(string="Quantity", default=1, |
||||
|
help="Quantity of the Amenities") |
||||
|
amount = fields.Float(string="Amount", help="Amount of the Amenities", |
||||
|
related='amenity_id.amount') |
||||
|
sub_total = fields.Float(string="Sub Total", |
||||
|
compute="_compute_extra_sub_total", |
||||
|
readonly=True, help="Sub Total of the Values") |
||||
|
|
||||
|
@api.depends('quantity', 'amount') |
||||
|
def _compute_extra_sub_total(self): |
||||
|
"""Compute function for the Amenities""" |
||||
|
for booking in self: |
||||
|
booking.sub_total = booking.quantity * booking.amount |
@ -0,0 +1,33 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 VenueType(models.Model): |
||||
|
"""Model for managing the Venue types""" |
||||
|
_name = 'venue.type' |
||||
|
_description = 'Venue Type' |
||||
|
|
||||
|
name = fields.Char(string="Name", help="Name of the venue type") |
||||
|
image = fields.Binary("Image", attachment=True, |
||||
|
help="This field holds the image used as " |
||||
|
"image for the event, limited to 1080x720px.") |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 form_venue_booking_report |
||||
|
from . import venue_booking_report |
@ -0,0 +1,78 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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 pytz |
||||
|
from odoo import api, fields, models |
||||
|
from odoo.exceptions import ValidationError |
||||
|
|
||||
|
|
||||
|
class VenueBookingReport(models.AbstractModel): |
||||
|
"""Class is used to print pdf report for the venue_booking |
||||
|
module form view""" |
||||
|
_name = 'report.venue_booking_management.report_venue_booking' |
||||
|
|
||||
|
@api.model |
||||
|
def _get_report_values(self, docids, data=None): |
||||
|
"""Function to return values for the report, |
||||
|
docids: it will provide the current id the model""" |
||||
|
current = fields.datetime.now().astimezone( |
||||
|
pytz.timezone(self.env.user.tz)) |
||||
|
current = current.strftime("%d-%m-%Y %H:%M:%S") |
||||
|
if docids: |
||||
|
doc_ids = self.env['venue.booking'].sudo().browse(docids) |
||||
|
return { |
||||
|
'doc_ids': doc_ids, |
||||
|
'today_date': current, |
||||
|
} |
||||
|
else: |
||||
|
form_data = data['form'] |
||||
|
# Initialize the SQL WHERE clause |
||||
|
where = '1=1' |
||||
|
# Check if the start_date is greater than end_date |
||||
|
if form_data['start_date'] and form_data['end_date'] and form_data[ |
||||
|
'start_date'] > form_data['end_date']: |
||||
|
raise ValidationError('Start Date must be less than End Date') |
||||
|
# Add conditions to the WHERE clause based on form data |
||||
|
if form_data["partner_id"]: |
||||
|
where += """ AND tb.partner_id = %s""" % form_data['partner_id'][0] |
||||
|
if form_data['start_date']: |
||||
|
where += """ AND tb.date >= '%s'""" % form_data['start_date'] |
||||
|
if form_data['end_date']: |
||||
|
where += """ AND tb.date <= '%s'""" % form_data['end_date'] |
||||
|
if form_data['venue_id']: |
||||
|
where += """ AND tb.venue_id = %s""" % form_data['venue_id'][0] |
||||
|
# Execute the SQL query with the WHERE clause |
||||
|
self.env.cr.execute(""" |
||||
|
SELECT tb.ref, pr.name, fv.name as venue, tb.booking_type, |
||||
|
tb.date, tb.start_date, tb.end_date, tb.state |
||||
|
FROM venue_booking as tb |
||||
|
INNER JOIN res_partner as pr ON pr.id = tb.partner_id |
||||
|
INNER JOIN venue as fv ON fv.id = tb.venue_id |
||||
|
WHERE %s |
||||
|
""" % where) |
||||
|
# Fetch the query results |
||||
|
rec = self.env.cr.dictfetchall() |
||||
|
# Return the data for the report |
||||
|
return { |
||||
|
'docs': rec, |
||||
|
'docs2': form_data, |
||||
|
'today_date': current, |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Fathima Mazlin AM (odoo@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, tools |
||||
|
|
||||
|
|
||||
|
class VenueBookingReport(models.Model): |
||||
|
_name = "venue.booking.report" |
||||
|
_description = "Venue Booking Analysis Report" |
||||
|
_auto = False |
||||
|
_rec_name = 'date' |
||||
|
_order = 'date desc' |
||||
|
|
||||
|
name = fields.Char('Booking Reference', readonly=True, |
||||
|
help="Booking Reference field for the Reporting") |
||||
|
date = fields.Datetime('Booking Date', readonly=True, |
||||
|
help="Booking Date field for the Reporting") |
||||
|
partner_id = fields.Many2one('res.partner', |
||||
|
'Customer', readonly=True, |
||||
|
help="Partner ID field for the Reporting") |
||||
|
total = fields.Float('Total', readonly=True, |
||||
|
help="Total amount for the Booking Values") |
||||
|
state = fields.Selection([ |
||||
|
('draft', 'Enquiry'), |
||||
|
('confirm', 'Confirmed'), |
||||
|
('invoice', 'Invoiced'), |
||||
|
('close', 'Closed'), |
||||
|
('cancel', 'Cancelled'), |
||||
|
], string='Status', readonly=True, help="The selection field for " |
||||
|
"the Booking") |
||||
|
|
||||
|
def init(self): |
||||
|
"""Initialize the function to get the Booking Details""" |
||||
|
tools.drop_view_if_exists(self._cr, self._table) |
||||
|
self._cr.execute(""" |
||||
|
CREATE OR REPLACE VIEW %s AS ( |
||||
|
SELECT |
||||
|
vb.id as id, |
||||
|
vb.name as name, |
||||
|
vb.date as date, |
||||
|
vb.partner_id as partner_id, |
||||
|
vb.total as total, |
||||
|
vb.state as state |
||||
|
FROM venue_booking vb |
||||
|
WHERE vb.state IN ('confirm', 'invoice') |
||||
|
GROUP BY |
||||
|
vb.id, |
||||
|
vb.name, |
||||
|
vb.date, |
||||
|
vb.partner_id, |
||||
|
vb.total, |
||||
|
vb.state |
||||
|
ORDER BY vb.id |
||||
|
) |
||||
|
""" % (self._table,)) |
@ -0,0 +1,186 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<!-- Template used to create form for the booking report--> |
||||
|
<template id="report_venue_booking"> |
||||
|
<t t-call="web.html_container"> |
||||
|
<t t-call="web.external_layout"> |
||||
|
<div class="page"> |
||||
|
<div class="oe_structure"/> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12" style="text-align: center;"> |
||||
|
<h2> |
||||
|
<span>Venue/Event Booking Report</span> |
||||
|
</h2> |
||||
|
</div> |
||||
|
</div> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<div> |
||||
|
<span>Date:</span> |
||||
|
<span t-esc="today_date"/> |
||||
|
</div> |
||||
|
<br/> |
||||
|
<t t-if="doc_ids"> |
||||
|
<div> |
||||
|
<span t-if="doc_ids['start_date']"> |
||||
|
<b>From:</b> |
||||
|
<span style="margin-left:3px;margin-right:17px;margin-bottom:3px" |
||||
|
t-esc="doc_ids['start_date']"/> |
||||
|
</span> |
||||
|
<span t-if="doc_ids['end_date']"> |
||||
|
<b>To:</b> |
||||
|
<span style="margin-left:3px;margin-right:17px;margin-bottom:3px" |
||||
|
t-esc="doc_ids['end_date']"/> |
||||
|
</span> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<span t-if="doc_ids['partner_id']"> |
||||
|
<b>Customer:</b> |
||||
|
<span style="margin-left:3px;margin-bottom:3px" |
||||
|
t-esc="doc_ids['partner_id'].name"/> |
||||
|
</span> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<span t-if="doc_ids['venue_id']"> |
||||
|
<b>Venue:</b> |
||||
|
<span style="margin-left:3px;margin-bottom:3px" |
||||
|
t-esc="doc_ids['venue_id'].name"/> |
||||
|
</span> |
||||
|
</div> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<table class="table table-sm"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Sl.no</th> |
||||
|
<th>Ref.No</th> |
||||
|
<th>Venue</th> |
||||
|
<th>Booking Type</th> |
||||
|
<th>Customer</th> |
||||
|
<th>Start Date</th> |
||||
|
<th>End Date</th> |
||||
|
<th>State</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<t t-foreach="doc_ids" t-as="l"> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<t t-esc="l_index + 1"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['ref']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['venue_id'].name"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="{'hour': 'Hour', 'day': 'Day'} |
||||
|
[l['booking_type']]"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['partner_id'].name"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['start_date']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['end_date']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="{'draft': 'Draft', 'confirm': 'Confirm', 'invoice': 'Invoiced', 'cancel': 'Cancelled', 'close': 'Closed'} |
||||
|
[l['state']]"/> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
<t t-if="docs2"> |
||||
|
<div> |
||||
|
<span t-if="docs2['start_date']"> |
||||
|
<b>From:</b> |
||||
|
<span style="margin-left:3px;margin-right:17px;margin-bottom:3px" |
||||
|
t-esc="docs2['start_date']"/> |
||||
|
</span> |
||||
|
<span t-if="docs2['end_date']"> |
||||
|
<b>To:</b> |
||||
|
<span style="margin-left:3px;margin-right:17px;margin-bottom:3px" |
||||
|
t-esc="docs2['end_date']"/> |
||||
|
</span> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<span t-if="docs2['partner_id']"> |
||||
|
<b>Customer:</b> |
||||
|
<span style="margin-left:3px;margin-bottom:3px" |
||||
|
t-esc="docs2['partner_id'][1]"/> |
||||
|
</span> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<span t-if="docs2['venue_id']"> |
||||
|
<b>Venue:</b> |
||||
|
<span style="margin-left:3px;margin-bottom:3px" |
||||
|
t-esc="docs2['venue_id'][1]"/> |
||||
|
</span> |
||||
|
</div> |
||||
|
<br/> |
||||
|
<br/> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<table class="table table-sm"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Sl.no</th> |
||||
|
<th>Ref.No</th> |
||||
|
<th>Venue</th> |
||||
|
<th>Booking Type</th> |
||||
|
<th>Customer</th> |
||||
|
<th>Start Date</th> |
||||
|
<th>End Date</th> |
||||
|
<th>State</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<t t-foreach="docs" t-as="l"> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<t t-esc="l_index + 1"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['ref']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['venue']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="{'hour': 'Hour', 'day': 'Day'} |
||||
|
[l['booking_type']]"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['name']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['start_date']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="l['end_date']"/> |
||||
|
</td> |
||||
|
<td> |
||||
|
<span t-esc="{'draft': 'Draft', 'confirm': 'Confirm', 'invoice': 'Invoiced', 'cancel': 'Cancelled', 'close': 'Closed'} |
||||
|
[l['state']]"/> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
<div class="oe_structure"/> |
||||
|
</div> |
||||
|
</t> |
||||
|
</t> |
||||
|
</template> |
||||
|
</odoo> |
@ -0,0 +1,106 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<!--Pivot view for the Report--> |
||||
|
<record id="venue_booking_report_view_pivot" model="ir.ui.view"> |
||||
|
<field name="name">venue.booking.report.view.pivot</field> |
||||
|
<field name="model">venue.booking.report</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<pivot string="Venue Booking Analysis" sample="1"> |
||||
|
<field name="partner_id" type="col"/> |
||||
|
<field name="date" interval="month" type="row"/> |
||||
|
<field name="total" type="measure"/> |
||||
|
<field name="partner_id" type="measure"/> |
||||
|
</pivot> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Graph view for the report--> |
||||
|
<record id="venue_booking_report_view_graph" model="ir.ui.view"> |
||||
|
<field name="name">venue.booking.report.view.graph</field> |
||||
|
<field name="model">venue.booking.report</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<graph string="Venue Booking Analysis" type="line" sample="1"> |
||||
|
<field name="date" interval="day"/> |
||||
|
<field name="total" type="measure"/> |
||||
|
<field name="partner_id" type="measure"/> |
||||
|
</graph> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Tree view for the booking Report--> |
||||
|
<record id="venue_booking_report_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">venue.booking.report.view.tree</field> |
||||
|
<field name="model">venue.booking.report</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree string="Venue Booking Analysis"> |
||||
|
<field name="date" widget="date"/> |
||||
|
<field name="partner_id" optional="hide"/> |
||||
|
<field name="total" optional="hide" sum="Sum of Total"/> |
||||
|
<field name="state" optional="hide"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Search view for the Booking Report--> |
||||
|
<record id="venue_booking_report_view_search" model="ir.ui.view"> |
||||
|
<field name="name">venue.booking.report.view.search</field> |
||||
|
<field name="model">venue.booking.report</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<search string="Venue Booking Analysis"> |
||||
|
<field name="date"/> |
||||
|
<filter string="Date" name="year" invisible="1" date="date" |
||||
|
default_period="this_year"/> |
||||
|
<filter string="Enquiry" name="Enquiry" |
||||
|
domain="[('state','in', ('draft'))]"/> |
||||
|
<filter string="Confirmed Booking" name="Confirmed Booking" |
||||
|
domain="[('state','not in',('draft', 'cancel', 'close'))]"/> |
||||
|
<separator/> |
||||
|
<filter name="filter_date" date="date" |
||||
|
default_period="this_month"/> |
||||
|
<filter name="filter_order_date" invisible="1" |
||||
|
string="Order Date: Last 365 Days" |
||||
|
domain="[('date', '>=', (datetime.datetime.combine(context_today() + relativedelta(days=-365), datetime.time(0,0,0))).strftime('%Y-%m-%d %H:%M:%S'))]"/> |
||||
|
<separator/> |
||||
|
<field name="partner_id"/> |
||||
|
<group expand="1" string="Group By"> |
||||
|
<filter string="Customer" name="Customer" |
||||
|
context="{'group_by':'partner_id'}"/> |
||||
|
<filter string="Status" name="status" |
||||
|
context="{'group_by':'state'}"/> |
||||
|
<separator/> |
||||
|
<filter string="Order Date" name="date" |
||||
|
context="{'group_by':'date'}" |
||||
|
invisible="context.get('sale_report_view_hide_date')"/> |
||||
|
<filter string="Order Date" name="group_by_date_day" |
||||
|
context="{'group_by':'date:day'}" |
||||
|
invisible="not context.get('sale_report_view_hide_date')"/> |
||||
|
</group> |
||||
|
</search> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!-- Action record for the Report--> |
||||
|
<record id="venue_booking_report_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Venue Booking Analysis</field> |
||||
|
<field name="res_model">venue.booking.report</field> |
||||
|
<field name="view_mode">graph,pivot</field> |
||||
|
<field name="search_view_id" ref="venue_booking_report_view_search"/> |
||||
|
<field name="context">{'group_by_no_leaf':1,'group_by':[], 'search_default_filter_order_date': 1}</field> |
||||
|
<field name="help">This report performs analysis on your Venue Booking. |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="report_all_venue_booking_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Venue Booking Analysis</field> |
||||
|
<field name="res_model">venue.booking.report</field> |
||||
|
<field name="view_mode">pivot</field> |
||||
|
</record> |
||||
|
<!--MenuItems for the Reports--> |
||||
|
<menuitem id="venue_booking_menu_report" |
||||
|
name="Reporting" action="venue_booking_report_action" |
||||
|
parent="venue_booking_menu_root" |
||||
|
groups="venue_booking_management.venue_booking_management_group_venue_manager" |
||||
|
sequence="40"/> |
||||
|
<menuitem id="venue_booking_menu_report_sub_menu" |
||||
|
name="Venue Booking Analysis" |
||||
|
action="venue_booking_report_action" |
||||
|
parent="venue_booking_menu_report" |
||||
|
groups="venue_booking_management.venue_booking_management_group_venue_manager" |
||||
|
sequence="10"/> |
||||
|
</odoo> |
@ -0,0 +1,13 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8" ?> |
||||
|
<odoo> |
||||
|
<!-- Generate the Booking report as pdf Format--> |
||||
|
<record id="action_venue_booking_management_report" model="ir.actions.report"> |
||||
|
<field name="name">Venue Booking</field> |
||||
|
<field name="model">venue.booking</field> |
||||
|
<field name="report_type">qweb-pdf</field> |
||||
|
<field name="report_name">venue_booking_management.report_venue_booking</field> |
||||
|
<field name="report_file">venue_booking_management.report_venue_booking</field> |
||||
|
<field name="binding_model_id" ref="model_venue_booking"/> |
||||
|
<field name="binding_type">report</field> |
||||
|
</record> |
||||
|
</odoo> |
|
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<record model="ir.module.category" id="module_venue_booking_management"> |
||||
|
<field name="name">Venue Booking Management</field> |
||||
|
<field name="description">Category for Venue Booking Management</field> |
||||
|
</record> |
||||
|
<!--Group For User--> |
||||
|
<record id="venue_booking_management_group_venue_user" model="res.groups"> |
||||
|
<field name="name">User</field> |
||||
|
<field name="category_id" ref="module_venue_booking_management"/> |
||||
|
</record> |
||||
|
<!--Group For Manager--> |
||||
|
<record id="venue_booking_management_group_venue_manager" |
||||
|
model="res.groups"> |
||||
|
<field name="name">Manager</field> |
||||
|
<field name="category_id" ref="module_venue_booking_management"/> |
||||
|
<field name="implied_ids" |
||||
|
eval="[(4, ref('venue_booking_management_group_venue_user'))]"/> |
||||
|
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> |
||||
|
</record> |
||||
|
</odoo> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8" ?> |
||||
|
<odoo> |
||||
|
<!--XML for viewing created records of logged user--> |
||||
|
<record id="venue_booking_rule_space_user" model="ir.rule"> |
||||
|
<field name="name">View Own Docs</field> |
||||
|
<field ref="model_venue_booking" name="model_id"/> |
||||
|
<field name="domain_force">['|',('user_id', '=', user.id),('create_uid', '=', user.id)] |
||||
|
</field> |
||||
|
<field name="groups" |
||||
|
eval="[(4, ref('venue_booking_management.venue_booking_management_group_venue_user'))]"/> |
||||
|
</record> |
||||
|
<!--XML for viewing all the records of all the users--> |
||||
|
<record id="venue_booking_rule_space_manager" model="ir.rule"> |
||||
|
<field name="name">View All Docs</field> |
||||
|
<field ref="model_venue_booking" name="model_id"/> |
||||
|
<field name="domain_force">[(1, '=', 1)]</field> |
||||
|
<field name="groups" |
||||
|
eval="[(4, ref('venue_booking_management.venue_booking_management_group_venue_manager'))]"/> |
||||
|
</record> |
||||
|
</odoo> |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 177 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 35 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: 576 B |
After Width: | Height: | Size: 733 B |
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: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
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: 1.2 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 243 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 83 KiB |