@ -0,0 +1,45 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-green.svg |
|||
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
Website Helpdesk Support Ticket Management |
|||
========================================== |
|||
- HelpDesk Support for Odoo 18 community edition |
|||
|
|||
Installation |
|||
============ |
|||
- www.odoo.com/documentation/18.0/setup/install.html |
|||
- Install our custom addon |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (LGPL v3). |
|||
(https://www.odoo.com/documentation/user/18.0/legal/licenses/licenses.html) |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
* Developers: (V18) Bhagyadev KP, |
|||
* Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@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 |
|||
|
|||
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: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from . import controller |
|||
from . import models |
@ -0,0 +1,75 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
{ |
|||
'name': "Website Helpdesk Support Ticket Management", |
|||
'version': '18.0.1.0.0', |
|||
'category': 'Website', |
|||
'summary': """The website allows for the creation of tickets, which can |
|||
then be controlled from the backend.""", |
|||
'description': """A ticket can be created from the website and subsequently |
|||
managed from the backend. Additionally, a bill can be generated for the |
|||
ticket, which includes the service cost.""", |
|||
'author': "Cybrosys Techno Solutions", |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': "http://www.cybrosys.com", |
|||
'depends': ['website', 'project', 'sale_project', 'hr_timesheet', |
|||
'mail', 'contacts'], |
|||
'data': [ |
|||
'security/odoo_website_helpdesk_groups.xml', |
|||
'security/odoo_website_helpdesk_security.xml', |
|||
'security/ir.model.access.csv', |
|||
'data/helpdesk_category_data.xml', |
|||
'data/helpdesk_replay_template_data.xml', |
|||
'data/helpdesk_type_data.xml', |
|||
'data/ir_cron_data.xml', |
|||
'data/ir_sequence_data.xml', |
|||
'data/mail_template_data.xml', |
|||
'data/ticket_stage_data.xml', |
|||
'views/helpdesk_category_views.xml', |
|||
'views/helpdesk_tag_views.xml', |
|||
'views/helpdesk_type_views.xml', |
|||
'views/merge_ticket_views.xml', |
|||
'views/odoo_website_helpdesk_portal_templates.xml', |
|||
'views/portal_templates.xml', |
|||
'views/rating_form.xml', |
|||
'report/helpdesk_ticket_report_template.xml', |
|||
'views/res_config_settings_views.xml', |
|||
'views/team_helpdesk_views.xml', |
|||
'views/ticket_helpdesk_views.xml', |
|||
'views/ticket_stage_views.xml', |
|||
'views/website_form.xml', |
|||
'views/helpdesk_menu_views.xml', |
|||
], |
|||
'assets': { |
|||
'web.assets_frontend': [ |
|||
'/odoo_website_helpdesk/static/src/js/ticket_details.js', |
|||
'/odoo_website_helpdesk/static/src/js/portal_search.js', |
|||
'/odoo_website_helpdesk/static/src/js/multiple_product_choose.js', |
|||
] |
|||
}, |
|||
'images': ['static/description/banner.png'], |
|||
'license': 'LGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,27 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from . import odoo_website_helpdesk |
|||
from . import portal |
|||
from . import ticket_group_by |
|||
from . import ticket_search |
|||
from . import website_form |
|||
from . import website_ticket |
@ -0,0 +1,162 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
import datetime as DT |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class HelpDeskDashboard(http.Controller): |
|||
"""Controller for handling Help Desk dashboard requests.""" |
|||
|
|||
@http.route(['/helpdesk_dashboard'], type='json', auth="public") |
|||
def helpdesk_dashboard(self): |
|||
"""Retrieves statistics for tickets in different stages. |
|||
Returns:dict: Dashboard statistics including counts and IDs for each |
|||
stage. |
|||
""" |
|||
stage_names = ['Inbox', 'Draft', 'In Progress', 'Canceled', 'Done', |
|||
'Closed'] |
|||
stage_ids = { |
|||
name: request.env['ticket.stage'].search([('name', '=', name)], |
|||
limit=1).id for name in |
|||
stage_names} |
|||
new_stages = [stage_ids['Inbox'], stage_ids['Draft']] |
|||
def get_ticket_data(stage_ids): |
|||
tickets = request.env["ticket.helpdesk"].search( |
|||
[('stage_id', 'in', stage_ids)]) |
|||
return len(tickets), [ticket.id for ticket in tickets] |
|||
dashboard_values = { |
|||
'new': (get_ticket_data(new_stages))[0], |
|||
'new_id': (get_ticket_data(new_stages))[1], |
|||
'in_progress': (get_ticket_data([stage_ids['In Progress']]))[0], |
|||
'in_progress_id': (get_ticket_data([stage_ids['In Progress']]))[1], |
|||
'canceled': (get_ticket_data([stage_ids['Canceled']]))[0], |
|||
'canceled_id': (get_ticket_data([stage_ids['Canceled']]))[1], |
|||
'done': (get_ticket_data([stage_ids['Done']]))[0], |
|||
'done_id': (get_ticket_data([stage_ids['Done']]))[1], |
|||
'closed': (get_ticket_data([stage_ids['Closed']]))[0], |
|||
'closed_id': (get_ticket_data([stage_ids['Closed']]))[1]} |
|||
return dashboard_values |
|||
|
|||
def helpdesk_dashboard_week(self): |
|||
""" Retrieves statistics for tickets created in the past week. |
|||
Returns: |
|||
dict: Dashboard statistics including counts and IDs for each stage.""" |
|||
today = DT.date.today() |
|||
week_ago = str(today - DT.timedelta(days=7)) + ' ' |
|||
stage_names = ['Inbox', 'Draft', 'In Progress', 'Canceled', 'Done', |
|||
'Closed'] |
|||
stages = { |
|||
name: request.env['ticket.stage'].search([('name', '=', name)], |
|||
limit=1).id for name in |
|||
stage_names} |
|||
stage_ids = [stages['Inbox'], stages['Draft']] |
|||
def get_ticket_data(stage_id): |
|||
count = request.env["ticket.helpdesk"].search_count( |
|||
[('stage_id', '=', stage_id), ('create_date', '>', week_ago)]) |
|||
ids = request.env["ticket.helpdesk"].search( |
|||
[('stage_id', '=', stage_id), |
|||
('create_date', '>', week_ago)]).ids |
|||
return count, ids |
|||
new_count, new_ids = get_ticket_data(stage_ids) |
|||
in_progress_count, in_progress_ids = get_ticket_data( |
|||
stages['In Progress']) |
|||
canceled_count, canceled_ids = get_ticket_data(stages['Canceled']) |
|||
done_count, done_ids = get_ticket_data(stages['Done']) |
|||
closed_count, closed_ids = get_ticket_data(stages['Closed']) |
|||
dashboard_values = { |
|||
'new': new_count, |
|||
'in_progress': in_progress_count, |
|||
'canceled': canceled_count, |
|||
'done': done_count, |
|||
'closed': closed_count, |
|||
'new_id': new_ids, |
|||
'in_progress_id': in_progress_ids, |
|||
'canceled_id': canceled_ids, |
|||
'done_id': done_ids, |
|||
'closed_id': closed_ids, |
|||
} |
|||
return dashboard_values |
|||
|
|||
@http.route(['/helpdesk_dashboard_month'], type='json', auth="public") |
|||
def helpdesk_dashboard_month(self): |
|||
"""Retrieves statistics for tickets created in the past month. |
|||
Returns: |
|||
dict: Dashboard statistics including counts and IDs for each stage.""" |
|||
today = DT.date.today() |
|||
month_ago = today - DT.timedelta(days=30) |
|||
week_ago = str(month_ago) + ' ' |
|||
stages = request.env['ticket.stage'].search([('name', 'in', |
|||
['Inbox', 'Draft', |
|||
'In Progress', |
|||
'Canceled', 'Done', |
|||
'Closed'])]) |
|||
stage_ids = {stage.name: stage.id for stage in stages} |
|||
def get_stage_data(stage_names): |
|||
stage_ids_list = [stage_ids[name] for name in stage_names] |
|||
tickets = request.env["ticket.helpdesk"].search( |
|||
[('stage_id', 'in', stage_ids_list), |
|||
('create_date', '>', week_ago)]) |
|||
return len(tickets), [ticket.id for ticket in tickets] |
|||
new_count, new_ids = get_stage_data(['Inbox', 'Draft']) |
|||
in_progress_count, in_progress_ids = get_stage_data(['In Progress']) |
|||
canceled_count, canceled_ids = get_stage_data(['Canceled']) |
|||
done_count, done_ids = get_stage_data(['Done']) |
|||
closed_count, closed_ids = get_stage_data(['Closed']) |
|||
dashboard_values = { |
|||
'new': new_count, |
|||
'in_progress': in_progress_count, |
|||
'canceled': canceled_count, |
|||
'done': done_count, |
|||
'closed': closed_count, |
|||
'new_id': new_ids, |
|||
'in_progress_id': in_progress_ids, |
|||
'canceled_id': canceled_ids, |
|||
'done_id': done_ids, |
|||
'closed_id': closed_ids, |
|||
} |
|||
return dashboard_values |
|||
|
|||
@http.route(['/helpdesk_dashboard_year'], type='json', auth="public") |
|||
def helpdesk_dashboard_year(self): |
|||
"""Retrieves statistics for tickets created in the past year. |
|||
Returns: |
|||
dict: Dashboard statistics including counts and IDs for each stage. |
|||
""" |
|||
today = DT.date.today() |
|||
year_ago = today - DT.timedelta(days=360) |
|||
stages = ['Inbox', 'Draft', 'In Progress', 'Canceled', 'Done', 'Closed'] |
|||
stage_ids = { |
|||
stage: request.env['ticket.stage'].search([('name', '=', stage)], |
|||
limit=1).id for stage in |
|||
stages} |
|||
def get_ticket_data(stage_name): |
|||
stage_id = stage_ids[stage_name] |
|||
tickets = request.env["ticket.helpdesk"].search( |
|||
[('stage_id', '=', stage_id), ('create_date', '>', year_ago)]) |
|||
return len(tickets), [ticket.id for ticket in tickets] |
|||
dashboard_values = {} |
|||
for stage in stages: |
|||
count, ids = get_ticket_data(stage) |
|||
dashboard_values[stage.lower()] = count |
|||
dashboard_values[f'{stage.lower()}_id'] = ids |
|||
return dashboard_values |
@ -0,0 +1,111 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import http |
|||
from odoo.addons.portal.controllers import portal |
|||
from odoo.http import request |
|||
|
|||
|
|||
class TicketPortal(portal.CustomerPortal): |
|||
|
|||
def _prepare_home_portal_values(self, counters): |
|||
""" |
|||
Prepare values for the home portal, including ticket count. Args: |
|||
counters (dict): A dictionary containing counters for various portal |
|||
information. Returns: dict: A dictionary of values for the home portal. |
|||
""" |
|||
values = super()._prepare_home_portal_values(counters) |
|||
if 'ticket_count' in counters: |
|||
ticket_count = request.env['ticket.helpdesk'].search_count( |
|||
self._get_tickets_domain()) if request.env[ |
|||
'ticket.helpdesk'].check_access_rights( |
|||
'read', raise_exception=False) else 0 |
|||
values['ticket_count'] = ticket_count |
|||
return values |
|||
|
|||
def _get_tickets_domain(self): |
|||
""" |
|||
Define the domain for searching tickets related to the current customer. |
|||
Returns: |
|||
list: A list representing the domain for ticket search. |
|||
""" |
|||
return [('customer_id', '=', request.env.user.partner_id.id)] |
|||
|
|||
@http.route(['/my/tickets'], type='http', auth="user", website=True) |
|||
def portal_my_tickets(self): |
|||
""" |
|||
Route to display the tickets associated with the current customer. |
|||
Returns: |
|||
http.Response: The HTTP response rendering the tickets page. |
|||
""" |
|||
domain = self._get_tickets_domain() |
|||
tickets = request.env['ticket.helpdesk'].sudo().search(domain) |
|||
values = { |
|||
'default_url': "/my/tickets", |
|||
'tickets': tickets, |
|||
'page_name': 'ticket', |
|||
} |
|||
return request.render("odoo_website_helpdesk.portal_my_tickets", |
|||
values) |
|||
|
|||
@http.route(['/my/tickets/<int:id>'], type='http', auth="public", |
|||
website=True) |
|||
def portal_tickets_details(self, **kwargs): |
|||
""" |
|||
Route to display the details of a specific ticket. |
|||
Args: |
|||
ticket_id (int): The ID of the ticket to be displayed. |
|||
Returns: |
|||
http.Response: The HTTP response rendering the ticket details page. |
|||
""" |
|||
ticket_id = kwargs.get("id") |
|||
details = request.env['ticket.helpdesk'].sudo().browse(ticket_id) |
|||
data = { |
|||
'page_name': 'ticket', |
|||
'ticket': True, |
|||
'details': details, |
|||
} |
|||
return request.render("odoo_website_helpdesk.portal_ticket_details", |
|||
data) |
|||
|
|||
@http.route('/my/tickets/download/<id>', auth='public', |
|||
type='http', |
|||
website=True) |
|||
def ticket_download_portal(self, **kwargs): |
|||
""" |
|||
Route to download a PDF version of a specific ticket. |
|||
Args: |
|||
ticket (str): The ID of the ticket to be downloaded. |
|||
Returns: |
|||
http.Response: The HTTP response with the PDF file for download. |
|||
""" |
|||
ticket_id = int(kwargs.get('id')) |
|||
data = { |
|||
'help': request.env['ticket.helpdesk'].sudo().browse(ticket_id)} |
|||
report = request.env.ref( |
|||
'odoo_website_helpdesk.report_ticket') |
|||
pdf, _ = report.sudo()._render_qweb_pdf( |
|||
report, res_ids=ticket_id, data=data) |
|||
pdf_http_headers = [('Content-Type', 'application/pdf'), |
|||
('Content-Length', len(pdf)), |
|||
('Content-Disposition', |
|||
'attachment; filename="Helpdesk Ticket.pdf"')] |
|||
return request.make_response(pdf, headers=pdf_http_headers) |
@ -0,0 +1,80 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class TicketGroupBy(http.Controller): |
|||
"""Controller for handling ticket grouping based on different criteria.""" |
|||
|
|||
@http.route(['/ticketgroupby'], type='json', auth="public", website=True) |
|||
def ticket_group_by(self, **kwargs): |
|||
"""grouping tickets based on user-defined criteria. |
|||
Args: |
|||
- kwargs (dict): Keyword arguments received from the HTTP request. |
|||
Returns: |
|||
- http.Response: Rendered HTTP response containing grouped ticket information. |
|||
""" |
|||
context = [] |
|||
group_value = kwargs.get("search_value") |
|||
if group_value == '0': |
|||
context = [] |
|||
tickets = request.env["ticket.helpdesk"].search( |
|||
[('user_id', '=', request.env.user.id)]) |
|||
if tickets: |
|||
context.append({ |
|||
'name': '', |
|||
'data': tickets |
|||
}) |
|||
if group_value == '1': |
|||
context = [] |
|||
stage_ids = request.env['ticket.stage'].search([]) |
|||
for stage in stage_ids: |
|||
ticket_ids = request.env['ticket.helpdesk'].search([ |
|||
('stage_id', '=', stage.id), |
|||
('user_id', '=', request.env.user.id) |
|||
]) |
|||
if ticket_ids: |
|||
context.append({ |
|||
'name': stage.name, |
|||
'data': ticket_ids |
|||
}) |
|||
if group_value == '2': |
|||
context = [] |
|||
type_ids = request.env['helpdesk.type'].search([]) |
|||
for types in type_ids: |
|||
ticket_ids_1 = request.env['ticket.helpdesk'].search([ |
|||
('ticket_type_id', '=', types.id), |
|||
('user_id', '=', request.env.user.id) |
|||
]) |
|||
if ticket_ids_1: |
|||
context.append({ |
|||
'name': types.name, |
|||
'data': ticket_ids_1 |
|||
}) |
|||
values = { |
|||
'tickets': context, |
|||
} |
|||
response = http.Response( |
|||
template='odoo_website_helpdesk.ticket_group_by_table', |
|||
qcontext=values) |
|||
return response.render() |
@ -0,0 +1,45 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class TicketSearch(http.Controller): |
|||
@http.route(['/ticketsearch'], type='json', auth="public", website=True) |
|||
def ticket_search(self, **kwargs): |
|||
""" |
|||
Search for tickets based on the provided search value. |
|||
:param search_value: The value to search for in the ticket name or subject. |
|||
:type search_value: str |
|||
:return: A JSON response containing the matching tickets. |
|||
:rtype: http.Response |
|||
""" |
|||
search_value = kwargs.get("search_value") |
|||
tickets = request.env["ticket.helpdesk"].search( |
|||
['|', ('name', 'ilike', search_value), |
|||
('subject', 'ilike', search_value)]) |
|||
values = { |
|||
'tickets': tickets, |
|||
} |
|||
response = http.Response(template='odoo_website_helpdesk.ticket_table', |
|||
qcontext=values) |
|||
return response.render() |
@ -0,0 +1,151 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
import base64 |
|||
import json |
|||
from psycopg2 import IntegrityError |
|||
from odoo import http, _ |
|||
from odoo.http import request |
|||
from odoo.exceptions import ValidationError |
|||
from odoo.addons.website.controllers.form import WebsiteForm |
|||
|
|||
|
|||
class HelpdeskProduct(http.Controller): |
|||
""" Controller for handling helpdesk products. |
|||
""" |
|||
|
|||
@http.route('/product', auth='public', type='json') |
|||
def product(self): |
|||
prols = [] |
|||
acc = request.env['product.template'].sudo().search([]) |
|||
for i in acc: |
|||
dic = {'name': i['name'], |
|||
'id': i['id']} |
|||
prols.append(dic) |
|||
return prols |
|||
|
|||
|
|||
class WebsiteFormInherit(WebsiteForm): |
|||
|
|||
def _handle_website_form(self, model_name, **kwargs): |
|||
""" |
|||
Handle the submission of website forms. |
|||
:param model_name: The name of the model associated with the form. |
|||
:type model_name: str |
|||
:param kwargs: Keyword arguments containing form data. |
|||
:type kwargs: dict |
|||
:return: JSON response indicating the success or failure of form submission. |
|||
:rtype: str |
|||
""" |
|||
customer = request.env.user.partner_id |
|||
lowest_stage_id = None |
|||
if model_name == 'ticket.helpdesk': |
|||
tickets = request.env['ticket.stage'].sudo().search([]) |
|||
if tickets: |
|||
sequence = tickets.mapped('sequence') |
|||
lowest_sequence = tickets.filtered( |
|||
lambda x: x.sequence == min(sequence)) |
|||
if lowest_sequence: |
|||
lowest_stage_id = lowest_sequence[0] |
|||
if lowest_stage_id is None: |
|||
return json.dumps( |
|||
{'error': "No stage found with the lowest sequence."}) |
|||
products = kwargs.get('product') |
|||
if products: |
|||
split_product = products.split(',') |
|||
product_list = [int(i) for i in split_product] |
|||
rec_val = { |
|||
'customer_name': kwargs.get('customer_name'), |
|||
'subject': kwargs.get('subject'), |
|||
'description': kwargs.get('description'), |
|||
'email': kwargs.get('email_from'), |
|||
'phone': kwargs.get('phone'), |
|||
'priority': kwargs.get('priority'), |
|||
'product_ids': product_list, |
|||
'stage_id': lowest_stage_id.id, |
|||
'customer_id': customer.id, |
|||
'ticket_type_id': kwargs.get('ticket_type_id'), |
|||
'category_id': kwargs.get('category'), |
|||
} |
|||
else: |
|||
rec_val = { |
|||
'customer_name': kwargs.get('customer_name'), |
|||
'subject': kwargs.get('subject'), |
|||
'description': kwargs.get('description'), |
|||
'email': kwargs.get('email_from'), |
|||
'phone': kwargs.get('phone'), |
|||
'priority': kwargs.get('priority'), |
|||
'stage_id': lowest_stage_id.id, |
|||
'customer_id': customer.id, |
|||
'ticket_type_id': kwargs.get('ticket_type_id'), |
|||
'category_id': kwargs.get('category'), |
|||
} |
|||
ticket_id = request.env['ticket.helpdesk'].sudo().create(rec_val) |
|||
request.session['ticket_number'] = ticket_id.name |
|||
request.session['ticket_id'] = ticket_id.id |
|||
model_record = request.env['ir.model'].sudo().search( |
|||
[('model', '=', model_name)]) |
|||
attachments = [] |
|||
attachment_index = 0 |
|||
while f"ticket_attachment[0][{attachment_index}]" in kwargs: |
|||
attachment_key = f"ticket_attachment[0][{attachment_index}]" |
|||
if attachment_key in kwargs: |
|||
attachment = kwargs[attachment_key] |
|||
attachments.append(attachment) |
|||
attachment_index += 1 |
|||
for attachment in attachments: |
|||
attached_file = attachment.read() |
|||
request.env['ir.attachment'].sudo().create({ |
|||
'name': attachment.filename, |
|||
'res_model': 'ticket.helpdesk', |
|||
'res_id': ticket_id.id, |
|||
'type': 'binary', |
|||
'datas': base64.encodebytes(attached_file), |
|||
}) |
|||
request.session['form_builder_model_model'] = model_record.model |
|||
request.session['form_builder_model'] = model_record.name |
|||
request.session['form_builder_id'] = ticket_id.id |
|||
return json.dumps({'id': ticket_id.id}) |
|||
else: |
|||
model_record = request.env['ir.model'].sudo().search( |
|||
[('model', '=', model_name)]) |
|||
if not model_record: |
|||
return json.dumps( |
|||
{'error': _("The form's specified model does not exist")}) |
|||
try: |
|||
data = self.extract_data(model_record, request.params) |
|||
except ValidationError as e: |
|||
return json.dumps({'error_fields': e.args[0]}) |
|||
try: |
|||
id_record = self.insert_record(request, model_record, |
|||
data['record'], data['custom'], |
|||
data.get('meta')) |
|||
if id_record: |
|||
self.insert_attachment(model_record, id_record, |
|||
data['attachments']) |
|||
if model_name == 'mail.mail': |
|||
request.env[model_name].sudo().browse(id_record).send() |
|||
except IntegrityError: |
|||
return json.dumps(False) |
|||
request.session['form_builder_model_model'] = model_record.model |
|||
request.session['form_builder_model'] = model_record.name |
|||
request.session['form_builder_id'] = id_record |
|||
return json.dumps({'id': id_record}) |
@ -0,0 +1,74 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import http |
|||
from odoo.http import request |
|||
|
|||
|
|||
class WebsiteDesk(http.Controller): |
|||
@http.route(['/helpdesk_ticket'], type='http', auth="public", website=True, |
|||
sitemap=True) |
|||
def helpdesk_ticket(self, **kwargs): |
|||
""" |
|||
Route to display the helpdesk ticket creation form. |
|||
Returns: |
|||
http.Response: The HTTP response rendering the helpdesk ticket form. |
|||
""" |
|||
types = request.env['helpdesk.type'].sudo().search([]) |
|||
categories = request.env['helpdesk.category'].sudo().search([]) |
|||
product = request.env['product.template'].sudo().search([]) |
|||
values = {} |
|||
values.update({ |
|||
'types': types, |
|||
'categories': categories, |
|||
'product_website': product |
|||
}) |
|||
return request.render('odoo_website_helpdesk.ticket_form', values) |
|||
|
|||
@http.route(['/rating/<int:ticket_id>'], type='http', auth="public", |
|||
website=True, |
|||
sitemap=True) |
|||
def rating(self, ticket_id): |
|||
""" |
|||
Route to display the rating form for a specific ticket. Args: |
|||
ticket_id (int): The ID of the ticket for which the rating form is |
|||
displayed. Returns: http.Response: The HTTP response rendering the |
|||
rating form. |
|||
""" |
|||
ticket = request.env['ticket.helpdesk'].browse(ticket_id) |
|||
data = { |
|||
'ticket': ticket.id, |
|||
} |
|||
return request.render('odoo_website_helpdesk.rating_form', data) |
|||
|
|||
@http.route(['/rating/<int:ticket_id>/submit'], type='http', auth="user", |
|||
website=True, csrf=False, |
|||
sitemap=True) |
|||
def rating_backend(self, ticket_id, **post): |
|||
""" |
|||
Customer Rating |
|||
""" |
|||
ticket = request.env['ticket.helpdesk'].browse(ticket_id) |
|||
ticket.write({ |
|||
'customer_rating': post['rating'], |
|||
'review': post['message'], |
|||
}) |
|||
return request.render('odoo_website_helpdesk.rating_thanks') |
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Record for a helpdesk ticket category representing internal tickets.--> |
|||
<record id="ticket_categories_1" model="helpdesk.category"> |
|||
<field name="name">Internal</field> |
|||
</record> |
|||
<!-- Record for a helpdesk ticket category representing technical tickets.--> |
|||
<record id="ticket_categories_2" model="helpdesk.category"> |
|||
<field name="name">Technical</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,55 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!-- Helpdesk Replay mail template --> |
|||
<record id="helpdesk_replay_email_template" model="mail.template"> |
|||
<field name="name">Helpdesk Reply Email Template</field> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="auto_delete" eval="True"/> |
|||
<field name="subject">{{ object.name }} Replay</field> |
|||
<field name="body_html" type="html"> |
|||
<div class="container"> |
|||
<h4 style="text-align:center">Helpdesk Replay</h4> |
|||
<div style="min-height:30vh;">message........</div> |
|||
<t t-set="base_url" |
|||
t-value="request.env['ir.config_parameter'].sudo().get_param('web.base.url')"/> |
|||
<h6> |
|||
<a t-attf-href="{{base_url}}/my/tickets/{{object.id}}" |
|||
class="btn btn-primary">View Ticket |
|||
</a> |
|||
</h6> |
|||
<table class="table table-light" width="100%"> |
|||
<tbody> |
|||
<tr> |
|||
<th>Name</th> |
|||
<td> |
|||
<t t-if="object.assigned_user_id"> |
|||
<t t-esc="object.assigned_user_id.name"/> |
|||
</t> |
|||
<t t-else="">username</t> |
|||
<t t-esc="base_url"/> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Team name</th> |
|||
<td> |
|||
<t t-if="object.team_id"> |
|||
<t t-esc="object.team_id.name"/> |
|||
</t> |
|||
<t t-else="">Team name</t> |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<th>Replied On</th> |
|||
<td> |
|||
<t t-set="date" |
|||
t-value="(datetime.date.today())"/> |
|||
<t t-esc="date"/> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,21 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Record for a helpdesk ticket type representing a general question.--> |
|||
<record id="ticket_type_1" model="helpdesk.type"> |
|||
<field name="name">Question</field> |
|||
</record> |
|||
<!-- Record for a helpdesk ticket type representing an issue.--> |
|||
<record id="ticket_type_2" model="helpdesk.type"> |
|||
<field name="name">Issue</field> |
|||
</record> |
|||
<!-- Record for a helpdesk ticket type representing a repair request.--> |
|||
<record id="ticket_type_3" model="helpdesk.type"> |
|||
<field name="name">Repair</field> |
|||
</record> |
|||
<!-- Record for a helpdesk ticket type representing a maintenance request.--> |
|||
<record id="ticket_type_4" model="helpdesk.type"> |
|||
<field name="name">Maintenance</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Scheduled task (cron job) for automatically closing tickets--> |
|||
<record id="auto_close_ticket" model="ir.cron"> |
|||
<field name="name">Auto Close Ticket</field> |
|||
<field name="model_id" ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="state">code</field> |
|||
<field name="code">model.auto_close_ticket()</field> |
|||
<field name="user_id" ref="base.user_root"/> |
|||
<field name="interval_number">1</field> |
|||
<field name="interval_type">days</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,23 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Define a sequence for help tickets --> |
|||
<record id="sequence_help_ticket_seq" model="ir.sequence"> |
|||
<field name="name">Helpdesk</field> |
|||
<field name="code">ticket.helpdesk</field> |
|||
<field name="prefix">TKT</field> |
|||
<field name="padding">5</field> |
|||
<field eval="1" name="number_increment"/> |
|||
<field eval="False" name="company_id"/> |
|||
</record> |
|||
<!-- Define a sequence for help ticket invoices --> |
|||
<record id="sequence_help_ticket_inv_seq" model="ir.sequence"> |
|||
<field name="name">Helpdesk Invoice</field> |
|||
<field name="code">ticket.invoice</field> |
|||
<field name="prefix">INV/TKT/%(year)s/</field> |
|||
<field name="padding">3</field> |
|||
<field eval="1" name="number_increment"/> |
|||
<field eval="False" name="company_id"/> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,213 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Mail Template for notifying customers about the creation of a ticket.--> |
|||
<record id="ticket_created" model="mail.template"> |
|||
<field name="name">Create Ticket</field> |
|||
<field name="model_id" ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="auto_delete" eval="True"/> |
|||
<field name="email_to">{{object.customer_id.email}}</field> |
|||
<field name="subject">Ticket Created</field> |
|||
<field name="body_html" type="html"> |
|||
<p> |
|||
Dear |
|||
<t t-out="object.customer_id.name"/> |
|||
<br/> |
|||
Your Ticket |
|||
<t t-out="object.name"/> |
|||
is Created and Assigned. Kindly Wait while we're resolving your Query |
|||
<br/> |
|||
<br/> |
|||
Thanks. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
<!-- Mail Template for notifying customers about the closure of a ticket and prompting them for feedback.--> |
|||
<record id="helpdesk_rating" model="mail.template"> |
|||
<field name="name">Close Ticket And Feedback</field> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="auto_delete" eval="True"/> |
|||
<field name="email_to">{{object.customer_id.email}}</field> |
|||
<field name="subject">Ticket Closed</field> |
|||
<field name="body_html" type="html"> |
|||
<p> |
|||
Dear |
|||
<t t-out="object.customer_id.name"/> |
|||
Your Ticket |
|||
<t t-out="object.name"/> |
|||
Is Closed |
|||
<br/> |
|||
<button class="btn btn-info" role="button" |
|||
style="border:1px solid black;background-color:purple;padding-x:20px;padding-y:15px;border-radius:10px; width:120px;"> |
|||
<a t-attf-href="/rating/{{object.id}}" |
|||
style="text-decoration:none;color:white;font-weight:bold;font-size:18px;"> |
|||
Rate Now |
|||
</a> |
|||
</button> |
|||
<br/> |
|||
<br/> |
|||
Thanks. |
|||
</p> |
|||
|
|||
</field> |
|||
</record> |
|||
<!-- Mail Template for notifying customers about the reopening of a ticket.--> |
|||
<record id="ticket_reopened" model="mail.template"> |
|||
<field name="name">Reopened Ticket</field> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="auto_delete" eval="True"/> |
|||
<field name="email_to">{{object.customer_id.email}}</field> |
|||
<field name="subject">Ticket Reopened</field> |
|||
<field name="body_html" type="html"> |
|||
<p> |
|||
Dear |
|||
<t t-out="object.customer_id.name"/> |
|||
<br/> |
|||
Your Ticket |
|||
<t t-out="object.name"/> |
|||
is Reopened |
|||
<br/> |
|||
<br/> |
|||
Thanks. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
<!-- Mail Template for notifying customers about the approval and resolution of a ticket.--> |
|||
<record id="ticket_approved" model="mail.template"> |
|||
<field name="name">Approve Ticket</field> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="auto_delete" eval="True"/> |
|||
<field name="email_to">{{object.customer_id.email}}</field> |
|||
<field name="subject">Ticket Solved</field> |
|||
<field name="body_html" type="html"> |
|||
<p> |
|||
Dear |
|||
<t t-out="object.customer_id.name"/> |
|||
<br/> |
|||
i think your Problem is Solved. |
|||
Your Ticket |
|||
<t t-out="object.name"/> |
|||
is Done |
|||
<br/> |
|||
<br/> |
|||
Thanks. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
<!-- An email template for notifying customers about the cancellation--> |
|||
<!-- of a ticket.--> |
|||
<record id="ticket_canceled" model="mail.template"> |
|||
<field name="name">Cancel Ticket</field> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="auto_delete" eval="True"/> |
|||
<field name="email_to">{{object.customer_id.email}}</field> |
|||
<field name="subject">Ticket Canceled</field> |
|||
<field name="body_html" type="html"> |
|||
<p> |
|||
Dear |
|||
<t t-out="object.customer_id.name"/> |
|||
<br/> |
|||
Your Ticket |
|||
<t t-out="object.name"/> |
|||
is Canceled Due to Some Reasons |
|||
<br/> |
|||
<br/> |
|||
Thanks. |
|||
</p> |
|||
</field> |
|||
</record> |
|||
<!-- This XML record defines an email template for notifying the team head about the assignment of a ticket.--> |
|||
<!-- The template is associated with the "mail.template" model and is designed to be triggered when a ticket--> |
|||
<!-- is assigned to a specific team.--> |
|||
<record id="odoo_website_helpdesk_assign" |
|||
model="mail.template"> |
|||
<field name="name">Ticket Assign</field> |
|||
<field name="email_from"/> |
|||
<field name="email_to"/> |
|||
<field name="subject"/> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="body_html" type="html"> |
|||
<div style="margin: 0px; padding: 0px;"> |
|||
<p>Dear, |
|||
<t t-esc="object.team_head_id.name"/> |
|||
</p> |
|||
<p>Your team has been given the ticket [<t t-esc="object.name"/>] kindly complete your work |
|||
carefully. |
|||
</p> |
|||
<br/> |
|||
<h2>Details</h2> |
|||
<p>Subject : |
|||
<t t-esc="object.subject"/> |
|||
</p> |
|||
<p>Customer : |
|||
<t t-esc="object.customer_id.name"/> |
|||
</p> |
|||
<p>Description : |
|||
<t t-esc="object.description"/> |
|||
</p> |
|||
<br/> |
|||
<p>Best regards</p> |
|||
</div> |
|||
</field> |
|||
</record> |
|||
<!-- An email template for notifying an assigned user about the assignment of a ticket.--> |
|||
<record id="odoo_website_helpdesk_assign_user" |
|||
model="mail.template"> |
|||
<field name="name">Ticket Assign to User</field> |
|||
<field name="email_from"/> |
|||
<field name="email_to"/> |
|||
<field name="subject"/> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="body_html" type="html"> |
|||
<div style="margin: 0px; padding: 0px;"> |
|||
<p>Dear, |
|||
<t t-esc="object.assigned_user_id.name"/> |
|||
</p> |
|||
<p>Ticket [<t t-esc="object.name"/>] Assign to you , kindly complete your work carefully. |
|||
</p> |
|||
<br/> |
|||
<h2>Details</h2> |
|||
<p>Subject : |
|||
<t t-esc="object.subject"/> |
|||
</p> |
|||
<p>Customer : |
|||
<t t-esc="object.customer_id.name"/> |
|||
</p> |
|||
<p>Description : |
|||
<t t-esc="object.description"/> |
|||
</p> |
|||
<br/> |
|||
<p>Best regards</p> |
|||
</div> |
|||
</field> |
|||
</record> |
|||
<!-- This record defines an email template for notifying a customer about the assignment of a helpdesk ticket to a user.--> |
|||
<record id="odoo_website_helpdesk_to_customer" |
|||
model="mail.template"> |
|||
<field name="name">Mail To Customer Assign to User</field> |
|||
<field name="email_from"/> |
|||
<field name="email_to"/> |
|||
<field name="subject"/> |
|||
<field name="model_id" |
|||
ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
|||
<field name="body_html" type="html"> |
|||
<div style="margin: 0px; padding: 0px;"> |
|||
<p>Dear, |
|||
<t t-esc="object.customer_id.name"/> |
|||
</p> |
|||
<p>Ticket [<t t-esc="object.name"/>] successfully assigned to "<t |
|||
t-esc="object.assigned_user_id.name"/>". |
|||
</p> |
|||
<br/> |
|||
<p>Best regards</p> |
|||
</div> |
|||
</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,39 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Project Stages --> |
|||
<!-- - stage_inbox: Initial stage where tickets are received.--> |
|||
<record id="stage_inbox" model="ticket.stage"> |
|||
<field name="sequence">10</field> |
|||
<field name="name">Inbox</field> |
|||
</record> |
|||
<!-- - stage_draft: Tickets in the drafting phase.--> |
|||
<record id="stage_draft" model="ticket.stage"> |
|||
<field name="sequence">15</field> |
|||
<field name="name">Draft</field> |
|||
</record> |
|||
<!-- - stage_done: Final stage indicating completion.--> |
|||
<record id="stage_done" model="ticket.stage"> |
|||
<field name="sequence">25</field> |
|||
<field name="name">Done</field> |
|||
</record> |
|||
<!-- - stage_in_progress: Tickets actively being worked on.--> |
|||
<record id="stage_in_progress" model="ticket.stage"> |
|||
<field name="sequence">20</field> |
|||
<field name="starting_stage" eval="True"/> |
|||
<field name="name">In Progress</field> |
|||
</record> |
|||
<!-- - stage_closed: Closing stage for resolved tickets.--> |
|||
<record id="stage_closed" model="ticket.stage"> |
|||
<field name="sequence">30</field> |
|||
<field name="closing_stage">True</field> |
|||
<field name="name">Closed</field> |
|||
</record> |
|||
<!-- - stage_canceled: Stage for canceled or invalidated tickets.--> |
|||
<record id="stage_canceled" model="ticket.stage"> |
|||
<field name="sequence">35</field> |
|||
<field name="cancel_stage">True</field> |
|||
<field name="name">Canceled</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,7 @@ |
|||
## Module <odoo_website_helpdesk> |
|||
|
|||
#### 30.10.2024 |
|||
#### Version 18.0.1.0.0 |
|||
#### ADD |
|||
|
|||
- Initial commit for Website Helpdesk Support Ticket Management |
@ -0,0 +1,34 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from . import account_move |
|||
from . import helpdesk_category |
|||
from . import helpdesk_tag |
|||
from . import helpdesk_type |
|||
from . import mail_compose_message |
|||
from . import merge_ticket |
|||
from . import project_task |
|||
from . import res_config_settings |
|||
from . import support_ticket |
|||
from . import team_helpdesk |
|||
from . import ticket_helpdesk |
|||
from . import ticket_stage |
|||
from . import website_menu |
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class AccountMove(models.Model): |
|||
"""Inheriting the account.move model""" |
|||
_inherit = 'account.move' |
|||
|
|||
ticket_id = fields.Many2one('ticket.helpdesk', |
|||
string='Ticket', help='ID of the ticket.') |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class HelpdeskCategory(models.Model): |
|||
"""Category Model""" |
|||
_name = 'helpdesk.category' |
|||
_description = 'Categories' |
|||
|
|||
name = fields.Char('Name', help='Category name of the helpdesk') |
|||
sequence = fields.Integer('Sequence', default=0, |
|||
help='Sequence of the helpdesk category') |
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class HelpdeskTag(models.Model): |
|||
"""Helpdesk tags""" |
|||
_name = 'helpdesk.tag' |
|||
_description = 'Helpdesk Tags' |
|||
|
|||
name = fields.Char(string='Tag', help='Tag name of the helpdesk.') |
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class HelpdeskType(models.Model): |
|||
"""Helpdesk type """ |
|||
_name = 'helpdesk.type' |
|||
_description = 'Helpdesk Type' |
|||
|
|||
name = fields.Char(string='Type', help='Types of help desk.') |
@ -0,0 +1,42 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class MailComposeMessage(models.TransientModel): |
|||
"""Inheriting the Mail compose message""" |
|||
_inherit = 'mail.compose.message' |
|||
|
|||
def _action_send_mail(self, auto_commit=False): |
|||
"""Send mail function""" |
|||
if self.model == 'ticket.helpdesk': |
|||
try: |
|||
res_ids_list = eval(self.res_ids) |
|||
if not isinstance(res_ids_list, list): |
|||
raise ValueError("Invalid format for res_ids") |
|||
except Exception as e: |
|||
raise ValueError( |
|||
"Error converting res_ids to list: {}".format(e)) |
|||
ticket_ids = self.env['ticket.helpdesk'].browse(res_ids_list) |
|||
ticket_ids.replied_date = fields.Date.today() |
|||
return super(MailComposeMessage, self)._action_send_mail( |
|||
auto_commit=auto_commit) |
@ -0,0 +1,111 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class MergeTicket(models.Model): |
|||
"""Tickets merging class""" |
|||
_name = 'merge.ticket' |
|||
_description = 'Merging the selected tickets' |
|||
_rec_name = 'support_ticket_id' |
|||
|
|||
user_id = fields.Many2one('res.partner', |
|||
string='Responsible User', |
|||
help='User name of responsible person.', |
|||
default=lambda self: self.env.user.partner_id.id) |
|||
support_team_id = fields.Many2one('team.helpdesk', |
|||
string='Support Team', |
|||
help='Name of the support team.') |
|||
customer_id = fields.Many2one('res.partner', string='Customer', |
|||
help='Name of the Customer ') |
|||
support_ticket_id = fields.Many2one('ticket.helpdesk', |
|||
string='Support Ticket', |
|||
help="Name of the support ticket") |
|||
new_ticket = fields.Boolean(string='Create New Ticket ?', |
|||
help='Creating new tickets or not.', |
|||
default=False) |
|||
subject = fields.Char(string='Subject', help='Enter the New Ticket Subject') |
|||
merge_reason = fields.Char(string='Merge Reason', |
|||
help='Reason for Merging the tickets. ') |
|||
support_ticket_ids = fields.One2many('support.ticket', |
|||
'support_ticket_id', |
|||
string='Support Tickets', |
|||
help='Merged tickets') |
|||
active = fields.Boolean(string='Disable Record', help='Disable Record', |
|||
default=True) |
|||
|
|||
def default_get(self, fields_list): |
|||
"""Override the default_get method to provide default values for fields |
|||
when creating a new record.""" |
|||
defaults = super(MergeTicket, self).default_get(fields_list) |
|||
active_ids = self._context.get('active_ids', []) |
|||
selected_tickets = self.env['ticket.helpdesk'].browse(active_ids) |
|||
customer_ids = selected_tickets.mapped('customer_id') |
|||
subjects = selected_tickets.mapped('subject') |
|||
display_names = selected_tickets.mapped('display_name') |
|||
helpdesk_team = selected_tickets.mapped('team_id') |
|||
descriptions = selected_tickets.mapped('description') |
|||
if len(customer_ids): |
|||
defaults.update({ |
|||
'customer_id': customer_ids[0].id, |
|||
'support_team_id': helpdesk_team, |
|||
'support_ticket_ids': [(0, 0, { |
|||
'subject': subject, |
|||
'display_name': display_name, |
|||
'description': description, |
|||
}) for subject, display_name, description in |
|||
zip(subjects, display_names, |
|||
descriptions)] |
|||
}) |
|||
return defaults |
|||
|
|||
def action_merge_ticket(self): |
|||
"""Merging the tickets or creating new tickets""" |
|||
if self.new_ticket: |
|||
description = "\n\n".join( |
|||
f"{ticket.subject}\n{'-' * len(ticket.subject)}\n{ticket.description}" |
|||
for ticket in self.support_ticket_ids |
|||
) |
|||
self.env['ticket.helpdesk'].create({ |
|||
'subject': self.subject, |
|||
'description': description, |
|||
'customer_id': self.customer_id.id, |
|||
'team_id': self.support_team_id.id, |
|||
}) |
|||
else: |
|||
if len(self.support_ticket_ids): |
|||
description = "\n\n".join( |
|||
f"{ticket.subject}\n{'-' * len(ticket.subject)}\n{ticket.description}" |
|||
for ticket in self.support_ticket_ids |
|||
) |
|||
self.support_ticket_id.write({ |
|||
'description': description, |
|||
'merge_ticket_invisible': True, |
|||
'merge_count': len(self.support_ticket_ids), |
|||
}) |
|||
|
|||
@api.onchange('support_ticket_id') |
|||
def _onchange_support_ticket_id(self): |
|||
"""Onchange function to add the support ticket id.""" |
|||
self.support_ticket_ids.write({ |
|||
'merged_ticket': self.support_ticket_id |
|||
}) |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ProjectTask(models.Model): |
|||
"""Inheriting the project task""" |
|||
_inherit = 'project.task' |
|||
|
|||
ticket_id = fields.Many2one('ticket.helpdesk', string='Ticket', |
|||
help='ID of the ticket .') |
|||
ticket_billed = fields.Boolean('Billed', default=False, |
|||
help='Billed Tickets') |
@ -0,0 +1,94 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ResConfigSettings(models.TransientModel): |
|||
"""Inheriting the res config settings model""" |
|||
_inherit = 'res.config.settings' |
|||
|
|||
show_create_task = fields.Boolean(string="Create Tasks", |
|||
config_parameter='odoo_website_helpdesk.show_create_task', |
|||
help='Enable this option to allow users' |
|||
'to create tasks directly from the ' |
|||
'helpdesk module. When activated, users ' |
|||
'will have the ability to generate and ' |
|||
'assign tasks as part of their workflow ' |
|||
'within the helpdesk interface.') |
|||
show_category = fields.Boolean(string="Category", |
|||
config_parameter='odoo_website_helpdesk.show_category', |
|||
help='Enable this option to display the ' |
|||
'category field in the helpdesk tickets. ' |
|||
'This can be useful for organizing and ' |
|||
'filtering tickets based on their category.', |
|||
implied_group='odoo_website_helpdesk.group_show_category') |
|||
product_website = fields.Boolean(string="Product On Website", |
|||
config_parameter='odoo_website_helpdesk.product_website', |
|||
help='Product on website') |
|||
auto_close_ticket = fields.Boolean(string="Auto Close Ticket", |
|||
config_parameter='odoo_website_helpdesk.auto_close_ticket', |
|||
help='Auto Close ticket') |
|||
no_of_days = fields.Integer(string="No Of Days", |
|||
config_parameter='odoo_website_helpdesk.no_of_days', |
|||
help='No of Days') |
|||
closed_stage_id = fields.Many2one( |
|||
'ticket.stage', string='Closing stage', |
|||
help='Closing Stage of the ticket.', |
|||
config_parameter='odoo_website_helpdesk.closed_stage_id') |
|||
|
|||
reply_template_id = fields.Many2one('mail.template', |
|||
domain="[('model', '=', 'ticket.helpdesk')]", |
|||
config_parameter='odoo_website_helpdesk.reply_template_id', |
|||
help='Reply Template of the helpdesk' |
|||
' ticket.') |
|||
helpdesk_menu_show = fields.Boolean('Helpdesk Menu', |
|||
config_parameter= |
|||
'odoo_website_helpdesk.helpdesk_menu_show', |
|||
help='Helpdesk menu') |
|||
|
|||
@api.onchange('closed_stage_id') |
|||
def _onchange_closed_stage_id(self): |
|||
"""Closing stage function""" |
|||
if self.closed_stage_id: |
|||
stage = self.closed_stage_id.id |
|||
in_stage = self.env['ticket.stage'].search([('id', '=', stage)]) |
|||
not_in_stage = self.env['ticket.stage'].search( |
|||
[('id', '!=', stage)]) |
|||
in_stage.closing_stage = True |
|||
for each in not_in_stage: |
|||
each.closing_stage = False |
|||
|
|||
@api.constrains('show_category') |
|||
def _constrains_show_category_subcategory(self): |
|||
"""Show category and the sub category""" |
|||
if self.show_category: |
|||
group_cat = self.env.ref( |
|||
'odoo_website_helpdesk.group_show_category') |
|||
group_cat.write({ |
|||
'users': [(4, self.env.user.id)] |
|||
}) |
|||
else: |
|||
group_cat = self.env.ref( |
|||
'odoo_website_helpdesk.group_show_category') |
|||
group_cat.write({ |
|||
'users': [(5, False)] |
|||
}) |
@ -0,0 +1,40 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class SupportTicket(models.Model): |
|||
"""Creating onetoMany model""" |
|||
_name = 'support.ticket' |
|||
_description = 'Support Tickets' |
|||
|
|||
subject = fields.Char(string='Subject', help='Subject of the merged ' |
|||
'tickets.') |
|||
display_name = fields.Char(string='Display Name', |
|||
help='Display name of the merged tickets.') |
|||
description = fields.Char(string='Description', |
|||
help='Description of the tickets.') |
|||
support_ticket_id = fields.Many2one('merge.ticket', |
|||
string='Support Tickets', |
|||
help='Support tickets') |
|||
merged_ticket = fields.Integer(string='Merged Ticket ID', |
|||
help='Storing merged ticket id') |
@ -0,0 +1,59 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class TeamHelpDesk(models.Model): |
|||
"""Helpdesk team""" |
|||
_name = 'team.helpdesk' |
|||
_description = 'Helpdesk Team' |
|||
|
|||
name = fields.Char('Name', help='Helpdesk Team Name') |
|||
team_lead_id = fields.Many2one('res.users', string='Team Leader', |
|||
help='Team Leader Name', |
|||
domain=lambda self: [ |
|||
('groups_id', 'in', self.env.ref( |
|||
'odoo_website_helpdesk.helpdesk_team_leader').id)]) |
|||
member_ids = fields.Many2many('res.users', string='Members', |
|||
help='Team Members', |
|||
domain=lambda self: [ |
|||
('groups_id', 'in', self.env.ref( |
|||
'odoo_website_helpdesk.helpdesk_user').id)]) |
|||
email = fields.Char('Email', help='Email of the team member.') |
|||
project_id = fields.Many2one('project.project', |
|||
string='Project', |
|||
help='Projects related helpdesk team.') |
|||
create_task = fields.Boolean(string="Create Task", |
|||
help="Task created or not") |
|||
|
|||
@api.onchange('team_lead_id') |
|||
def _onchange_team_lead_id(self): |
|||
"""Members selection function""" |
|||
fetch_members = self.env['res.users'].search([]) |
|||
filtered_members = fetch_members.filtered( |
|||
lambda x: x.id != self.team_lead_id.id) |
|||
return {'domain': {'member_ids': |
|||
[('id', '=', filtered_members.ids), ( |
|||
'groups_id', 'in', |
|||
self.env.ref('base.group_user').id), |
|||
('groups_id', 'not in', self.env.ref( |
|||
'odoo_website_helpdesk.helpdesk_team_leader').id)]}} |
@ -0,0 +1,387 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
import logging |
|||
from odoo import api, fields, models, _ |
|||
from odoo.exceptions import UserError |
|||
from odoo.exceptions import ValidationError |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
|
|||
PRIORITIES = [ |
|||
('0', 'Very Low'), |
|||
('1', 'Low'), |
|||
('2', 'Normal'), |
|||
('3', 'High'), |
|||
('4', 'Very High'), |
|||
] |
|||
RATING = [ |
|||
('0', 'Very Low'), |
|||
('1', 'Low'), |
|||
('2', 'Normal'), |
|||
('3', 'High'), |
|||
('4', 'Very High'), |
|||
('5', 'Extreme High') |
|||
] |
|||
|
|||
|
|||
class TicketHelpDesk(models.Model): |
|||
"""Help_ticket model""" |
|||
_name = 'ticket.helpdesk' |
|||
_description = 'Helpdesk Ticket' |
|||
_inherit = ['mail.thread', 'mail.activity.mixin'] |
|||
|
|||
def _default_show_create_task(self): |
|||
"""Task creation""" |
|||
return self.env['ir.config_parameter'].sudo().get_param( |
|||
'odoo_website_helpdesk.show_create_task') |
|||
|
|||
def _default_show_category(self): |
|||
"""Show category default""" |
|||
return self.env['ir.config_parameter'].sudo().get_param( |
|||
'odoo_website_helpdesk.show_category') |
|||
|
|||
name = fields.Char('Name', default=lambda self: self.env['ir.sequence']. |
|||
next_by_code('ticket.helpdesk') or _('New'), |
|||
help='Ticket Name') |
|||
customer_id = fields.Many2one('res.partner', |
|||
string='Customer Name', |
|||
help='Customer Name') |
|||
customer_name = fields.Char('Customer Name', help='Customer Name') |
|||
subject = fields.Text('Subject', required=True, |
|||
help='Subject of the Ticket') |
|||
description = fields.Text('Description', required=True, |
|||
help='Description') |
|||
email = fields.Char('Email', help='Email') |
|||
phone = fields.Char('Phone', help='Contact Number') |
|||
team_id = fields.Many2one('team.helpdesk', string='Helpdesk Team', |
|||
help='Helpdesk Team Name') |
|||
product_ids = fields.Many2many('product.template', |
|||
string='Product', |
|||
help='Product Name') |
|||
project_id = fields.Many2one('project.project', |
|||
string='Project', |
|||
readonly=False, |
|||
related='team_id.project_id', |
|||
store=True, |
|||
help='Project Name') |
|||
priority = fields.Selection(PRIORITIES, default='1', help='Priority of the' |
|||
' Ticket') |
|||
stage_id = fields.Many2one('ticket.stage', string='Stage', |
|||
tracking=True, |
|||
group_expand='_read_group_stage_ids', |
|||
help='Stages') |
|||
user_id = fields.Many2one('res.users', |
|||
default=lambda self: self.env.user, |
|||
check_company=True, |
|||
index=True, tracking=True, |
|||
help='Login User', string='User') |
|||
cost = fields.Float('Cost per hour', help='Cost Per Unit') |
|||
service_product_id = fields.Many2one('product.product', |
|||
string='Service Product', |
|||
help='Service Product', |
|||
domain=[ |
|||
('type', '=', 'service')]) |
|||
create_date = fields.Datetime('Creation Date', help='Created date') |
|||
start_date = fields.Datetime('Start Date', help='Start Date') |
|||
end_date = fields.Datetime('End Date', help='End Date') |
|||
public_ticket = fields.Boolean(string="Public Ticket", |
|||
help='Public Ticket') |
|||
invoice_ids = fields.Many2many('account.move', |
|||
string='Invoices', |
|||
help='Invoicing id' |
|||
) |
|||
task_ids = fields.Many2many('project.task', |
|||
string='Tasks', |
|||
help='Task id') |
|||
color = fields.Integer(string="Color", help='Color') |
|||
replied_date = fields.Datetime('Replied date', help='Replied Date') |
|||
last_update_date = fields.Datetime('Last Update Date', |
|||
help='Last Update Date') |
|||
ticket_type_id = fields.Many2one('helpdesk.type', |
|||
string='Ticket Type', help='Ticket Type') |
|||
team_head_id = fields.Many2one('res.users', string='Team Leader', |
|||
compute='_compute_team_head_id', |
|||
help='Team Leader Name') |
|||
assigned_user_id = fields.Many2one('res.users', string='Assigned User', |
|||
domain=lambda self: [('groups_id', 'in', |
|||
self.env.ref( |
|||
'odoo_website_helpdesk.helpdesk_user').id)], |
|||
help='Assigned User Name') |
|||
category_id = fields.Many2one('helpdesk.category', string='Category', |
|||
help='Category') |
|||
tags_ids = fields.Many2many('helpdesk.tag', help='Tags', string='Tags') |
|||
assign_user = fields.Boolean(default=False, help='Assign User', |
|||
string='Assign User') |
|||
attachment_ids = fields.One2many('ir.attachment', 'res_id', |
|||
help='Attachment Line', |
|||
string='Attachments') |
|||
merge_ticket_invisible = fields.Boolean(string='Merge Ticket', |
|||
help='Merge Ticket Invisible or ' |
|||
'Not', default=False) |
|||
merge_count = fields.Integer(string='Merge Count', help='Merged Tickets ' |
|||
'Count') |
|||
active = fields.Boolean(default=True, help='Active', string='Active') |
|||
|
|||
show_create_task = fields.Boolean(string="Show Create Task", |
|||
help='Show created task or not', |
|||
default=_default_show_create_task, |
|||
compute='_compute_show_create_task') |
|||
create_task = fields.Boolean(string="Create Task", readonly=False, |
|||
help='Create task or not', |
|||
related='team_id.create_task', store=True) |
|||
billable = fields.Boolean(string="Billable", default=False, |
|||
help='Is billable or not', ) |
|||
show_category = fields.Boolean(default=_default_show_category, |
|||
string="Show Category", |
|||
help='Show category or not', |
|||
compute='_compute_show_category') |
|||
customer_rating = fields.Selection(RATING, default='0', readonly=True) |
|||
review = fields.Char('Review', readonly=True, help='Ticket review') |
|||
kanban_state = fields.Selection([ |
|||
('normal', 'Ready'), |
|||
('done', 'In Progress'), |
|||
('blocked', 'Blocked'), ], default='normal') |
|||
|
|||
@api.onchange('team_id', 'team_head_id') |
|||
def _onchange_team_id(self): |
|||
"""Changing the team leader when selecting the team""" |
|||
li = self.team_id.member_ids.mapped(id) |
|||
return {'domain': {'assigned_user_id': [('id', 'in', li)]}} |
|||
|
|||
@api.depends('team_id') |
|||
def _compute_team_head_id(self): |
|||
"""Compute the team head function""" |
|||
self.team_head_id = self.team_id.team_lead_id.id |
|||
|
|||
@api.onchange('stage_id') |
|||
def _onchange_stage_id(self): |
|||
"""Sending mail to the user function""" |
|||
rec_id = self._origin.id |
|||
data = self.env['ticket.helpdesk'].search([('id', '=', rec_id)]) |
|||
data.last_update_date = fields.Datetime.now() |
|||
if self.stage_id.starting_stage: |
|||
data.start_date = fields.Datetime.now() |
|||
if self.stage_id.closing_stage or self.stage_id.cancel_stage: |
|||
data.end_date = fields.Datetime.now() |
|||
if self.stage_id.template_id: |
|||
mail_template = self.stage_id.template_id |
|||
mail_template.send_mail(self._origin.id, force_send=True) |
|||
|
|||
def assign_to_teamleader(self): |
|||
"""Assigning team leader function""" |
|||
if self.team_id: |
|||
self.team_head_id = self.team_id.team_lead_id.id |
|||
mail_template = self.env.ref( |
|||
'odoo_website_helpdesk.odoo_website_helpdesk_assign') |
|||
mail_template.sudo().write({ |
|||
'email_to': self.team_head_id.email, |
|||
'subject': self.name |
|||
}) |
|||
mail_template.sudo().send_mail(self.id, force_send=True) |
|||
else: |
|||
raise ValidationError("Please choose a Helpdesk Team") |
|||
|
|||
def _compute_show_category(self): |
|||
"""Compute show category""" |
|||
show_category = self._default_show_category() |
|||
for rec in self: |
|||
rec.show_category = show_category |
|||
|
|||
def _compute_show_create_task(self): |
|||
"""Compute the created task""" |
|||
show_create_task = self._default_show_create_task() |
|||
for record in self: |
|||
record.show_create_task = show_create_task |
|||
|
|||
def auto_close_ticket(self): |
|||
"""Automatically closing the ticket""" |
|||
auto_close = self.env['ir.config_parameter'].sudo().get_param( |
|||
'odoo_website_helpdesk.auto_close_ticket') |
|||
if auto_close: |
|||
no_of_days = self.env['ir.config_parameter'].sudo().get_param( |
|||
'odoo_website_helpdesk.no_of_days') |
|||
records = self.env['ticket.helpdesk'].search([]) |
|||
for rec in records: |
|||
days = (fields.Datetime.today() - rec.create_date).days |
|||
if days >= int(no_of_days): |
|||
close_stage_id = self.env['ticket.stage'].search( |
|||
[('closing_stage', '=', True)]) |
|||
if close_stage_id: |
|||
rec.stage_id = close_stage_id |
|||
|
|||
def default_stage_id(self): |
|||
"""Method to return the default stage""" |
|||
return self.env['ticket.stage'].search( |
|||
[('name', '=', 'Draft')], limit=1).id |
|||
|
|||
def _read_group_stage_ids(self, stages, domain): |
|||
""" |
|||
return the stages to stage_ids |
|||
""" |
|||
stage_ids = self.env['ticket.stage'].search([]) |
|||
return stage_ids |
|||
|
|||
@api.model_create_multi |
|||
def create(self, vals_list): |
|||
"""Create function""" |
|||
for vals in vals_list: |
|||
if vals.get('name', _('New')) == _('New'): |
|||
vals['name'] = self.env['ir.sequence'].next_by_code( |
|||
'ticket.helpdesk') |
|||
return super(TicketHelpDesk, self).create(vals_list) |
|||
|
|||
def write(self, vals): |
|||
"""Write function""" |
|||
result = super(TicketHelpDesk, self).write(vals) |
|||
return result |
|||
|
|||
def action_create_invoice(self): |
|||
"""Create Invoice based on the ticket""" |
|||
tasks = self.env['project.task'].search( |
|||
[('project_id', '=', self.project_id.id), |
|||
('ticket_id', '=', self.id)]).filtered( |
|||
lambda line: not line.ticket_billed) |
|||
if not tasks: |
|||
raise UserError('No Tasks to Bill') |
|||
total = sum(x.effective_hours for x in tasks if |
|||
x.effective_hours > 0 and not x.some_flag) |
|||
invoice_no = self.env['ir.sequence'].next_by_code( |
|||
'ticket.invoice') |
|||
self.env['account.move'].create([ |
|||
{ |
|||
'name': invoice_no, |
|||
'move_type': 'out_invoice', |
|||
'partner_id': self.customer_id.id, |
|||
'ticket_id': self.id, |
|||
'date': fields.Date.today(), |
|||
'invoice_date': fields.Date.today(), |
|||
'invoice_line_ids': [(0, 0, |
|||
{ |
|||
'product_id': self.service_product_id.id, |
|||
'name': self.service_product_id.name, |
|||
'quantity': total, |
|||
'product_uom_id': self.service_product_id.uom_id.id, |
|||
'price_unit': self.cost, |
|||
'account_id': self.service_product_id.categ_id.property_account_income_categ_id.id, |
|||
})], |
|||
}, ]) |
|||
for task in tasks: |
|||
task.ticket_billed = True |
|||
return { |
|||
'effect': { |
|||
'fadeout': 'medium', |
|||
'message': 'Billed Successfully!', |
|||
'type': 'rainbow_man', |
|||
} |
|||
} |
|||
|
|||
def action_create_tasks(self): |
|||
"""Task creation""" |
|||
task_id = self.env['project.task'].create({ |
|||
'name': self.name + '-' + self.subject, |
|||
'project_id': self.project_id.id, |
|||
'company_id': self.env.company.id, |
|||
'ticket_id': self.id, |
|||
}) |
|||
self.write({ |
|||
'task_ids': [(4, task_id.id)] |
|||
}) |
|||
return { |
|||
'name': 'Tasks', |
|||
'res_model': 'project.task', |
|||
'view_id': False, |
|||
'res_id': task_id.id, |
|||
'view_mode': 'form', |
|||
'type': 'ir.actions.act_window', |
|||
'target': 'new', |
|||
} |
|||
|
|||
def action_open_tasks(self): |
|||
"""View the Created task """ |
|||
return { |
|||
'name': 'Tasks', |
|||
'domain': [('ticket_id', '=', self.id)], |
|||
'res_model': 'project.task', |
|||
'view_id': False, |
|||
'view_mode': 'list,form', |
|||
'type': 'ir.actions.act_window', |
|||
} |
|||
|
|||
def action_open_invoices(self): |
|||
"""View the Created invoice""" |
|||
return { |
|||
'name': 'Invoice', |
|||
'domain': [('ticket_id', '=', self.id)], |
|||
'res_model': 'account.move', |
|||
'view_id': False, |
|||
'view_mode': 'list,form', |
|||
'type': 'ir.actions.act_window', |
|||
} |
|||
|
|||
def action_open_merged_tickets(self): |
|||
"""Open the merged tickets list view""" |
|||
ticket_ids = self.env['support.ticket'].search( |
|||
[('merged_ticket', '=', self.id)]) |
|||
helpdesk_ticket_ids = ticket_ids.mapped('display_name') |
|||
help_ticket_records = self.env['ticket.helpdesk'].search( |
|||
[('name', 'in', helpdesk_ticket_ids)]) |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'Helpdesk Ticket', |
|||
'view_mode': 'list,form', |
|||
'res_model': 'ticket.helpdesk', |
|||
'domain': [('id', 'in', help_ticket_records.ids)], |
|||
'context': self.env.context, |
|||
} |
|||
|
|||
def action_send_reply(self): |
|||
"""Action to sent reply button""" |
|||
template_id = self.env['ir.config_parameter'].sudo().get_param( |
|||
'odoo_website_helpdesk.reply_template_id' |
|||
) |
|||
template_id = self.env['mail.template'].browse(int(template_id)) |
|||
if template_id: |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'mail', |
|||
'res_model': 'mail.compose.message', |
|||
'view_mode': 'form', |
|||
'target': 'new', |
|||
'views': [[False, 'form']], |
|||
'context': { |
|||
'default_model': 'ticket.helpdesk', |
|||
'default_res_ids': self.ids, |
|||
'default_template_id': template_id.id |
|||
} |
|||
} |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'mail', |
|||
'res_model': 'mail.compose.message', |
|||
'view_mode': 'form', |
|||
'target': 'new', |
|||
'views': [[False, 'form']], |
|||
'context': { |
|||
'default_model': 'ticket.helpdesk', |
|||
'default_res_ids': self.ids, |
|||
} |
|||
} |
@ -0,0 +1,66 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models, _ |
|||
from odoo.exceptions import UserError |
|||
|
|||
|
|||
class TicketStage(models.Model): |
|||
"""Stage Ticket model """ |
|||
_name = 'ticket.stage' |
|||
_description = 'Ticket Stage' |
|||
_order = 'sequence, id' |
|||
_fold_name = 'fold' |
|||
|
|||
name = fields.Char('Name', help='Name of the ticket stage') |
|||
active = fields.Boolean(string='Active', default=True, help='Active option' |
|||
'for ticket ' |
|||
'stage.') |
|||
sequence = fields.Integer(string='Sequence', default=50, |
|||
help='Sequence number of the ticket stage.') |
|||
closing_stage = fields.Boolean('Closing Stage', default=False, |
|||
help='Closing stage of the ticket.') |
|||
cancel_stage = fields.Boolean('Cancel Stage', default=False, |
|||
help='Cancel stage of the ticket.') |
|||
starting_stage = fields.Boolean('Start Stage', default=False, |
|||
help='Starting Stage of the ticket.') |
|||
folded = fields.Boolean('Folded in Kanban', default=False, |
|||
help='Folded Stage of the ticket.') |
|||
template_id = fields.Many2one('mail.template', |
|||
help='Templates', string='Template', |
|||
domain="[('model', '=', 'ticket.helpdesk')]") |
|||
group_ids = fields.Many2many('res.groups', help='Group', string='Groups') |
|||
fold = fields.Boolean(string='Fold', help='Folded option in ticket.') |
|||
|
|||
def unlink(self): |
|||
"""Unlinking Function to unlink the stage""" |
|||
for rec in self: |
|||
tickets = rec.search([]) |
|||
sequence = tickets.mapped('sequence') |
|||
lowest_sequence = tickets.filtered( |
|||
lambda x: x.sequence == min(sequence)) |
|||
if self.name == "Draft": |
|||
raise UserError(_("Cannot Delete This Stage")) |
|||
if rec == lowest_sequence: |
|||
raise UserError(_("Cannot Delete '%s'" % (rec.name))) |
|||
else: |
|||
res = super().unlink() |
|||
return res |
@ -0,0 +1,39 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Bhagyadev KP (<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import models |
|||
|
|||
|
|||
class WebsiteMenu(models.Model): |
|||
"""Inheriting the website menu""" |
|||
_inherit = "website.menu" |
|||
|
|||
def _compute_visible(self): |
|||
"""Compute function for to visible the menu based on the boolean |
|||
field visibility""" |
|||
super()._compute_visible() |
|||
show_menu_header = self.env['ir.config_parameter'].sudo().get_param( |
|||
'odoo_website_helpdesk.helpdesk_menu_show') |
|||
for menu in self: |
|||
if menu.name == 'Helpdesk' and not show_menu_header: |
|||
menu.is_visible = False |
|||
if menu.name == 'Helpdesk' and show_menu_header: |
|||
menu.is_visible = True |
@ -0,0 +1,114 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!-- Define a Qweb template for the support ticket report --> |
|||
<template id="report_helpdesk_ticket"> |
|||
<t t-call="web.html_container"> |
|||
<t t-foreach="docs" t-as="o"> |
|||
<t t-call="web.external_layout"> |
|||
<div class="page"> |
|||
<div style="margin-bottom: 10px;"> |
|||
<div class="text-center" |
|||
style="font-weight: 400 !important; font-size: 2rem !important;"> |
|||
<t t-esc="o.name"/> |
|||
- |
|||
<t t-esc="o.subject"/> |
|||
</div> |
|||
<br/> |
|||
<table class="table table-bordered mt32"> |
|||
<thead> |
|||
<tr> |
|||
<th class="text-center"> |
|||
<span>Customer</span> |
|||
</th> |
|||
<th class="text-center"> |
|||
<span>Description </span> |
|||
</th> |
|||
<th class="text-center"> |
|||
<span>Priority </span> |
|||
</th> |
|||
<th class="text-center"> |
|||
<span>Products</span> |
|||
</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr class="text-center"> |
|||
<td> |
|||
<span t-field="o.customer_id" |
|||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/> |
|||
</td> |
|||
<td> |
|||
<span t-field="o.description"/> |
|||
</td> |
|||
<td> |
|||
<span t-field="o.priority"/> |
|||
</td> |
|||
<td> |
|||
<span t-field="o.product_ids"/> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<t t-set="tasks" |
|||
t-value="request.env['project.task'].search([('ticket_id', '=', o.id)])"/> |
|||
<t t-if="tasks"> |
|||
<div> |
|||
<h3 class="text-center"> |
|||
<strong>Tasks</strong> |
|||
</h3> |
|||
</div> |
|||
<table class="table table-bordered mt32"> |
|||
<thead> |
|||
<tr> |
|||
<th class="text-center"> |
|||
<span>Task Name</span> |
|||
</th> |
|||
<th class="text-center"> |
|||
<span>Analytic Account</span> |
|||
</th> |
|||
<th class="text-center"> |
|||
<span>Assigned to</span> |
|||
</th> |
|||
<th class="text-center"> |
|||
<span>Total Hours Spend</span> |
|||
</th> |
|||
</tr> |
|||
</thead> |
|||
<t t-foreach="tasks" t-as="task"> |
|||
<tbody> |
|||
<tr class="text-center"> |
|||
<td> |
|||
<span t-field="task.name"/> |
|||
</td> |
|||
<td> |
|||
<span t-field="task.analytic_account_id"/> |
|||
</td> |
|||
<td> |
|||
<span t-esc="', '.join(map(lambda x: (x.name), task.user_ids))"/> |
|||
</td> |
|||
<td> |
|||
<span t-field="task.effective_hours"/> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</t> |
|||
</table> |
|||
</t> |
|||
</div> |
|||
</t> |
|||
</t> |
|||
</t> |
|||
</template> |
|||
<!-- Define an action for the Support Ticket report --> |
|||
<record id="report_ticket" model="ir.actions.report"> |
|||
<field name="name">Support Ticket</field> |
|||
<field name="model">ticket.helpdesk</field> |
|||
<field name="report_type">qweb-pdf</field> |
|||
<field name="report_name">odoo_website_helpdesk.report_helpdesk_ticket</field> |
|||
<field name="report_file">odoo_website_helpdesk.report_helpdesk_ticket</field> |
|||
<field name="binding_model_id" ref="model_ticket_helpdesk"/> |
|||
<field name="binding_view_types"/> |
|||
<field name="binding_type">report</field> |
|||
</record> |
|||
</odoo> |
|
@ -0,0 +1,38 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Helpdesk Access Groups Category --> |
|||
<record id="module_category_helpdesk" model="ir.module.category"> |
|||
<field name="name">Helpdesk</field> |
|||
<field name="description">Helpdesk Access Groups</field> |
|||
<field name="sequence">20</field> |
|||
</record> |
|||
<!-- Helpdesk User Group --> |
|||
<record id="helpdesk_user" model="res.groups"> |
|||
<field name="name">User</field> |
|||
<field name="category_id" ref="odoo_website_helpdesk.module_category_helpdesk"/> |
|||
</record> |
|||
<!-- Helpdesk Team Leader Group --> |
|||
<record id="helpdesk_team_leader" model="res.groups"> |
|||
<field name="name">Team Leader</field> |
|||
<field name="category_id" ref="odoo_website_helpdesk.module_category_helpdesk"/> |
|||
<field name="implied_ids" eval="[(4, ref('odoo_website_helpdesk.helpdesk_user'))]"/> |
|||
</record> |
|||
<!-- Helpdesk Manager Group --> |
|||
<record id="helpdesk_manager" model="res.groups"> |
|||
<field name="name">Manager</field> |
|||
<field name="category_id" ref="odoo_website_helpdesk.module_category_helpdesk"/> |
|||
<field name="implied_ids" eval="[(4, ref('odoo_website_helpdesk.helpdesk_team_leader'))]"/> |
|||
</record> |
|||
<!-- Group Show Category --> |
|||
<record id="group_show_category" model="res.groups"> |
|||
<field name="name">group_show_category</field> |
|||
</record> |
|||
<!-- Group Show Subcategory --> |
|||
<record id="group_show_subcategory" model="res.groups"> |
|||
<field name="name">group_show_subcategory</field> |
|||
</record> |
|||
<!-- Default User with Helpdesk Manager Group --> |
|||
<record id="base.default_user" model="res.users"> |
|||
<field name="groups_id" eval="[(4,ref('odoo_website_helpdesk.helpdesk_manager'))]"/> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,85 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<!-- Record rule for team leader --> |
|||
<record id="help_desk_dashboard_team_leader" |
|||
model="ir.rule"> |
|||
<field name="name">Record Rule for team leader</field> |
|||
<field name="model_id" ref="model_ticket_helpdesk"/> |
|||
<field name="groups" |
|||
eval="[(4, ref('odoo_website_helpdesk.helpdesk_team_leader'))]"/> |
|||
<field name="domain_force">['&', ('team_head_id.id', '=', user.id), ('stage_id.group_ids.users', 'in', |
|||
user.id)] |
|||
</field> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
</record> |
|||
<!-- Record rule for manager --> |
|||
<record id="help_desk_dashboard_manager" |
|||
model="ir.rule"> |
|||
<field name="name">Record Rule for manager</field> |
|||
<field name="model_id" ref="model_ticket_helpdesk"/> |
|||
<field name="groups" |
|||
eval="[(4, ref('odoo_website_helpdesk.helpdesk_manager'))]"/> |
|||
<field name="domain_force">[(1, '=', 1)]</field> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
</record> |
|||
<!-- Record rule for user --> |
|||
<record id="help_desk_dashboard_user" |
|||
model="ir.rule"> |
|||
<field name="name">Record Rule for user</field> |
|||
<field ref="model_ticket_helpdesk" name="model_id"/> |
|||
<field name="groups" |
|||
eval="[(4, ref('odoo_website_helpdesk.helpdesk_user'))]"/> |
|||
<field name="domain_force">['&', ('assigned_user_id', '=', user.id), ('stage_id.group_ids.users', 'in', |
|||
user.id)] |
|||
</field> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
</record> |
|||
<!-- Record rule for stages leader --> |
|||
<record id="help_desk_stage_rule_leader" |
|||
model="ir.rule"> |
|||
<field name="name">Record Rule stages leader</field> |
|||
<field ref="model_ticket_stage" name="model_id"/> |
|||
<field name="groups" |
|||
eval="[(4, ref('odoo_website_helpdesk.helpdesk_team_leader'))]"/> |
|||
<field name="domain_force">[('group_ids.users', 'in', user.id)]</field> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
</record> |
|||
<!-- Record rule for stages manager --> |
|||
<record id="help_desk_stage_rule_manager" |
|||
model="ir.rule"> |
|||
<field name="name">Record Rule stages manager</field> |
|||
<field ref="model_ticket_stage" name="model_id"/> |
|||
<field name="groups" |
|||
eval="[(4, ref('odoo_website_helpdesk.helpdesk_manager'))]"/> |
|||
<field name="domain_force">[(1, '=', 1)]</field> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
</record> |
|||
<!-- Record rule for stages user --> |
|||
<record id="help_desk_stage_rule_user" |
|||
model="ir.rule"> |
|||
<field name="name">Record Rule stages user</field> |
|||
<field ref="model_ticket_stage" name="model_id"/> |
|||
<field name="groups" |
|||
eval="[(4, ref('odoo_website_helpdesk.helpdesk_user'))]"/> |
|||
<field name="domain_force">[('group_ids.users', 'in', user.id)]</field> |
|||
<field name="perm_read" eval="True"/> |
|||
<field name="perm_write" eval="True"/> |
|||
<field name="perm_create" eval="True"/> |
|||
<field name="perm_unlink" eval="True"/> |
|||
</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: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 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: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 738 KiB |
After Width: | Height: | Size: 2.2 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: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 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.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |