|
| 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) |
0 commit comments