@ -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 |