@ -1,266 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
############################################################################# |
|
||||
# |
|
||||
# Cybrosys Technologies Pvt. Ltd. |
|
||||
# |
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|
||||
# |
|
||||
# You can modify it under the terms of the GNU 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): |
|
||||
"""Website helpdesk dashboard""" |
|
||||
|
|
||||
@http.route(['/helpdesk_dashboard'], type='json', auth="public") |
|
||||
def helpdesk_dashboard(self): |
|
||||
"""Helpdesk dashboard controller""" |
|
||||
stage_new = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Inbox')], limit=1).id |
|
||||
stage_draft = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Draft')], limit=1).id |
|
||||
stage_inprogress = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'In Progress')], limit=1).id |
|
||||
stage_canceled = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Canceled')], limit=1).id |
|
||||
stage_done = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Done')], limit=1).id |
|
||||
stage_closed = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Closed')], limit=1).id |
|
||||
stage_ids = [stage_new, stage_draft] |
|
||||
new = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', 'in', stage_ids)]) |
|
||||
new_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', 'in', stage_ids)]) |
|
||||
new_id_ls = [data.id for data in new_id] |
|
||||
in_progress = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_inprogress)]) |
|
||||
in_progress_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_inprogress)]) |
|
||||
in_progress_ls = [data.id for data in in_progress_id] |
|
||||
canceled = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_canceled)]) |
|
||||
canceled_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_canceled)]) |
|
||||
canceled_id_ls = [data.id for data in canceled_id] |
|
||||
done = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_done)]) |
|
||||
done_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_done)]) |
|
||||
done_id_ls = [data.id for data in done_id] |
|
||||
closed = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_closed)]) |
|
||||
closed_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_closed)]) |
|
||||
closed_id_ls = [data.id for data in closed_id] |
|
||||
dashboard_values = { |
|
||||
'new': new, |
|
||||
'in_progress': in_progress, |
|
||||
'canceled': canceled, |
|
||||
'done': done, |
|
||||
'closed': closed, |
|
||||
'new_id': new_id_ls, |
|
||||
'in_progress_id': in_progress_ls, |
|
||||
'canceled_id': canceled_id_ls, |
|
||||
'done_id': done_id_ls, |
|
||||
'closed_id': closed_id_ls, |
|
||||
} |
|
||||
return dashboard_values |
|
||||
|
|
||||
@http.route(['/helpdesk_dashboard_week'], type='json', auth="public") |
|
||||
def helpdesk_dashboard_week(self): |
|
||||
"""Week based sorting controller""" |
|
||||
today = DT.date.today() |
|
||||
stage_new = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Inbox')], limit=1).id |
|
||||
stage_draft = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Draft')], limit=1).id |
|
||||
stage_inprogress = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'In Progress')], limit=1).id |
|
||||
stage_canceled = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Canceled')], limit=1).id |
|
||||
stage_done = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Done')], limit=1).id |
|
||||
stage_closed = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Closed')], limit=1).id |
|
||||
stage_ids = [stage_new, stage_draft] |
|
||||
week_ago = str(today - DT.timedelta(days=7)) + ' ' |
|
||||
new = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', 'in', stage_ids), ('create_date', '>', week_ago)]) |
|
||||
new_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', 'in', stage_ids), ('create_date', '>', week_ago)]) |
|
||||
new_id_ls = [data.id for data in new_id] |
|
||||
in_progress = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_inprogress), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
in_progress_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_inprogress), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
in_progress_ls = [data.id for data in in_progress_id] |
|
||||
canceled = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_canceled), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
canceled_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_canceled), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
canceled_id_ls = [data.id for data in canceled_id] |
|
||||
done = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_done), ('create_date', '>', week_ago)]) |
|
||||
done_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_done), ('create_date', '>', week_ago)]) |
|
||||
done_id_ls = [data.id for data in done_id] |
|
||||
closed = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_closed), ('create_date', '>', week_ago)]) |
|
||||
closed_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_closed), ('create_date', '>', week_ago)]) |
|
||||
closed_id_ls = [data.id for data in closed_id] |
|
||||
dashboard_values = { |
|
||||
'new': new, |
|
||||
'in_progress': in_progress, |
|
||||
'canceled': canceled, |
|
||||
'done': done, |
|
||||
'closed': closed, |
|
||||
'new_id': new_id_ls, |
|
||||
'in_progress_id': in_progress_ls, |
|
||||
'canceled_id': canceled_id_ls, |
|
||||
'done_id': done_id_ls, |
|
||||
'closed_id': closed_id_ls, |
|
||||
} |
|
||||
return dashboard_values |
|
||||
|
|
||||
@http.route(['/helpdesk_dashboard_month'], type='json', auth="public") |
|
||||
def helpdesk_dashboard_month(self): |
|
||||
"""Month based sorting controller""" |
|
||||
today = DT.date.today() |
|
||||
stage_new = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Inbox')], limit=1).id |
|
||||
stage_draft = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Draft')], limit=1).id |
|
||||
stage_inprogress = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'In Progress')], limit=1).id |
|
||||
stage_canceled = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Canceled')], limit=1).id |
|
||||
stage_done = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Done')], limit=1).id |
|
||||
stage_closed = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Closed')], limit=1).id |
|
||||
stage_ids = [stage_new, stage_draft] |
|
||||
week_ago = str(today - DT.timedelta(days=30)) + ' ' |
|
||||
new = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', 'in', stage_ids), ('create_date', '>', week_ago)]) |
|
||||
new_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', 'in', stage_ids), ('create_date', '>', week_ago)]) |
|
||||
new_id_ls = [data.id for data in new_id] |
|
||||
in_progress = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_inprogress), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
in_progress_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_inprogress), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
in_progress_ls = [data.id for data in in_progress_id] |
|
||||
canceled = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_canceled), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
canceled_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_canceled), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
canceled_id_ls = [data.id for data in canceled_id] |
|
||||
done = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_done), ('create_date', '>', week_ago)]) |
|
||||
done_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_done), ('create_date', '>', week_ago)]) |
|
||||
done_id_ls = [data.id for data in done_id] |
|
||||
closed = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_closed), ('create_date', '>', week_ago)]) |
|
||||
closed_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_closed), ('create_date', '>', week_ago)]) |
|
||||
closed_id_ls = [data.id for data in closed_id] |
|
||||
dashboard_values = { |
|
||||
'new': new, |
|
||||
'in_progress': in_progress, |
|
||||
'canceled': canceled, |
|
||||
'done': done, |
|
||||
'closed': closed, |
|
||||
'new_id': new_id_ls, |
|
||||
'in_progress_id': in_progress_ls, |
|
||||
'canceled_id': canceled_id_ls, |
|
||||
'done_id': done_id_ls, |
|
||||
'closed_id': closed_id_ls, |
|
||||
} |
|
||||
return dashboard_values |
|
||||
|
|
||||
@http.route(['/helpdesk_dashboard_year'], type='json', auth="public") |
|
||||
def helpdesk_dashboard_year(self): |
|
||||
"""Year based sorting""" |
|
||||
today = DT.date.today() |
|
||||
stage_new = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Inbox')], limit=1).id |
|
||||
stage_draft = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Draft')], limit=1).id |
|
||||
stage_inprogress = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'In Progress')], limit=1).id |
|
||||
stage_canceled = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Canceled')], limit=1).id |
|
||||
stage_done = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Done')], limit=1).id |
|
||||
stage_closed = request.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Closed')], limit=1).id |
|
||||
stage_ids = [stage_new, stage_draft] |
|
||||
week_ago = str(today - DT.timedelta(days=360)) + ' ' |
|
||||
new = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', 'in', stage_ids), ('create_date', '>', week_ago)]) |
|
||||
new_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', 'in', stage_ids), ('create_date', '>', week_ago)]) |
|
||||
new_id_ls = [data.id for data in new_id] |
|
||||
in_progress = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_inprogress), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
in_progress_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_inprogress), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
in_progress_ls = [data.id for data in in_progress_id] |
|
||||
canceled = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_canceled), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
canceled_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_canceled), |
|
||||
('create_date', '>', week_ago)]) |
|
||||
canceled_id_ls = [data.id for data in canceled_id] |
|
||||
done = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_done), ('create_date', '>', week_ago)]) |
|
||||
done_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_done), ('create_date', '>', week_ago)]) |
|
||||
done_id_ls = [data.id for data in done_id] |
|
||||
closed = request.env["help.ticket"].search_count( |
|
||||
[('stage_id', '=', stage_closed), ('create_date', '>', week_ago)]) |
|
||||
closed_id = request.env["help.ticket"].search( |
|
||||
[('stage_id', '=', stage_closed), ('create_date', '>', week_ago)]) |
|
||||
closed_id_ls = [data.id for data in closed_id] |
|
||||
dashboard_values = { |
|
||||
'new': new, |
|
||||
'in_progress': in_progress, |
|
||||
'canceled': canceled, |
|
||||
'done': done, |
|
||||
'closed': closed, |
|
||||
'new_id': new_id_ls, |
|
||||
'in_progress_id': in_progress_ls, |
|
||||
'canceled_id': canceled_id_ls, |
|
||||
'done_id': done_id_ls, |
|
||||
'closed_id': closed_id_ls, |
|
||||
} |
|
||||
return dashboard_values |
|
@ -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 |
@ -1,173 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
############################################################################# |
|
||||
# |
|
||||
# Cybrosys Technologies Pvt. Ltd. |
|
||||
# |
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|
||||
# |
|
||||
# You can modify it under the terms of the GNU 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 odoo import _, http |
|
||||
from psycopg2 import IntegrityError |
|
||||
from odoo.http import request |
|
||||
from odoo.exceptions import ValidationError |
|
||||
from odoo.addons.website.controllers.form import WebsiteForm |
|
||||
|
|
||||
|
|
||||
class HelpdeskProduct(http.Controller): |
|
||||
"""It controls the website products and return the product.""" |
|
||||
@http.route('/product', auth='public', type='json') |
|
||||
def product(self): |
|
||||
"""Product control function""" |
|
||||
products = request.env['product.template'].sudo().search_read([], |
|
||||
['name', |
|
||||
'id']) |
|
||||
return products |
|
||||
|
|
||||
|
|
||||
class WebsiteFormInherit(WebsiteForm): |
|
||||
"""This module extends the functionality of the website form controller |
|
||||
to handle the creation of new help desk tickets. It provides a new |
|
||||
controller to display a list of tickets for the current user in their |
|
||||
portal, and overrides the website form controller's method for handling |
|
||||
form submissions to create a new help desk ticket instead.""" |
|
||||
def _handle_website_form(self, model_name, **kwargs): |
|
||||
"""Website Help Desk Form""" |
|
||||
if model_name == 'help.ticket': |
|
||||
tickets = request.env['ticket.stage'].sudo().search([]) |
|
||||
for rec in tickets: |
|
||||
sequence = tickets.mapped('sequence') |
|
||||
lowest_sequence = tickets.filtered( |
|
||||
lambda x: x.sequence == min(sequence)) |
|
||||
if rec == lowest_sequence: |
|
||||
lowest_stage_id = lowest_sequence |
|
||||
products = kwargs.get('product') |
|
||||
partner_create = request.env['res.partner'].sudo().create({ |
|
||||
'name':kwargs.get('customer_name'), |
|
||||
'company_name':kwargs.get('company'), |
|
||||
'phone':kwargs.get('phone'), |
|
||||
'email':kwargs.get('email_from') |
|
||||
}) |
|
||||
if products: |
|
||||
splited_product = products.split(',') |
|
||||
product_list = [int(i) for i in splited_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': partner_create.id, |
|
||||
'ticket_type': kwargs.get('ticket_type'), |
|
||||
'category_id': kwargs.get('category'), |
|
||||
} |
|
||||
ticket_id = request.env['help.ticket'].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)]) |
|
||||
data = self.extract_data(model_record, request.params) |
|
||||
if ('ticket_attachment' in request.params or |
|
||||
request.httprequest.files or data.get( |
|
||||
'attachments')): |
|
||||
attached_files = data.get('attachments') |
|
||||
for attachment in attached_files: |
|
||||
attached_file = attachment.read() |
|
||||
request.env['ir.attachment'].sudo().create({ |
|
||||
'name': attachment.filename, |
|
||||
'res_model': 'help.ticket', |
|
||||
'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: |
|
||||
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': partner_create.id, |
|
||||
'ticket_type': kwargs.get('ticket_type'), |
|
||||
'category_id': kwargs.get('category'), |
|
||||
} |
|
||||
ticket_id = request.env['help.ticket'].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)]) |
|
||||
data = self.extract_data(model_record, request.params) |
|
||||
if ('ticket_attachment' in request.params or |
|
||||
request.httprequest.files or data.get( |
|
||||
'attachments')): |
|
||||
attached_files = data.get('attachments') |
|
||||
for attachment in attached_files: |
|
||||
attached_file = attachment.read() |
|
||||
request.env['ir.attachment'].sudo().create({ |
|
||||
'name': attachment.filename, |
|
||||
'res_model': 'help.ticket', |
|
||||
'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) |
|
||||
# If we encounter an issue while extracting data |
|
||||
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']) |
|
||||
# In case of an email, we want to send it immediately instead of waiting |
|
||||
# For the email queue to process |
|
||||
if model_name == 'mail.mail': |
|
||||
request.env[model_name].sudo().browse(id_record).send() |
|
||||
|
|
||||
# Some fields have additional SQL constraints that we can't check generically |
|
||||
# Ex: crm.lead.probability which is a float between 0 and 1 |
|
||||
# TODO: How to get the name of the erroneous field ? |
|
||||
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,169 @@ |
|||||
|
# -*- 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 |
||||
|
""" |
||||
|
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') |
||||
|
partner_create = request.env['res.partner'].sudo().create({ |
||||
|
'name': kwargs.get('customer_name'), |
||||
|
'company_name': kwargs.get('company'), |
||||
|
'phone': kwargs.get('phone'), |
||||
|
'email': kwargs.get('email_from') |
||||
|
}) |
||||
|
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': partner_create.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': partner_create.id, |
||||
|
'ticket_type_id': kwargs.get('ticket_type_id'), |
||||
|
'category_id': kwargs.get('category'), |
||||
|
} |
||||
|
ticket_id = request.env['ticket.helpdesk'].sudo().create(rec_val) |
||||
|
if ticket_id and partner_create.email: |
||||
|
request.env['mail.mail'].sudo().create({ |
||||
|
'subject': 'Your Ticket Has Been Created', |
||||
|
'body_html': f"<p>Hello {partner_create.name},</p><p>Your ticket <strong>{ticket_id.name}</strong> with the subject <strong>{ticket_id.subject}</strong> has been successfully submitted. Our support team will contact you soon.</p> <p>Thank You.</p>", |
||||
|
'email_to': partner_create.email, |
||||
|
'email_from': request.env.user.email or 'support@example.com', |
||||
|
}).send() |
||||
|
ticket_id.message_post( |
||||
|
body="A confirmation email regarding the ticket creation has been sent to the customer.", |
||||
|
subject="Ticket Confirmation Email", |
||||
|
message_type='email', |
||||
|
subtype_xmlid="mail.mt_comment", |
||||
|
) |
||||
|
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,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> |
@ -1,22 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<odoo> |
|
||||
<!-- Ticket types--> |
|
||||
<record id="helpdesk_types_question" model="helpdesk.types"> |
|
||||
<field name="name">Question</field> |
|
||||
</record> |
|
||||
<record id="helpdesk_types_issue" model="helpdesk.types"> |
|
||||
<field name="name">Issue</field> |
|
||||
</record> |
|
||||
<record id="helpdesk_types_repair" model="helpdesk.types"> |
|
||||
<field name="name">Repair</field> |
|
||||
</record> |
|
||||
<record id="helpdesk_types_maintenance" model="helpdesk.types"> |
|
||||
<field name="name">Maintenance</field> |
|
||||
</record> |
|
||||
<record id="helpdesk_categories_internal" model="helpdesk.categories"> |
|
||||
<field name="name">Internal</field> |
|
||||
</record> |
|
||||
<record id="helpdesk_categories_technical" model="helpdesk.categories"> |
|
||||
<field name="name">Technical</field> |
|
||||
</record> |
|
||||
</odoo> |
|
@ -1,17 +1,15 @@ |
|||||
<?xml version="1.0" encoding="UTF-8" ?> |
<?xml version="1.0" encoding="UTF-8" ?> |
||||
<odoo> |
<odoo> |
||||
<data noupdate="1"> |
<data noupdate="1"> |
||||
<!-- Auto close ticket--> |
<!-- Scheduled task (cron job) for automatically closing tickets--> |
||||
<record id="ir_cron_auto_close_ticket" model="ir.cron"> |
<record id="auto_close_ticket" model="ir.cron"> |
||||
<field name="name">Auto Close Ticket</field> |
<field name="name">Auto Close Ticket</field> |
||||
<field name="model_id" |
<field name="model_id" ref="odoo_website_helpdesk.model_ticket_helpdesk"/> |
||||
ref="odoo_website_helpdesk.model_help_ticket"/> |
|
||||
<field name="state">code</field> |
<field name="state">code</field> |
||||
<field name="code">model.auto_close_ticket()</field> |
<field name="code">model.auto_close_ticket()</field> |
||||
<field name="user_id" ref="base.user_root"/> |
<field name="user_id" ref="base.user_root"/> |
||||
<field name="interval_number">1</field> |
<field name="interval_number">1</field> |
||||
<field name="interval_type">days</field> |
<field name="interval_type">days</field> |
||||
<field name="numbercall">-1</field> |
|
||||
</record> |
</record> |
||||
</data> |
</data> |
||||
</odoo> |
</odoo> |
||||
|
@ -1,39 +1,39 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<odoo> |
<odoo> |
||||
|
<data noupdate="1"> |
||||
<!-- Project Stages --> |
<!-- Project Stages --> |
||||
<record id="ticket_stage_inbox" model="ticket.stage"> |
<!-- - stage_inbox: Initial stage where tickets are received.--> |
||||
|
<record id="stage_inbox" model="ticket.stage"> |
||||
<field name="sequence">10</field> |
<field name="sequence">10</field> |
||||
<field name="name">Inbox</field> |
<field name="name">Inbox</field> |
||||
</record> |
</record> |
||||
<!-- Draft--> |
<!-- - stage_draft: Tickets in the drafting phase.--> |
||||
<record id="ticket_stage_draft" model="ticket.stage"> |
<record id="stage_draft" model="ticket.stage"> |
||||
<field name="sequence">15</field> |
<field name="sequence">15</field> |
||||
<field name="name">Draft</field> |
<field name="name">Draft</field> |
||||
</record> |
</record> |
||||
<!--In progress--> |
<!-- - stage_done: Final stage indicating completion.--> |
||||
<record id="ticket_stage_in_progress" model="ticket.stage"> |
<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="sequence">20</field> |
||||
<field name="starting_stage" eval="True"/> |
<field name="starting_stage" eval="True"/> |
||||
<field name="name">In Progress</field> |
<field name="name">In Progress</field> |
||||
</record> |
</record> |
||||
<!--Done--> |
<!-- - stage_closed: Closing stage for resolved tickets.--> |
||||
<record id="ticket_stage_done" model="ticket.stage"> |
<record id="stage_closed" model="ticket.stage"> |
||||
<field name="sequence">25</field> |
|
||||
<field name="name">Done</field> |
|
||||
<field name="folded" eval="True"/> |
|
||||
</record> |
|
||||
<!--Cancelled--> |
|
||||
<record id="ticket_stage_cancel" model="ticket.stage"> |
|
||||
<field name="sequence">30</field> |
<field name="sequence">30</field> |
||||
<field name="name">Canceled</field> |
<field name="closing_stage">True</field> |
||||
<field name="cancel_stage" eval="True"/> |
|
||||
<field name="folded" eval="True"/> |
|
||||
</record> |
|
||||
<!-- Closed--> |
|
||||
<record id="ticket_stage_closed" model="ticket.stage"> |
|
||||
<field name="sequence">29</field> |
|
||||
<field name="name">Closed</field> |
<field name="name">Closed</field> |
||||
<field name="closing_stage" eval="True"/> |
|
||||
<field name="folded" eval="True"/> |
|
||||
</record> |
</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> |
</odoo> |
@ -1,11 +1,25 @@ |
|||||
## Module <odoo_website_helpdesk> |
## Module <odoo_website_helpdesk> |
||||
|
|
||||
#### 04.12.2023 |
#### 30.10.2024 |
||||
#### Version 16.0.1.0.0 |
#### Version 18.0.1.0.0 |
||||
#### ADD |
#### ADD |
||||
|
|
||||
- Initial commit for Website Helpdesk Support Ticket Management |
- Initial commit for Website Helpdesk Support Ticket Management |
||||
|
|
||||
#### 12.02.2025 |
#### 12.02.2025 |
||||
#### Version 16.0.3.0.1 |
#### Version 18.0.1.0.1 |
||||
#### UPDT |
##### UPDT |
||||
-A new contact record is created upon form submission. |
-A new contact record is created upon form submission. |
||||
|
|
||||
|
#### 13.05.2025 |
||||
|
#### Version 18.0.1.0.2 |
||||
|
##### UPDT |
||||
|
-A confirmation email will be sent to the customer upon ticket creation. |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
@ -1,65 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
############################################################################# |
|
||||
# |
|
||||
# Cybrosys Technologies Pvt. Ltd. |
|
||||
# |
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|
||||
# |
|
||||
# You can modify it under the terms of the GNU 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 HelpTeam(models.Model): |
|
||||
""" This class represents a Helpdesk Team in the system, providing |
|
||||
information about the team members, leader, and related project.""" |
|
||||
_name = 'help.team' |
|
||||
_description = 'Helpdesk Team' |
|
||||
|
|
||||
name = fields.Char(string='Name', help='Name of the Helpdesk Team. It ' |
|
||||
'identify the helpdesk team') |
|
||||
team_lead_id = fields.Many2one( |
|
||||
'res.users', |
|
||||
string='Team Leader', |
|
||||
help='Name of the Helpdesk Team Leader.', |
|
||||
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='Users who belong to that Helpdesk Team', |
|
||||
domain=lambda self: [('groups_id', 'in', self.env.ref( |
|
||||
'odoo_website_helpdesk.helpdesk_user').id)]) |
|
||||
email = fields.Char(string='Email', help='Email') |
|
||||
project_id = fields.Many2one('project.project', |
|
||||
string='Project', |
|
||||
help='The Project they are currently in') |
|
||||
create_task = fields.Boolean(string="Create Task", |
|
||||
help="Enable for allowing team to " |
|
||||
"create tasks from tickets") |
|
||||
|
|
||||
@api.onchange('team_lead_id') |
|
||||
def members_choose(self): |
|
||||
""" This method is triggered when the Team Leader is changed. It |
|
||||
updates the available team members based on the selected leader and |
|
||||
filters out the leader from the list of potential members.""" |
|
||||
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)]}} |
|
@ -1,465 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
############################################################################# |
|
||||
# |
|
||||
# Cybrosys Technologies Pvt. Ltd. |
|
||||
# |
|
||||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|
||||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|
||||
# |
|
||||
# You can modify it under the terms of the GNU 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 HelpTicket(models.Model): |
|
||||
"""This model represents the Helpdesk Ticket, which allows users to raise |
|
||||
tickets related to products, services or any other issues. Each ticket has a |
|
||||
name, customer information, description, team responsible for handling |
|
||||
requests, associated project, priority level, stage, cost per hour, service |
|
||||
product, start and end dates, and related tasks and invoices.""" |
|
||||
|
|
||||
_name = 'help.ticket' |
|
||||
_description = 'Help Ticket' |
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin'] |
|
||||
|
|
||||
name = fields.Char(string='Name', default=lambda self: _('New'), |
|
||||
help='The name of the help ticket. By default, a new ' |
|
||||
'unique sequence number is assigned to each ' |
|
||||
'help ticket, unless a name is provided.', |
|
||||
readonly=True) |
|
||||
active = fields.Boolean(default=True, help='Active', string='Active') |
|
||||
customer_id = fields.Many2one('res.partner', |
|
||||
string='Customer Name', |
|
||||
help='Select the Customer Name') |
|
||||
customer_name = fields.Char(string='Customer Name', |
|
||||
help='Add the Customer Name') |
|
||||
subject = fields.Text(string='Subject', required=True, |
|
||||
help='Subject of the Ticket') |
|
||||
description = fields.Text(string='Description', required=True, |
|
||||
help='Issue Description') |
|
||||
email = fields.Char(string='Email', help='Email of the User.') |
|
||||
phone = fields.Char(string='Phone', help='Phone Number of the user') |
|
||||
team_id = fields.Many2one('help.team', string='Helpdesk Team', |
|
||||
help='The helpdesk team responsible for ' |
|
||||
'handling requests related to this ' |
|
||||
'record') |
|
||||
product_ids = fields.Many2many('product.template', |
|
||||
string='Product', |
|
||||
help='The product associated with this ' |
|
||||
'record.This field allows you to select' |
|
||||
'an existing product from the product ' |
|
||||
'catalog.') |
|
||||
project_id = fields.Many2one('project.project', |
|
||||
string='Project', |
|
||||
readonly=False, |
|
||||
related='team_id.project_id', |
|
||||
store=True, |
|
||||
help='The project associated with this team.' |
|
||||
'This field is automatically filled ' |
|
||||
'based on the project assigned to ' |
|
||||
'the team.') |
|
||||
priority = fields.Selection(PRIORITIES, |
|
||||
default='1', |
|
||||
help='Set the priority level', |
|
||||
string='Priority') |
|
||||
stage_id = fields.Many2one('ticket.stage', string='Stage', |
|
||||
default=lambda self: self.env[ |
|
||||
'ticket.stage'].search( |
|
||||
[('name', '=', 'Draft')], limit=1).id, |
|
||||
tracking=True, |
|
||||
group_expand='_read_group_stage_ids', |
|
||||
help='Stages of the ticket.') |
|
||||
user_id = fields.Many2one('res.users', |
|
||||
default=lambda self: self.env.user, |
|
||||
check_company=True, |
|
||||
index=True, tracking=True, |
|
||||
help='Login User') |
|
||||
cost = fields.Float(string='Cost per hour', |
|
||||
help='The cost per hour for this record. This field ' |
|
||||
'specifies the hourly cost associated with the' |
|
||||
'record, which can be used in various ' |
|
||||
'calculations or reports.') |
|
||||
service_product_id = fields.Many2one('product.product', |
|
||||
string='Service Product', |
|
||||
help='The product associated with this' |
|
||||
'service. Only service products ' |
|
||||
'are available for selection.', |
|
||||
domain=[ |
|
||||
('detailed_type', '=', 'service')]) |
|
||||
create_date = fields.Datetime(string='Creation Date', help='Created date of' |
|
||||
'the Ticket') |
|
||||
start_date = fields.Datetime(string='Start Date', help='Start Date of the ' |
|
||||
'Ticket') |
|
||||
end_date = fields.Datetime(string='End Date', help='End Date of the Ticket') |
|
||||
public_ticket = fields.Boolean(string="Public Ticket", help='Public Ticket') |
|
||||
invoice_ids = fields.Many2many('account.move', |
|
||||
string='Invoices', |
|
||||
help='To Generate Invoice based on hours ' |
|
||||
'spent on the ticket' |
|
||||
) |
|
||||
task_ids = fields.Many2many('project.task', |
|
||||
string='Tasks', |
|
||||
help='Related Task of the Ticket') |
|
||||
color = fields.Integer(string="Color", help='Color') |
|
||||
replied_date = fields.Datetime(string='Replied date', |
|
||||
help='Replied Date of the Ticket') |
|
||||
last_update_date = fields.Datetime(string='Last Update Date', |
|
||||
help='Last Update Date of Ticket') |
|
||||
ticket_type = fields.Many2one('helpdesk.types', |
|
||||
string='Ticket Type', help='Ticket Type') |
|
||||
team_head = fields.Many2one('res.users', string='Team Leader', |
|
||||
compute='_compute_team_head', |
|
||||
help='Team Leader Name') |
|
||||
assigned_user = fields.Many2one( |
|
||||
'res.users', |
|
||||
string='Assigned User', |
|
||||
domain=lambda self: [('groups_id', 'in', self.env.ref( |
|
||||
'odoo_website_helpdesk.helpdesk_user').id)], |
|
||||
help='Choose the Assigned User Name') |
|
||||
category_id = fields.Many2one('helpdesk.categories', |
|
||||
help='Choose the Category', string='Category') |
|
||||
tags = fields.Many2many('helpdesk.tag', help='Choose the Tags', |
|
||||
string='Tag') |
|
||||
assign_user = fields.Boolean(string='Assigned User', help='Assign User') |
|
||||
attachment_ids = fields.One2many('ir.attachment', |
|
||||
'res_id', |
|
||||
help='Attachment Line', |
|
||||
string='Attachment') |
|
||||
merge_ticket_invisible = fields.Boolean(string='Merge Ticket', |
|
||||
help='Merge Ticket Invisible or ' |
|
||||
'Not') |
|
||||
merge_count = fields.Integer(string='Merge Count', help='Merged Tickets ' |
|
||||
'Count') |
|
||||
|
|
||||
@api.onchange('team_id', 'team_head') |
|
||||
def team_leader_domain(self): |
|
||||
"""Update the domain for the assigned user based on the selected team. |
|
||||
|
|
||||
This onchange method is triggered when the helpdesk team or team leader |
|
||||
is changed. It updates the domain for the assigned user field to include |
|
||||
only the members of the selected team.""" |
|
||||
teams = [] |
|
||||
for rec in self.team_id.member_ids: |
|
||||
teams.append(rec.id) |
|
||||
return {'domain': {'assigned_user': [('id', 'in', teams)]}} |
|
||||
|
|
||||
@api.depends('team_id') |
|
||||
def _compute_team_head(self): |
|
||||
"""Compute the team head based on the selected team. |
|
||||
|
|
||||
This method is triggered when the helpdesk team is changed. It computes |
|
||||
and updates the team head field based on the team's lead. |
|
||||
""" |
|
||||
self.team_head = self.team_id.team_lead_id.id |
|
||||
|
|
||||
@api.onchange('stage_id') |
|
||||
def mail_snd(self): |
|
||||
"""Send an email when the stage of the ticket is changed. |
|
||||
|
|
||||
This onchange method is triggered when the stage of the ticket is |
|
||||
changed. It updates the last update date, start date, and end date |
|
||||
fields accordingly. If a template is associated with the stage, it |
|
||||
sends an email using that template.""" |
|
||||
rec_id = self._origin.id |
|
||||
data = self.env['help.ticket'].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): |
|
||||
"""Assign the ticket to the team leader and send a notification. |
|
||||
|
|
||||
This function checks if a helpdesk team is selected and assigns the |
|
||||
team leader to the ticket. It then sends a notification email to the |
|
||||
team leader.""" |
|
||||
if self.team_id: |
|
||||
self.team_head = self.team_id.team_lead_id.id |
|
||||
mail_template = self.env.ref( |
|
||||
'odoo_website_helpdesk.' |
|
||||
'mail_template_odoo_website_helpdesk_assign') |
|
||||
mail_template.sudo().write({ |
|
||||
'email_to': self.team_head.email, |
|
||||
'subject': self.name |
|
||||
}) |
|
||||
mail_template.sudo().send_mail(self.id, force_send=True) |
|
||||
else: |
|
||||
raise ValidationError("Please choose a Helpdesk Team") |
|
||||
|
|
||||
def _default_show_create_task(self): |
|
||||
"""Get the default value for the 'show_create_task' field. |
|
||||
|
|
||||
This method retrieves the default value for the 'show_create_task' |
|
||||
field from the configuration settings.""" |
|
||||
return self.env['ir.config_parameter'].sudo().get_param( |
|
||||
'odoo_website_helpdesk.show_create_task') |
|
||||
|
|
||||
show_create_task = fields.Boolean(string="Create Task", |
|
||||
default=_default_show_create_task, |
|
||||
compute='_compute_show_create_task', |
|
||||
help='Determines whether the Create Task' |
|
||||
' button should be shown for this ' |
|
||||
'ticket.') |
|
||||
create_task = fields.Boolean(string="Create Task", readonly=False, |
|
||||
related='team_id.create_task', |
|
||||
store=True, |
|
||||
help='Defines if a task should be created when' |
|
||||
' this ticket is created.') |
|
||||
billable = fields.Boolean(string="Billable", help='Indicates whether the ' |
|
||||
'ticket is billable or ' |
|
||||
'not.') |
|
||||
|
|
||||
def _default_show_category(self): |
|
||||
"""Its display the default category""" |
|
||||
return self.env['ir.config_parameter'].sudo().get_param( |
|
||||
'odoo_website_helpdesk.show_category') |
|
||||
|
|
||||
show_category = fields.Boolean(default=_default_show_category, |
|
||||
compute='_compute_show_category', |
|
||||
help='Display the default category') |
|
||||
customer_rating = fields.Selection(RATING, default='0', readonly=True, |
|
||||
string='Customer Rating', |
|
||||
help='Display the customer rating.') |
|
||||
|
|
||||
review = fields.Char(string='Review', readonly=True, |
|
||||
help='Customer review of the ticket.') |
|
||||
kanban_state = fields.Selection([ |
|
||||
('normal', 'Ready'), |
|
||||
('done', 'In Progress'), |
|
||||
('blocked', 'Blocked'), ], default='normal') |
|
||||
|
|
||||
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 value of the 'show_create_task' field for each record in |
|
||||
the current recordset.""" |
|
||||
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 based on the closing date.""" |
|
||||
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['help.ticket'].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): |
|
||||
"""Search your stage""" |
|
||||
return self.env['ticket.stage'].search( |
|
||||
[('name', '=', 'Draft')], limit=1).id |
|
||||
|
|
||||
@api.model |
|
||||
def _read_group_stage_ids(self, stages, domain, order): |
|
||||
""" |
|
||||
Return the available stages for grouping. |
|
||||
|
|
||||
This static method is used to provide the available stages for |
|
||||
grouping when displaying records in a grouped view. |
|
||||
|
|
||||
""" |
|
||||
stage_ids = self.env['ticket.stage'].search([]) |
|
||||
return stage_ids |
|
||||
|
|
||||
@api.model |
|
||||
def create(self, vals_list): |
|
||||
"""Create a new helpdesk ticket. |
|
||||
This method is called when creating a new helpdesk ticket. It |
|
||||
generates a unique name for the ticket using a sequence if no |
|
||||
name is provided. |
|
||||
""" |
|
||||
if vals_list.get('name', _('New')) == _('New'): |
|
||||
vals_list['name'] = self.env['ir.sequence'].next_by_code( |
|
||||
'help.ticket') or _('New') |
|
||||
return super().create(vals_list) |
|
||||
|
|
||||
def action_create_invoice(self): |
|
||||
"""Create Invoice for Help Desk Ticket. |
|
||||
This function creates an invoice for the help desk ticket based on |
|
||||
the associated tasks with billed hours. |
|
||||
""" |
|
||||
tasks = self.env['project.task'].search( |
|
||||
[('project_id', '=', self.project_id.id), |
|
||||
('ticket_id', '=', self.id)]).filtered( |
|
||||
lambda line: line.ticket_billed == True) |
|
||||
if not tasks: |
|
||||
raise UserError('No Tasks to Bill') |
|
||||
total = sum(x.effective_hours for x in tasks if x.effective_hours > 0) |
|
||||
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): |
|
||||
"""Create Task for HelpDesk Ticket |
|
||||
This function creates a task associated with the helpdesk ticket |
|
||||
and updates the task_ids field. |
|
||||
""" |
|
||||
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): |
|
||||
"""Smart Button of Task to view the Tasks of HelpDesk Ticket""" |
|
||||
return { |
|
||||
'name': 'Tasks', |
|
||||
'domain': [('ticket_id', '=', self.id)], |
|
||||
'res_model': 'project.task', |
|
||||
'view_id': False, |
|
||||
'view_mode': 'tree,form', |
|
||||
'type': 'ir.actions.act_window', |
|
||||
} |
|
||||
|
|
||||
def action_open_invoices(self): |
|
||||
"""Smart Button of Invoice to view the Invoices for HelpDesk Ticket""" |
|
||||
return { |
|
||||
'name': 'Invoice', |
|
||||
'domain': [('ticket_id', '=', self.id)], |
|
||||
'res_model': 'account.move', |
|
||||
'view_id': False, |
|
||||
'view_mode': 'tree,form', |
|
||||
'type': 'ir.actions.act_window', |
|
||||
} |
|
||||
|
|
||||
def action_open_merged_tickets(self): |
|
||||
""" Smart button of the merged tickets""" |
|
||||
ticket_ids = self.env['support.tickets'].search( |
|
||||
[('merged_ticket', '=', self.id)]) |
|
||||
# Get the display_name matching records from the support.tickets |
|
||||
helpdesk_ticket_ids = ticket_ids.mapped('display_name') |
|
||||
# Get the IDs of the help.ticket records matching the display names |
|
||||
help_ticket_records = self.env['help.ticket'].search( |
|
||||
[('name', 'in', helpdesk_ticket_ids)]) |
|
||||
return { |
|
||||
'type': 'ir.actions.act_window', |
|
||||
'name': 'Helpdesk Ticket', |
|
||||
'view_mode': 'tree,form', |
|
||||
'res_model': 'help.ticket', |
|
||||
'domain': [('id', 'in', help_ticket_records.ids)], |
|
||||
'context': self.env.context, |
|
||||
} |
|
||||
|
|
||||
def action_send_reply(self): |
|
||||
"""Compose and send a reply to the customer. |
|
||||
This function opens a window for composing and sending a reply to |
|
||||
the customer. It uses the configured email template for replies. |
|
||||
""" |
|
||||
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': 'help.ticket', |
|
||||
'default_res_id': self.id, |
|
||||
'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': 'help.ticket', |
|
||||
'default_res_id': self.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, |
||||
|
} |
||||
|
} |
@ -1,110 +0,0 @@ |
|||||
<odoo> |
|
||||
<!-- Pdf report template--> |
|
||||
<template id="report_helpdesk_ticket"> |
|
||||
<t t-call="web.html_container"> |
|
||||
<t t-foreach="help" 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> |
|
||||
<h3 t-field="o.description"/> |
|
||||
</td> |
|
||||
<td> |
|
||||
<h3 t-field="o.priority"/> |
|
||||
</td> |
|
||||
<td> |
|
||||
<h3 t-field="o.product_ids"/> |
|
||||
</td> |
|
||||
</tr> |
|
||||
</tbody> |
|
||||
</table> |
|
||||
</div> |
|
||||
<t t-set="tasks" |
|
||||
t-value="request.env['project.task'].sudo().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> |
|
||||
<!-- Pdf report action--> |
|
||||
<record id="action_report_helpdesk_ticket" model="ir.actions.report"> |
|
||||
<field name="name">Helpdesk Ticket Report</field> |
|
||||
<field name="model">help.ticket</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_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> |
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: 929 B |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 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: 1.2 KiB |
Before Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 600 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: 1.2 KiB |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
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 |
Before Width: | Height: | Size: 3.8 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: 1.1 KiB |
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 589 B |
Before Width: | Height: | Size: 3.4 KiB |