Skip to content

Commit 085e5b1

Browse files
author
John75SunCity
committed
feat: Portal cleanup - add features controller, remove duplicates, add ACL
- Add portal_features.py controller with routes for: - Mobile interface (/my/mobile/*) - E-learning/Training (/my/training/*) - Compliance/Audit (/my/compliance/*) - Profile/Settings (/my/profile, /my/settings) - Global search (/my/search) - Support tickets (/my/support) - Remove 8 duplicate templates: - container_detail (portal_containers.xml) - portal_container_form (portal_containers.xml) - location_detail (portal_locations.xml) - portal_location_detail (portal_locations.xml) - portal_document_retrieval (portal_document_retrieval.xml) - portal_feedback_form (portal_missing_templates.xml) - portal_requests (my_portal_inventory.xml) - portal_custody_log (portal_records_templates.xml) - Add ACL for portal users: - records_document (read access) - records_document_type (read access)
1 parent 740d258 commit 085e5b1

File tree

10 files changed

+559
-586
lines changed

10 files changed

+559
-586
lines changed

records_management/controllers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@
3434
from . import portal_access # portal account access for admin users
3535
from . import portal_calendar # portal service calendar (shredding, pickups, retrievals)
3636
from . import destruction_portal # destruction & shredding dashboard
37+
from . import portal_features # mobile, e-learning, compliance features
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Portal Additional Features Controller
4+
Provides routes for Mobile, E-learning, and Compliance features
5+
"""
6+
7+
from odoo import http, fields, _
8+
from odoo.http import request
9+
from odoo.addons.portal.controllers.portal import CustomerPortal
10+
11+
12+
class PortalFeaturesController(CustomerPortal):
13+
"""
14+
Controller for additional portal features:
15+
- Mobile interface
16+
- E-learning/Training
17+
- Compliance/Audit
18+
"""
19+
20+
# ============================================================================
21+
# MOBILE INTERFACE ROUTES
22+
# ============================================================================
23+
24+
@http.route(['/my/mobile'], type='http', auth="user", website=True)
25+
def portal_mobile_dashboard(self, **kw):
26+
"""Mobile-optimized dashboard"""
27+
values = self._prepare_portal_layout_values()
28+
29+
partner = request.env.user.partner_id
30+
31+
# Get summary counts for mobile dashboard
32+
Container = request.env['records.container'].sudo()
33+
Request = request.env['portal.request'].sudo()
34+
35+
values.update({
36+
'container_count': Container.search_count([('partner_id', '=', partner.id)]),
37+
'pending_requests': Request.search_count([
38+
('partner_id', '=', partner.id),
39+
('state', 'in', ['draft', 'submitted', 'in_progress'])
40+
]),
41+
'page_name': 'mobile',
42+
})
43+
44+
return request.render('records_management.portal_mobile_template', values)
45+
46+
@http.route(['/my/mobile/inventory'], type='http', auth="user", website=True)
47+
def portal_mobile_inventory(self, **kw):
48+
"""Mobile inventory browser"""
49+
values = self._prepare_portal_layout_values()
50+
51+
partner = request.env.user.partner_id
52+
Container = request.env['records.container'].sudo()
53+
54+
containers = Container.search([
55+
('partner_id', '=', partner.id)
56+
], limit=50, order='write_date desc')
57+
58+
values.update({
59+
'containers': containers,
60+
'page_name': 'mobile_inventory',
61+
})
62+
63+
return request.render('records_management.portal_mobile_inventory_browser', values)
64+
65+
@http.route(['/my/mobile/settings'], type='http', auth="user", website=True)
66+
def portal_mobile_settings(self, **kw):
67+
"""Mobile settings page"""
68+
values = self._prepare_portal_layout_values()
69+
70+
values.update({
71+
'page_name': 'mobile_settings',
72+
})
73+
74+
return request.render('records_management.portal_mobile_settings', values)
75+
76+
@http.route(['/my/mobile/export'], type='http', auth="user", website=True)
77+
def portal_mobile_export(self, **kw):
78+
"""Mobile export interface"""
79+
values = self._prepare_portal_layout_values()
80+
81+
values.update({
82+
'page_name': 'mobile_export',
83+
})
84+
85+
return request.render('records_management.portal_mobile_export', values)
86+
87+
@http.route(['/my/mobile/work-order/create'], type='http', auth="user", website=True)
88+
def portal_mobile_work_order_create(self, **kw):
89+
"""Mobile work order creation"""
90+
values = self._prepare_portal_layout_values()
91+
92+
partner = request.env.user.partner_id
93+
Container = request.env['records.container'].sudo()
94+
95+
values.update({
96+
'containers': Container.search([('partner_id', '=', partner.id)], limit=100),
97+
'page_name': 'mobile_work_order',
98+
})
99+
100+
return request.render('records_management.portal_mobile_work_order_create', values)
101+
102+
# ============================================================================
103+
# E-LEARNING / TRAINING ROUTES
104+
# ============================================================================
105+
106+
@http.route(['/my/training'], type='http', auth="user", website=True)
107+
def portal_training_dashboard(self, **kw):
108+
"""Training courses dashboard"""
109+
values = self._prepare_portal_layout_values()
110+
111+
# Get training courses if model exists
112+
ElearningCourse = request.env.get('elearning.course')
113+
courses = []
114+
115+
if ElearningCourse:
116+
courses = ElearningCourse.sudo().search([
117+
('active', '=', True),
118+
('is_published', '=', True)
119+
])
120+
121+
values.update({
122+
'courses': courses,
123+
'page_name': 'training',
124+
})
125+
126+
return request.render('records_management.portal_elearning_courses', values)
127+
128+
@http.route(['/my/training/course/<int:course_id>'], type='http', auth="user", website=True)
129+
def portal_training_course(self, course_id, **kw):
130+
"""Training course detail"""
131+
values = self._prepare_portal_layout_values()
132+
133+
ElearningCourse = request.env.get('elearning.course')
134+
course = None
135+
136+
if ElearningCourse:
137+
course = ElearningCourse.sudo().browse(course_id)
138+
139+
values.update({
140+
'course': course,
141+
'page_name': 'training_course',
142+
})
143+
144+
return request.render('records_management.portal_elearning_course', values)
145+
146+
@http.route(['/my/training/progress'], type='http', auth="user", website=True)
147+
def portal_training_progress(self, **kw):
148+
"""User's training progress"""
149+
values = self._prepare_portal_layout_values()
150+
151+
values.update({
152+
'page_name': 'training_progress',
153+
})
154+
155+
return request.render('records_management.portal_elearning_progress', values)
156+
157+
@http.route(['/my/training/certificates'], type='http', auth="user", website=True)
158+
def portal_training_certificates(self, **kw):
159+
"""User's training certificates"""
160+
values = self._prepare_portal_layout_values()
161+
162+
values.update({
163+
'page_name': 'training_certificates',
164+
})
165+
166+
return request.render('records_management.portal_training_certificates', values)
167+
168+
# ============================================================================
169+
# COMPLIANCE / AUDIT ROUTES
170+
# ============================================================================
171+
172+
@http.route(['/my/compliance'], type='http', auth="user", website=True)
173+
def portal_compliance_dashboard(self, **kw):
174+
"""Compliance status dashboard"""
175+
values = self._prepare_portal_layout_values()
176+
177+
partner = request.env.user.partner_id
178+
179+
# Get compliance-related data
180+
NaidCertificate = request.env['naid.certificate'].sudo()
181+
ChainOfCustody = request.env['chain.of.custody'].sudo()
182+
183+
certificates = NaidCertificate.search([
184+
('partner_id', '=', partner.id)
185+
], limit=10, order='create_date desc')
186+
187+
custody_records = ChainOfCustody.search([
188+
('partner_id', '=', partner.id)
189+
], limit=20, order='create_date desc')
190+
191+
values.update({
192+
'certificates': certificates,
193+
'custody_records': custody_records,
194+
'page_name': 'compliance',
195+
})
196+
197+
return request.render('records_management.portal_compliance_status', values)
198+
199+
@http.route(['/my/compliance/audit-logs'], type='http', auth="user", website=True)
200+
def portal_audit_logs(self, **kw):
201+
"""Audit logs for customer"""
202+
values = self._prepare_portal_layout_values()
203+
204+
partner = request.env.user.partner_id
205+
206+
# Get audit logs
207+
NaidAuditLog = request.env['naid.audit.log'].sudo()
208+
209+
logs = NaidAuditLog.search([
210+
('partner_id', '=', partner.id)
211+
], limit=50, order='create_date desc')
212+
213+
values.update({
214+
'audit_logs': logs,
215+
'page_name': 'audit_logs',
216+
})
217+
218+
return request.render('records_management.portal_audit_logs', values)
219+
220+
@http.route(['/my/compliance/custody-log'], type='http', auth="user", website=True)
221+
def portal_custody_log(self, **kw):
222+
"""Chain of custody log"""
223+
values = self._prepare_portal_layout_values()
224+
225+
partner = request.env.user.partner_id
226+
227+
ChainOfCustody = request.env['chain.of.custody'].sudo()
228+
229+
custody_records = ChainOfCustody.search([
230+
('partner_id', '=', partner.id)
231+
], limit=100, order='create_date desc')
232+
233+
values.update({
234+
'custody_records': custody_records,
235+
'page_name': 'custody_log',
236+
})
237+
238+
return request.render('records_management.portal_custody_log', values)
239+
240+
# ============================================================================
241+
# PROFILE / SETTINGS ROUTES
242+
# ============================================================================
243+
244+
@http.route(['/my/profile'], type='http', auth="user", website=True)
245+
def portal_profile(self, **kw):
246+
"""User profile page"""
247+
values = self._prepare_portal_layout_values()
248+
249+
values.update({
250+
'page_name': 'profile',
251+
})
252+
253+
return request.render('records_management.portal_profile', values)
254+
255+
@http.route(['/my/settings'], type='http', auth="user", website=True)
256+
def portal_settings(self, **kw):
257+
"""User settings page"""
258+
values = self._prepare_portal_layout_values()
259+
260+
values.update({
261+
'page_name': 'settings',
262+
})
263+
264+
return request.render('records_management.portal_settings', values)
265+
266+
@http.route(['/my/search'], type='http', auth="user", website=True)
267+
def portal_search_results(self, q='', **kw):
268+
"""Global search results"""
269+
values = self._prepare_portal_layout_values()
270+
271+
partner = request.env.user.partner_id
272+
results = []
273+
274+
if q and len(q) >= 3:
275+
# Search containers
276+
Container = request.env['records.container'].sudo()
277+
containers = Container.search([
278+
('partner_id', '=', partner.id),
279+
'|', '|',
280+
('name', 'ilike', q),
281+
('barcode', 'ilike', q),
282+
('description', 'ilike', q)
283+
], limit=20)
284+
285+
for c in containers:
286+
results.append({
287+
'type': 'container',
288+
'name': c.name,
289+
'description': c.description or '',
290+
'url': '/my/containers/%s' % c.id,
291+
})
292+
293+
# Search documents
294+
Document = request.env['records.document'].sudo()
295+
documents = Document.search([
296+
('partner_id', '=', partner.id),
297+
'|',
298+
('name', 'ilike', q),
299+
('barcode', 'ilike', q)
300+
], limit=20)
301+
302+
for d in documents:
303+
results.append({
304+
'type': 'document',
305+
'name': d.name,
306+
'description': d.document_type_id.name if d.document_type_id else '',
307+
'url': '/my/documents/%s' % d.id,
308+
})
309+
310+
values.update({
311+
'query': q,
312+
'results': results,
313+
'page_name': 'search',
314+
})
315+
316+
return request.render('records_management.portal_search_results', values)
317+
318+
# ============================================================================
319+
# SUPPORT ROUTES
320+
# ============================================================================
321+
322+
@http.route(['/my/support'], type='http', auth="user", website=True)
323+
def portal_support_tickets(self, **kw):
324+
"""Support tickets list"""
325+
values = self._prepare_portal_layout_values()
326+
327+
values.update({
328+
'page_name': 'support',
329+
})
330+
331+
return request.render('records_management.portal_support_tickets', values)

records_management/security/ir.model.access.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,8 @@ access_records_container_portal,records_container_portal,model_records_container
673673
access_records_location_portal,records_location_portal,stock.model_stock_location,base.group_portal,1,0,0,0
674674
access_records_department_portal,records_department_portal,model_records_department,base.group_portal,1,0,0,0
675675
access_customer_feedback_portal,customer_feedback_portal,model_customer_feedback,base.group_portal,1,1,1,0
676+
access_records_document_portal,records_document_portal,model_records_document,base.group_portal,1,0,0,0
677+
access_records_document_type_portal,records_document_type_portal,model_records_document_type,base.group_portal,1,0,0,0
676678
access_portal_access_log_manager,portal_access_log_manager,model_portal_access_log,records_management.group_records_manager,1,1,1,1
677679
access_portal_access_log_admin,portal_access_log_manager_admin,model_portal_access_log,records_management.group_records_admin,1,1,1,1
678680
access_portal_access_log_user,portal_access_log_user,model_portal_access_log,records_management.group_records_user,1,0,0,0

0 commit comments

Comments
 (0)