helpdesk_portal.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo import http
  4. from odoo.http import request
  5. from odoo.addons.helpdesk.controllers.portal import (
  6. CustomerPortal as HelpdeskCustomerPortal,
  7. )
  8. from odoo.exceptions import AccessError, MissingError
  9. class CustomerPortal(HelpdeskCustomerPortal):
  10. """Extend CustomerPortal to show tickets block for collaborators without ticket count condition"""
  11. def _prepare_home_portal_values(self, counters):
  12. values = super()._prepare_home_portal_values(counters)
  13. if "ticket_count" in counters:
  14. # Check if user is portal and is a collaborator in any team
  15. if request.env.user and request.env.user._is_portal():
  16. partner = request.env.user.partner_id.commercial_partner_id
  17. # Check if user is a collaborator in any helpdesk team
  18. is_collaborator = (
  19. request.env["helpdesk.team.collaborator"]
  20. .sudo()
  21. .search_count([("partner_id", "=", partner.id)])
  22. > 0
  23. )
  24. if is_collaborator:
  25. # If user is collaborator, ensure block is always shown
  26. # by setting ticket_count to at least 1
  27. # This removes the ticket count condition for collaborators
  28. current_count = values.get("ticket_count", 0)
  29. if current_count == 0:
  30. # Force ticket_count to 1 so the block is always shown for collaborators
  31. values["ticket_count"] = 1
  32. # If not collaborator or not portal, use default behavior
  33. # (values already calculated by parent)
  34. return values
  35. def _prepare_my_tickets_values(
  36. self,
  37. page=1,
  38. date_begin=None,
  39. date_end=None,
  40. sortby=None,
  41. filterby="all",
  42. search=None,
  43. groupby="none",
  44. search_in="name",
  45. ):
  46. values = super()._prepare_my_tickets_values(
  47. page, date_begin, date_end, sortby, filterby, search, groupby, search_in
  48. )
  49. # Check if user is portal and is a collaborator in any team
  50. if request.env.user and request.env.user._is_portal():
  51. partner = request.env.user.partner_id
  52. commercial_partner = partner.commercial_partner_id
  53. # Get teams where user is a collaborator (try both partner_id and commercial_partner_id)
  54. collaborator_records = (
  55. request.env["helpdesk.team.collaborator"]
  56. .sudo()
  57. .search(
  58. [
  59. "|",
  60. ("partner_id", "=", partner.id),
  61. ("partner_id", "=", commercial_partner.id),
  62. ]
  63. )
  64. )
  65. # Get teams where user is a collaborator and has website form enabled
  66. collaborator_teams = collaborator_records.mapped("team_id").filtered(
  67. lambda t: t.use_website_helpdesk_form
  68. )
  69. # DEBUG: Log para verificar qué está pasando
  70. import logging
  71. _logger = logging.getLogger(__name__)
  72. _logger.info(
  73. f"DEBUG: User {request.env.user.name} (partner_id={partner.id}, commercial_partner_id={commercial_partner.id})"
  74. )
  75. _logger.info(
  76. f"DEBUG: Found {len(collaborator_records)} collaborator records"
  77. )
  78. _logger.info(
  79. f"DEBUG: Found {len(collaborator_teams)} teams with website form enabled"
  80. )
  81. # Filter by website published for portal users (managers see all)
  82. # TEMPORALMENTE: Comentado para debug - verificar si el problema es el filtro
  83. # if not request.env.user.has_group("helpdesk.group_helpdesk_manager"):
  84. # collaborator_teams = collaborator_teams.filtered(
  85. # lambda t: t.website_published and t.website_id == request.website
  86. # )
  87. # If user is collaborator in teams with website form, get first available team
  88. _logger.info(
  89. f"DEBUG: collaborator_teams length = {len(collaborator_teams)}"
  90. )
  91. _logger.info(f"DEBUG: collaborator_teams ids = {collaborator_teams.ids}")
  92. if collaborator_teams:
  93. # Get the first team (can be improved to select based on priority)
  94. available_team = collaborator_teams[0]
  95. # Build URL to the team form with contact_form parameter
  96. team_url = available_team.website_url
  97. team_url += "/?contact_form=1"
  98. values["collaborator_team"] = available_team
  99. values["collaborator_team_form_url"] = team_url
  100. _logger.info(f"DEBUG: Set collaborator_team_form_url = {team_url}")
  101. _logger.info(
  102. f"DEBUG: Team name = {available_team.name}, website_url = {available_team.website_url}"
  103. )
  104. else:
  105. # Always set the value, even if False, for template debugging
  106. _logger.info(
  107. "DEBUG: No collaborator teams found, setting form_url to False"
  108. )
  109. values["collaborator_team_form_url"] = False
  110. # Get teams where user is admin for "Manage Collaborators" button
  111. admin_teams = self._get_teams_where_admin()
  112. values["admin_teams"] = admin_teams
  113. _logger.info(f"DEBUG: admin_teams count = {len(admin_teams)}")
  114. for team in admin_teams:
  115. _logger.info(f"DEBUG: admin team = {team.name} (ID: {team.id})")
  116. else:
  117. # Not portal user
  118. values["collaborator_team_form_url"] = False
  119. values["admin_teams"] = request.env['helpdesk.team']
  120. return values
  121. def _get_teams_where_admin(self):
  122. """Get teams where current user is admin collaborator"""
  123. if not request.env.user or not request.env.user._is_portal():
  124. return request.env['helpdesk.team']
  125. partner = request.env.user.partner_id
  126. collaborator_records = request.env['helpdesk.team.collaborator'].search([
  127. ('partner_id', '=', partner.id),
  128. ('access_mode', '=', 'admin'),
  129. ])
  130. return collaborator_records.mapped('team_id')
  131. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators'], type='http', auth="user", website=True)
  132. def portal_team_collaborators(self, team_id=None, **kw):
  133. """Page to manage collaborators for a team (only for admin users)"""
  134. from odoo import _
  135. team = request.env['helpdesk.team'].browse([team_id])
  136. if not team.exists():
  137. raise MissingError(_("This team does not exist."))
  138. # Check if user is admin of this team
  139. admin_teams = self._get_teams_where_admin()
  140. if team not in admin_teams:
  141. raise AccessError(_("You don't have permission to manage collaborators for this team."))
  142. # Get admin's commercial_partner_id to filter available partners
  143. admin_user = request.env.user
  144. admin_commercial_partner_id = admin_user.partner_id.commercial_partner_id.id
  145. # Get all collaborators of the team (use sudo() to bypass security rules for admin users)
  146. # Filter to only show collaborators from the same network
  147. all_collaborators = team.sudo().collaborator_ids
  148. collaborators = all_collaborators.filtered(
  149. lambda c: c.partner_id.commercial_partner_id.id == admin_commercial_partner_id
  150. ).sorted(lambda c: c.partner_id.name or '')
  151. values = {
  152. 'team': team,
  153. 'collaborators': collaborators,
  154. 'page_name': 'team_collaborators',
  155. 'admin_commercial_partner_id': admin_commercial_partner_id,
  156. 'current_partner_id': admin_user.partner_id.id,
  157. }
  158. return request.render('helpdesk_extras.portal_team_collaborators', values)
  159. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/new'], type='http', auth="user", website=True)
  160. def portal_new_collaborator(self, team_id=None, **kw):
  161. """Page to create a new contact and add as collaborator"""
  162. from odoo import _
  163. team = request.env['helpdesk.team'].browse([team_id])
  164. if not team.exists():
  165. raise MissingError(_("This team does not exist."))
  166. # Check if user is admin of this team
  167. admin_teams = self._get_teams_where_admin()
  168. if team not in admin_teams:
  169. raise AccessError(_("You don't have permission to manage collaborators for this team."))
  170. values = {
  171. 'team': team,
  172. 'page_name': 'new_collaborator',
  173. }
  174. return request.render('helpdesk_extras.portal_new_collaborator', values)
  175. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/add'], type='http', auth="user", website=True, methods=['POST'], csrf=True)
  176. def portal_add_collaborator(self, team_id=None, partner_id=None, access_mode='user_own', **kw):
  177. """Add a new collaborator to the team (only for admin users)"""
  178. from odoo import _
  179. from odoo.exceptions import ValidationError
  180. team = request.env['helpdesk.team'].browse([team_id])
  181. if not team.exists():
  182. request.session['error'] = _("This team does not exist.")
  183. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  184. # Check if user is admin of this team
  185. admin_teams = self._get_teams_where_admin()
  186. if team not in admin_teams:
  187. request.session['error'] = _("You don't have permission to manage collaborators for this team.")
  188. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  189. # Get admin's commercial_partner_id
  190. admin_user = request.env.user
  191. admin_commercial_partner_id = admin_user.partner_id.commercial_partner_id.id
  192. # Validate partner_id
  193. if not partner_id:
  194. request.session['error'] = _("Partner is required.")
  195. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  196. try:
  197. partner_id = int(partner_id)
  198. partner = request.env['res.partner'].sudo().browse([partner_id])
  199. if not partner.exists():
  200. request.session['error'] = _("Partner does not exist.")
  201. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  202. # Validate partner belongs to same network
  203. if partner.commercial_partner_id.id != admin_commercial_partner_id:
  204. request.session['error'] = _("You can only add contacts from your company network.")
  205. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  206. # Validate partner is external
  207. if not partner.partner_share:
  208. request.session['error'] = _("Only external partners can be added as collaborators.")
  209. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  210. # Validate access_mode
  211. if access_mode not in ['admin', 'user_all', 'user_own']:
  212. access_mode = 'user_own'
  213. # Check if collaborator already exists
  214. existing = team.sudo().collaborator_ids.filtered(lambda c: c.partner_id.id == partner_id)
  215. if existing:
  216. request.session['error'] = _("This partner is already a collaborator.")
  217. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  218. # Create collaborator
  219. team.sudo().write({
  220. 'collaborator_ids': [(0, 0, {
  221. 'partner_id': partner_id,
  222. 'access_mode': access_mode,
  223. })]
  224. })
  225. # Subscribe partner to team messages
  226. team.sudo().message_subscribe(partner_ids=[partner_id])
  227. request.session['success'] = _("Collaborator added successfully.")
  228. except (ValueError, TypeError, ValidationError) as e:
  229. request.session['error'] = str(e)
  230. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  231. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/<int:collaborator_id>/update'], type='http', auth="user", website=True, methods=['POST'], csrf=True)
  232. def portal_update_collaborator(self, team_id=None, collaborator_id=None, access_mode=None, **kw):
  233. """Update a collaborator's access mode (only for admin users)"""
  234. from odoo import _
  235. team = request.env['helpdesk.team'].browse([team_id])
  236. if not team.exists():
  237. request.session['error'] = _("This team does not exist.")
  238. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  239. # Check if user is admin of this team
  240. admin_teams = self._get_teams_where_admin()
  241. if team not in admin_teams:
  242. request.session['error'] = _("You don't have permission to manage collaborators for this team.")
  243. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  244. # Get admin's commercial_partner_id
  245. admin_user = request.env.user
  246. admin_commercial_partner_id = admin_user.partner_id.commercial_partner_id.id
  247. # Find collaborator
  248. collaborator = team.sudo().collaborator_ids.filtered(lambda c: c.id == collaborator_id)
  249. if not collaborator:
  250. request.session['error'] = _("Collaborator not found.")
  251. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  252. # Validate collaborator belongs to same network
  253. if collaborator.partner_id.commercial_partner_id.id != admin_commercial_partner_id:
  254. request.session['error'] = _("You can only manage contacts from your company network.")
  255. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  256. # Validate access_mode
  257. if access_mode and access_mode in ['admin', 'user_all', 'user_own']:
  258. collaborator.sudo().write({'access_mode': access_mode})
  259. request.session['success'] = _("Collaborator updated successfully.")
  260. else:
  261. request.session['error'] = _("Invalid access mode.")
  262. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  263. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/<int:collaborator_id>/delete'], type='http', auth="user", website=True, methods=['POST'], csrf=True)
  264. def portal_delete_collaborator(self, team_id=None, collaborator_id=None, **kw):
  265. """Delete a collaborator from the team (only for admin users)"""
  266. from odoo import _
  267. team = request.env['helpdesk.team'].browse([team_id])
  268. if not team.exists():
  269. request.session['error'] = _("This team does not exist.")
  270. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  271. # Check if user is admin of this team
  272. admin_teams = self._get_teams_where_admin()
  273. if team not in admin_teams:
  274. request.session['error'] = _("You don't have permission to manage collaborators for this team.")
  275. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  276. # Get admin's commercial_partner_id
  277. admin_user = request.env.user
  278. admin_commercial_partner_id = admin_user.partner_id.commercial_partner_id.id
  279. # Find collaborator
  280. collaborator = team.sudo().collaborator_ids.filtered(lambda c: c.id == collaborator_id)
  281. if not collaborator:
  282. request.session['error'] = _("Collaborator not found.")
  283. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  284. # Validate collaborator belongs to same network
  285. if collaborator.partner_id.commercial_partner_id.id != admin_commercial_partner_id:
  286. request.session['error'] = _("You can only manage contacts from your company network.")
  287. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  288. # Prevent self-deletion
  289. current_partner = admin_user.partner_id
  290. if collaborator.partner_id.id == current_partner.id:
  291. request.session['error'] = _("You cannot remove yourself as a collaborator.")
  292. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  293. # Delete collaborator
  294. collaborator.sudo().unlink()
  295. request.session['success'] = _("Collaborator removed successfully.")
  296. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  297. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/search-partners'], type='json', auth="user", website=True, methods=['POST'], csrf=False)
  298. def portal_search_partners(self, team_id=None, search_term='', limit=20, **kw):
  299. """Search partners in the same commercial_partner_id network"""
  300. from odoo import _
  301. team = request.env['helpdesk.team'].browse([team_id])
  302. if not team.exists():
  303. return {'error': _("This team does not exist.")}
  304. # Check if user is admin of this team
  305. admin_teams = self._get_teams_where_admin()
  306. if team not in admin_teams:
  307. return {'error': _("You don't have permission to manage collaborators for this team.")}
  308. # Get admin's commercial_partner_id
  309. admin_user = request.env.user
  310. admin_partner = admin_user.partner_id
  311. admin_commercial_partner = admin_partner.commercial_partner_id
  312. # Search partners in the same network
  313. # Use 'id' with 'child_of' to find all contacts in the commercial partner network
  314. # This recursively finds:
  315. # - The commercial partner itself
  316. # - All child contacts (using parent_id relationship)
  317. # This is the standard Odoo way to find all related contacts
  318. domain = [
  319. ('id', 'child_of', admin_commercial_partner.id),
  320. ('partner_share', '=', True),
  321. '|',
  322. ('name', 'ilike', search_term),
  323. ('email', 'ilike', search_term),
  324. ]
  325. # Also exclude contacts that are already collaborators in this team
  326. existing_collaborator_ids = team.sudo().collaborator_ids.mapped('partner_id').ids
  327. if existing_collaborator_ids:
  328. domain.append(('id', 'not in', existing_collaborator_ids))
  329. partners = request.env['res.partner'].sudo().search(domain, limit=limit)
  330. # Debug logging
  331. import logging
  332. _logger = logging.getLogger(__name__)
  333. _logger.info(f"Search partners - term: '{search_term}', found: {len(partners)}")
  334. for p in partners:
  335. _logger.info(f" - {p.name} (ID: {p.id}, email: {p.email})")
  336. result = {
  337. 'partners': [
  338. {
  339. 'id': p.id,
  340. 'name': p.name,
  341. 'email': p.email or '',
  342. 'display_name': p.display_name,
  343. }
  344. for p in partners
  345. ]
  346. }
  347. _logger.info(f"Returning result: {len(result['partners'])} partners")
  348. return result
  349. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/create'], type='http', auth="user", website=True, methods=['POST'], csrf=True)
  350. def portal_create_collaborator(self, team_id=None, **kw):
  351. """Create a new contact and add as collaborator in one step"""
  352. from odoo import _
  353. from odoo.exceptions import ValidationError
  354. from odoo.tools import email_normalize
  355. team = request.env['helpdesk.team'].browse([team_id])
  356. if not team.exists():
  357. request.session['error'] = _("This team does not exist.")
  358. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  359. # Check if user is admin of this team
  360. admin_teams = self._get_teams_where_admin()
  361. if team not in admin_teams:
  362. request.session['error'] = _("You don't have permission to manage collaborators for this team.")
  363. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  364. # Get form data
  365. name = kw.get('name', '').strip()
  366. email = kw.get('email', '').strip()
  367. phone = kw.get('phone', '').strip()
  368. function_ = kw.get('function', '').strip()
  369. access_mode = kw.get('access_mode', 'user_own')
  370. # Validate inputs
  371. if not name:
  372. request.session['error'] = _("Name is required.")
  373. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators/new')
  374. if not email:
  375. request.session['error'] = _("Email is required.")
  376. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators/new')
  377. # Validate and normalize email
  378. email_normalized = email_normalize(email)
  379. if not email_normalized:
  380. request.session['error'] = _("Invalid email address format.")
  381. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators/new')
  382. # Validate access_mode
  383. if access_mode not in ['admin', 'user_all', 'user_own']:
  384. access_mode = 'user_own'
  385. # Check if email already exists
  386. existing_partner = request.env['res.partner'].sudo().search([
  387. ('email_normalized', '=', email_normalized)
  388. ], limit=1)
  389. if existing_partner:
  390. # Check if already a collaborator
  391. existing_collaborator = team.sudo().collaborator_ids.filtered(
  392. lambda c: c.partner_id.id == existing_partner.id
  393. )
  394. if existing_collaborator:
  395. request.session['error'] = _("This contact is already a collaborator in this team.")
  396. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  397. # Use existing partner
  398. partner = existing_partner
  399. else:
  400. # Get admin's commercial_partner_id
  401. admin_user = request.env.user
  402. admin_commercial_partner = admin_user.partner_id.commercial_partner_id
  403. # Create new partner
  404. try:
  405. partner_vals = {
  406. 'name': name,
  407. 'email': email_normalized,
  408. 'partner_share': True,
  409. 'type': 'contact',
  410. 'parent_id': admin_commercial_partner.id,
  411. }
  412. if phone:
  413. partner_vals['phone'] = phone
  414. if function_:
  415. partner_vals['function'] = function_
  416. partner = request.env['res.partner'].sudo().create(partner_vals)
  417. # Log creation
  418. import logging
  419. _logger = logging.getLogger(__name__)
  420. _logger.info(f"Created new partner: {partner.name} (ID: {partner.id}) by admin {admin_user.name}")
  421. except Exception as e:
  422. import logging
  423. _logger = logging.getLogger(__name__)
  424. _logger.error(f"Error creating partner: {str(e)}", exc_info=True)
  425. request.session['error'] = _("An error occurred while creating the contact. Please try again.")
  426. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators/new')
  427. # Add as collaborator
  428. try:
  429. # Check if already a collaborator
  430. existing_collaborator = team.sudo().collaborator_ids.filtered(
  431. lambda c: c.partner_id.id == partner.id
  432. )
  433. if existing_collaborator:
  434. # Update access mode
  435. existing_collaborator.sudo().write({'access_mode': access_mode})
  436. request.session['success'] = _("Collaborator updated successfully.")
  437. else:
  438. # Create new collaborator
  439. team.sudo().write({
  440. 'collaborator_ids': [(0, 0, {
  441. 'partner_id': partner.id,
  442. 'access_mode': access_mode,
  443. })]
  444. })
  445. # Subscribe partner to team messages
  446. team.sudo().message_subscribe(partner_ids=[partner.id])
  447. request.session['success'] = _("Contact created and added as collaborator successfully.")
  448. except Exception as e:
  449. import logging
  450. _logger = logging.getLogger(__name__)
  451. _logger.error(f"Error adding collaborator: {str(e)}", exc_info=True)
  452. request.session['error'] = _("An error occurred while adding the collaborator. Please try again.")
  453. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators/new')
  454. return request.redirect(f'/my/helpdesk/teams/{team_id}/collaborators')
  455. @http.route(['/my/helpdesk/teams/<int:team_id>/collaborators/create-partner'], type='json', auth="user", website=True, methods=['POST'], csrf=False)
  456. def portal_create_partner(self, team_id=None, name='', email='', phone='', function='', **kw):
  457. """Create a new partner in the same commercial_partner_id network"""
  458. from odoo import _
  459. from odoo.exceptions import ValidationError
  460. from odoo.tools import email_normalize
  461. team = request.env['helpdesk.team'].browse([team_id])
  462. if not team.exists():
  463. return {'error': _("This team does not exist.")}
  464. # Check if user is admin of this team
  465. admin_teams = self._get_teams_where_admin()
  466. if team not in admin_teams:
  467. return {'error': _("You don't have permission to manage collaborators for this team.")}
  468. # Validate inputs
  469. if not name or not name.strip():
  470. return {'error': _("Name is required.")}
  471. name = name.strip()
  472. # Validate and normalize email if provided
  473. email_normalized = None
  474. if email and email.strip():
  475. email = email.strip()
  476. email_normalized = email_normalize(email)
  477. if not email_normalized:
  478. return {'error': _("Invalid email address format.")}
  479. # Check if email already exists
  480. existing_partner = request.env['res.partner'].sudo().search([
  481. ('email_normalized', '=', email_normalized)
  482. ], limit=1)
  483. if existing_partner:
  484. return {
  485. 'error': _("A contact with email %s already exists.") % email,
  486. 'existing_partner_id': existing_partner.id,
  487. 'existing_partner_name': existing_partner.name,
  488. }
  489. # Get admin's commercial_partner_id
  490. admin_user = request.env.user
  491. admin_commercial_partner = admin_user.partner_id.commercial_partner_id
  492. # Create new partner
  493. try:
  494. partner_vals = {
  495. 'name': name,
  496. 'partner_share': True,
  497. 'type': 'contact',
  498. }
  499. if email_normalized:
  500. partner_vals['email'] = email_normalized
  501. if phone and phone.strip():
  502. partner_vals['phone'] = phone.strip()
  503. if function and function.strip():
  504. partner_vals['function'] = function.strip()
  505. # Set parent to commercial_partner (creates as child contact)
  506. partner_vals['parent_id'] = admin_commercial_partner.id
  507. partner = request.env['res.partner'].sudo().create(partner_vals)
  508. # Log creation
  509. import logging
  510. _logger = logging.getLogger(__name__)
  511. _logger.info(f"Created new partner: {partner.name} (ID: {partner.id}) by admin {admin_user.name}")
  512. return {
  513. 'partner': {
  514. 'id': partner.id,
  515. 'name': partner.name,
  516. 'email': partner.email or '',
  517. 'display_name': partner.display_name,
  518. }
  519. }
  520. except ValidationError as e:
  521. return {'error': str(e)}
  522. except Exception as e:
  523. import logging
  524. _logger = logging.getLogger(__name__)
  525. _logger.error(f"Error creating partner: {str(e)}", exc_info=True)
  526. return {'error': _("An error occurred while creating the contact. Please try again.")}
  527. @http.route(['/my/ticket/<int:ticket_id>/approve'], type='http', auth='public', methods=['POST'], website=True, csrf=True)
  528. def ticket_approve(self, ticket_id, access_token=None, **kwargs):
  529. """Approve ticket by customer"""
  530. from odoo import _, fields
  531. try:
  532. ticket_sudo = self._document_check_access('helpdesk.ticket', ticket_id, access_token)
  533. except (AccessError, MissingError):
  534. return request.redirect('/my')
  535. # Approve
  536. ticket_sudo.write({
  537. 'customer_approval_status': 'approved',
  538. })
  539. # Message in chatter
  540. ticket_sudo.message_post(
  541. body=_("Ticket approved by customer"),
  542. message_type='notification',
  543. subtype_xmlid='mail.mt_comment',
  544. )
  545. return request.redirect(ticket_sudo.get_portal_url() + '?message=approved')
  546. @http.route(['/my/ticket/<int:ticket_id>/reject'], type='http', auth='public', methods=['POST'], website=True, csrf=True)
  547. def ticket_reject(self, ticket_id, access_token=None, reason=None, **kwargs):
  548. """Reject ticket by customer"""
  549. from odoo import _, fields
  550. try:
  551. ticket_sudo = self._document_check_access('helpdesk.ticket', ticket_id, access_token)
  552. except (AccessError, MissingError):
  553. return request.redirect('/my')
  554. # Reject
  555. ticket_sudo.write({
  556. 'customer_approval_status': 'rejected',
  557. 'customer_rejection_reason': reason or _('No reason provided'),
  558. })
  559. # Message in chatter
  560. rejection_msg = reason or _('No reason provided')
  561. ticket_sudo.message_post(
  562. body=_("Ticket rejected by customer: %s") % rejection_msg,
  563. message_type='comment',
  564. subtype_xmlid='mail.mt_comment',
  565. )
  566. return request.redirect(ticket_sudo.get_portal_url() + '?message=rejected')